跟我一起開發商業級IM(1)—— 技術選型及協議定義

技術選型及協議定義Banner

寫在前面

終于可以開始寫這個系列的文章了,本系列文章預計將分為13篇,由于IM涉及的知識點稍復雜,所以每個知識點都會單獨用一篇文章來闡述,盡量講透徹,方便大家理解。

靈魂拷問

  • 為什么需要寫這個系列的文章呢?
    可能大家會問,有了之前的NettyChat開源一個自用的Android IM庫,基于Netty+TCP+Protobuf實現,為什么還需要寫這個系列的文章呢?主要是因為一開始開源NettyChat和發布文章的時候,旨在起一種拋磚引玉的作用,帶領大家入門IM而已。而且一篇文章難以闡述所有的知識點,加上NettyChat也是一個Demo,有些代碼寫得不嚴謹。一年多以來,從群里的反饋、文章的評論可以看到大家對這一塊知識有不少的需求,大家去集成NettyChat到自己項目里也比較麻煩,網上也缺乏完整的IM實現,大多是零零碎碎的知識點。所以打算從零開始,手把手教大家實現自己的IM系統。


  • 對比NettyChat有什么新增的功能和優化?
    根據大家的反饋,支持TCP/WebSocket、Protobuf/Json等。優化消息重發管理器,不再是一條消息一個Timer實現(嚴重浪費資源,影響程序性能)。另外,優化代碼結構,提升可擴展性、可維護性等。


  • 項目包含服務端代碼嗎?
    這也是重新寫本系列文章的重要原因,在本系列文章中,將包含Java服務端代碼及Android代碼,至于IOS,后續有時間會增加。文章完成后,會在Github開源三個項目,分別是:

  1. ims_kula[Android IM Service SDK]
  2. kulachat_server[Java 服務端]
  3. KulaChat[Android 客戶端]


  • 為什么項目叫“Kula”?
    其實就是一時興起,Kula是作者比較喜歡的SNK拳皇系列里的一個角色,沒什么具體含義。附上Kula高清圖:
    Kula


    大綱如圖:
    跟我一起開發商業級IM——大綱

本系列文章將包含:

  • 技術選型及協議定義
  • 接口定義及封裝
  • 粘包/粘包處理之自定義消息編解碼器
  • 長連接穩定性之連接及重連
  • 心跳機制
  • 消息重發機制
  • 離線消息機制
  • 消息順序控制
  • 數據庫表設計
  • 單聊實現
  • 群聊實現
  • 系統消息實現
  • UI封裝

本文為第一篇:技術選型及協議定義

項目架構

首先,講一下項目整體架構以及使用到的開源框架。根據群里小伙伴的建議以及大家對JavaKotlin的熟悉程度,Android端開發語言還是采用Java,后續有時間會考慮用Kotlin開發一個版本。
由于項目未完成,目前是邊寫文章邊寫代碼的方式,所以在后續項目完成后,會專門寫一篇文章介紹項目架構,包括Android客戶端Java服務端

Android客戶端
項目整體采用MVP架構,用到的開源庫如下:

  • RxJava 2.2.8
  • RxAndroid 2.1.1
  • Retrofit 2.8.1
  • GreenDao 3.3.0
  • Dagger 2.27
  • Butterknife 10.2.1
  • Glide 4.11.0
  • StatusBarUtil 1.5.1
  • BaseRecyclerViewAdapterHelper 3.0.4
  • AutoSize 1.2.1
  • Fastjson 1.1.71.android
  • LogginInterceptor 3.1.0-rc5
  • RxPermissions 0.10.2
  • Toasty 1.4.2
  • Background 1.6.5
  • EventCenter 1.0.1
  • NiceImageView 1.0.5
  • More...

Java服務端
還在開發中,就不一一列舉了。
感謝以上開源庫的作者。

以下的通信協議選型、傳輸協議選型、通信框架選型在開源一個自用的Android IM庫,基于Netty+TCP+Protobuf實現文章中有說明過,但既然打算重新寫本系列的文章,就在此重新講解一下,一來加入自己新的理解,二來可以整合到本系列文章里。

以下所涉及的原理和理論知識,由于篇幅原因及作者水平有限,沒辦法詳細闡述。本系列文章的著重點在于教大家怎么去實現自己的IM系統,至于偏理論的知識,作者幫大家找了一下原文鏈接,感興趣的可以跳轉原文閱讀。同時感謝以下文章的作者。

