前前后后做的IM和推送系统已经有好几个了,一直都想好好总结下,因此就有了这篇文章。在我刚学编程的那会儿,觉得网络通信是一个很牛逼和门槛很高的一门技术,但是随着开源技术的发展和互联网知识的共享,现在要写出高质量的网络通信程序已经变得容易多了。

只要谈通讯肯定绕不开协议,鉴于本人经验下面只谈本人撸过的三种协议:

转自: http://www.yangguo.info/2015/08/17/%E6%BC%AB%E8%B0%88%E9%80%9A%E8%AE%AF%E6%9E%B6%E6%9E%84/

  1. XMPP
  2. MQTT
  3. 私有协议

XMPP

XMPP(Extensible Messaging and Presence Protocol),也叫Jabber,它是基于稳定长连接网络环境所设计的,对于不够稳定和带宽小的移动网络不是非常合适。由于XMPP基于XML,所以流量大,流量问题对于移动网络来说非常敏感,然后就是消息不可靠、CMWAP兼容、开源项目对协议实现不完善等问题,也是XMPP面临的问题。当然XML可以通过精简压缩来实现流量可控,目前这也是XMPP优化的可行方案,消息的不可靠可以通过扩展XMPP来实现ACK,随着3/4G的发展,CMWAP网关毕竟是末日黄花,但是开源项目对协议只是部分实现等问题,也是使用XMPP绕不过去的坎。Openfire是XMPP领域最知名的开源项目,它简单易用,是很多团队的首选方案,这是国内使用最多的开源方案。Openfire虽然优点很多,但是缺点也不少,最致命的就是它的分布式扩展能力很弱,当用户量很大的时候,水平扩展就成为它的瓶颈所在。还有一个不得不提的项目就是Tigase,这是笔者接触的第一个XMPP开源项目,它在分布式扩展能力上和架构设计上比Openfire强了不少。由于该项目开始是一个私人项目,现在好像在商业化,所以使用者并不是很多,虽然国外有成熟案例,但是国内目前并不多,所以当时趟了Tigase的很多坑,目前平安好医生的聊天系统就是基于此搭建的。如果对Tigase感兴趣,可以阅读我之前写的一篇文章《Tigase集群方案及配置说明文档》。不论使用哪个开源项目,虽然看起来开箱即用,但是要成为稳定成熟的产品,还需要深度的二次开发才行。

虽然XMPP有很多弊端,但是它的生态目前是最完善的,如果从成本角度来考量,XMPP是前期投入最小产出最快的。但是如果是搭建一个SAAS平台或者千万量级的IM,XMPP就不是最优的选择了。当然这是一家之言,国内外目前商业化的IM SAAS平台有好几家都是基于XMPP实现的,这个大家可以自行Google。


MQTT

MQTT是轻量级基于代理的发布/订阅的消息传输协议,它的最大特点就是协议开销非常小,伴随着的就是协议简单(40多页)、网络带宽要求极低和移动设备省电。有幸接触到该协议是笔者在开发Android推送系统时,对它进行了较细致的研究,虽然最终方案中没有使用该协议,但是自己定制的私有协议也参考了很多MQTT的设计。MQTT整个协议的组成,可以分为三个部分:

  1. 固定头部:通用消息数据包格式
  2. 可变头部:特定消息数据包格式
  3. 消息体:有效载荷

固定头部

每个MQTT命令消息的消息头部都包含一个固定头部,固定头部的格式如下:

Byte 1
消息类型和标志字段

Byte 2
剩余长度字段(至少1个字节,最多4个字节),采用big-endian模式存储

Message Type

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0:保留
1:客户端请求连接服务器
2:连接确认
3:发布消息
4:发布确认
5:发布接收(有保证的交付第1部分)
6:发布释放(有保证的交付第2部分)
7:发布完成(有保证的交付第3部分)
8:客户端订阅请求
9:订阅确认
10:客户端取消订阅请求
11:取消订阅确认
12:PING请求
13:PING回复
14:客户端断开连接
15:保留

