分布式文件存储系统FastDFS

文章目录

  • 1 分布式文件存储
    • 1_分布式文件存储的由来
    • 2_常见的分布式存储框架
  • 2 FastDFS介绍
  • 3 FastDFS安装
    • 1_拉取镜像文件
    • 2_构建Tracker服务
    • 3_构建Storage服务
    • 4_测试图片上传
  • 4 客户端操作
    • 1_Fastdfs-java-client
    • 2_文件上传
    • 3_文件下载
    • 4_获取文件信息
    • 5_问题
  • 5 SpringBoot整合

1 分布式文件存储

1_分布式文件存储的由来

在我们的项目中有很多需要存储的内容出现,比如图片,视频,文件等等,在早期的时候用户量不大,产生的文件也不是很多,这时我们可以把文件和服务程序放在一个服务器中。

在这里插入图片描述

后面随着文件越来越多,服务器的资源会被文件资源大量占据,从而影响到服务器的稳定,这时我们可以单独的把文件服务器拆出来。

在这里插入图片描述

拆解出来后,文件服务的使用不会影响到我们的系统服务的稳定,但是当用户量越来越大,存储的文件就会越来越多,这时如果还是单台的文件服务,比如100T的文件,这时是存储不下去的,这时就产生了我们将的分布式文件存储,

在这里插入图片描述

也就是我们解决如何将这100T的文件分散的存储到各个节点上,然后当我们需要读取文件的时候又能非常快的帮我们把文件找到。这个就是分布式文件系统帮我们解决的问题了。

2_常见的分布式存储框架

接下来我们看看在国内常用的分布式存储的框架选择有哪些

分布式框架说明
FastDFS我们介绍的主角,国产
HDFSHadoop组件中分布式存储框架
MinIOMinIO是在Apache下的产品,最适合存储非结构化的数据,比如照片,视频,日志文件,备份和容器等。
阿里云对象存储当然我们还可以花费一点费用来使用其他厂商提供的对象存储服务

2 FastDFS介绍

FastDFS是余庆(国人,淘宝)开发的一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。

特别适合以文件为载体的在线服务,如相册网站、视频网站等等。

FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。

FastDFS的特点:

  • FastDFS是一个轻量级的开源分布式文件系统
  • FastDFS主要解决了大容量的文件存储和高并发访问的问题,文件存取时实现了负载均衡
  • FastDFS实现了软件方式的RAID,可以使用廉价的IDE硬盘进行存储
  • 支持存储服务器在线扩容
  • 支持相同内容的文件只保存一份,节约磁盘空间
  • FastDFS只能通过Client API访问,不支持POSIX访问方式
  • FastDFS特别适合大中型网站使用,用来存储资源文件(如:图片、文档、音频、视频等等)

架构图:

在这里插入图片描述

相关术语讲解:

名词描述
Tracker Server跟踪服务器,主要做调度工作,在访问上起负载均衡的作用。记录storage server的状态,是连接Client和Storage server的枢纽
Storage Server存储服务器,文件和meta data都保存到存储服务器上
group组,也可称为卷。同组内服务器上的文件是完全相同的
文件标识包括两部分:组名和文件名(包含路径)
meta-data文件相关属性,键值对(Key Value Pair)方式,如:width=1024,heigth=768

架构解读:

  • 只有两个角色,tracker server和storage server,不需要存储文件索引信息。
  • 所有服务器都是对等的,不存在Master-Slave关系。
  • 存储服务器采用分组方式,同组内存储服务器上的文件完全相同(RAID 1)。
  • 不同组的storage server之间不会相互通信。
  • 由storage server主动向tracker server报告状态信息,tracker server之间不会相互通信。

3 FastDFS安装

在这里插入图片描述

FastDFS的安装我们还是通过Docker来安装实现吧,直接在Linux上还装还是比较繁琐的,但就学习而言Docker安装还是非常高效的。

Docker环境请自行安装哦,不清楚的可以看看我的Docker专题的内容。

1_拉取镜像文件

首先我们可以通过 docker search fastdfs 来查询下有哪些镜像文件。

在这里插入图片描述

