最近沉迷Redis網路模型,無法自拔!終於知道Redis為啥這麼快了

来源:https://www.cnblogs.com/jiagooushi/archive/2022/12/07/16962996.html
-Advertisement-
Play Games

1. 用戶空間和內核態空間 1.1 為什麼要區分用戶和內核 伺服器大多都採用 Linux 系統,這裡我們以 Linux 為例來講解: ubuntu 和 Centos 都是 Linux 的發行版,發行版可以看成對 linux 包了一層殼,任何 Linux 發行版,其系統內核都是 Linux 。我們的應 ...


1. 用戶空間和內核態空間

1.1 為什麼要區分用戶和內核

伺服器大多都採用 Linux 系統,這裡我們以 Linux 為例來講解:

ubuntu 和 Centos 都是 Linux 的發行版,發行版可以看成對 linux 包了一層殼,任何 Linux 發行版,其系統內核都是 Linux 。我們的應用都需要通過 Linux 內核與硬體交互

file

用戶的應用,比如 redis ,mysql 等其實是沒有辦法去執行訪問我們操作系統的硬體的,所以我們可以通過發行版的這個殼子去訪問內核,再通過內核去訪問電腦硬體
file

電腦硬體包括,如 cpu,記憶體,網卡等等,內核(通過定址空間)可以操作硬體的,但是內核需要不同設備的驅動,有了這些驅動之後,內核就可以去對電腦硬體去進行 記憶體管理,文件系統的管理,進程的管理等等

file

我們想要用戶的應用來訪問,電腦就必須要通過對外暴露的一些介面,才能訪問到,從而簡介的實現對內核的操控,但是內核本身上來說也是一個應用,所以他本身也需要一些記憶體,cpu 等設備資源,用戶應用本身也在消耗這些資源,如果不加任何限制,用戶去操作隨意的去操作我們的資源,就有可能導致一些衝突,甚至有可能導致我們的系統出現無法運行的問題,因此我們需要把用戶和內核隔離開

1.2 進程定址空間

進程的定址空間劃分成兩部分:內核空間、用戶空間

什麼是定址空間呢?我們的應用程式也好,還是內核空間也好,都是沒有辦法直接去物理記憶體的,而是通過分配一些虛擬記憶體映射到物理記憶體中,我們的內核和應用程式去訪問虛擬記憶體的時候,就需要一個虛擬地址,這個地址是一個無符號的整數。

比如一個 32 位的操作系統,他的帶寬就是 32,他的虛擬地址就是 2 的 32 次方,也就是說他定址的範圍就是 0~2 的 32 次方, 這片定址空間對應的就是 2 的 32 個位元組,就是 4GB,這個 4GB,會有 3 個 GB 分給用戶空間,會有 1GB 給內核系統

file

在 linux 中,他們許可權分成兩個等級,0 和 3,用戶空間只能執行受限的命令(Ring3),而且不能直接調用系統資源,必須通過內核提供的介面來訪問內核空間可以執行特權命令(Ring0),調用一切系統資源,所以一般情況下,用戶的操作是運行在用戶空間,而內核運行的數據是在內核空間的,而有的情況下,一個應用程式需要去調用一些特權資源,去調用一些內核空間的操作,所以此時他倆需要在用戶態和內核態之間進行切換。

比如:

Linux 系統為了提高 IO 效率,會在用戶空間和內核空間都加入緩衝區:

  • 寫數據時,要把用戶緩衝數據拷貝到內核緩衝區,然後寫入設備
  • 讀數據時,要從設備讀取數據到內核緩衝區,然後拷貝到用戶緩衝區

針對這個操作:我們的用戶在寫讀數據時,會去向內核態申請,想要讀取內核的數據,而內核數據要去等待驅動程式從硬體上讀取數據,當從磁碟上載入到數據之後,內核會將數據寫入到內核的緩衝區中,然後再將數據拷貝到用戶態的 buffer 中,然後再返回給應用程式,整體而言,速度慢,就是這個原因,為了加速,我們希望 read 也好,還是 wait for data 也最好都不要等待,或者時間儘量的短。

file

2. 網路模型

2.1 阻塞IO

  • 過程 1:應用程式想要去讀取數據,他是無法直接去讀取磁碟數據的,他需要先到內核裡邊去等待內核操作硬體拿到數據,這個過程是需要等待的,等到內核從磁碟上把數據載入出來之後,再把這個數據寫給用戶的緩存區。
  • 過程 2:如果是阻塞 IO,那麼整個過程中,用戶從發起讀請求開始,一直到讀取到數據,都是一個阻塞狀態。
    file