DUP(Duplicate delivery)

保证消息可靠传输,默认为0,只占用一个bit,表示是否第一次发送,它不能用于检测消息重复发送。只适用于客户端或服务器端尝试重发PUBLISH, PUBREL, SUBSCRIBE 或 UNSUBSCRIBE消息,注意需要满足以下条件:

1
2
QoS > 0
即消息需要回复确认

此时,在可变头部需要包含消息ID。当值为1时,表示当前消息先前已经被传送过。

Qos(Quality of Service)

该标志位标明 PUBLISH 消息的交付质量级别:

RETAIN

仅针对PUBLISH消息。不同值,不同含义:

1:表示发送的消息需要一直持久保存(不受服务器重启影响),不但要发送给当前的订阅者,并且以后新来的订阅了此Topic name的订阅者会马上得到推送。

备注:新来乍到的订阅者,只会取出最新的一个RETAIN flag = 1的消息推送。

0:仅仅为当前订阅者推送此消息。

假如服务器收到一个空消息体(zero-length payload)、RETAIN = 1、已存在Topic name的PUBLISH消息,服务器可以删除掉对应的已被持久化的PUBLISH消息。

Remaining Length

这个字节包含当前消息的剩余部分,包括变量头部和负载的数据。

可变长度的编码方式使用一个单独的字节使消息可以达到127字节的长度上限。协议限制最多4个字节,这样程序可以发送最大256M的消息。

上面便是最核心的固定头部的内容,至于可变头部和消息体可以自己查询资料,目前有很多公司在使用MQTT实现Android的推送,但是目前笔者暂时不知道谁家的IM在使用它。

私有协议

一万人眼中就有一万个哈姆雷特,同样的一万人眼中就有一万个私有协议。应用场景、设计风格,都会导致协议的设计千奇百怪。例如:数据量传输大的场景,压缩方案可能也被设计到协议中,因为不同的环境可能用到不同的压缩方式;传输质量,我们可能就默认某一个级别,可能就从协议中移除,具体的设计得靠经验和应用场景来设计。


架构

做了好几个系统,我将我喜欢使用的一套架构抛出来供大家探讨。

  • CM-*:Connection Manager,可以分为WebSocket和Tcp两种承载方式。
  • SM:Session Manager。
  • Web:Rest接口,HTTP承载。历史消息,好友关系,个人信息管理等。

一套很简单的架构,CM只负责链路的管理,链路和用户ID的关系维护在Redis中,SM负责业务逻辑和消息路由。CMSM内部通过RPC调用,CMSM内部全部采用事件驱动的方式,全部采用异步的方式。任何一个模块都可以水平扩展,并且SM如果达到非常复杂的地步,还可以拆分。最终的压力基本就到了Redis和Mysql,这些高可用和高并发的方案,已经非常成熟,就不用多说了。

下图是登录流程和消息发送流程

鉴于笔者经验,开发的IM最多承载用户数也就百万级别,所以架构上或者设计方案不一定完美,仅供参考!

注意事项

    1. CM一定要采用多队列网卡,否者会出现服务器的一个CPU 100%,而别的CPU却很空闲,从而导致系统吞吐量上不去。因为单队列网卡的I/O中断都被分配到了一个CPU核上,大量数据包到来时,单个CPU核无法全部处理,导致LVS不断丢包连接中断。