我们看到搜索到的镜像还是蛮多的,这里我们使用 delron/fastdfs 你也可以尝试使用其他的镜像来安装,你也可以制作自己的镜像来给别人使用哦,只是不同的镜像在使用的时候配置会有一些不一样,有些镜像没有提供Nginx的相关配置,使用的时候会繁琐一点。

接下来通过 docker pull delron/fastdfs命令把镜像拉取下来(YGQYGQ2/fastdfs-nginx)。

在这里插入图片描述

2_构建Tracker服务

准备基本环境

mkdir -p /mydata/fastdfs/tracker
mkdir -p /mydata/fastdfs/storage

首先我们需要通过Docker命令来创建Tracker服务。命令为

docker run -d --name tracker --network=host -v /mydata/fastdfs/tracker:/var/fdfs delron/fastdfs tracker

tracker服务默认的端口为22122,-v 实现了容器和本地目录的挂载操作。

在这里插入图片描述

3_构建Storage服务

接下来创建Storage服务,具体的执行命令如下

docker run -d --name storage --network=host  -e TRACKER_SERVER=192.168.200.129:22122 -v /mydata/fastdfs/storage:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage

在执行上面命令的时候要注意对应的修改下,其中TRACKER_SERVER中的IP要修改为你的Tracker服务所在的服务IP地址。

在这里插入图片描述

默认情况下在Storage服务中是帮我们安装了Nginx服务的,相关的端口为

服务默认端口
tracker22122
storage23000
Nginx8888

当然如果你发现这些相关的端口被占用了,或者想要修改对应的端口信息也可以的。

不过这需要配置,你可以先进入容器中查看下相关的配置文件信息。

# 进入容器
docker exec -it storage bash
# 进入目录
cd /etc/fdfs
# 查看配置问价
ls

在这里插入图片描述

然后查看storage.conf文件(可以设置group、storage端口和nginx端口)

在这里插入图片描述

这个是storage监听的Nginx的端口8888,如果要修改那么我们还需要修改Nginx中的服务配置,这块的配置在 /usr/local/nginx/conf目录下

在这里插入图片描述

查看下文件

在这里插入图片描述

所以要修改端口号的话,这两个位置都得修改了,当然本文我们就使用默认的端口号来使用了。

4_测试图片上传

好了,安装我们已经完成了,那么到底是否可以使用呢?我们来测试下。

首先在/mydata/fastdfs/storage下保存一张图片。

在这里插入图片描述

然后我们再进入到storage容器中。并且进入到 /var/fdfs目录下,可以看到我们挂载的文件了

在这里插入图片描述

然后执行如下命令即可完成图片的上传操作

/usr/bin/fdfs_upload_file /etc/fdfs/client.conf 1.jpg

在这里插入图片描述

通过上面的提示我们看到文件上传成功了,而且返回了文件在storage中存储的信息。

这时我们就可以通过这个信息来拼接访问的地址在浏览器中访问了:http://192.168.200.129:8888/group1/M00/00/00/wKjIgWf19-mAWO23AABSIMuaaeU816.jpg(如果访问不到可以尝试关闭防火墙)

在这里插入图片描述

到这儿FastDFS的服务安装成功了。

4 客户端操作

1_Fastdfs-java-client

首先我们来看下如何实现FastDFS中提供的JavaAPI来直接实现对应的文件上传和下载操作。

创建一个普通的maven项目,然后引入对应的依赖

<dependencies><dependency><groupId>cn.bestwu</groupId><artifactId>fastdfs-client-java</artifactId><version>1.27</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.4</version></dependency>
</dependencies>

然后编写FastDFS的配置文件,内容如下:注意ip修改为你自己对应的ip即可

connect_timeout = 10
network_timeout = 30
charset = UTF-8
http.tracker_http_port = 8080
tracker_server = 192.168.200.129:22122

在这里插入图片描述

然后导入对应的工具类,在工具类中完成了StorageClient的实例化,并提供了相关的上传和下载的方法。