用戶去讀取數據時,會去先發起 recvform 一個命令,去嘗試從內核上載入數據,如果內核沒有數據,那麼用戶就會等待,此時內核會去從硬體上讀取數據,內核讀取數據之後,會把數據拷貝到用戶態,並且返回 ok,整個過程,都是阻塞等待的,這就是阻塞 IO

總結如下:

顧名思義,阻塞 IO 就是兩個階段都必須阻塞等待:

階段一:

  • 用戶進程嘗試讀取數據(比如網卡數據)
  • 此時數據尚未到達,內核需要等待數據
  • 此時用戶進程也處於阻塞狀態

階段二:

  • 數據到達並拷貝到內核緩衝區,代表已就緒
  • 將內核數據拷貝到用戶緩衝區
  • 拷貝過程中,用戶進程依然阻塞等待
  • 拷貝完成,用戶進程解除阻塞,處理數據

可以看到,阻塞 IO 模型中,用戶進程在兩個階段都是阻塞狀態。

file

2.2 非阻塞 IO

顧名思義,非阻塞 IO 的 recvfrom 操作會立即返回結果而不是阻塞用戶進程

階段一:

  • 用戶進程嘗試讀取數據(比如網卡數據)
  • 此時數據尚未到達,內核需要等待數據
  • 返回異常給用戶進程
  • 用戶進程拿到 error 後,再次嘗試讀取
  • 迴圈往複,直到數據就緒

階段二:

  • 將內核數據拷貝到用戶緩衝區
  • 拷貝過程中,用戶進程依然阻塞等待
  • 拷貝完成,用戶進程解除阻塞,處理數據
  • 可以看到,非阻塞 IO 模型中,用戶進程在第一個階段是非阻塞,第二個階段是阻塞狀態。雖然是非阻塞,但性能並沒有得到提高。而且忙等機制會導致 CPU 空轉,CPU 使用率暴增。

file

2.3 信號驅動

信號驅動 IO 是與內核建立 SIGIO 的信號關聯並設置回調,當內核有 FD 就緒時,會發出 SIGIO 信號通知用戶,期間用戶應用可以執行其它業務,無需阻塞等待。

階段一:

  • 用戶進程調用 sigaction ,註冊信號處理函數
  • 內核返回成功,開始監聽 FD
  • 用戶進程不阻塞等待,可以執行其它業務
  • 當內核數據就緒後,回調用戶進程的 SIGIO 處理函數

階段二:

  • 收到 SIGIO 回調信號
  • 調用 recvfrom ,讀取
  • 內核將數據拷貝到用戶空間
  • 用戶進程處理數據

file
當有大量 IO 操作時,信號較多,SIGIO 處理函數不能及時處理可能導致信號隊列溢出,而且內核空間與用戶空間的頻繁信號交互性能也較低。

2.4 非同步 IO

這種方式,不僅僅是用戶態在試圖讀取數據後,不阻塞,而且當內核的數據準備完成後,也不會阻塞

他會由內核將所有數據處理完成後,由內核將數據寫入到用戶態中,然後才算完成,所以性能極高,不會有任何阻塞,全部都由內核完成,可以看到,非同步 IO 模型中,用戶進程在兩個階段都是非阻塞狀態。

file

2.5 IO 多路復用

場景引入

為了更好的理解 IO ,現在假設這樣一種場景:一家餐廳

  • A 情況:這家餐廳中現在只有一位服務員,並且採用客戶排隊點餐的方式,就像這樣:

file
每排到一位客戶要吃到飯,都要經過兩個步驟:

思考要吃什麼
顧客開始點餐,廚師開始炒菜

由於餐廳只有一位服務員,因此一次只能服務一位客戶,並且還需要等待當前客戶思考出結果,這浪費了後續排隊的人非常多的時間,效率極低。這就是阻塞 IO。

當然,為了緩解這種情況,老闆完全可以多雇幾個人,但這也會增加成本,而在極大客流量的情況下,仍然不會有很高的效率提升

  • B 情況: 這家餐廳中現在只有一位服務員,並且採用客戶排隊點餐的方式。

每排到一位客戶要吃到飯,都要經過兩個步驟:

  1. 思考要吃什麼
  2. 顧客開始點餐,廚師開始炒菜

與 A 情況不同的是,此時服務員會不斷詢問顧客:“你想吃番茄雞蛋蓋澆飯嗎?那滑蛋牛肉呢?那肉末茄子呢?……”