Go -- 漫谈IM通信架构的更多相关文章

  1. 泡泡堂、QQ堂游戏通信架构分析

    http://blog.csdn.net/sodme/article/details/468327#comments ————————————————————————————————————————— ...

  2. [Contiki系列论文之2]WSN的自适应通信架构

    说明:本系列文章翻译自Contiki之父Adam Dunkels经典论文,版权归原作者全部. Contiki是由Adam Dunkels及其团队开发的系统.研读其论文是对深入理解Contiki系统的最 ...

  3. 漫谈企业应用架构的演变 CRM & etc

    漫谈企业应用架构的演变 goYangKunhttps://mp.weixin.qq.com/s?__biz=MzIzMTc3NTA2NQ==&mid=2247483698&idx=1& ...

  4. [转帖]intel发布会之前,漫谈CPU核心架构:CCX、Ring Bus、Mesh

    intel发布会之前,漫谈CPU核心架构:CCX.Ring Bus.Mesh https://baijiahao.baidu.com/s?id=1607585351741429318&wfr= ...

  5. WEBRTC三种类型(Mesh、MCU 和 SFU)的多方通信架构

    WebRTC 本身提供的是 1 对 1 的通信模型,在 STUN/TURN 的辅助下,如果能实现 NAT 穿越,那么两个浏览器是可以直接进行媒体数据交换的:如果不能实现 NAT 穿越,那么只能通过 T ...

  6. 漫谈Web缓存架构

    计算机领域多处地方用到缓存,比如说为了缓解CPU和内存之间的速度不匹配问题,我们往往通过增加一级.二级.三级缓存,CPU先从缓存中取指令,如果取不到,再从内存中取,并更新缓存,同时,根据程序的局部性原 ...

  7. Unity3D游戏,TCP,WEBCOSKT,HTTP通信架构 weaving-socket

    weaving-socket 详细介绍 项目简介 2017-8-8:新发布功能 增加U3D游戏客户的通讯项目支持,并提供示例内容. 2017-5-5: 新发布 weaving-socket 架构的.n ...

  8. 漫谈微服务架构:什么是Spring Cloud,为何要选择Spring Cloud

        Spring Cloud是基于Spring Boot的,因此还在使用SpringMVC的同学要先了解Spring Boot.先上一段官话,Spring Cloud是一个基于Spring Boo ...

  9. XMPP 和 OpenFire

    XMPP XMPP(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测.是一种数据传输协议. XMPP的前身是Jabber,一个开源形式组织产生的网络 ...

随机推荐

  1. GIMP中的新建Layer与更改Layer大小

    这边可以直接New Layer,新建一个Layer,还可以New from Visible,第二种是将当前的状态下图像复制出来. 改变Layer的大小,一般的方法两种: Crop to Selecti ...

  2. 格式化输出,基本运算符,流程控制主if

    5.5自我总结 一.格式化输出 1.占位符 a = 1 b = 2 print('%S %s'%(a,b)) #1 2 print('%s %s'%(1,2)) #1 2 2.format格式化 a ...

  3. django第8天(在测试文件中运行django项目|单表操作)

    django第8天 在测试文件中运行django项目 1.将项目配置文件数据库该为mysql,修改配置信息 PORT = '127.0.0.1' DATABASES = { 'default': { ...

  4. LeetCode答案(python)

    1. 两数之和 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重复利用这 ...

  5. bash快捷键及输入输出重定向

    bash特性之快捷键:     Ctrl+a: 跳转至命令首部     Ctrl+e: 跳转至命令尾部         Ctrl+l: 清屏     Ctrl+c: 中止或取消         Ctr ...

  6. 面试准备——JVM相关

    https://www.cnblogs.com/goody9807/p/6511480.html https://www.cnblogs.com/java1024/p/8594784.html htt ...

  7. ctype.h 第2章

    ctype.h ctype.h是c标准函数库中的头文件   定义了一批c语言字符分类函数   (c character classification functions) 用于测试字符是否属于特定的字 ...

  8. 九度oj 题目1472:求两个多项式的和

    题目描述: 输入两个多项式,计算它们的和. 每个多项式有若干对整数表示,每组整数中,第一个整数表示系数(非0),第二个整数表示该项的次数. 如由3 3 5 -2 1 4 0表示3x^5 - 2 * x ...

  9. cf493E Vasya and Polynomial

    Vasya is studying in the last class of school and soon he will take exams. He decided to study polyn ...

  10. Pizza Delivery

    Pizza Delivery 时间限制: 2 Sec  内存限制: 128 MB 题目描述 Alyssa is a college student, living in New Tsukuba Cit ...