通信協議選型

常用的通信協議有以下幾種,分別簡單地講講每種通信協議的優缺點及適用場景。

  • UDP

    • 簡單概述
      UDP是一個面向報文的、非連接的協議,也就是無連接的。即發送數據前,雙方無需建立連接,數據發送完畢后,也無需斷開連接(沒有連接可斷開)。這樣一來,減少了連接和斷開連接的開銷(無需像TCP一樣連接時需要三次握手,斷開連接時需要四次揮手)。同時,UDP不存在擁塞機制,即網絡擁塞時,不會使源主機的發送速率降低。另外,UDP一個很大的特點就是盡最大努力交付(即不保證交付),也就是可能會存在丟包、亂序等情況。總的來說,UDP是一個不可靠的協議

    • 優點

    • 效率高
      由于UDP在發送數據前,無需建立連接,并且沒有TCP一系列的確認機制、重傳機制、擁塞機制等,所以在數據傳輸上,效率較高。

    • 開銷小
      UDP首部開銷僅8個字節(源端口[16bit],目的端口[16bit],長度[16bit]、校驗和[16bit])。

    • 稍安全
      UDP沒有TCP擁有的各種機制,被攻擊利用的機制就少一些,但是也無法避免被攻擊。

    • 支持廣播單播組播

    • 缺點

    • 不可靠
      由于UDP沒有TCP一系列的可靠性機制保證傳輸,在網絡質量不好時,很容易丟包。所以在使用UDP作為通信協議時,往往需要自己實現可靠性保證,例如確認重傳等。據說QQ早期是使用UDP作為通信協議,自己在UDP的基礎上,實現類似TCP的可靠性保證,這樣一來,既可實現高速率的傳輸,又可兼顧可靠性。網上找到的一篇討論文:為什么QQ用的是UDP協議而不是TCP協議?,感興趣的可以看看。

    • 亂序

    • 適用場景
      對網絡通訊質量要求不高、實時性要求較高的情況下,可用UDP。比如:

    • 實時音視頻聊天,這種情況丟一些幀影響不大,不需要重傳,對傳輸速度要求高。

    • 遙控器,丟一些指令不影響。


  • TCP

    • 簡單概述
      TCP是一個面向字節流、面向連接的全雙工協議。通信雙方在進行通信之前,需要通過三次握手建立連接,確認雙方都具有數據收發的能力。由于TCP是全雙工協議,因此在數據發送完畢后,每個方向都需要單獨進行關閉,所以需要通過四次揮手來斷開TCP連接。另外,TCP提供了確認應答超時重傳滑動窗口流量控制慢啟動擁塞機制等機制來保證數據有序及可靠性。總的來說,TCP是一個可靠的協議
    • 優點
    • 可靠
      TCP利用一系列機制保證數據傳輸的可靠性,比如確認應答超時重傳校驗和最大消息長度滑動窗口機制等。
    • 有序
      TCP協議在發送數據時,會對每個分片做順序化,也就是會給每個數據包分配一個序列號,并且在特定的時間內等待接收主機對分配的這個序列號進行確認,如果發送主機在一個特定時間內沒有收到接收主機的確認,則發送主機會重傳此數據包。接收主機利用序列號對接收的數據進行確認,以便檢測對方發送的數據是否有丟失或者亂序等,接收主機一旦收到已經順序化的數據,它就將這些數據按正確的順序重組成數據流并傳遞到高層進行處理。
    • 缺點
    • 效率低
      TCP由于發送數據前通過三次握手建立連接,發送數據完成后需要通過四次揮手斷開連接,并且在發送數據過程中,需要一系列機制來保證數據的可靠性以及順序性,所以相對于UDP來說,傳輸效率就低。
    • 開銷大
      TCP首部開銷為最小20個字節(源端口[16bit]、目標端口[16bit]、序列號[32bit]、回應序號[32bit]、TCP頭長度[4bit]、reserved[3bit]、控制代碼[6bit]、窗口大小[16bit]、偏移量[16bit]、校驗和[16bit]、選項[長度可變,最長可達40字節](可選))
    • 適用場景
      對網絡通訊質量要求較高、傳輸效率要求相對低,要求數據準確無誤傳輸的場景。比如:金融類、社交App類等。

    注:作者給大家找了幾篇很不錯的文章,有興趣的話可以閱讀:

    1. 網絡基礎:TCP協議-如何保證傳輸可靠性
    2. TCP如何保證消息順序以及可靠性到達
    3. 深入理解 TCP 協議:從原理到實戰


  • WebSocket

    • 簡單概述
      WebSocket是基于TCP實現的協議,本身就是TCP長連接應用的一種。不同的是,WebSocket握手采用Http請求,客戶端在連接服務器成功后,主動向服務端發起一個Http握手請求,服務端在收到握手請求并驗證通過后,給客戶端返回一個Response,至此,一個合法的長連接就建立了。握手完成后,往后發送數據就是通過TCP協議了,也就是說WebSocket只是使用Http協議來完成握手操作。總的來說,WebSocket是一個基于TCP實現并封裝的協議
    • 優點
      由于WebSocket是基于TCP實現的協議,所以優點跟TCP基本相同。另外,遵循RFC規范的WebSocket標準實現,協議自帶包長,是不會產生粘包/拆包的情況的,因此,甚至可以認為WebSocket的功能之一就是專門處理TCP粘包問題。當然,不一定所有的WebSocket實現都遵循RFC規范,所以極端的情況,還是得自己處理,可以參考一下該答案的解釋:Websocket需要像TCP Socket那樣進行邏輯數據包的分包與合包嗎。另外,WebSocket在TCP的基礎上,封裝了一些幀實現,方便我們使用。
    • 缺點
      同TCP。
    • 適用場景
      同TCP。另外,從WebSocket的命名可以看出,比較適合Browser的即時通訊實現。

    注:WebSocket和HTTP一樣都是基于TCP的應用層協議。握手部分的設計目的就是兼容現有的基于HTTP的服務端組件(web服務器軟件)或者中間件(代理服務器軟件)。這樣一個端口就可以同時接受普通的HTTP請求或者WebSocket請求了。為了這個目的,WebSocket客戶端的握手是一個 HTTP升級版的請求(HTTP Upgrade request)。感興趣可以閱讀以下文章:

  1. WebSocket(1) 簡單介紹及握手連接


  • MQTT

    • 簡單概述
      MQTT是一種基于發布/訂閱模式的“輕量級”通信協議,也是TCP長連接應用的一種(當然中間可能多一層WebSocket)。為什么既然有了TCP和WebSocket了,還需要有MQTT的存在呢?這是因為,MQTT有一個很大的優點:可以以極少的代碼和有限的帶寬,為連接遠程設備提供實時可靠的消息服務,同時根據網絡環境不同,可以選擇三種消息發布的質量(QoS Level):
    1. QoS0,At most once,至多一次
    2. QoS1,At least once,至少一次
    3. QoS2,Exactly once,確保只有一次

    作為一種低開銷、低帶寬占用的即時通訊協議,使其在物聯網、小型設備、移動應用等方面有較廣泛的應用。在MQTT協議中,一個MQTT數據包由固定頭(Fixed header)、可變頭(Variable header)、消息體(payload)三部分構成:

    1. 固定頭(Fixed header)。存在于所有MQTT數據包中,表示數據包類型及數據包的分組類標識。
    2. 可變頭(Variable header)。存在于部分MQTT數據包中,數據包類型決定了可變頭是否存在及其具體內容。
    3. 消息體(Payload)。存在于部分MQTT數據包中,表示客戶端收到的具體內容。

    總的來說,MQTT是在TCP之上的應用層協議,對物聯網應用環境做了非常多的優化,TCP傳輸層協議,是更通用層的協議

    • 優點
      由于MQTT是基于TCP實現的協議,所以優點跟TCP基本相同。同時,MQTT也是標準的RFC協議,相比于私有協議而言更加標準,優點有:
    1. 協議非常完善,能夠馬上用于生產。各端實現同一套協議之后,就能進行通信;私有協議還需要進行大量的驗證,看有無缺陷或欠考慮的地方。
    2. 協議的標準化帶來大量的開源組件,降低開發難度。隨著物聯網+5G生態越來越好,開源組件越來越多,可以減少重復編碼量。
    3. 標準協議利于第三方接入。當第三方設備、平臺想要對接的時候,拿出一套標準的MQTT協議拍在他們臉上,再也沒人有理由要求改接口了。
    4. 有很多開源的Broker,接入較方便等。

    當然,以上用TCP自己開發協議也能實現,那為什么需要MQTT呢?其實就是MQTT另外還實現了很多功能,降低了開發復雜度,比如:心跳機制、異步機制、遺囑消息、訂閱發布機制,QoS消息質量等,而且MQTT做了一些優化,比如消息頭最小只有兩個字節等。所以,可以簡單理解為,MQTT其實就是TCP協議的一種封裝實現,在TCP的基礎上做了一系列優化,并且封裝了很多實用的機制,一句話總結:MQTT就是觀察者模式的網絡放大版

    • 缺點
      同TCP。另外,雖然MQTT封裝了很多機制,但還是不夠成熟,實現起來較復雜。

    • 適用場景

    • 物聯網IoT

    • 即時通訊IM

    • 嵌入式開發設備(不能經常聯網或網絡環境較差)

    • 推送

    • 車聯網平臺

    • 其它協議開銷較小的場景等

    注:感興趣可閱讀以下文章:

    1. 一文讀懂MQTT協議
    2. MQTT 入門介紹
    3. MQTT相比于TCP長連接透傳的優勢

