KeeWiDB的高性能修煉之路:架構篇

来源:https://www.cnblogs.com/tencentdb/archive/2022/11/09/16873932.html
-Advertisement-
Play Games

數據也有冷熱之分,你知道嗎? 根據訪問的頻率的高低可將數據分為熱數據和冷數據,訪問頻率高的則為熱數據,低為冷數據。如果熱、冷數據不區分,一併存儲,顯然不科學。將冷數據也存儲在昂貴的記憶體中,那麼你想,成本得多高呢? 有趣的是,根據我們實際的觀察,目前很多使用 Redis 的業務就是這樣操作的。 得益於 ...


數據也有冷熱之分,你知道嗎?

根據訪問的頻率的高低可將數據分為熱數據和冷數據,訪問頻率高的則為熱數據,低為冷數據。如果熱、冷數據不區分,一併存儲,顯然不科學。將冷數據也存儲在昂貴的記憶體中,那麼你想,成本得多高呢?

有趣的是,根據我們實際的觀察,目前很多使用 Redis 的業務就是這樣操作的。

得益於高性能以及豐富的數據結構命令,Redis 成為目前最受歡迎的 KV 記憶體資料庫。但隨著業務數據量的爆炸增長,Redis 的記憶體消耗也會隨之爆炸。無論客戶是自建伺服器還是雲伺服器,記憶體都是一個必須考慮的成本問題,它不僅貴還要持續購買。

此外 Redis 雖然提供了 AOF 和 RDB 兩種方案來實現數據的持久化,但是使用不當可能會對性能造成影響甚至引發丟數據的問題。

好在,隨著科技的發展,持久化硬體的發展速度也在提升,持久記憶體的出現進一步縮小了與記憶體的性能差距。或許,合理利用新型持久化技術會成為一個好的成本解決方案

基於這一思路,為解決 Redis 可能帶來的記憶體成本、容量限制以及持久化等一系列問題,騰訊雲資料庫團隊推出了新一代分散式KV存儲資料庫 KeeWiDB。本文將詳細介紹KeeWiDB 的架構設計思路、實現路徑及成效。先簡單總結一下 KeeWiDB 的特性:

  • 友好:完全相容 Redis 協議,原先使用 Redis 的業務無需修改任何代碼便可以遷移到 KeeWiDB 上;
  • 高性能低延遲:通過創新性的分級存儲架構設計,單節點讀寫能力超過 18 萬 QPS,訪問延遲達到毫秒級;
  • 更低的成本:內核自動區分冷熱數據,冷數據存儲在相對低價的 SSD 上;
  • 更大的容量:節點支持 TB 級別的數據存儲,集群支持 PB 級別的數據存儲;
  • 保證了事務的 ACID (原子性 Atomicity、一致性 Consitency、隔離性 Isolation、持久性 Durability)四大特性;

一、整體架構

KeeWiDB 的架構由代理層和服務層兩個部分構成:
代理層:由多個無狀態的Proxy節點組成,主要功能是負責與客戶端進行交互;
服務層:由多個Server節點組成的集群,負責數據的存儲以及在機器發生故障時可以自動進行故障切換。

file

圖:KeeWiDB整體架構圖

代理層

客戶端通過 Proxy 連接來進行訪問,由於 Proxy 內部維護了後端集群的路由信息,所以 Proxy 可以將客戶端的請求轉發到正確的節點進行處理,從而客戶端無需關心集群的路由變化,用戶可以像使用 Redis 單機版一樣來使用 KeeWiDB。