雖然服務員在不停的問,但是在網路中,這並不會增加數據的就緒速度,主要還是等顧客自己確定。所以,這並不會提高餐廳的效率,說不定還會招來更多差評。這就是非阻塞 IO。

  • C 情況: 這家餐廳中現在只有一位服務員,但是不再採用客戶排隊的方式,而是顧客自己獲取菜單並點餐,點完後通知服務員,就像這樣:

file

每排到一位客戶要吃到飯,還是都要經過兩個步驟:

  1. 看著菜單,思考要吃什麼
  2. 通知服務員,我點好了

與 A B 不同的是,這種情況服務員不必再等待顧客思考吃什麼,只需要在收到顧客通知後,去接收菜單就好。這樣相當於餐廳在只有一個服務員的情況下,同時服務了多個人,而不像 A B,同一時刻只能服務一個人。此時餐廳的效率自然就提高了很多。

映射到我們的網路服務中,就是這樣:

  • 客人:客戶端請求
  • 點餐內容:客戶端發送的實際數據
  • 老闆:操作系統
  • 人力成本:系統資源
  • 菜單:文件狀態描述符。操作系統對於一個進程能夠同時持有的文件狀態描述符的個數是有限制的,在 linux 系統中 $ulimit -n 查看這個限制值,當然也是可以 (並且應該) 進行內核參數調整的。
  • 服務員:操作系統內核用於 IO 操作的線程 (內核線程)
  • 廚師:應用程式線程 (當然廚房就是應用程式進程咯)
  • 餐單傳遞方式:包括了阻塞式和非阻塞式兩種。
    • 方法 A: 阻塞 IO
    • 方法 B: 非阻塞 IO
    • 方法 C: 多路復用 IO

2.6 多路復用 IO 的實現

目前流程的多路復用 IO 實現主要包括四種: select、poll、epoll、kqueue。下表是他們的一些重要特性的比較:

IO 模型 相對性能 關鍵思路 操作系統 JAVA 支持情況
select 較高 Reactor windows/Linux 支持,Reactor 模式 (反應器設計模式)。Linux 操作系統的 kernels 2.4 內核版本之前,預設使用 select;而目前 windows 下對同步 IO 的支持,都是 select 模型
poll 較高 Reactor Linux Linux 下的 JAVA NIO 框架,Linux kernels 2.6 內核版本之前使用 poll 進行支持。也是使用的 Reactor 模式
epoll Reactor/Proactor Linux Linux kernels 2.6 內核版本及以後使用 epoll 進行支持;Linux kernels 2.6 內核版本之前使用 poll 進行支持;另外一定註意,由於 Linux 下沒有 Windows 下的 IOCP 技術提供真正的 非同步 IO 支持,所以 Linux 下使用 epoll 模擬非同步 IO
kqueue Proactor Linux 目前 JAVA 的版本不支持

多路復用 IO 技術最適用的是 “高併發” 場景,所謂高併發是指 1 毫秒內至少同時有上千個連接請求準備好。其他情況下多路復用 IO 技術發揮不出來它的優勢。另一方面,使用 JAVA NIO 進行功能實現,相對於傳統的 Socket 套接字實現要複雜一些,所以實際應用中,需要根據自己的業務需求進行技術選擇。

2.6.1 select

select 是 Linux 最早是由的 I/O 多路復用技術:

linux 中,一切皆文件,socket 也不例外,我們把需要處理的數據封裝成 FD,然後在用戶態時創建一個 fd_set 的集合(這個集合的大小是要監聽的那個 FD 的最大值 + 1,但是大小整體是有限制的 ),這個集合的長度大小是有限制的,同時在這個集合中,標明出來我們要控制哪些數據。

其內部流程:

用戶態下:

  1. 創建 fd_set 集合,包括要監聽的 讀事件、寫事件、異常事件 的集合
  2. 確定要監聽的 fd_set 集合
  3. 將要監聽的集合作為參數傳入 select () 函數中,select 中會將 集合複製到內核 buffer 中

內核態:

  1. 內核線程在得到 集合後,遍歷該集合
  2. 沒數據就緒,就休眠
  3. 當數據來時,線程被喚醒,然後再次遍歷集合,標記就緒的 fd 然後將整個集合,複製回用戶 buffer 中
  4. 用戶線程遍歷 集合,找到就緒的 fd ,再發起讀請求。

file

不足之處:

  • 集合大小固定為 1024 ,也就是說最多維持 1024 個 socket,在海量數據下,不夠用
  • 集合需要在 用戶 buffer 和內核 buffer 中反覆複製,涉及到 用戶態和內核態的切換,非常影響性能

2.6.2 poll

poll 模式對 select 模式做了簡單改進,但性能提升不明顯。