傳輸協議選型

常用的傳輸協議有以下幾種,分別講講每種傳輸協議的優缺點及使用場景。

  • JSON

    • 簡單概述
      JSON是為了數據交換設計的一種數據格式,采用key/value鍵值對存儲。相信大家平時在開發時已經有過許多接觸,就不展開介紹了。
    • 優點
    • 可讀性良好
    • 更加小巧,傳輸效率良好
    • 易解析
    • 缺點
    • 傳輸效率不算高(有需要冗余的符號,比如":"、空格等)
    • 適用場景
      絕大多數需要數據交互的場景,都可以使用。


  • XML

    • 簡單概述
      XML指可擴展標記語言,這里也不作過多介紹。
    • 優點
    • 格式統一,符合標準
    • 可讀性較高、自我描述性高
    • 簡單、可擴展性高
    • 缺點
    • 體積龐大、無用的冗余信息較多
    • 較難解析
    • 適用場景
      配置文件、XMPP的通信格式。


  • Protobuf

    • 簡單概述
      Protocol Buffers(簡稱Protobuf) ,是Google出品的序列化框架,與開發語言無關,和平臺無關,具有良好的可擴展性。Protobuf和所有的序列化框架一樣,都可以用于數據存儲、通訊協議。
      Protobuf的序列化的結果體積要比XML、JSON小很多,XML和JSON的描述信息太多了,導致消息要大;此外Portobuf還使用了Varint編碼,減少數據對空間的占用。
      Protobuf序列化和反序列化速度比XML、JSON快很多,是直接把對象和字節數組做轉換,而XML和JSON還需要構建成XML或者JSON對象結構。
      在一個需要大量的數據傳輸的場景中,如果數據量很大,那么選擇protobuf可以明顯的減少數據量,減少網絡IO,從而減少網絡傳輸所消耗的時間。考慮到作為一個主打社交的產品,消息數據量會非常大,同時為了節約流量,所以采用protobuf是一個不錯的選擇。
    • 優點
    • 傳輸效率快(序列化后體積較小)
    • 支持跨平臺多語言
    • 序列化/反序列化速度很快
    • 缺點
    • 可讀性較差(二進制格式)
    • 缺乏自描述
    • 使用不太方便(貌似找不到支持原生c語言的protobuf,大都是經過別人編譯后的庫)
    • 適用場景
      數據量大并且要求傳輸效率較高的場景。