Proxy 的引入,還帶來了諸多優勢:

  • 客戶端直接和 Proxy 進行交互,後端集群在擴縮容場景不會影響客戶端請求
  • Proxy 內部有自己的連接池和後端 KeeWiDB 進行交互,可大大減少 KeeWiDB 上的連接數量,同時有效避免業務短連接場景下反覆建連斷連對內核造成性能的影響
  • 支持讀寫分離,針對讀多寫少的場景,通過添加副本數量可以有效分攤 KeeWiDB 的訪問壓力
  • 支持命令攔截和審計功能,針對高危命令進行攔截和日誌審計,大幅度提高系統的安全性
  • 由於 Proxy 是無狀態的,負載較高場景下可以通過增加 Proxy 數量來緩解壓力,此外我們的 Proxy 支持熱升級功能,後續 Proxy 添加了新功能或者性能優化,存量 KeeWiDB 實例的 Proxy 都可以進行對客戶端無感知的平滑升級

file

圖:Proxy上的功能

服務層

KeeWiDB 的後端採用了集群的架構,這是因為集群具有高可用、可擴展性、分散式、容錯等優質特性;同時,在具體的實現上參考了 Redis 的集群模式。KeeWiDB 集群同樣由若幹個分片構成,而每個分片上又存在若幹個節點,由這些節點共同組成一主多從的高可用架構;此外每個分片的主節點負責集群中部分 Slot 的數據,並且可以通過動態修改主節點負責 Slot 區間的形式來實現橫向的擴縮容,為客戶提供了容量可彈性伸縮的能力。

二、Server內部模型

在 Server 內部存在一個集群管理模塊,該模塊通過 Gossip 協議與集群中的其他節點進行通信,獲取集群的最新狀態信息;另外 Server 內部存在多個工作線程,KeeWiDB 會將當前 Server 負責的 Slot 區間按一定的規則劃分給各個工作線程進行處理, 並且每個工作線程都有自己獨立的連接管理器,事務模塊以及存儲引擎等重要組件,線程之間不存在資源共用,做到了進程內部的 Shared-Nothing。正是這種 Shared-Nothing 的體繫結構,減少了 KeeWiDB 進程內部線程之間由於競爭資源的等待時間,獲得了良好並行處理能力以及可擴展的性能。

file
圖:Server內部模塊

線程模型

KeeWiDB 的設計目的,就是為瞭解決 Redis 的痛點問題。所以大容量,高性能以及低延遲是 KeeWiDB 追求的目標。

和數據都存放在記憶體中的 Redis 不同,KeeWiDB 的數據是存儲在 PMem(Persistent Memory)和相對低價的 SSD 上。在用戶執行讀寫訪問請求期間 KeeWiDB 都有可能會涉及到跟硬碟的交互,所以如果還像 Redis 一樣採用單進程單線程方案的話,單節點的性能肯定會大打折扣。因此,KeeWiDB 採取了單進程多線程的方案,一方面可以更好的利用整機資源來提升單節點的性能,另一方面也能降低運維門檻

多線程方案引入的核心思想是通過提高並行度來提升單節點的吞吐量,但是在處理用戶寫請求期間可能會涉及到不同線程操作同一份共用資源的情況,比如存儲引擎內部為了保證事務的原子性和持久性需要寫 WAL,主從之間進行同步需要寫 Binlog,這些日誌文件在寫入的過程中通常會涉及到持久化操作,相對較慢。雖然我們也採取了一系列的優化措施,例如使用組提交策略來降低持久化的頻率,但是優化效果有限。

同時為了保證線程安全,在這類日誌的寫入期間通常都要進行加鎖,這樣一來,一方面雖然上層可以多線程並行的處理用戶請求,但是到了寫日誌期間卻退化成了串列執行;另一方面,申請和釋放鎖通常會涉及到用戶態和內核態的切換,頻繁的申請釋放操作會給 CPU 帶來額外的開銷,顯然會導致性能問題。

file
圖:線程模型

正是由於進程內不同線程訪問同一份共用資源需要加鎖,而大量的鎖衝突無法將多線程的性能發揮到極致,所以我們將節點內部負責的 Slot 區間進行進一步的拆分,每個工作線程負責特定一組 Slot 子區間的讀寫請求,互不衝突;此外每個工作線程都擁有自己獨立的事務模塊以及存儲引擎等重要組件,不再跨線程共用