IO 流程:

  • 創建 pollfd 數組,向其中添加關註的 fd 信息,數組大小自定義
  • 調用 poll 函數,將 pollfd 數組拷貝到內核空間,轉鏈表存儲,無上限
  • 內核遍歷 fd ,判斷是否就緒
  • 數據就緒或超時後,拷貝 pollfd 數組到用戶空間,返回就緒 fd 數量 n
  • 用戶進程判斷 n 是否大於 0, 大於 0 則遍歷 pollfd 數組,找到就緒的 fd

與 select 對比:

  • select 模式中的 fd_set 大小固定為 1024,而 pollfd 在內核中採用鏈表,理論上無上限,但實際上不能這麼做,因為的監聽 FD 越多,每次遍歷消耗時間也越久,性能反而會下降

file

2.6.3 epoll

epoll 模式是對 select 和 poll 的改進,它提供了三個函數:eventpoll 、epoll_ctl 、epoll_wait

  • eventpoll 函數內部包含了兩個東西 :
    • 紅黑樹 :用來記錄所有的 fd
    • 鏈表 : 記錄已就緒的 fd
  • epoll_ctl 函數 ,將要監聽的 fd 添加到 紅黑樹 上去,並且給每個 fd 綁定一個監聽函數,當 fd 就緒時就會被觸發,這個監聽函數的操作就是 將這個 fd 添加到 鏈表中去。
  • epoll_wait 函數,就緒等待。一開始,用戶態 buffer 中創建一個空的 events 數組,當就緒之後,我們的回調函數會把 fd 添加到鏈表中去,當函數被調用的時候,會去檢查鏈表(當然這個過程需要參考配置的等待時間,可以等一定時間,也可以一直等),如果鏈表中沒有有 fd 則 fd 會從紅黑樹被添加到鏈表中,此時再將鏈表中的的 fd 複製到 用戶態的空 events 中,並且返回對應的操作數量,用戶態此時收到響應後,會從 events 中拿到已經準備好的數據,在調用 讀方法 去拿數據。

2.6.4 總結:

select 模式存在的三個問題:

  • 能監聽的 FD 最大不超過 1024
  • 每次 select 都需要把所有要監聽的 FD 都拷貝到內核空間
  • 每次都要遍歷所有 FD 來判斷就緒狀態

poll 模式的問題:

  • poll 利用鏈表解決了 select 中監聽 FD 上限的問題,但依然要遍歷所有 FD,如果監聽較多,性能會下降

epoll 模式中如何解決這些問題的?

  • 基於 epoll 實例中的紅黑樹保存要監聽的 FD,理論上無上限,而且增刪改查效率都非常高
  • 每個 FD 只需要執行一次 epoll_ctl 添加到紅黑樹,以後每次 epol_wait 無需傳遞任何參數,無需重覆拷貝 FD 到內核空間
  • 利用 ep_poll_callback 機制來監聽 FD 狀態,無需遍歷所有 FD,因此性能不會隨監聽的 FD 數量增多而下降

2.7 基於 epoll 的伺服器端流程

一張圖搞定:

file

我們來梳理一下這張圖

  1. 伺服器啟動以後,服務端會去調用 epoll_create,創建一個 epoll 實例,epoll 實例中包含兩個數據
  • 紅黑樹(為空):rb_root 用來去記錄需要被監聽的 FD

  • 鏈表(為空):list_head,用來存放已經就緒的 FD

  1. 創建好了之後,會去調用 epoll_ctl 函數,此函數會會將需要監聽的 fd 添加到 rb_root 中去,並且對當前這些存在於紅黑樹的節點設置回調函數。

  2. 當這些被監聽的 fd 一旦準備就緒,與之相關聯的回調函數就會被調用,而調用的結果就是將紅黑樹的 fd 添加到 list_head 中去 (但是此時並沒有完成)

  3. fd 添加完成後,就會調用 epoll_wait 函數,這個函數會去校驗是否有 fd 準備就緒(因為 fd 一旦準備就緒,就會被回調函數添加到 list_head 中),在等待了一段時間 (可以進行配置)。

  4. 如果等夠了超時時間,則返回沒有數據,如果有,則進一步判斷當前是什麼事件,如果是建立連接事件,則調用 accept () 接受客戶端 socket ,拿到建立連接的 socket ,然後建立起來連接,如果是其他事件,則把數據進行寫出。

2.8 五種網路模型對比:

最後用一幅圖,來說明他們之間的區別

file

3. redis 通信協議

3.1 RESP 協議