package org.duration.fastdfs.config;import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.csource.common.NameValuePair;
import org.csource.fastdfs.*;import java.io.*;
import java.util.Objects;@Slf4j
public class FastDFSClient {private static final String CONF_FILENAME = Objects.requireNonNull(Thread.currentThread().getContextClassLoader().getResource("")).getPath() + "fastdfs-config.conf";private static StorageClient storageClient = null;/*只加载一次.*/static {try {ClientGlobal.init(CONF_FILENAME);TrackerClient trackerClient = new TrackerClient(ClientGlobal.g_tracker_group);TrackerServer trackerServer = trackerClient.getConnection();StorageServer storageServer = trackerClient.getStoreStorage(trackerServer);storageClient = new StorageClient(trackerServer, storageServer);} catch (Exception e) {log.error(e.getMessage(), e);}}/*** @param inputStream 上传的文件输入流* @param fileName    上传的文件原始名* @return 【group,file_path】*/public static String[] uploadFile(InputStream inputStream, String fileName) {try {// 文件的元数据NameValuePair[] meta_list = new NameValuePair[2];// 第一组元数据,文件的原始名称meta_list[0] = new NameValuePair("file name", fileName);// 第二组元数据meta_list[1] = new NameValuePair("file length", inputStream.available() + "");// 准备字节数组byte[] file_buff = null;if (inputStream.available() > 0) {// 查看文件的长度int len = inputStream.available();// 创建对应长度的字节数组file_buff = new byte[len];// 将输入流中的字节内容,读到字节数组中。int read = inputStream.read(file_buff);}// 上传文件。参数含义:要上传的文件的内容(使用字节数组传递),上传的文件的类型(扩展名),元数据return storageClient.upload_file(file_buff, getFileExt(fileName), meta_list);} catch (Exception ex) {log.error(ex.getMessage(), ex);return null;}}/*** @param file     文件* @param fileName 文件名* @return 返回Null则为失败*/public static String[] uploadFile(File file, String fileName) {FileInputStream fis = null;try {NameValuePair[] meta_list = null; // new NameValuePair[0];fis = new FileInputStream(file);byte[] file_buff = null;int len = fis.available();if (len > 0) {file_buff = new byte[len];int read = fis.read(file_buff);}return storageClient.upload_file(file_buff, getFileExt(fileName), meta_list);} catch (Exception ex) {return null;} finally {if (fis != null) {try {fis.close();} catch (IOException e) {log.error(e.getMessage(), e);}}}}/*** 根据组名和远程文件名来删除一个文件** @param groupName      例如 "group1" 如果不指定该值,默认为group1* @param remoteFileName 例如"M00/00/00/wKgxgk5HbLvfP86RAAAAChd9X1Y736.jpg"* @return 0为成功,非0为失败,具体为错误代码*/public static int deleteFile(String groupName, String remoteFileName) {try {return storageClient.delete_file(groupName == null ? "group1" : groupName, remoteFileName);} catch (Exception ex) {return 0;}}/*** 修改一个已经存在的文件** @param oldGroupName 旧的组名* @param oldFileName  旧的文件名* @param file         新文件* @param fileName     新文件名* @return 返回空则为失败*/public static String[] modifyFile(String oldGroupName, String oldFileName, File file, String fileName) {String[] fileids = null;try {// 先上传fileids = uploadFile(file, fileName);if (fileids == null) {return null;}// 再删除int delResult = deleteFile(oldGroupName, oldFileName);if (delResult != 0) {return null;}} catch (Exception ex) {return null;}return fileids;}/*** 文件下载** @param groupName      卷名* @param remoteFileName 文件名* @return 返回一个流*/public static InputStream downloadFile(String groupName, String remoteFileName) {try {byte[] bytes = storageClient.download_file(groupName, remoteFileName);return new ByteArrayInputStream(bytes);} catch (Exception ex) {return null;}}public static NameValuePair[] getMetaDate(String groupName, String remoteFileName) {try {return storageClient.get_metadata(groupName, remoteFileName);} catch (Exception ex) {log.error(ex.getMessage(), ex);return null;}}/*** 获取文件后缀名(不带点).** @return 如:"jpg" or "".*/private static String getFileExt(String fileName) {if (StringUtils.isBlank(fileName) || !fileName.contains(".")) {return "";} else {return fileName.substring(fileName.lastIndexOf(".") + 1); // 不带最后的点}}}

然后我们就可以来测试上传的操作了。

2_文件上传

先来看下文件上传的流程

在这里插入图片描述

上传流程的文字梳理为:

  1. 客户端访问Tracker
  2. Tracker 返回Storage的IP和端口
  3. 客户端直接访问Storage,把文件内容和元数据发送过去。
  4. Storage返回文件存储id。包含了组名和文件名
//不可获取元数据信息
public static void fileUpload() {File file = new File("D:\\WorkSpace\\IdeaProject\\fastdfs_demo\\src\\main\\resources\\10937845.jpg");String[] strings = FastDFSClient.uploadFile(file, file.getName());if (strings == null) {return;}System.out.println("文件上传成功" + Arrays.asList(strings));
}
//可获取元数据
public static void fileUploadByMetaData() {File file = new File("D:\\WorkSpace\\IdeaProject\\fastdfs_demo\\src\\main\\resources\\10937845.jpg");try (InputStream is = new FileInputStream(file)) {String[] strings = FastDFSClient.uploadFile(is, file.getName());if (strings == null) {return;}System.out.println("文件上传成功" + Arrays.asList(strings));} catch (Exception e) {log.error(e.getMessage(), e);}
}

在这里插入图片描述

访问即可:http://192.168.200.129:8888/group1/M00/00/00/wKjIgWf2Ju-AZEFGAAAqSN2jiTQ329.jpg

返回后的字符串的结构说明

在这里插入图片描述

3_文件下载

文件下载的流程,如下

在这里插入图片描述

文件下载的流程为:

  1. client询问tracker需要下载的文件的storage,参数为文件的标识(group加文件名)。
  2. tracker根据客户端的参数返回一台可用的storage。
  3. client根据返回的storage直接完成对应的文件的下载。

有了上面的基础,文件下载就非常简单了,我们只需要根据前面上传的文件的group和文件的存储路径就可以通过StorageClient中提供的downloadFile方法把对应的文件下载下来了,具体的代码如下

public static void downloadFile() {String[] params = {"group1", "M00/00/00/wKjIgWf2Ju-AZEFGAAAqSN2jiTQ329.jpg"};try (InputStream is = FastDFSClient.downloadFile(params[0], params[1]);OutputStream os = new FileOutputStream("D:\\WorkSpace\\IdeaProject\\fastdfs_demo\\src\\main\\resources\\12.jpg")) {if (is == null) {return;}int index = 0;while ((index = is.read()) != -1) {os.write(index);}os.flush();} catch (IOException e) {log.error(e.getMessage(), e);}
}

4_获取文件信息

获取文件元数据信息

public static void getFileInfo() {String[] params = {"group1", "M00/00/00/wKjIgWf2NPqAamQdAAAqSN2jiTQ216.jpg"};NameValuePair[] metaDate = FastDFSClient.getMetaDate(params[0], params[1]);if (metaDate == null) {return;}for (NameValuePair nameValuePair : metaDate) {log.info("信息如下:{}={}", nameValuePair.getName(), nameValuePair.getValue());}
}

还有一些其他接口比如获取文件扩展名,可自行测试

5_问题

注意:StorageClient是线程不安全的。那么我们的解决方案