通過對共用資源進行線程級別的拆分,各個線程在處理用戶請求時都可以快速的獲得所需要的資源,不發生等待事件,這無論是對單個請求延遲的降低還是多個請求併發的提升,都有巨大的好處;此外由於處理用戶請求所需的資源都線上程內部,KeeWiDB 無需再為了線程安全而上鎖,有效規避了由於頻繁上鎖帶來的額外性能開銷。

引入協程

通過上面的章節介紹,KeeWiDB 通過進程內部 Shared-Nothing 的體繫結構減少了線程之間由於競爭共用資源花費的等待時間,提升了進程內部的併發度。此時我們再將視角轉移到線程內部,在業務高峰時期工作線程也需要負責處理大量的客戶端請求,由於每次請求操作都有可能會涉及到和磁碟的交互,此時如果再採用同步 IO 的形式和磁碟進行交互的話,由於一個客戶端請求執行的 I/O 操作就會阻塞當前線程,此時後面所有的客戶端請求需要排隊等待處理, 顯然併發度會大打折扣。

這時候也許有讀者會提出按照 KeeWiDB 目前這套進程內部 Shared-Nothing 的體繫結構,線程之間不存在共用資源競爭了,是不是可以通過增加線程數來緩解這個問題?想法不錯,但是大量的線程引入可能會帶來另外一些問題:

  • 開啟過多的線程會耗費大量的系統資源, 包括記憶體;
  • 線程的上下文切換涉及到用戶空間和內核空間的切換, 大量線程的上下文切換同樣會給 CPU 帶來額外的開銷;

為了讓單個線程的性能能夠發揮到極致,不把時間浪費在等待磁碟 I/O 上,KeeWiDB 首先考慮的是採取非同步 I/O 的方案(應用層觸發 I/O 操作後立即返回,線程可以繼續處理其他事件,而當 I/O 操作已經完成的時候會得到 I/O 完成的通知)。

很明顯,使用非同步 I/O 來編寫程式性能會遠遠高於同步 I/O,但是非同步 I/O 的缺點是編程模型複雜。我們常規的編碼方式是自上而下的,但是非同步 I/O 編程模型大多採用非同步回調的方式。

隨著項目工程的複雜度增加,由於採用非同步回調編寫的代碼和常規編碼思維相悖,尤其是回調嵌套多層的時候,不僅開發維護成本指數級上升,出錯的幾率以及排查問題的難度也大幅度增加。

正是由於非同步 I/O 編程模型有上面提到的種種缺點, 我們經過一系列調研工作之後,決定引入協程來解決我們的痛點, 下麵先來看一下cppreference中對協程的描述:

A coroutine is a function that can suspend execution to be resumed later. Coroutines are stackless: they suspend execution by returning to the caller and the data that is required to resume execution is stored separately from the stack. This allows for sequential code that executes asynchronously (e.g. to handle non-blocking I/O without explicit callbacks).

fromhttps://en.cppreference.com/w/cpp/language/coroutines

協程實際上就是一個可以掛起(suspend)恢復(resume)的函數,我們可以暫停協程的執行,去做其他事情,然後在適當的時候恢復到之前暫停的位置繼續執行接下來的邏輯。總而言之,協程可以讓我們使用同步方式編寫非同步代碼

file
圖:協程切換示意圖

KeeWiDB 為每一個客戶端連接都創建了一個協程,以上圖為例,在工作線程內服務三個客戶端連接,就創建三個協程。在[T0,T1)階段協程0正在執行邏輯代碼,但是到了T1時刻協程0發現需要執行磁碟 I/O 操作獲取數據,於是讓出執行權並且等待 I/O 操作完成,此時協程2獲取到執行權,並且在[T1,T2)時間段內執行邏輯代碼,到了T2時刻協程2讓出執行權,並且此時協程0的 I/O 事件正好完成了,於是執行權又回到協程0手中繼續執行。

