跟我一起开发商业级IM(1)—— 技术选型及协议定义

2020-07-15  本文已影响0人  FreddyChen
技术选型及协议定义Banner

写在前面

终于可以开始写这个系列的文章了,本系列文章预计将分为13篇,由于IM涉及的知识点稍复杂,所以每个知识点都会单独用一篇文章来阐述,尽量讲透彻,方便大家理解。

灵魂拷问

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


本系列文章将包含:

本文为第一篇:技术选型及协议定义

项目架构

首先,讲一下项目整体架构以及使用到的开源框架。根据群里小伙伴的建议以及大家对JavaKotlin的熟悉程度,Android端开发语言还是采用Java,后续有时间会考虑用Kotlin开发一个版本。
由于项目未完成,目前是边写文章边写代码的方式,所以在后续项目完成后,会专门写一篇文章介绍项目架构,包括Android客户端Java服务端

Android客户端
项目整体采用MVP架构,用到的开源库如下:

Java服务端
还在开发中,就不一一列举了。
感谢以上开源库的作者。

以下的通信协议选型、传输协议选型、通信框架选型在开源一个自用的Android IM库,基于Netty+TCP+Protobuf实现文章中有说明过,但既然打算重新写本系列的文章,就在此重新讲解一下,一来加入自己新的理解,二来可以整合到本系列文章里。

以下所涉及的原理和理论知识,由于篇幅原因及作者水平有限,没办法详细阐述。本系列文章的着重点在于教大家怎么去实现自己的IM系统,至于偏理论的知识,作者帮大家找了一下原文链接,感兴趣的可以跳转原文阅读。同时感谢以下文章的作者。

通信协议选型

常用的通信协议有以下几种,分别简单地讲讲每种通信协议的优缺点及适用场景。

  1. WebSocket(1) 简单介绍及握手连接


传输协议选型

常用的传输协议有以下几种,分别讲讲每种传输协议的优缺点及使用场景。

通信框架选型

常用的通信框架有以下几种,分别讲讲每种通信框架的优缺点及使用场景。

通用协议定义

主要是讲讲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.

上一篇下一篇

猜你喜欢

热点阅读