通信框架選型

常用的通信框架有以下幾種,分別講講每種通信框架的優缺點及使用場景。

  • Java Nio

    • 簡單概述
      Java NIO 是 java 1.4, 之后新出的一套面向緩沖區、非阻塞的IO接口。既可以說是New IO,也有人認為是No-Blocking IO,但這種觀點不太嚴謹。NIO不僅僅就是等于Non-blocking IO(非阻塞IO),NIO中有實現非阻塞IO的具體類,但不代表NIO就是Non-blocking IO(非阻塞IO)。Java NIO由以下幾個核心部分組成:
    1. Buffer 緩沖區
    2. Channel 通道
    3. Selector 多路復用器

    傳統的IO操作面向數據流,意味著每次從流中讀一個或多個字節,直至完成,數據沒有被緩存在任何地方。NIO操作面向緩沖區,數據從Channel讀取到Buffer緩沖區,隨后在Buffer中處理數據。NIO主要的事件有:

    1. OP_READ 讀就緒
    2. OP_WRITE 寫就緒
    3. OP_CONNECT 客戶端連接服務端事件
    4. OP_ACCEPT 服務端接收客戶端連接事件

    更多細節可以查看官方文檔介紹。

    • 優點
    • 并發性高。相較于傳統的BIO(Blocking IO)的實現(一個連接占用一個線程資源,并且線程資源得不到充分利用),NIO的Selector模型允許一個單獨的線程來監視多個輸入通道,也就是啟用了少量的線程(也許是單個線程)去對事件池做監控,對比BIO來說會節省很多cpu的資源,BIO是每個連接建立一個線程,并且監控工作也是該線程完成,NIO將監控工作歸為很少的線程去處理,當然這個線程不會再用于通信,保證了每個線程的利用率,自然提升了高并發性能。
    • 缺點
    • API較復雜。必須對NIO的Buffer(緩沖區)、Channel(通道)、Selector(多路復用器)了解比較透徹才能開發出健壯的程序。
    • 需要很多額外的編程技能來輔助使用NIO,例如,因為NIO涉及了Reactor線程模型,所以必須必須對多線程和網絡編程非常熟悉才能寫出高質量的NIO程序。
    • 想要有高可靠性,工作量和難度都非常的大,因為服務端需要面臨客戶端頻繁的接入和斷開、網絡閃斷、半包讀寫、失敗緩存、網絡阻塞的問題,這些將嚴重影響我們的可靠性,而使用原生NIO解決它們的難度相當大。
    • JDK NIO中著名的BUG--epoll空輪詢,當select返回0時,會導致Selector空輪詢而導致CPU100%,官方表示JDK1.6之后修復了這個問題,其實只是發生的概率降低了,沒有根本上解決。
    • 適用場景
      服務器需要支持超大量的長時間連接。比如10000個連接以上,并且每個客戶端并不會頻繁地發送太多數據。例如總公司的一個中心服務器需要收集全國便利店各個收銀機的交易信息,只需要少量線程按需處理維護的大量長期連接。
      Jetty、Mina、Netty、ZooKeeper等都是基于NIO方式實現。


  • Mina

    • 簡單概述
      Mina其實跟Netty很像,大部分API都相同,因為是同一個作者開發的。但感覺Mina沒有Netty成熟,在使用Netty的過程中,出了問題很輕易地可以找到解決方案。在線程模型上,Mina和Netty并沒有太大的差異性,主要的差異還是在任務調度的粒度的不同。另外,Mina是早期作品,Netty是在Mina之后開發的,同一個作者,功能大致的框架,所以Netty是一個不錯的選擇。
    • 優點
      和Netty大致相同,只是某些實現上有所不同。
    • 缺點
    • Mina將內核和一些特性的聯系過于緊密,使得用戶在不需要這些特性的時候無法脫離,相比下性能會有所下降,Netty解決了這個設計問題。
    • Netty的文檔更清晰,很多Mina的特性在Netty里都有。
    • Netty更新周期更短,新版本的發布比較快。
    • 它們的架構差別不大,Mina靠apache生存,而Netty靠jboss,和jboss的結合度非常高,Netty有對google protocal buf的支持,有更完整的ioc容器支持(spring,guice,jbossmc和osgi)。
    • Netty比Mina使用起來更簡單,Netty里你可以自定義的處理upstream events或/和downstream events,可以使用decoder和encoder來解碼和編碼發送內容。
    • Netty和Mina在處理UDP時有一些不同,Netty將UDP無連接的特性暴露出來;而Mina對UDP進行了高級層次的抽象,可以把UDP當成”面向連接”的協議,而要Netty做到這一點比較困難。
    • 適用場景
      同Netty。


  • Netty

    • 簡單概述
      Netty是由JBOSS提供的基于Java NIO的開源框架,Netty提供異步非阻塞、事件驅動、高性能、高可靠、高可定制性的網絡應用程序和工具,可用于開發服務端和客戶端。
      Netty是目前最流行的 NIO 框架,Netty 在互聯網領域、大數據分布式計算領域、游戲行業、通信行業等獲得了廣泛的應用,知名的 Elasticsearch、Dubbo、阿里云IoT框架內部都采用了 Netty。
    • 優點
    • 設計優雅。適用于各種傳輸類型的統一 API 阻塞和非阻塞 Socket;基于靈活且可擴展的事件模型,可以清晰地分離關注點;高度可定制的線程模型 - 單線程,一個或多個線程池。
    • 使用方便。詳細記錄的 Javadoc,用戶指南和示例;沒有其他依賴項,JDK 5(Netty 3.x)或 6(Netty 4.x)就足夠了。
    • 高性能。吞吐量更高、延遲更低、減少資源消耗、最小化不必要的內存復制。
    • 安全。完整的 SSL/TLS 和 StartTLS 支持
    • 社區活躍。不斷更新、版本迭代周期短,發現的 Bug 可以被及時修復,同時更多的新功能會被加入。
    • 高穩定性。解決了JDK NIO臭名昭著的epoll空輪詢bug。
    • 定制能力高。可以通過ChannelHandler對通信框架進行靈活地拓展。
    • 支持各種協議。比如TCP、UDP、WebSocket、MQTT等,高度封裝的編解碼器,簡單易用。
    • 經歷了大規模的商業應用考驗,質量和可靠性都有很好的驗證。
    • 缺點
      暫未發現,有知道的可以告訴作者,謝謝。
    • 適用場景
    • 互聯網行業。在分布式系統中, 各個節點之間需要遠程服務調用, 高性能的 RPC 框架必不可少, Netty 作為異步高性能的通信框架, 往往作為基礎通信組件被這些 RPC 框架使用。
    • 游戲行業。Netty 作為高性能的基礎通信組件, 提供了 TCP/UDP 和 HTTP 協議棧, 方便定制和開發私有協議棧, 賬號登錄服務器;地圖服務器之間可以方便的通過 Netty 進行高性能的通信。
    • Netty除了能開發即時通訊類的應用外,也能用來實現HTTP服務器。


  • Socket.IO
    Socket.IO也是一個開源框架,可用于在瀏覽器和服務器之間進行實時,雙向和基于事件的通信。用得比較少,就不詳細介紹了。