可以看得出來,通過引入協程,我們有效解決了由於同步 I/O 操作導致線程阻塞的問題,使線程儘可能的繁忙起來,提高了線程內的併發;另外由於協程切換隻涉及基本的 CPU 上下文切換,並且完全在用戶空間進行,而線程切換涉及特權模式切換,需要在內核空間完成,所以協程切換比線程切換開銷更小,性能更優

三、數據存儲

在文章開頭有提到在持久記憶體出現後,進一步縮小了與記憶體的性能差距,持久記憶體是一種新的存儲技術,它結合了 DRAM 的性能和位元組定址能力以及像 SSD 等傳統存儲設備的可持久化特性,正是這些特性使得持久記憶體非常有前景,並且也非常適合用於資料庫系統。

file
圖:Dram和PMem以及SSD的性能比較

通過上圖可以看到,PMem(Persistent Memory)相對於 DRAM 有著更大的容量,但是相對於 SSD 有著更大的帶寬和更低的讀寫延遲,正是因為如此,它非常適合存儲引擎中的大容量Cache和高性能 WAL 日誌。

前面有提到過日誌文件在寫入的過程中涉及到的持久化操作有可能會成為整個系統的瓶頸,我們通過將 WAL 存放在 PMem 上,日誌持久化操作耗時大幅降低,提升了服務整體的性能;此外由於 PMem 的讀寫速度比 SSD 要快1~2個數量級,在故障恢復期間,回放 WAL 的時間也大幅度的縮短,整個系統的可用性得到了大幅度的提升。

考慮到 KeeWiDB 作為高性能低延遲的資料庫,我們不僅需要做到平均延遲低,更要做到長尾延遲可控。

雖然在涉及到文件操作的場景下,利用 Page Cache 技術能夠大幅提升文件的讀寫速度,但是由於 Page Cache 預設由操作系統調度分配,存在一定的不確定性(內核總是積極地將所有空閑記憶體都用作 Page Cache 和 Buffer Cache,當記憶體不夠用時就會使用 LRU 等演算法淘汰緩存頁, 此時有可能造成文件讀寫操作有時延抖動),在一些極端場景下可能會直接影響客戶實例的P99,P100。所以** KeeWiDB 採用了 Direct I/O的方式來繞過操作系統的 Page Cache 並自行維護一份應用層數據的 Cache,讓磁碟的 IO 更加可控**。

使用 Page cache 能夠大大加速文件的讀寫速度,那什麼是頁面緩存(Page Cache)呢?

In computing, a page cache, sometimes also called disk cache, is a transparent cache for the pages originating from a secondary storage device such as a hard disk drive (HDD) or a solid-state drive (SSD). The operating system keeps a page cache in otherwise unused portions of the main memory (RAM), resulting in quicker access to the contents of cached pages and overall performance improvements. A page cache is implemented in kernels with the paging memory management, and is mostly transparent to applications.

fromhttps://en.wikipedia.org/wiki/Page_cache#cite_note-1

和大多數磁碟資料庫一樣,KeeWiDB 將 Page 作為存儲引擎磁碟管理的最小單位,將數據文件內部劃分成若幹個 Page,每個 Page 的大小為 4K,用於存儲用戶數據和一些我們存儲引擎內部的元信息。從大容量低成本的角度出發,KeeWiDB 將數據文件存放在 SSD 上。

file
圖:數據頁的升溫和落冷

此外,得益於 PMem 接近於 DRAM 的讀寫速度以及支持位元組定址的能力,KeeWiDB在 PMem 上實現了存儲引擎的 Cache 模塊,在服務運行期間存放業務熱數據的數據頁會被載入到 PMem 上,KeeWiDB 在處理用戶請求期間不再直接操作 SSD 上的數據頁,而是操作讀寫延遲更低的 PMem,使得 KeeWiDB 的性能以及吞吐量得到了進一步的提升;

