分布式文件存儲系統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.pswp.cn/web/75343.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/75343.shtml
英文地址,請注明出處:http://en.pswp.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.從網絡請求…