  1. 对文件的操作的每个方法我们做同步处理
  2. 每次操作文件的时候我们都获取一个新的StorageClient对象(全局变局部)

第一种方式效率肯定是最低的,第二种方式每次都要建立新的连接效率同样的会受到影响,这时最好的方式其实是把StorageClient交给我们自定义的连接池来管理

5 SpringBoot整合

我们在实际工作中基本都是和SpringBoot整合在一起来使用的,那么我们就来看看FastDFS是如何在SpringBoot项目中来使用的。

首先创建一个普通的SpringBoot项目,然后导入fastdfs-spring-boot-starter这个依赖。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope>
</dependency>
<dependency><groupId>com.luhuiguo</groupId><artifactId>fastdfs-spring-boot-starter</artifactId><version>0.2.0</version>
</dependency>

既然是一个starter,那么必然会在spring.factories文件中提供对应的自动配置类。

在这里插入图片描述

可以看到给我们提供的配置类为FdfsAutoConfiguration进入后可以看到帮我们注入了很多的核心对象。

在这里插入图片描述

然后可以看到系统提供的配置信息,前缀为 fdfs

在这里插入图片描述

然后我们就可以在application.yaml中配置FastDFS的配置信息了。

fdfs:connect-timeout: 1000so-timeout: 1000tracker-list: 192.168.200.129:22122

配置完成后我们就可以测试文件的上传下载操作了

@SpringBootTest
class FastDfsSpringBootApplicationTests {@Autowiredpublic FastFileStorageClient storageClient;@Testvoid contextLoads() throws Exception{File file = new ClassPathResource("10937845.jpg").getFile();StorePath path = storageClient.uploadFile(null, new FileInputStream(file), file.length(), file.getName().substring(file.getName().lastIndexOf(".") + 1));System.out.println(path.getFullPath());}}

文件操作成功

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/75343.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

安装了VM Tools,仍无法复制拖动-解决方案

今天在安装ubuntu时遇到了困扰许久的问题&#xff0c;安装了VM Tools&#xff0c;仍无法拖动主机文件到虚拟机&#xff0c;主要有两种原因并对应解决办法。 1.相关虚拟机设置选项卡中-客户机隔离-两个功能没有勾选 解决方案&#xff1a;勾选重启虚拟机即可 2.&#xff08;这个…

Jmeter分布式测试启动

代理客户端配置 打开jmeter.properties文件&#xff0c;取消注释并设置端口&#xff08;如server_port1099&#xff09;&#xff0c; 并添加server.rmi.ssl.disabletrue禁用SSL加密。 &#xff08;Linux系统&#xff09;修改jmeter-server文件中的RMI_HOST_DEF为代理机实际IP。…

火语言RPA--Oracle-导入数据表格

【组件功能】&#xff1a;导入特定的表格数据到包含同样字段的数据表 将表格对象数据通过数据库操作对象导入到指定数据库。 配置预览 配置说明 源表格 表格来源有“来自表格对象”和“来自表达式”2种&#xff0c;表达式支持DataTable类型变量。 对象 对应来自表格对象&…

Java的Selenium的特殊元素操作与定位之验证码

1.使用OCR技术识别验证 步骤&#xff1a; 截取整个网页的截图。 定位验证码图片元素。 根据验证码图片的位置和大小&#xff0c;从截图中裁剪出验证码图片。 使用OCR工具&#xff08;如Tesseract&#xff09;识别验证码图片中的文本。 2.手动处理验证码 步骤&#xff1a;…

OpenStack Yoga版安装笔记(十七)安全组笔记

一、安全组与iptables的关系 OpenStack的安全组&#xff08;Security Group&#xff09;默认是通过Linux的iptables实现的。以下是其主要实现原理和机制&#xff1a; 安全组与iptables的关系 OpenStack的安全组规则通过iptables的规则链实现。每条安全组规则会被转换为相应的i…

starrocks split函数和trino split函数差异性

在trino419和starrocks3.2.8中分别执行下面这两条sql,出来的结果是不一样的 select split(,,,)[1] as t1 select coalesce(split(,,&#

Spring Data JPA中的List底层:深入解析ArrayList的奥秘!!!

&#x1f31f; Spring Data JPA中的List底层&#xff1a;深入解析ArrayList的奥秘 &#x1f4a1; 你是否好奇过&#xff0c;为什么Spring Data JPA的查询方法返回的List<T>总是默认为ArrayList&#xff1f;本文将通过技术原理解析、验证实验和性能优化指南&#xff0c;为…

腾讯云智测试开发面经

1、投递时间线 2.20投递简历,3.11第一轮面试,3.30第二轮面试,4.4第三轮面试,4.10第四轮面试,4.11offer意向书 2、第一轮面试 第一轮面试技术面,面试官是导师,面试时长40多分钟 1)自我介绍 2)数组和列表的区别 3)了解哪些数据库 4)进程和线程的区别 5)了解哪…

【深度学习】【目标检测】【Ultralytics-YOLO系列】YOLOV3源码整体结构解析

【深度学习】【目标检测】【Ultralytics-YOLO系列】YOLOV3源码整体结构解析 文章目录 【深度学习】【目标检测】【Ultralytics-YOLO系列】YOLOV3源码整体结构解析前言代码结构整体data文件结构模型训练超参数配置文件解析数据集配置文件解析 models文件结构utils文件结构runs文…

Python常用排序算法