同時為了能夠合理高效的利用 PMem 上的空間,KeeWiDB 內部實現了高效的 LRU 淘汰演算法,並且通過非同步刷髒的方式,將 PMem 中長時間沒有訪問的數據頁寫回到 SSD 上的數據文件中。

四、主從同步

文章開頭的架構圖有提到 KeeWiDB 集群由多個分片構成,每個分片內部有多個節點,這些節點共同組成一主多從的高可用架構。和 Redis 類似,用戶的請求會根據 Key 被路由到對應分片的主節點,主節點執行完後再將請求轉化為 Binlog Record 寫入本地的日誌文件並轉發給從節點,從節點通過應用日誌文件完成數據的複製。

在 Redis 的 Replication 實現中,從節點每接收一個請求都立即執行,然後再繼續處理下一個請求,如此往複。依賴其全記憶體的實現,單個請求的執行耗時非常短,從節點的回放相當於是單連接的 pipeline 寫入,其回放速度足以跟上主節點的執行速度。但這種方式卻不適合 KeeWiDB 這樣的存儲型資料庫,主要原因如下:

  • 存儲型資料庫的請求執行過程中涉及到磁碟 IO,單個請求的執行耗時本身就比較長;
  • 主節點同時服務多個客戶端連接,不同連接的請求併發執行,發揮了協程非同步 IO 的優勢,節點整體 QPS 有保障;
  • 主從同步只有一個連接,由於從庫順序回放請求,無法併發,回放的 QPS 遠遠跟不上主節點處理用戶請求的 QPS;

為了提升從節點回放的速度,避免在主庫高負載寫入場景下,出現從庫追不上主庫的問題,KeeWiDB 的 Replication 機製做了以下兩點改進:

  • 在從節點增加 RelayLog 作為中繼,將從節點的命令接收和回放兩個過程拆開,避免回放過程拖慢命令的接收速度;
  • 在主節點記錄 Binlog 的時候增加邏輯時鐘信息,回放的時候根據邏輯時鐘確定依賴關係,將互相之間沒有依賴的命令一起放進回放的協程池,併發完成這批命令的回放,提升從節點整體的回放 QPS;

file
圖:從庫併發回放

所謂的邏輯時鐘,對應到 KeeWiDB 的具體實現里,就是我們在每一條 BinLog record 中添加了 seqnumparent 兩個欄位:

  • seqnum 是主節點事務 commit 的序列號,每次有新的事務 commit,當前 seqnum 賦給當前事務,全局 seqnum 自增1;
  • parent 由主節點在每個事務開始執行前的 prepare 階段獲取,記錄此時已經 commit 的最大 seqnum,記為 max,說明當前事務是在 max 對應事務 commit 之後才開始執行,二者在主節點端有邏輯上的先後關係;

從節點回放 RelayLog 中的 Binlog Record 時,我們只需要簡單地將它的 parent 和 seqnum 看作一個區間,簡記為(P,S),如果它的(P,S)區間和當前正在回放的其它Record 的(P,S)區間有交集,說明他們在主節點端 Prepare 階段沒有衝突,可以把這條 Record 放進去一塊併發地回放,反之,則這條 Record 需要阻塞等待,等待當前正在回放的這批 Binlog Record 全部結束後再繼續。

通過在 Binlog 中添加 seqnum 和 parent 兩個欄位,我們在保證數據正確性的前提下實現了從庫的併發回放,確保了主庫在高負載寫入場景下,從庫依舊可以輕鬆的追上主庫,為我們整個系統的高可用提供了保障。

五、總結

