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

DUP(Duplicate delivery)

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

  1. 1
    2
  1. 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. 智能指针之 weak_ptr

    1. weak_ptr 介绍 std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性("弱")引用.在访问所引用的对象指针前必须 ...

  2. shell进阶

    shell 中的高级用法 1.if 单重判断 if cmd; then cmd cmd cmd fi 多重判断 单分支 if cmd;then cmd elif cmd fi 双分支 if cmd; ...

  3. clock gate

    clock gate 这个专题,比较复杂设计DC  PT PR.下面仅仅从RTL行为级说明一下.

  4. laravel中的gate

    public function boot(){ $this->registerPolicies(); Gate::define('update-post',function($user,$pos ...

  5. day12-图

  6. 【02】xmind如何修改默认线条设置

    [02]xmind如何修改不同主题的默认线条设置 魔芋:每次都是曲线.更喜欢为直线.因为曲线的路线是不确定的,看起来就显示很凌乱. 用everything搜索defaultStyles.xml     ...

  7. c#笔记2018-12-27

    using System; /*2018-12-27 c#学习笔记 * 1.c#判断if /else if /switch * 2.循环while/for/do-while * 3.循环实例: for ...

  8. 解决wordpress部分博客文章页面无法显示的问题

    搭建完wordpress,试着写了一篇博客.文章发布后,首页已经能显示出文章的标题,但是点进去后却提示该页无法显示. 百度一番,先后尝试网上的修改apache配置等方法后依然无效.折腾到最后无意间发现 ...

  9. 【LeetCode】Maximize Sum Of Array After K Negations(K 次取反后最大化的数组和)

    这道题是LeetCode里的第1005道题. 题目描述: 给定一个整数数组 A,我们只能用以下方法修改该数组:我们选择某个个索引 i 并将 A[i] 替换为 -A[i],然后总共重复这个过程 K 次. ...

  10. 九度oj 题目1012:畅通工程

    题目描述: 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路 ...