Redis 是一個 CS 架構的軟體,通信一般分兩步(不包括 pipeline 和 PubSub):

  • 客戶端(client)向服務端(server)發送一條命令,服務端解析並執行命令
  • 返迴響應結果給客戶端,因此客戶端發送命令的格式、服務端響應結果的格式必須有一個規範,這個規範就是通信協議。

而在 Redis 中採用的是 RESP(Redis Serialization Protocol)協議:

  • Redis 1.2 版本引入了 RESP 協議
  • Redis 2.0 版本中成為與 Redis 服務端通信的標準,稱為 RESP2
  • Redis 6.0 版本中,從 RESP2 升級到了 RESP3 協議,增加了更多數據類型並且支持 6.0 的新特性–客戶端緩存

但目前,預設使用的依然是 RESP2 協議。在 RESP 中,通過首位元組的字元來區分不同數據類型,常用的數據類型包括 5 種:

  • 單行字元串:首位元組是 ‘+’ ,後面跟上單行字元串,以 CRLF( “\r\n” )結尾。例如返回”OK”: “+OK\r\n”
  • 錯誤(Errors):首位元組是 ‘-’ ,與單行字元串格式一樣,只是字元串是異常信息,例如:”-Error message\r\n”
  • 數值:首位元組是 ‘:’ ,後面跟上數字格式的字元串,以 CRLF 結尾。例如:”:10\r\n”
  • 多行字元串:首位元組是 ‘$’ ,表示二進位安全的字元串,最大支持 512MB:
    • 如果大小為 0,則代表空字元串:”$0\r\n\r\n”
    • 如果大小為 - 1,則代表不存在:”$-1\r\n”
  • 數組:首位元組是 ‘*’,後面跟上數組元素個數,再跟上元素,元素數據類型不限 :

file

本文由傳智教育博學谷教研團隊發佈。

如果本文對您有幫助,歡迎關註點贊;如果您有任何建議也可留言評論私信,您的支持是我堅持創作的動力。

轉載請註明出處!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • "Good code is its own best documentation." - Steve McConnell “好代碼本身就是最好的文檔。” —— 史蒂夫·麥克康奈爾 0x00 大綱 0x01 前言 數據與結構的解耦 在上篇文章,我們通過將鏈表的節點放在具體數據類型的結構體內,這樣,抽象 ...
  • @(文章目錄) 前言 為了提高伺服器的利用率,且便於項目部署及發佈,伺服器採用docker部署多個項目jar包。該項目採用ssm+shiro+mysql+redis+mongdb等。 備註:本文以CentOs 7為例。 一、下載安裝docker: 1.前提工作 1.1 查看linux版本 Docke ...
  • 1.異常處理能夠使一個方法給它的調用者拋出一個異常。 2.Java異常是派生自java.lang.Throwable的類的實例。Java提供大量預定義的異常類,例如,Error、 Exception、RuntimeException、ClassNotFoundException、Nul1Pointe ...
  • 一、 docker開啟遠程連接訪問 首先我們要開啟docker的遠程連接訪問。保證不是docker所在的伺服器,也能夠遠程訪問docker。 Linux版的docker: 1、修改 docker.service 文件,添加監聽埠 -H tcp://0.0.0.0:2375 vi /usr/lib/ ...
  • 摘要:本篇文章將分享圖像分類原理,並介紹基於KNN、朴素貝葉斯演算法的圖像分類案例。 本文分享自華為雲社區《[Python圖像處理] 二十六.圖像分類原理及基於KNN、朴素貝葉斯演算法的圖像分類案例丨【百變AI秀】》,作者:eastmount 。 一.圖像分類 圖像分類(Image Classifica ...
  • 對於下圖所示的二叉樹 其先序、中序、後序遍歷的序列如下: 先序遍歷: A、B、D、F、G、C、E、H 中序遍歷: B、F、D、G、A、C、E、H 後序遍歷: F、G、D、B、H、E、C、A 層序遍歷: A、B、C、D、E、F、G、H /** * Definition for a binary tre ...
  • 原文地址:Kotlin學習快速入門(11)—— 枚舉類的使用 - Stars-One的雜貨小窩 由於有時候偶爾用到枚舉類,所以簡單記錄一下,和Java的一起對比記錄 下麵以一個簡單的四季設計一個枚舉類 基本使用 kotlin寫法 enum class Season{ SPRING,SUMMER,AU ...
  • std::function是一組函數對象包裝類的模板,實現了一個泛型的回調機制。function與函數指針比較相似,優點在於它允許用戶在目標的實現上擁有更大的彈性,即目標既可以是普通函數,也可以是函數對象和類的成員函數,而且可以給函數添加狀態。 聲明一個function時,需要給出所包裝的函數對象的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...