本篇文章先從整體架構介紹了 KeeWiDB 的各個組件,然後深入 Server 內部分析了線上程模型選擇時的一些思考以及面臨的挑戰,最後介紹了存儲引擎層面的數據文件以及相關日誌在不同存儲介質上的分佈情況,以及 KeeWiDB 是如何解決從庫回放 Binlog 低效的問題。通過本文,相信不少讀者對 KeeWiDB 又有了進一步的瞭解。那麼,在接下來的文章中我們還會深入到 KeeWiDB 自研存儲引擎內部,向讀者介紹 KeeWiDB 在存儲引擎層面如何實現高效的數據存儲和索引,敬請期待。

目前,KeeWiDB 正在公測階段(鏈接:https://cloud.tencent.com/product/keewidb ),現已在內外部已經接下了不少業務,其中不乏有一些超大規模以及百萬 QPS 級的業務,線上服務均穩定運行中。

後臺回覆“KeeWiDB”,試試看,有驚喜。

關於作者
吳顯堅,騰訊雲資料庫高級工程師。負責過開源項目Pika的核心研發工作,對資料庫、分散式存儲有一定瞭解,現從事騰訊雲Redis內核以及KeeWiDB的研發工作。


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

-Advertisement-
Play Games
更多相關文章
  • 一:背景 1.講故事 這篇文章起源於昨天的一位朋友發給我的dump文件,說它的程式出現了卡死,看了下程式的主線程棧,居然又碰到了 OnUserPreferenceChanged 導致的掛死問題,真的是經典中的經典,線程棧如下: 0:000:x86> !clrstack OS Thread Id: 0 ...
  • Red Giant PluralEyes for Mac雖然只是Shooter Suite其中的一部分,但是卻十分受歡迎,功能也非常強大。PluralEyes Mac版提供了用戶需要的音頻和視頻同步的一切功能,可以自動分析視頻和音頻文件,並同步起來。 詳情:Red Giant PluralEyes ...
  • PasteNow mac是一款強大的剪貼板工具,該軟體使你的日常工作更輕鬆和快捷。你可以通過它存儲各種各樣的臨時數據:文本、鏈接、圖像等等,功能非常的豐富。 詳情:PasteNow for Mac 功能特色 - 在多台設備同步數據 - 創建智能列表以更方便地過濾數據 - 支持各種風格不同的列表樣式 ...
  • EdgeView 3是一款運行在Mac系統上的圖片查看器,不僅可以打開JPEG、PNG、TIFF、BMP、DSlr、Eps、PDF、AI(Adobe illustrator)的RAW文件等各種圖像文件,還可以直接打開存檔中的圖像文件,無需提取。 詳情:EdgeView 3 for Mac 亮點特征: ...
  • TEE學習(一) OP-TEE OP-TEE CONCEPT OP-TEE(open source project Trusted Execution Environment),REE中的系統和應用無法直接訪問TEE中的資源,只能通過TEE提供的介面獲取一個結果。 main design goals ...
  • MergeTree擁有主鍵,但是它的主鍵卻沒有唯一鍵的約束。這意味著即便多行數據的主鍵相同,它們還是能夠被正常寫入。在某些使用場合,用戶並不希望數據表中含有重覆的數據。ReplacingMergeTree就是在這種背景下為了數據去重而設計的,它能夠在合併分區時刪除重覆的數據。但是ReplacingM ...
  • 課件獲取:關註公眾號 “數棧研習社”,後臺私信 “ChengYing” 獲得直播課件 視頻回放:點擊這裡 ChengYing 開源項目地址:github 丨 gitee 喜歡我們的項目給我們點個__ STAR!STAR!!STAR!!!(重要的事情說三遍)__ 技術交流釘釘 qun:30537511 ...
  • 不知不覺間,2022年的腳步已經走到了倒數第二個月。臨近年末,我們對產品本身以及客戶反饋的一些問題進行了持續的更新和優化,例如基線告警、數據服務平臺新增TDengine 數據源支持、行級許可權根據用戶屬性實現動態賦權。 以下為袋鼠雲產品功能更新報告第二期內容,更多探索,請繼續閱讀。 數棧DTinsig ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...