IM协议设计

Wesley13
• 阅读 857

IM协议设计

在实际开发中,为了及时的通知APP端一些事情,我们会借助第三方平台,进行推送。今天,我们来分析一下推送系统协议。

推送系统遇到的问题

在设计协议之前,我们考虑一些实际的问题:

  1. APP没有一个固定的网络地址,只能通过主动连接服务器,建立TCP长链接,来进行推送。

  2. 移动环境下,APP断网是非常普遍的,即推送任务会失败

  3. 在大并发的情况下,服务器宕机也是非常有可能的。

  4. APP可能不会随时连上服务器,而服务器需要保存一些非常重要的消息,等待APP连接上,推送给APP

  5. 移动环境下,节省流量也是非常重要的,毕竟不是人人都有4G

  6. 网络安全问题,日益严重,我们需要安全的推送

  7. 协议的兼容性

  8. 由于GSM网关,所以需要发送心跳包,避免NET转换被剔除

如何解决

可以发现,一个好的推送协议设计,还是非常困难的。总结下来,我们遇到的问题大致如下:

  1. 节约流量:采用protobuf这种二进制协议进行推送,但是也可以考虑JSON + UTF-8的方式

  2. 推送失败处理:无论服务器还是APP,都采用日志先行的策略,如果发送失败,则进行RETRY,直到到达推送极限次数后,报告错误。

  3. 安全性:采用SSL/TLS连接,基本上能解决安全链接的问题,如果条件比较拮据,可以把证书放在APP端。

  4. 协议兼容:可以参考APP兼容性设计中的协议兼容部分。

  5. 心跳包:一般来说,因为移动网络和互联网之间存在一个NET网关,它的有效时间为6分钟左右,所以APP端需要定时的发送心跳包到服务器,一般间隔时间为3-5分钟。同时,通过心跳包,服务器可以发现已经死掉的链接,及时的释放资源。

RETRY机制

在上述问题中,其他问题都比较好解决,但是消息重发机制,还是比较难以实现的。 这几介绍一下RETRY机制的设计:

  1. 加入UUID作为该消息的唯一ID,且在服务器端保留最后的100条该用户的消息,当APP端把消息推送到服务器的时候,服务器先去重,避免接受到相同的消息进行推送。

  2. 当APP连接上网络后,首先检测待发送的消息列表中是否为空,如果不为空,则根据FIFO来提取待发送的消息。直到服务器返回OK后,才把本条消息从待发送队列中删除。

  3. 发送失败的延迟推送算法,建议采用TCP的重发时间机制,也就是指数退避算法。

协议具体设计

这里使用JSON描述

{
    MODULE:PUSH
    VERSION:1 //版本号
    ID: UUID  //消息UUID
    TYPE: GROUP | PRIVATE | SYSTEM //消息会话类型
    TARGET_ID: STRING  //消息要发送到的目标ID,如果是GROUP,则是群的ID
    TIME:LONG //消息发送时间
    USER_ID : STRING //发送该该消息的用户ID
    CONTENT:{
        TYPE : STRING -> TEXT | IMAGE //消息的内容类型
        [DATA]: STRING -> BASE64_IMAGE | TEXT //消息的具体内容,可选
    }
}

一些实际问题

粘包

在使用TCP的过程中,会遇到粘包的问题,其根本原因是TCP是面向流链接的,所以,在传输一消息的时候,需要在开始处添加 该消息的长度 , 建议采用 [TCP长度,固定4byte + TCP报文]的格式。

消息有序性

在推送消息的时候,我们需要考虑消息的有序性。所以,在APP推送到服务器,或者服务器推送到APP端的时候,需要按序推送,只有前一条消息推送成功后,才能推送后一条。

总结

可以发现IM协议的设计和TCP协议是非常类似的,额外的添加了消息持久化的功能。对于消息有序性这个问题,引发的同一时间只有一条消息发送这个问题,可以考虑TCP的滑动窗口机制。

参考

Netty系列之Netty百万级推送服务设计要点

微信、陌陌等著名IM软件设计架构详解

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Stella981 Stella981
3年前
Android蓝牙连接汽车OBD设备
//设备连接public class BluetoothConnect implements Runnable {    private static final UUID CONNECT_UUID  UUID.fromString("0000110100001000800000805F9B34FB");
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这