通用協議定義

主要是講講Protobuf的文件格式定義,JSON就是key/value鍵值對,沒什么好說的。
我們先分析一下,怎樣的消息格式,才算是通用的,也就是單聊、群聊、系統消息等,都可以用的統一消息格式,這個比較重要,關系到后續的擴展性、通用性等,先看個圖:

Protobuf格式定義



對應編寫的msg.proto代碼如下:

syntax = "proto3";// 指定protobuf版本
option java_package = "com.freddy.kulaims.protobuf";// 指定包名
option java_outer_classname = "MessageProtobuf";// 指定類名

message Msg {
    Head head = 1;// 消息頭
    Body body = 2;// 消息體
}

message Head {
    string msgId = 1;// 消息id
    int32 msgType = 2;// 消息類型
    string sender = 3;// 發送者
    string receiver = 4;// 接收者
    int64 timestamp = 5;// 發送時間戳,單位:毫秒
    int32 report = 6;// 消息發送狀態報告
}

message Body {
    string content = 1;// 消息內容
    int32 contentType = 2;// 消息內容類型
    string data = 3;// 擴展字段,以key/value形式存儲的json字符串
}

編寫完msg.proto文件后,通過以下步驟即可生成我們需要用到的MessageProtobufJava類:

  1. 在項目src/main目錄下,新建proto文件夾,與src/main/java同級。

  2. msg.proto文件復制到項目src/main/proto文件夾。

  3. project級的build.gradle文件的dependencies節點下,加入

classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.12'
  1. app級的build.gradle文件,加入
apply plugin: 'com.google.protobuf'
  1. app級的build.gradle文件的android節點,加入
sourceSets {
    main {
        java {
            srcDir 'src/main/java'
        }

        proto {
            srcDir 'src/main/proto'
        }
    }
}
  1. app級的build.gradle文件的dependencies節點,加入
implementation 'com.google.protobuf:protobuf-java:3.8.0'
  1. app級的build.gradle文件根節點(也就是與androiddependencies節點同級),加入
protobuf {
    //配置protoc編譯器
    protoc {
        artifact = 'com.google.protobuf:protoc:3.8.0'
    }
    //這里配置生成目錄,編譯后會在build的目錄下生成對應的java文件
    generateProtoTasks {
        all().each { task ->
            task.builtins {
                remove java
            }
            task.builtins {
                java {}
            }
        }
    }
}
  1. 點擊build->Make Project,即可在項目生成的build/generated/source/proto/debug/java/proto文件java_package指定的包名下看到生成的MessageProtobuf.java文件,文件自動生成,不需要改動:
    生成的MessageProtobuf文件



    注:以上protobuf版本不是規定的,大家可以選擇自己喜歡的版本,但強烈建議前后端版本一致,否則有可能會出現兼容性的問題。


    不少同學也會用多個proto文件來表示不同消息,比如用戶登錄消息user_login.proto、聊天消息chat.proto,這樣也未嘗不可,只是這樣會有很多個proto文件,后期維護比較麻煩,這也就是為什么需要設計通用的proto文件格式的原因。


    最后,貼一張JSONProtobuf序列化后的字節長度對比圖,兩個User對象和一個timestamp字段,可以看到json序列化后,字節長度為140,而同樣的內容在Protobuf序列化后,字節長度為49
    JSON與Protobuf序列化字節長度對比



    拿到MessageProtobuf.java文件,意味著我們已經完成了第一步,距離我們開發完成的商業級IM系統又接近了一步,在下一篇文章,我將會詳細介紹接口定義及封裝,敬請期待。

