Jack Jiang

          我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
          posts - 506, comments - 13, trackbacks - 0, articles - 1

               摘要: 本文由騰訊PCG后臺開發工程師的SG4YK分享,進行了修訂和和少量改動。1、引言近日學習了 Protobuf 的編碼實現技術原理,借此機會,正好總結一下并整理成文。接上篇《由淺入深,從根上理解Protobuf的編解碼原理》,本篇將從Base64再到Base128編碼,帶你一起從底層來理解Protobuf的數據編碼原理。本文結構總體與 Protobuf 官方文檔相似,不少內容也來自官方文檔,并在官方...  閱讀全文

          posted @ 2022-12-02 12:33 Jack Jiang 閱讀(140) | 評論 (0)編輯 收藏

               摘要: 本文由碼農的荒島求生陸小風分享,為了提升閱讀體驗,進行了較多修訂和排版。1、引言搞即時通訊IM方面開發的程序員,在談到通訊層實現時,必然會提到網絡編程。那么計算機網絡編程中的一個非常基本的問題:到底該怎樣組織Client與server之間交互的數據呢?本篇文章我們不討論IM系統中的那些高端技術話題,我們回歸到通訊的本質——也就是數據在網絡中交互時的編解碼原理,并由淺入深從底...  閱讀全文

          posted @ 2022-11-24 11:43 Jack Jiang 閱讀(145) | 評論 (0)編輯 收藏

          本文由vivo技術團隊Li Guanyun分享,為了提升閱讀體驗,行了較多修訂和重新排版。

          1、引言

          Protobuf 作為一種跨平臺、語言無關、可擴展的序列化結構數據通訊協議,已廣泛應用于網絡數據交換的場景中(比如IM通信、分布式RPC調用等)。

          隨著互聯網的發展,分布式系統的異構性會愈發突出,跨語言的需求會愈加明顯,同時 gRPC 也大有取代Restful之勢,而 Protobuf 作為gRPC 跨語言、高性能的法寶,我們技術人有必要深入理解 Protobuf 原理,為以后的技術更新和選型打下基礎。

          借此機會,我將個人的Protobuf學習過程以及實踐經驗,總結成文,與大家一起探討學習。本篇主要從Protobuf的基礎概念開始,包括技術背景、技術原理、使用方法和優缺點。

          PS:本篇本跟上篇《Protobuf從入門到精通,一篇就夠!》類似,都適合作為Protobuf的入門文章,但本篇力求簡潔,盡量不涉及Protobuf的具體技術細節,目的是降低閱讀的門檻、提升閱讀效果,希望對你有用。

          學習交流:

          (本文已同步發布于:http://www.52im.net/thread-4081-1-1.html

          2、系列文章

          本文是系列文章中的第 2 篇,本系列總目錄如下:

          • IM通訊協議專題學習(一):Protobuf從入門到精通,一篇就夠!
          • IM通訊協議專題學習(二):快速理解Protobuf的背景、原理、使用、優缺點》(* 本文
          • 《IM通訊協議專題學習(三):由淺入深,從通信編解碼原理上理解Protobuf》(稍后發布..)
          • 《IM通訊協議專題學習(四):從Base64到Protobuf,詳解Protobuf的數據編碼原理》(稍后發布..)
          • 《IM通訊協議專題學習(五):Protobuf到底比JSON快幾倍?請看全方位實測!》(稍后發布..)
          • 《IM通訊協議專題學習(六):手把手教你如何在Android上從零使用Protobuf》(稍后發布..)
          • 《IM通訊協議專題學習(七):手把手教你如何在NodeJS中從零使用Protobuf》(稍后發布..)
          • 《IM通訊協議專題學習(八):金蝶隨手記團隊的Protobuf應用實踐(原理篇)  》(稍后發布..)
          • 《IM通訊協議專題學習(九):金蝶隨手記團隊的Protobuf應用實踐(實戰篇) 》(稍后發布..)

          3、什么是Protobuf?

          Protobuf(全稱是Protocol Buffers)是一種跨平臺、語言無關、可擴展的序列化結構數據的方法,可用于網絡通信數據交換及存儲。

          在序列化結構化數據的機制中,Protobuf是靈活、高效、自動化的,相對常見的XML、JSON,描述同樣的信息,Protobuf序列化后數據量更小、序列化/反序列化速度更快、更簡單。

          一旦定義了要處理的數據的數據結構之后,就可以利用Protobuf的代碼生成工具生成相關的代碼。只需使用 Protobuf 對數據結構進行一次描述,即可利用各種不同語言(proto3支持C++, Java, Python, Go, Ruby, Objective-C, C#)或從各種不同流中對你的結構化數據輕松讀寫。

          PS:類似的介紹,在上篇《Protobuf從入門到精通,一篇就夠!》中也有涉及,有興趣可以一并閱讀之。

          4、為什么是 Protobuf?

          4.1 技術背景

          大家可能會覺得 Google 發明 Protobuf 是為了解決序列化速度的,其實真實的原因并不是這樣的。

          Protobuf最先開始是 Google用來解決索引服務器 request/response 協議的。

          在沒有Protobuf之前,Google 已經存在了一種 request/response 格式,用于手動處理 request/response 的編解碼。

          這種sstk式也能支持多版本協議,不過代碼不夠優雅:

          if(protocolVersion=1) {

              doSomething();

          } elseif(protocolVersion=2) {

              doOtherThing();

          } ...

          如果是非常明確的格式化協議,會使新協議變得非常復雜。因為開發人員必須確保請求發起者與處理請求的實際服務器之間的所有服務器都能理解新協議,然后才能切換開關以開始使用新協議。

          這也就是每個服務器開發人員都遇到過的低版本兼容、新舊協議兼容相關的問題。

          為了解決這些問題,于是Protobuf就誕生了。

          4.2 Protobuf 誕生了

          Protobuf 最初被寄予以下 2 個期望:

          • 1)更容易引入新的字段,并且不需要檢查數據的中間服務器可以簡單地解析并傳遞數據(而無需了解所有字段);
          • 2)數據格式更加具有自我描述性,可以用各種語言來處理(比如C++, Java 等各種語言)。

          但這個版本的 Protobuf 仍需要自己手寫解析的代碼。

          隨著Protobuf的發展、演進,它具有了更多的特性:

          • 1)自動生成的序列化和反序列化代碼(避免了手動解析的需要。官方提供自動生成代碼工具,各個語言平臺的基本都有);
          • 2)除了用于數據交換之外,Protobuf也被用作某些持久化數據的便捷自描述格式。

          Protocol Buffers 命名的由來:

          Why the name "Protocol Buffers"?

          The name originates from the early days of the format, before we had the protocol buffer compiler to generate classes for us. At the time, there was a class called ProtocolBuffer which actually acted as a buffer for an individual method. Users would add tag/value pairs to this buffer individually by calling methods like AddValue(tag, value). The raw bytes were stored in a buffer which could then be written out once the message had been constructed.

          Since that time, the "buffers" part of the name has lost its meaning, but it is still the name we use. Today, people usually use the term "protocol message" to refer to a message in an abstract sense, "protocol buffer" to refer to a serialized copy of a message, and "protocol message object" to refer to an in-memory object representing the parsed message.

          4.3 Protobuf 在谷歌業務中的地位

          Protobuf 現在是 Google 用于數據交換和存儲的通用語言。

          谷歌代碼樹中定義了 48162 種不同的消息類型,包括 12183 個 .proto 文件。它們既用于 RPC 系統,也用于在各種存儲系統中持久存儲數據。

          Protobuf 誕生之初是為了解決服務器端新舊協議(高低版本)兼容性問題,名字也很體貼——“協議緩沖區”,只不過后期慢慢發展成用于傳輸數據。

          5、Protobuf 協議的工作原理

          如下圖所示:可以看到,對于序列化協議來說,使用方只需要關注業務對象本身,即 idl 定義,序列化和反序列化的代碼只需要通過工具生成即可。

          6、Protobuf 協議的消息定義

          Protobuf 的消息是在idl文件(.proto)中描述的。

          下面是本次樣例中使用到的消息描述符 customer.proto

          syntax = "proto3";

           

          package domain;

           

          option java_package = "com.Protobuf.generated.domain";

          option java_outer_classname = "CustomerProtos";

           

          message Customers {

              repeated Customer customer = 1;

          }

           

          message Customer {

              int32 id= 1;

              string firstName = 2;

              string lastName = 3;

           

              enum EmailType {

                  PRIVATE = 0;

                  PROFESSIONAL = 1;

              }

           

              message EmailAddress {

                  string email = 1;

                  EmailType type= 2;

              }

           

              repeated EmailAddress email = 5;

          }

          上面的消息比較簡單,Customers包含多個Customer(Customer包含一個id字段、一個firstName字段、一個lastName字段以及一個email的集合)。

          除了上述定義外,文件頂部還有三行可幫助代碼生成器的申明:

          • 1)syntax = "proto3":用于idl語法版本,目前有兩個版本proto2和proto3,兩個版本語法不兼容,如果不指定,默認語法是proto2(由于proto3比proto2支持的語言更多,語法更簡潔,本文使用的是proto3);
          • 2)package domain:此配置用于嵌套生成的類/對象;
          • 3)option java_package:生成器還使用此配置來嵌套生成的源(此處的區別在于這僅適用于Java,在使用Java創建代碼和使用JavaScript創建代碼時,使用了兩種配置來使生成器的行為有所不同。也就是說,Java類是在包com.Protobuf.generated.domain下創建的,而JavaScript對象是在包domain下創建的)。

          Protobuf 提供了更多選項和數據類型,本文不做詳細介紹,感興趣可以參考官方文檔

          7、Protobuf 的代碼生成

          首先安裝 Protobuf 編譯器 protoc(點這里有詳細的安裝教程)。

          安裝完成后,可以使用以下命令生成 Java 源代碼:

          1protoc --java_out=./src/main/java./src/main/idl/customer.proto

          上述命令的意圖是:從項目的根路徑執行該命令,并添加了兩個參數 java_out(即定義 ./src/main/java/ 為Java代碼的輸出目錄;而 ./src/main/idl/customer.proto 是.proto文件所在目錄)。

          生成的代碼非常復雜,但幸運的是它的用法卻非常簡單:

          CustomerProtos.Customer.EmailAddress email = CustomerProtos.Customer.EmailAddress.newBuilder()

                  .setType(CustomerProtos.Customer.EmailType.PROFESSIONAL)

                  .setEmail("crichardson@email.com").build();

           

          CustomerProtos.Customer customer = CustomerProtos.Customer.newBuilder()

                  .setId(1)

                  .setFirstName("Lee")

                  .setLastName("Richardson")

                  .addEmail(email)

                  .build();

          // 序列化

          byte[] binaryInfo = customer.toByteArray();

          System.out.println(bytes_String16(binaryInfo));

          System.out.println(customer.toByteArray().length);

          // 反序列化

          CustomerProtos.Customer anotherCustomer = CustomerProtos.Customer.parseFrom(binaryInfo);

          System.out.println(anotherCustomer.toString());

          8、Protobuf 的性能數據

          我們簡單地以上述Customers為模型,分別構造、選取小對象、普通對象、大對象進行性能對比。

          序列化耗時以及序列化后數據大小對比:

          反序列化耗時:

          更多性能數據可以參考官方的測試Benchmark

          9、Protobuf 的優點

          9.1效率高

          從序列化后的數據體積角度,與XML、JSON這類文本協議相比,Protobuf通過 T-(L)-V(TAG-LENGTH-VALUE)方式編碼,不需要", {, }, :等分隔符來結構化信息。同時在編碼層面使用varint壓縮。

          所以描述同樣的信息,Protobuf序列化后的體積要小很多,在網絡中傳輸消耗的網絡流量更少,進而對于網絡資源緊張、性能要求非常高的場景。比如在移動網絡下的IM即時通訊應用中,Protobuf協議就是非常不錯的選擇(PS:這也是我為什么著手分享Protobuf系列文章的原因啦)。

          我們來簡單做個對比。

          要描述如下JSON數據:

          1{"id":1,"firstName":"Chris","lastName":"Richardson","email":[{"type":"PROFESSIONAL","email":"crichardson@email.com"}]}

          使用JSON序列化后的數據大小為118byte:

          7b226964223a312c2266697273744e616d65223a224368726973222c226c6173744e616d65223a2252696368617264736f6e222c22656d61696c223a5b7b2274797065223a2250524f46455353494f4e414c222c22656d61696c223a226372696368617264736f6e40656d61696c2e636f6d227d5d7d

          而使用Protobuf序列化后的數據大小為48byte:

          0801120543687269731a0a52696368617264736f6e2a190a156372696368617264736f6e40656d61696c2e636f6d1001

          從序列化/反序列化速度角度,與XML、JSON相比,Protobuf序列化/反序列化的速度更快,比XML要快20-100倍。

          9.2支持跨平臺、多語言

          Protobuf是平臺無關的,無論是Android、iOS、PC,還是C#與Java,都可以利用Protobuf進行無障礙通訊。

          proto3支持C++、Java、Python、Go、Ruby、Objective-C、C#(詳見《Protobuf從入門到精通,一篇就夠》)。

          9.3擴展性、兼容性好

          Protobuf具有向后兼容的特性:更新數據結構以后,老版本依舊可以兼容,這也是Protobuf誕生之初被寄予解決的問題,因為編譯器對不識別的新增字段會跳過不處理。

          9.4使用簡單

          Protobuf 提供了一套編譯工具,可以自動生成序列化、反序列化的樣板代碼,這樣開發者只要關注業務數據idl,簡化了編碼解碼工作以及多語言交互的復雜度。

          10、Protobuf 的缺點

          Protobuf的優點很突出,但缺點也很明顯。

          Protobuf的缺點主要是:

          • 1)不具備自描述能力:跟XML、JSON相比,這兩者是自描述的,而ProtoBuf則不是;
          • 2)數據可讀性非常差:ProtoBuf是二進制協議,如果沒有idl文件,就無法理解二進制數據流,對調試非常不友好。

          不過:Charles已經支持Protobuf協議,導入數據的描述文件即可,詳情可參考 Charles Protocol Buffers

          然而:由于沒有idl文件無法解析二進制數據流,ProtoBuf在一定程度上可以保護數據,提升核心數據被破解的門檻,降低核心數據被盜爬的風險(也算是缺點變優點的典型范例)。

          11、參考資料

          [1] Protobuf官方網站

          [2] Protobuf從入門到精通,一篇就夠!

          [3] 如何選擇即時通訊應用的數據傳輸格式

          [4] 強列建議將Protobuf作為你的即時通訊應用數據傳輸格式

          [5] APP與后臺通信數據格式的演進:從文本協議到二進制協議

          [6] 面試必考,史上最通俗大小端字節序詳解

          [7] 移動端IM開發需要面對的技術問題(含通信協議選擇)

          [8] 簡述移動端IM開發的那些坑:架構設計、通信協議和客戶端

          [9] 理論聯系實際:一套典型的IM通信協議設計詳解

          [10] 58到家實時消息系統的協議設計等技術實踐分享

          (本文已同步發布于:http://www.52im.net/thread-4081-1-1.html

          posted @ 2022-11-17 10:52 Jack Jiang 閱讀(96) | 評論 (0)編輯 收藏

          為了更好地分類閱讀52im.net 總計1000多篇精編文章,我將在每周三推送新的一期技術文集,本次是第5 期。

          * 評語:本系列文章盡量使用最淺顯易懂的文字、圖片來組織內容,力求通信技術零基礎的人群也能看懂。但個人建議,至少稍微了解過網絡通信方面的知識后再看,會更有收獲。特別推薦即時通訊開發者來閱讀,因為針對移動弱網的問題,確實可以找到很多有價值的答案。


          [- 1 -] IM開發者的零基礎通信技術入門(一):通信交換技術的百年發展史(上)

          [鏈接]http://www.52im.net/thread-2354-1-1.html

          [摘要] 本文(上下兩篇)將帶你了解當今通信交換技術最初的模樣以及發展過程。學習技術更要了解技術的前世今生,技術本無聊,故事很有趣。


          [- 2 -]IM開發者的零基礎通信技術入門(二):通信交換技術的百年發展史(下)

          [鏈接]http://www.52im.net/thread-2356-1-1.html

          [摘要] 接上篇,本篇里我們需要暫停一下,回過頭來看看我們國家的交換機發展情況。


          [- 3 -] IM開發者的零基礎通信技術入門(三):國人通信方式的百年變遷

          [鏈接] http://www.52im.net/thread-2360-1-1.html

          [摘要] 本文通過大量珍貴歷史圖片,從中國第一條電報線路,到如今觸手可及的5G網絡,回顧過去、展望未來,一起來看國人通信方式的百年歷史變遷。


          [- 4 -]IM開發者的零基礎通信技術入門(四):手機的演進,史上最全移動終端發展史

          [鏈接] http://www.52im.net/thread-2369-1-1.html

          [摘要] 本文將通過大量歷史圖片,講述手機這種移動終端的演化過程,為您呈現如今已深度融入人類生活的智能手機本來的樣子。了解過去,才能更好地展望未來。


          [- 5 -] IM開發者的零基礎通信技術入門(五):1G到5G,30年移動通信技術演進史

          [鏈接] http://www.52im.net/thread-2373-1-1.html

          [摘要] 今天的5G,3.5GHz+大規模MIMO+波束賦形,還有固定無線應用,不禁讓人看到了當年3G時代WiMax的影子,但WiMax為何輸給了LTE,難道命運也喜歡對技術開玩笑嗎?一部跨越三十年驚心動魄的移動通信史,為你揭曉答案。


          [- 6 -] IM開發者的零基礎通信技術入門(六):移動終端的接頭人——“基站”技術

          [鏈接] http://www.52im.net/thread-2375-1-1.html

          [摘要]自上個世紀70年代末移動通信網絡誕生以來,移動通信基站已經陪伴人類40年了,為人類社會帶來了空前的變革,但你知道它的故事嗎?


          [- 7 -] IM開發者的零基礎通信技術入門(七):移動終端的千里馬——“電磁波”

          [鏈接] http://www.52im.net/thread-2382-1-1.html

          [摘要] 本文將回歸到無線通信的技術之魂——“電磁波”,盡量用通俗易懂的文字講述這個稍顯枯燥的通信技術基礎知識。


          [- 8 -] IM開發者的零基礎通信技術入門(八):零基礎,史上最強“天線”原理掃盲

          [鏈接] http://www.52im.net/thread-2385-1-1.html

          [摘要] 實際生活中,無線通信中的天線都長什么樣?有哪些用途?更重要的是,天線的技術原理是怎樣的?本文將通過大量的圖片,為你講述這些內容。本文力求通俗易懂,面向零基礎讀者,希望繼續給即時通訊網的開發者帶來更多通信技術方面的收獲。


          [- 9 -] IM開發者的零基礎通信技術入門(九):無線通信網絡的中樞——“核心網”

          [鏈接]http://www.52im.net/thread-2391-1-1.html

          [摘要] 對于通信專業的人來說,幾乎每個人都認為核心網難(不只是難,而且是非常難),很難有人能通俗易懂地講明白它是什么東西。所以本文想借此機會,為零基礎的IM開發者或其他移動端應用層程序員們,講清楚這個話題。


          [- 10 -] IM開發者的零基礎通信技術入門(十):零基礎,史上最強5G技術掃盲

          [鏈接] http://www.52im.net/thread-2394-1-1.html

          [摘要] 作為IM開發者,或者移動端開發者來說,提前了解5G技術顯然是很有必要的。那么什么是5G技術?技術原理是怎么樣的?5G技術將帶來哪些技術革新?本文將以零基礎的應用程序開發者為閱讀對象,幫你找到這些問題的答案。


          [- 11 -] IM開發者的零基礎通信技術入門(十一):為什么WiFi信號差?一文即懂!

          [鏈接] http://www.52im.net/thread-2402-1-1.html

          [摘要] 為什么WiFi信號會受影響?什么情況下該使用何種方式組網?如何改善WiFi信號差的問題?等等,本文將通俗易懂地為你找到這些問題的答案。


          [- 12 -] IM開發者的零基礎通信技術入門(十二):上網卡頓?網絡掉線?一文即懂!

          [鏈接]http://www.52im.net/thread-2406-1-1.html

          [摘要] 本文將詳細介紹生活中遇到的常見網絡問題,及可能的解決方法,雖說是一篇技術文章,但內容將一如既往地通俗易懂,簡單實用。


          [- 13 -] IM開發者的零基礎通信技術入門(十三):為什么手機信號差?一文即懂!

          [鏈接] http://www.52im.net/thread-2415-1-1.html

          [摘要] 關于手機信號的問題真的不是大家想象得那么簡單。本文正好收集整理了這一塊的通信技術知識,一如既往的力求通俗易懂,希望對你有用。


          [- 14 -] IM開發者的零基礎通信技術入門(十四):高鐵上無線上網有多難?一文即懂!

          [鏈接] http://www.52im.net/thread-2419-1-1.html

          [摘要] 為什么在高鐵上手機信號會這么差?這個無線通信難題真的無法解決嗎?今天,作為通信老司機的筆者,就詳細和大家聊聊這個問題。


          [- 15 -] IM開發者的零基礎通信技術入門(十五):理解定位技術,一篇就夠

          [鏈接]http://www.52im.net/thread-2428-1-1.html

          [摘要] 定位技術到底是怎么實現的?技術原理怎樣?有哪些局限性?貌似我們平時也沒有做更多了解,既然這樣,那就跟著本文來一窺究竟吧。


          ??52im社區本周新文:《IM通訊協議專題學習(一):Protobuf從入門到精通,一篇就夠! http://www.52im.net/thread-4080-1-1.html》,歡迎閱讀!??

          我是Jack Jiang,我為自已帶鹽!https://github.com/JackJiang2011/MobileIMSDK/

          posted @ 2022-11-11 11:33 Jack Jiang 閱讀(221) | 評論 (0)編輯 收藏

               摘要: 本文由IBM開發者社區分享,有較多修訂和改動。1、引言在當今移動網絡時代,手機流量和電量是寶貴的資源,對于移動端最常見的即時通訊IM應用,由于實時通信基于Socket長連接,它對于流量和電量的需求較一般應用來說更高(詳見《移動端IM實踐:WhatsApp、Line、微信的心跳策略分析》)。在IM應用中,優化數據流量消耗過多的基本方法就是使用高度壓縮的通訊協議,而數據壓縮后流量減小帶來的自然結果也就...  閱讀全文

          posted @ 2022-11-10 11:49 Jack Jiang 閱讀(119) | 評論 (0)編輯 收藏

          關于MobileIMSDK

          MobileIMSDK 是一套專門為移動端開發的開源IM即時通訊框架,超輕量級、高度提煉,一套API優雅支持UDP 、TCP 、WebSocket 三種協議,支持iOS、Android、H5、標準Java平臺,服務端基于Netty編寫。

          工程開源地址是:

          關于RainbowChat

          ► 詳細產品介紹:http://www.52im.net/thread-19-1-1.html
          ► iOS端更新記錄:http://www.52im.net/thread-2735-1-1.html
          ► 全部運行截圖:iOS端全部運行截圖 (另:Android端運行截圖 點此查看
          ► 在線體驗下載:App Store安裝地址 (另:Android端下載體驗 點此查看

           

          RainbowChat是一套基于開源IM聊天框架 MobileIMSDK 的產品級移動端IM系統。RainbowChat源于真實運營的產品,解決了大量的屏幕適配、細節優化、機器兼容問題(可自行下載體驗:專業版下載安裝)。

          RainbowChat可能是市面上提供im即時通訊聊天源碼的,唯一一款同時支持TCP、UDP兩種通信協議的IM產品(通信層基于開源IM聊天框架 MobileIMSDK 實現)。

          v6.1 版更新內容

          此版更新內容更多歷史更新日志):

          • 1)[bug] 在聊天信息界面中查找消息時,點擊查看指定消息,在聊天界面中不能自動滾動到這條消息;
          • 2)[bug] 點擊首頁“消息”列表中遺留的陌生人聊天信息時,無法重置消息未讀數的問題;
          • 3)[bug] 在聊天界面中進入別的界面再回來時,底部面板沒有自動關閉/收起;
          • 4)[優化] 優化了標題欄彈出菜單的圓角效果(使之更符合最新iOS美感設計);
          • 5)[優化] 優化了APP中各種文本輸入框UI效果,以及其它UI細節;
          • 6)[優化] 優化了短視頻錄制界面在iOS16“靈動島”手機上的ui適配。

          此版主要功能運行截圖更多截圖點此查看):

           

          posted @ 2022-11-05 17:42 Jack Jiang 閱讀(92) | 評論 (0)編輯 收藏

          1、引言

          在《IM消息ID技術專題》系列文章的前幾篇中,我們已經深切體會到消息ID在分布式IM聊天系統中的重要性以及技術實現難度,各種消息ID生成算法及實現雖然各有優勢,但受制于具體的應用場景,也并不能一招吃遍天下,所以真正在IM系統中該如何落地消息ID算法和實現邏輯,還是要因地致宜,根據自已系統的設計邏輯和產品定義取其精華,綜合應用之。

          本文將基于網易嚴選的訂單ID使用現狀,分享我們是如何結合業內常用的分布式ID解決方案,從而在此基礎之上進行ID特性豐富,并不斷提升系統可用性和穩定性保障。同時,也對ID生成算法的落地實踐過程中遇到坑進行了深入剖析。

          本篇中的訂單ID雖然不同于IM系統中的消息ID,但其技術實踐仍然相通,希望能給你的IM系統消息ID技術選型也來更多的啟發。

          學習交流:

          本文已同步發布于:http://www.52im.net/thread-4069-1-1.html

          2、關于作者

          西狂:服務端研發工程師, 早期參與嚴選采購、嚴選財務、嚴選合伙人以及報警平臺等系統后端建設,目前主要致力于嚴選交易域技術演進以及業務研發工作。

          3、系列文章

          本文是系列文章中的第7篇,本系列總目錄如下:

          4、為什么需要分布式ID?

          4.1 業務背景

          如上圖所示,對于網易嚴選的主站、分銷和tob都會生成各自的訂單ID,在同步訂單數據到訂單中心的時候,訂單中心會生成一個訂單中心內部的一個訂單號,只是推送給到下游倉配時使用的訂單ID略有不同。

          4.2 帶來的問題

           因為訂單ID使用的混亂,導致了一系列問題的產生,例如: 溝通壁壘 、管控困難以及代碼腐化等等。

          4.3 技術目標

           我們希望通過分布式ID來幫助生成訂單ID,在業務規則上必須全局唯一、安全性高,在性能上要高可用、低延遲。

          5、我們的分布式ID架構原理

          5.1 技術選型

          下表是業內常見的分布式ID解決方案:

          綜合考慮是否支持水平擴展以及能夠顯示指定ID長度,最終選擇的是Leaf的Segment模式(詳見《深度解密美團的分布式ID生成算法》)。

          5.2 架構簡介

          Leaf采用了預分發的方式來生成ID(如下圖所示),在DB之上搭載若干個Server,每個Server在啟動的時候,都會去DB中拿固定長度的ID列表,存放于內存中,因為ID是基于內存分發的,所以可以做到很高效。

          在數據持久化方面,每次去DB拿固定長度的ID列表,只是把最大的ID持久化。

          整體架構實現比較簡單,主要是為了盡快解決業務層DB壓力的問題,但是在生產環境中也暴露出一些問題。

          比如:

          • 1)TP999數據波動大,當號段使用完之后還是會hang在更新數據庫的I/O上,tp999數據會出現偶爾的尖刺;
          • 2)當更新DB號段的時候,如果DB宕機或者發生主從切換,會導致一段時間的服務不可用。

          5.3 可用性優化

          為了解決上面提到這個兩個問題,引入雙Buffer機制和異步更新策略,當一個Buffer消耗到某個臨界點時,就會異步的觸發任務,把下一個號段加載到內存中。

          保證無論何時DB出現問題,都能有一個Buffer的號段可以正常對外提供服務,只要DB在一個Buffer的下發的周期內恢復,就不會影響整個Leaf的可用性。

          5.4 步長動態調整

          號段長度在固定不變的前提下,流量的突增和銳減都會使得正常流量下維持原有號段正常工作的時間縮短和提升。

          可以嘗試使用以下關系表達式來描述:

          Q * T = L

          (Q:服務qps  L:號段長度  T:號段更新周期)

          但是Leaf的本質是希望T固定,如果Q和L可以正相關,T就可以趨于一個定值。

          所以在Leaf每次更新號段的時候,會根據上一次號段更新的周期T和號段長度step,來決定下一次號段長度nextStep。

          如下所示:

          T < 15min,nextStep = step * 2

          15min < T < 30min,nextStep = step

          T > 30min,nextStep = step / 2

          (初始指定step <= nextStep <= 最大值(自定義:100W))

          6、我們做了什么改進?

          6.1 特性豐富

           

          通過結合嚴選的實際業務場景,進行了特性化支持,例如支持批量ID獲取、大促提前擴容以及提前跳段處理。

          6.2 可用性保障

          1)針對DB:

           DB(MySql)采用主從模式(讀寫分離、降低主庫壓力),一主兩從的配置方式,Master和Slave之間采用的是半同步復制(數據一致性要求,后期可考慮使用MySql Group Replication)。同時還添加了雙1配置,保證不丟數據。

          2)引入SDK:

          通過引入SDK可以降低各個業務方的接入成本、降低Leaf服務端壓力以及在Leaf服務不可用時,客戶端起到短暫降級的效果。

          SDK的實現原理和Leaf類似,在項目啟動之初會加載業務關心參數配置信息,在應用構建本地緩存,同樣采用了雙Buffer存儲模式。

          6.3 穩定性保障

          1)運維方面:

          主要分為3個方面:

          • 1)日志監控:可以幫助發現預期之外的異常情況;
          • 2)流量監控:流有助于號段長度的評估范圍,預防號段被快速消費的極端場景;
          • 3)線上巡檢:可以時刻對服務進行存活校驗。

          2)SLA高可用方面:

          除了運維之外還做了SLA的接入,通常用SLA來衡量系統的穩定性,除此之外我們還按照接口維度設定了SLO目標規則,目前的指標項比較單一只有請求延遲和錯誤率這兩項。

           


           

          7、我們遇到的坑

          7.1 問題發現

          如下圖所示,我們發現每次服務啟動上線接口的rt(響應時間)都要比平時高的多,但是過了一段時間之后卻又恢復成正常水平。

          7.2 問題探究

          在分析之前,我們可以先簡單的回顧下java虛擬機是如何運行Java字節碼的。

          虛擬機視角下Java字節碼如何被虛擬機運行:

          Java虛擬機將class文件加載到虛擬機中,然后將字節碼翻譯成機器碼給底層硬件執行,而這里的翻譯有兩種形式,解釋執行和編譯執行。前者的優勢在于無需等待編譯,后者的優勢在于實際運行速度更快。HotSpot默認采用混合模式,它會先解釋執行字節碼,然后將其中反復執行的熱點代碼,以方法為單位進行即時編譯,JVM是依據方法的調用次數以及循環回邊的執行次數來觸發JIT編譯的。

          在Java7之前我們可以根據程序的特性選擇對應的即時編譯器。Java7開始引入分層編譯機制(-XX:+TieredCompilation):綜合了C1的啟動性能優勢和C2的峰值性能優勢。

          分層編譯將JVM的執行狀態分為了5個層次:

          • L0:解釋執行(也會profiling);
          • L1:執行不帶profiling的C1代碼;
          • L2:執行僅帶方法調用次數和循環回邊執行次數profiling的C1代碼;
          • L3:執行帶所有profiling的C1代碼;
          • L4:執行C2代碼。

          對于C1編譯的三個層次,按執行效率從高至低:L1 > L2 > L3, 這是因為profiling越多,額外的性能開銷越大。通常情況下,C2代碼的執行效率比C1代碼高出30%以上。(這里需要注意的是Java8默認開啟了分層編譯)

          這張圖列出了常見的分層編譯的編譯路徑:

          • 1)通常情況下,熱點方法會被第三層的C1編譯器編譯,再被C2編譯器編譯(0-> 3-> 4);
          • 2)如果方法的字節數目比較少并且第三層的profilling沒有可收集的數據,jvm會判定該方法對于C1和C2的執行效率相同,在經過3層的C1編譯過后,直接回到1層的C1(0-> 3-> 1);
          • 3)在C1忙碌的情況下,JVM在解釋執行過程中對程序進行profiling,而后直接由4層的C2編譯(0-> 4);
          • 4)在C2忙碌的情況下,方法會被2層的C1編譯,然后再被3層的C1編譯,以減少方法在3層的執行時間(0-> 2-> 3-> 4)。

          上圖是項目啟動時的分層編譯日志以及整個過程接口響應RT。

          從圖中可以看到先是執行了C1編譯,再執行C2編譯(日志文件中的3和4分別打標L3和L4),滿足 0->3->4 編譯順序。

          發現從C1編譯到C2編譯耗時過程比較長,這符合我們一開始提出的疑問,為什么項目啟動需要經過一段時間接口RT才能趨于穩定。

          7.3 解決方案

          為了能在項目啟動之初,快速達到接口RT峰值,因此只要盡最大程度縮短解釋執行這個中間過程即可。

          相應的解決方案:

          • 方案 1:關閉分層編譯,降低編譯閾值;
          • 方案 2:Mock接口數據, 快速觸發JIT編譯以及C2編譯;
          • 方案 3:Java9 AOT提前編譯。

          針對方案3:Java9中支持新特性AOT提前編譯,相比較于JIT即時編譯而言,AOT在運行前就已經編譯好了,避免 JIT 編譯器的運行時性能消耗,同時避免解釋程序的早期性能開銷,可以極大提高java代碼性能。

          8、落地使用概況

          Leaf已經在線上環境投入使用,各個業務方(包括主站、渠道、tob)也相應接入進行統一整改,自此嚴選訂單ID生成得到統一收攏。

          在整個嚴選的落地情況,按照業務維度,目前累計接入3個業務,分別是訂單ID、訂單快照ID、訂單商品快照ID,都經受住了雙十一和雙十二考驗。

          9、參考資料

          [1] 微信的海量IM聊天消息序列號生成實踐(算法原理篇)

          [2] 解密融云IM產品的聊天消息ID生成策略

          [3] 深度解密美團的分布式ID生成算法

          [4] 深度解密滴滴的高性能ID生成器(Tinyid)

          本文已同步發布于:http://www.52im.net/thread-4069-1-1.html

          posted @ 2022-11-03 11:45 Jack Jiang 閱讀(115) | 評論 (0)編輯 收藏

          為了更好地分類閱讀52im.net 總計1000多篇精編文章,我將在每周三推送新的一期技術文集,本次是第4 期。

          [- 1 -] 不為人知的網絡編程(一):淺析TCP協議中的疑難雜癥(上篇)

          [鏈接] http://www.52im.net/thread-1003-1-1.html

          [摘要] 可能大家都知道TCP是三次交互完成連接的建立,四次交互來斷開一個連接,那為什么是三次握手和四次揮手呢?反過來不行嗎?


          [- 2 -] 不為人知的網絡編程(二):淺析TCP協議中的疑難雜癥(下篇)

          [鏈接] http://www.52im.net/thread-1004-1-1.html

          [摘要] 接上篇《不為人知的網絡編程(一):淺析TCP協議中的疑難雜癥(上篇)》,我們提到第6個疑問:TCP的頭號疼癥TIME_WAIT狀態,下面我們繼續這個問題的解答。


          [-3 -] 不為人知的網絡編程(三):關閉TCP連接時為什么會TIME_WAIT、CLOSE_WAIT

          [鏈接] http://www.52im.net/thread-1007-1-1.html

          [摘要] 這次就和大家分享一下我們的netframework服務總會拋出一個“connet reset by peer”的原因吧。


          [-4 -] 不為人知的網絡編程(四):深入研究分析TCP的異常關閉

          [鏈接] http://www.52im.net/thread-1014-1-1.html

          [摘要] 大家都明白是“網絡被對端重置了”,但究竟什么情況下會導致這種情況呢?本文就對TCP的各種關閉情況做了進一步的測試研究。


          [- 5 -] 不為人知的網絡編程(五):UDP的連接性和負載均衡

          [鏈接] http://www.52im.net/thread-1018-1-1.html

          [摘要] 本文將從實踐出發,討論UDP在實際應用中的連接性和負載均衡問題。


          [- 6 -] 不為人知的網絡編程(六):深入地理解UDP協議并用好它

          [鏈接] http://www.52im.net/thread-1024-1-1.html

          [摘要]本文接上篇《不為人知的網絡編程(五):UDP的連接性和負載均衡》,將從實踐出發,討論如何深入地理解UDP協議并在實踐中用好它。


          [- 7 -] 不為人知的網絡編程(七):如何讓不可靠的UDP變的可靠?

          [鏈接] http://www.52im.net/thread-1293-1-1.html

          [摘要] 在 UDP 之上做一層可靠,很多朋友認為這是很不靠譜的事情,也有朋友認為這是一個大殺器,可以解決實時領域里大部分問題。涉及到實時傳輸我們都會先考慮 RUDP,RUDP 應用在我們APP核心傳輸體系的各個方面,但不同的系統場景我們設計了不同的 RUDP 方式,所以基于那些激烈的討論和我們使用的經驗,我決定扒一扒 RUDP,來給大家分享如何讓UDP變的可靠的實踐經驗。


          [- 8 -] 不為人知的網絡編程(八):從數據傳輸層深度解密HTTP

          [鏈接] http://www.52im.net/thread-2456-1-1.html

          [摘要] 市面上講HTTP協議的文章很多,但深入到傳輸層從2進制的角度來解析,則相當少見。保證全篇讀完之后,你對HTTP的理解會上升一個臺階!


          [- 9 -] 不為人知的網絡編程(九):理論聯系實際,全方位深入理解DNS

          [鏈接] http://www.52im.net/thread-2740-1-1.html

          [摘要] 當我們發現可以上QQ但不能瀏覽網頁時,我們會想到可能是域名服務器掛掉了;當我們用別人提供的hosts文件瀏覽到一個“不存在”的網頁時,我們會了解到域名解析系統的脆弱。然而關于DNS還有一大堆故事值得我們去傾聽,去思考。


          [- 10 -] 不為人知的網絡編程(十):深入操作系統,從內核理解網絡包的接收過程(Linux篇)

          [鏈接] http://www.52im.net/thread-3247-1-1.html

          [摘要] 這篇文章將用圖解的方式,從操作系統這一層來深度理解一下網絡包的接收過程。


          [- 11 -] 不為人知的網絡編程(十一):從底層入手,深度分析TCP連接耗時的秘密

          [鏈接] http://www.52im.net/thread-3265-1-1.html

          [摘要] TCP的開銷到底有多大,能否進行量化。一條TCP連接的建立需要耗時延遲多少,是多少毫秒,還是多少微秒?能不能有一個哪怕是粗略的量化估計?我今天只分享我在工作實踐中遇到的比較高發的各種情況。


          [- 12 -] 不為人知的網絡編程(十二):徹底搞懂TCP協議層的KeepAlive保活機制

          [鏈接] http://www.52im.net/thread-3506-1-1.html

          [摘要] 次借本文想把TCP協議的KeepAlive保活機制給詳細的整理出來,以便大家能深入其中一窺究竟。


          [- 13 -] 不為人知的網絡編程(十三):深入操作系統,徹底搞懂127.0.0.1本機網絡通信

          [鏈接] http://www.52im.net/thread-3590-1-1.html

          [摘要] 今天咱們就把 127.0.0.1 本機網絡通信相關問題搞搞清楚!


          [- 14 -] 不為人知的網絡編程(十四):拔掉網線再插上,TCP連接還在嗎?一文即懂!

          [鏈接] http://www.52im.net/thread-3846-1-1.html

          [摘要] 本篇文章,我們就從系統層面深入地探討一個有趣的TCP技術問題:拔掉網線后,再插上,原本的這條TCP連接還在嗎?或者說它還“好”嗎?

          我是Jack Jiang,我為自已帶鹽!

          https://github.com/JackJiang2011/MobileIMSDK/

          posted @ 2022-11-01 12:14 Jack Jiang 閱讀(105) | 評論 (0)編輯 收藏

          本文作者網易云信高級前端開發工程師李寧,本文有修訂。

          1、引言

          在IM客戶端的使用場景中,基于本地數據的全文檢索功能扮演著重要的角色,最常用的比如:查找聊天記錄、聯系人等。

          類似于IM中的聊天記錄查找、聯系人搜索這類功能,有了全文檢索能力后,確實能大大提高內容查找的效率,不然,讓用戶手動翻找,確實降低了用戶體驗。

          本文將要分享的是,網易云信基于Electron的PC端是如何實現IM客戶端全文檢索能力的。

          學習交流:

          (本文已同步發布于:http://www.52im.net/thread-4065-1-1.html

          2、關于作者

          李寧:網易云信高級前端開發工程師,負責音視頻 IM SDK 的應用開發、組件化開發及解決方案開發,對 React、PaaS 組件化設計、多平臺的開發與編譯有豐富的實戰經驗。

          3、系列文章

          本文是系列文章中的第6篇,本系列總目錄如下:

          4、什么是全文檢索

          所謂全文檢索,就是要在大量內容中找到包含某個單詞出現位置的技術。

          在傳統的關系型數據庫中,只能通過LIKE條件查詢來實現,這樣有幾個弊端:

          • 1)無法使用數據庫索引,需要遍歷全表,性能較差;
          • 2)搜索效果差,只能首尾位模糊匹配,無法實現復雜的搜索需求;
          • 3)無法得到內容與搜索條件的相關性。

          我們在 IM 的 iOS、安卓以及桌面端中都實現了基于 SQLite 等庫的本地數據全文檢索功能,但是在 Web 端和 基于Electron的PC端上缺少了這部分功能。

          因為在 Web 端,由于瀏覽器環境限制,能使用的本地存儲數據庫只有 IndexDB,暫不在討論的范圍內。但在基于Electron的PC端上,雖然也是內置了 Chromium 的內核,但是因為可以使用 Node.js 的能力,于是乎選擇的范圍就多了一些。本文內容我們具體以基于Electron的IM客戶端為例,來討論全文檢索技術實現。

          PS:如果你不了解什么是Electron技術,讀一下這篇《快速了解Electron:新一代基于Web的跨平臺桌面技術》。

          我們先來具體看下該如何實現全文檢索。

          要實現全文檢索,離不開以下兩個知識點:

          • 1)倒排索引;
          • 2)分詞。

          這兩個技術是實現全文檢索的技術以及難點,其實現的過程相對比較復雜,在聊全文索引的實現前,我們具體學習一下這兩個技術的原理。

          5、什么是倒排索引

          先簡單介紹下倒排索引,倒排索引的概念區別于正排索引:

          • 1)正排索引:是以文檔對象的唯一 ID 作為索引,以文檔內容作為記錄的結構;
          • 2)倒排索引:是以文檔內容中的單詞作為索引,將包含該詞的文檔 ID 作為記錄的結構。

          以倒排索引庫 search-index 舉個實際的例子:

          在我們的 IM 中,每條消息對象都有 idClient 作為唯一 ID,接下來我們輸入「今天天氣真好」,將其每個中文單獨分詞(分詞的概念我們在下文會詳細分享),于是輸入變成了「今」、「天」、「天」、「氣」、「真」、「好」。再通過 search-index 的 PUT 方法將其寫入庫中。

          最后看下上述例子存儲內容的結構:

          如是圖所示:可以看到倒排索引的結構,key 是分詞后的單個中文、value 是包含該中文消息對象的 idClient 組成的數組。

          當然:search-index 除了以上這些內容,還有一些其他內容,例如 Weight、Count 以及正排的數據等,這些是為了排序、分頁、按字段搜索等功能而存在的,本文就不再細細展開了。

          6、什么是分詞

          6.1基本概念

          分詞就是將原先一條消息的內容,根據語義切分成多個單字或詞句,考慮到中文分詞的效果以及需要在 Node 上運行,我們選擇了Nodejieba作為基礎分詞庫。

          以下是 jieba 分詞的流程圖:

          以“去北京大學玩”為例,我們選擇其中最為重要的幾個模塊分析一下。

          6.2加載詞典

          jieba 分詞會在初始化時先加載詞典,大致內容如下:

          6.3構建前綴詞典

          接下來會根據該詞典構建前綴詞典,結構如下:

          其中:“北京大”作為“北京大學”的前綴,它的詞頻是0,這是為了便于后續構建 DAG 圖。

          6.4構建 DAG 圖

          DAG 圖是 Directed Acyclic Graph 的縮寫,即有向無環圖。

          基于前綴詞典,對輸入的內容進行切分。

          其中:

          • 1)“去”沒有前綴,因此只有一種切分方式;
          • 2)對于“北”,則有“北”、“北京”、“北京大學”三種切分方式;
          • 3)對于“京”,也只有一種切分方式;
          • 4)對于“大”,有“大”、“大學”兩種切分方式;
          • 5)對于“學”和“玩”,依然只有一種切分方式。

          如此,可以得到每個字作為前綴詞的切分方式。

          其 DAG 圖如下圖所示:

          6.5最大概率路徑計算

          以上 DAG 圖的所有路徑如下:

          • 去/北/京/大/學/玩
          • 去/北京/大/學/玩
          • 去/北京/大學/玩
          • 去/北京大學/玩

          因為每個節點都是有權重(Weight)的,對于在前綴詞典里的詞語,它的權重就是它的詞頻。因此我們的問題就是想要求得一條最大路徑,使得整個句子的權重最高。

          這是一個典型的動態規劃問題,首先我們確認下動態規劃的兩個條件。

          1)重復子問題:

          對于節點 i 和其可能存在的多個后繼節點 j 和 k:

          • 1)任意通過i到達j的路徑的權重 = 該路徑通過i的路徑權重 + j的權重,即 R(i -> j) = R(i) + W(j);
          • 2)任意通過i到達k的路徑的權重 = 該路徑通過i的路徑權重 + k的權重,即 R(i -> k) = R(i) + W(k)。

          即對于擁有公共前驅節點 i 的 j 和 k,需要重復計算到達 i 路徑的權重。

          2)最優子結構:

          設整個句子的最優路徑為 Rmax,末端節點為 x,多個可能存在的前驅節點為 i、j、k。

          得到公式如下:

          Rmax = max(Rmaxi, Rmaxj, Rmaxk) + W(x)

          于是問題變成了求解 Rmaxi、Rmaxj 以及 Rmaxk,子結構里的最優解即是全局最優解的一部分。

          如上,最后計算得出最優路徑為“去/北京大學/玩”。

          6.6HMM 隱式馬爾科夫模型

          對于未登陸詞,jieba 分詞采用 HMM(Hidden Markov Model 的縮寫)模型進行分詞。

          它將分詞問題視為一個序列標注問題,句子為觀測序列,分詞結果為狀態序列。

          jieba 分詞作者在 issue 中提到,HMM 模型的參數基于網上能下載到的 1998 人民日報的切分語料,一個 MSR 語料以及自己收集的 TXT 小說、用 ICTCLAS 切分,最后用 Python 腳本統計詞頻而成。

          該模型由一個五元組組成,并有兩個基本假設。

          五元組:

          • 1)狀態值集合;
          • 2)觀察值集合;
          • 3)狀態初始概率;
          • 4)狀態轉移概率;
          • 5)狀態發射概率。

          基本假設:

          • 1)齊次性假設:即假設隱藏的馬爾科夫鏈在任意時刻 t 的狀態只依賴于其前一時刻 t-1 的狀態,與其它時刻的狀態及觀測無關,也與時刻 t 無關;
          • 2)觀察值獨立性假設:即假設任意時刻的觀察值只與該時刻的馬爾科夫鏈的狀態有關,與其它觀測和狀態無關。

          狀態值集合即{ B: begin, E: end, M: middle, S: single },表示每個字所處在句子中的位置,B 為開始位置,E 為結束位置,M 為中間位置,S 是單字成詞。

          觀察值集合就是我們輸入句子中每個字組成的集合。

          狀態初始概率表明句子中的第一個字屬于 B、M、E、S 四種狀態的概率,其中 E 和 M 的概率都是0,因為第一個字只可能 B 或者 S,這與實際相符。

          狀態轉移概率表明從狀態 1 轉移到狀態 2 的概率,滿足齊次性假設,結構可以用一個嵌套的對象表示:

          P = {

              B: {E: -0.510825623765990, M: -0.916290731874155},

              E: {B: -0.5897149736854513, S: -0.8085250474669937},

              M: {E: -0.33344856811948514, M: -1.2603623820268226},

              S: {B: -0.7211965654669841, S: -0.6658631448798212},

          }

          P['B']['E'] 表示從狀態 B 轉移到狀態 E 的概率(結構中為概率的對數,方便計算)為 0.6,同理,P['B']['M'] 表示下一個狀態是 M 的概率為 0.4,說明當一個字處于開頭時,下一個字處于結尾的概率高于下一個字處于中間的概率,符合直覺,因為二個字的詞比多個字的詞要更常見。

          狀態發射概率表明當前狀態,滿足觀察值獨立性假設,結構同上,也可以用一個嵌套的對象表示:

          P = {

              B: {'突': -2.70366861046, '肅': -10.2782270947, '適': -5.57547658034},

              M: {'要': -4.26625051239, '合': -2.1517176509, '成': -5.11354837278},

              S: {……},

              E: {……},

          }

          P['B']['突'] 的含義就是狀態處于 B,觀測的字是“突”的概率的對數值等于 -2.70366861046。

          最后,通過Viterbi算法,輸入觀察值集合,將狀態初始概率、狀態轉移概率、狀態發射概率作為參數,輸出狀態值集合(即最大概率的分詞結果)。關于Viterbi算法,本文不再詳細展開,有興趣的讀者可以自行查閱。

          7、技術實現

          上節中介紹的全文檢索這兩塊技術,是我們架構的技術核心。基于此,我們對IM 的 Electron 端技術架構做了改進。以下將詳細介紹之。

          7.1架構圖詳解

          考慮到全文檢索只是 IM 中的一個功能,為了不影響其他 IM 的功能,并且能更快的迭代需求,所以采用了如下的架構方案。

          架構圖如下:

          如上圖所示,右邊是之前的技術架構,底層存儲庫使用了 indexDB,上層有讀寫兩個模塊。

          讀寫模塊的具體作用是:

          • 1)當用戶主動發送消息、主動同步消息、主動刪除消息以及收到消息的時候,會將消息對象同步到 indexDB;
          • 2)當用戶需要查詢關鍵字的時候,會去 indexDB 中遍歷所有的消息對象,再使用 indexOf 判斷每一條消息對象是否包含所查詢的關鍵字(類似 LIKE)。

          那么,當數據量大的時候,查詢的速度是非常緩慢的。

          左邊是加入了分詞以及倒排索引數據庫的新的架構方案,這個方案不會對之前的方案有任何影響,只是在之前的方案之前加了一層。

          現在,讀寫模塊的工作邏輯:

          • 1)當用戶主動發送消息、主動同步消息、主動刪除消息以及收到消息的時候,會將每一條消息對象中的消息經過分詞后同步到倒排索引數據庫;
          • 2)當用戶需要查詢關鍵字的時候,會先去倒排索引數據庫中找出對應消息的 idClient,再根據 idClient 去 indexDB 中找出對應的消息對象返回給用戶。

          7.2架構優點

          該方案有以下4個優點:

          • 1)速度快:通過 search-index 實現倒排索引,從而提升了搜索速度;
          • 2)跨平臺:因為 search-index 與 indexDB 都是基于 levelDB,因此 search-index 也支持瀏覽器環境,這樣就為 Web 端實現全文檢索提供了可能性;
          • 3)獨立性:倒排索引庫與 IM 主業務庫 indexDB 分離;
          • 4)靈活性:全文檢索以插件的形式接入。

          針對上述第“3)”點:當 indexDB 寫入數據時,會自動通知到倒排索引庫的寫模塊,將消息內容分詞后,插入到存儲隊列當中,最后依次插入到倒排索引數據庫中。當需要全文檢索時,通過倒排索引庫的讀模塊,能快速找到對應關鍵字的消息對象的 idClient,根據 idClient 再去 indexDB 中找到消息對象并返回。

          針對上述第“4)”點:它暴露出一個高階函數,包裹 IM 并返回新的經過繼承擴展的 IM,因為 JS 面向原型的機制,在新的 IM 中不存在的方法,會自動去原型鏈(即老的 IM)當中查找,因此,使得插件可以聚焦于自身方法的實現上,并且不需要關心 IM 的具體版本,并且插件支持自定義分詞函數,滿足不同用戶不同分詞需求的場景

          7.3使用效果

          使用了如上架構后,經過我們的測試,在數據量 20W 的級別上,搜索時間從最開始的十幾秒降到一秒內,搜索速度快了 20 倍左右。

          8、本文小結

          本文中,我們便基于Nodejiebasearch-index在 Electron 上實現了IM聊天消息的全文檢索,加快了聊天記錄的搜索速度。

          當然,后續我們還會針對以下方面做更多的優化,比如以下兩點:

          1)寫入性能 :在實際的使用中,發現當數據量大了以后,search-index 依賴的底層數據庫 levelDB 會存在寫入性能瓶頸,并且 CPU 和內存的消耗較大。經過調研,SQLite 的寫入性能相對要好很多,從觀測來看,寫入速度只與數據量成正比,CPU 和內存也相對穩定,因此,后續可能會考慮用將 SQLite 編譯成 Node 原生模塊來替換 search-index。

          2)可擴展性 :目前對于業務邏輯的解耦還不夠徹底。倒排索引庫當中存儲了某些業務字段。后續可以考慮倒排索引庫只根據關鍵字查找消息對象的 idClient,將帶業務屬性的搜索放到 indexDB 中,將倒排索引庫與主業務庫徹底解耦。

          以上,就是本文的全部分享,希望我的分享能對大家有所幫助。

          9、參考資料

          [1]微信移動端的全文檢索優化之路

          [2]微信移動端的全文檢索多音字問題解決方案

          [3]微信iOS端的最新全文檢索技術優化實踐

          [4]蘑菇街基于Electron開發IM客戶端的技術實踐

          [5]融云基于Electron的IM跨平臺SDK改造實踐總結

          [6]閑魚IM基于Flutter的移動端跨端改造實踐

          (本文已同步發布于:http://www.52im.net/thread-4065-1-1.html

          posted @ 2022-10-27 11:18 Jack Jiang 閱讀(98) | 評論 (0)編輯 收藏

          為了更好地分類閱讀52im.net 總計1000多篇精編文章,我將在每周三推送新的一期技術文集,本次是第3 期。

          第 

          [標題] 高性能網絡編程(一):單臺服務器并發TCP連接數到底可以有多少

          [鏈接] http://www.52im.net/thread-561-1-1.html

          [摘要] 到底一臺服務器能夠支持多少TCP并發連接呢?這就是本文要討論的問題。


          第 

          [標題] 高性能網絡編程(二):上一個10年,著名的C10K并發連接問題

          [鏈接] http://www.52im.net/thread-566-1-1.html

          [摘要] 了解C10K問題及其解決思路,通過舉一反三,或許可以為你以后面對類似問題提供更多可借鑒的思想和解決問題的實踐思路。


          第 

          [標題] 高性能網絡編程(三):下一個10年,是時候考慮C10M并發問題了

          [鏈接] http://www.52im.net/thread-568-1-1.html

          [摘要] 本文將討論單機服務器實現C10M(即單機千萬并發連接)的可能性及其思路。


          第 

          [標題] 高性能網絡編程(四):從C10K到C10M高性能網絡應用的理論探索

          [鏈接] http://www.52im.net/thread-578-1-1.html

          [摘要] 本文內容由京東的資深架構師閆國旗分享,以分享者多年的實踐和總結,進一步探討解決C10M問題的理論可行性。


          第 

          [標題] 高性能網絡編程(五):一文讀懂高性能網絡編程中的I/O模型

          [鏈接] http://www.52im.net/thread-1935-1-1.html

          [摘要] 本文旨在為大家提供有用的高性能網絡編程的I/O模型概覽以及網絡服務進程模型的比較,以揭開設計和實現高性能網絡架構的神秘面紗。


          第 

          [標題] 高性能網絡編程(六):一文讀懂高性能網絡編程中的線程模型

          [鏈接] http://www.52im.net/thread-1939-1-1.html

          [摘要] 限于篇幅原因,請將本文與《高性能網絡編程(五):一文讀懂高性能網絡編程中的I/O模型》連起來讀,這樣會讓知識更連貫。


          第 

          [標題] 高性能網絡編程(七):到底什么是高并發?一文即懂!

          [鏈接]http://www.52im.net/thread-3120-1-1.html

          [摘要] 在面視即時通訊相關工作的時候,高并發也是必談問題,那到底什么是高并發?嗯,真要說出個所以然來,還真有點懵...本文就與大家一起探討學習一下。


          第 

          [標題] 從根上理解高性能、高并發(一):深入計算機底層,理解線程與線程池

          [鏈接] http://www.52im.net/thread-3272-1-1.html

          [摘要] 返璞歸真、回歸本質,這些技術特征背后的底層原理到底是什么?如何能通俗易懂、毫不費力真正透徹理解這些技術背后的原理,正是《從根上理解高性能、高并發》系列文章所要分享的。


          第 

          [標題] 從根上理解高性能、高并發(二):深入操作系統,理解I/O與零拷貝技術

          [鏈接] http://www.52im.net/thread-3280-1-1.html

          [摘要] 對于即時通訊IM這種系統的開發來說,網絡通信知識確實非常重要,但回歸到技術本質,實現網絡通信本身的這些技術特征:包括上篇提到的線程池、零拷貝、多路復用、事件驅動等等,它們的本質是什么?底層原理又是怎樣?這就是整理本系列文章的目的,希望對你有用。


          第 10 

          [標題] 從根上理解高性能、高并發(三):深入操作系統,徹底理解I/O多路復用

          [鏈接] http://www.52im.net/thread-3287-1-1.html

          [摘要] 本篇將以更具象的文件這個話題入手,帶你一步步理解高性能、高并發服務端編程時無法回避的I/O多路復用及相關技術。


          第 11 

          [標題] 從根上理解高性能、高并發(四):深入操作系統,徹底理解同步與異步

          [鏈接] http://www.52im.net/thread-3296-1-1.html

          [摘要] 本篇將從基礎著眼,為你講解什么是同步和異步,以及這兩個極為重要的概念在高并發、高性能技術中編程中到底意味著什么。


          第 12 

          [標題] 從根上理解高性能、高并發(五):深入操作系統,理解高并發中的協程

          [鏈接] http://www.52im.net/thread-3306-1-1.html

          [摘要] 了解和掌握協程技術對于很多程序員(尤其海量網絡通信應用的后端程序員)來說是相當有必要的,本文正是為你解惑協程技術原理而寫。


          第 13 

          [標題] 從根上理解高性能、高并發(六):通俗易懂,高性能服務器到底是如何實現的

          [鏈接] http://www.52im.net/thread-3315-1-1.html

          [摘要] 本篇是本系列文章的完結篇,你將能了解到,一個典型的服務器端是如何利用前5篇中講解的各單項技術從而實現高性能高并發的。


          第 14 

          [標題] 從根上理解高性能、高并發(七):深入操作系統,一文讀懂進程、線程、協程

          [鏈接]http://www.52im.net/thread-3357-1-1.html

          [摘要] 本篇是本系列文章的臨時續篇,本篇將由淺入深,總結進程、線程、協程這3個技術概念,將3者的技術原理、用途、關系進行了系統梳理和總結,希望有助于解決你這方面的技術困惑。

          我是Jack Jiang,我為自已帶鹽!

          https://github.com/JackJiang2011/MobileIMSDK/

          posted @ 2022-10-24 12:04 Jack Jiang 閱讀(110) | 評論 (0)編輯 收藏

          僅列出標題
          共51頁: First 上一頁 16 17 18 19 20 21 22 23 24 下一頁 Last 
          Jack Jiang的 Mail: jb2011@163.com, 聯系QQ: 413980957, 微信: hellojackjiang
          主站蜘蛛池模板: 麦盖提县| 文登市| 大丰市| 广昌县| 华池县| 泊头市| 钦州市| 扬中市| 商南县| 临桂县| 永济市| 灵宝市| 肃南| 兴仁县| 静乐县| 海阳市| 仲巴县| 辛集市| 西城区| 富锦市| 滁州市| 舟山市| 淮北市| 东辽县| 阜宁县| 高阳县| 兴义市| 岱山县| 麦盖提县| 阳山县| 厦门市| 高台县| 邹平县| 姚安县| 青阳县| 高青县| 西充县| 漾濞| 玉龙| 胶南市| 昭通市|