1. 冒泡排序 冒泡排序是一种简单的排序算法&#xff0c;它重复地遍历要排序的列表&#xff0c;比较相邻的元素&#xff0c;如果他们的顺序错误就交换他们。 def bubble_sort(arr):# 遍历所有数组元素for i in range(len(arr)):# 最后i个元素是已经排序好的for j in range(0, …

解锁塔能科技,开启工厂绿色转型与可持续发展双引擎

在全球积极推进可持续发展的大背景下&#xff0c;能源的高效利用与节能减排&#xff0c;已成为各行各业迈向高质量发展进程中无法回避的核心任务。工厂作为能源消耗大户与污染排放重点源头&#xff0c;其绿色转型迫在眉睫&#xff0c;这不仅关乎企业自身的长远发展&#xff0c;…

Spring Boot 线程池配置详解

Spring Boot 线程池配置详解 一、核心配置参数及作用 基础参数核心线程数 (corePoolSize)‌ 作用‌:线程池中始终保持存活的线程数量,即使空闲也不回收‌。 建议‌:根据任务类型设定(如 I/O 密集型任务可设为 CPU 核心数 2)‌。 最大线程数 (maxPoolSize)‌ 作用‌:…

入侵检测系统(IDS)和入侵防御系统(IPS)有啥区别?

入侵检测系统&#xff08;IDS&#xff09;和入侵防御系统&#xff08;IPS&#xff09;是网络安全中的两种关键技术&#xff0c;它们的核心区别在于 检测后的响应方式 和 部署位置。以下是详细对比&#xff1a; 1. 核心功能 - IDS&#xff08;入侵检测系统&#xff09; - 仅监…

【MySQL 数据库】数据表的操作

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 目录 1. 表的查看 1.1 语法 2. 表的创建 2.1 语法 2.2 练习 3. 查看表结构 3.1 语法 3.2 示例 4. 表的修改 4.1 语法 4.2 示例操作 4.2.1 向表中添加字段…

sqli-labs靶场 less5

文章目录 sqli-labs靶场less 5 报错注入 sqli-labs靶场 每道题都从以下模板讲解&#xff0c;并且每个步骤都有图片&#xff0c;清晰明了&#xff0c;便于复盘。 sql注入的基本步骤 注入点注入类型 字符型&#xff1a;判断闭合方式 &#xff08;‘、"、’、“”&#xf…

C# 状态模式深度解析:构建灵活的状态驱动系统

一、状态模式概述 状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象在其内部状态改变时改变其行为&#xff0c;使对象看起来像是修改了它的类。这种模式将特定状态相关的行为局部化&#xff0c;并且将不同状态的行为分割开来。 状态…

vue实现二维码生成器和解码器

vue实现二维码生成器和解码器 1.生成基本二维码&#xff1a;根据输入的value生成二维码。 2.可定制尺寸&#xff1a;通过size调整大小。 3.颜色和背景色&#xff1a;设置二维码颜色和背景。 4.静区&#xff08;quiet zone&#xff09;支持&#xff1a;通过quietZone调整周围的…

Nacos:Nacos服务注册与服务发现超详细的源码解析(二)

&#x1fa81;&#x1f341; 希望本文能给您带来帮助&#xff0c;如果有任何问题&#xff0c;欢迎批评指正&#xff01;&#x1f405;&#x1f43e;&#x1f341;&#x1f425; 文章目录 一、背景二、环境与依赖三、服务注册与服务发现总流程图四、服务注册源码4.1 客户端4.1.1…

ECMAScript 6 新特性(二)

ECMAScript 6 新特性&#xff08;二&#xff09; ECMAScript 6 新特性&#xff08;一&#xff09; ECMAScript 6 新特性&#xff08;二&#xff09;&#xff08;本文&#xff09; ECMAScript 7~10 新特性 1. 生成器 生成器函数是 ES6 提供的一种解决异步编程方案&#xff0c;一…

深入理解 RxSwift 中的 Driver:用法与实践

目录 前言 一、什么是Driver 1.不会发出错误 2.主线程保证 3.可重放 4.易于绑定 二、Driver vs Observable 三、使用场景 1.绑定数据到UI控件 2.响应用户交互 3.需要线程安全的逻辑 4.如何使用Driver? 1.绑定文本输入到Label 2.处理按钮点击事件 3.从网络请求…