最終技術選型

綜上所述,在即時通訊方面,最終技術選型如下:

通信協議

通信協議采用TCPWebSocket兩種,UDP不考慮,至于MQTT,后續如果有需要的話會考慮實現。

傳輸協議

傳輸協議采用ProtobufJSON兩種,在IM SDK初始化時指定。XML不考慮。

通信框架

通信框架采用Netty,后續如果有需要,會采用Java NIOMina實現。Socket.IO不考慮。

寫在最后

之前在開源一個自用的Android IM庫,基于Netty+TCP+Protobuf實現,有同學評論,TCP是面向字節流的,沒有包的概念,哪來的拆包/粘包的說法呢?首先說明,作者不會誤導大家,TCP確實沒有拆包/粘包的說法,相關的TCP/IP書籍上也沒有提到過,這個說法只是誤傳,但已經深入人心,所以作者也就用這詞了。拆包/粘包的概念應只存在應用層,TCP不存在粘包/拆包的說法,只是沒有消息邊界而已。后續在第3篇文章,會專門解釋。


終于寫完了,發現寫原創文章太難了,一來要考慮表述的方式,二來要考慮排版是否美觀,還要考慮是否符合大家的需要,所以拖延癥又發作了~ 但會堅持把整個系列的文章都寫完,把項目完善并開源,希望對大家有所幫助。之所以分系列文章來寫,一方面是因為一篇文章實在沒辦法講清楚。另一方面,希望在寫文章的過程中,大家可以給我提點意見或建議。一個人精力及水平有限,有很多觀點也許不太正確和完善,希望大家體諒。歡迎吐槽,歡迎拍磚,接受批評。


PS:新開的公眾號不能留言,如果大家有不同的意見或建議,可以到掘金上評論或者加到QQ群:1015178804,如果群滿人的話,也可以在公眾號給我私信,謝謝。

The end.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 寫在前面 在上一篇文章跟我一起開發商業級IM(1)—— 技術選型及協議定義中,我們完成了技術選型,回顧一下: 通信...
    FreddyChen閱讀 756評論 0 5
  • 一、前言 IM發展至今,已是非常重要的互聯網應用形態之一,尤其移動互聯網時代,它正以無與論比的優勢降低了溝通成本和...
    Sky109閱讀 4,312評論 1 60
  • 久違的晴天,家長會。 家長大會開好到教室時,離放學已經沒多少時間了。班主任說已經安排了三個家長分享經驗。 放學鈴聲...
    飄雪兒5閱讀 7,561評論 16 22
  • 創業是很多人的夢想,多少人為了理想和不甘選擇了創業來實現自我價值,我就是其中一個。 創業后,我由女人變成了超人,什...
    亦寶寶閱讀 1,865評論 4 1
  • 今天感恩節哎,感謝一直在我身邊的親朋好友。感恩相遇!感恩不離不棄。 中午開了第一次的黨會,身份的轉變要...
    迷月閃星情閱讀 10,609評論 0 11