Jack Jiang

我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
posts - 490, comments - 13, trackbacks - 0, articles - 1

     摘要: 本文作者“Carson”,现就职于腾讯公司,原题“高效保活长连接:手把手教你实现自适应的心跳保活机制”,有较多修订和改动。1、引言当要实现IM即时通讯聊天、消息推送等高实时性需求时,我们一般会选择长连接的通信方式。而真正当实现长连接方式时,会遇到很多技术问题,比如最常见的长连接保活问题。今天,我将通过本篇文章,手把手教大家实现一套可自适应的心跳保活机...  阅读全文

posted @ 2022-05-18 15:09 Jack Jiang 阅读(232) | 评论 (0)编辑 收藏

本文由ELab技术团队分享,原题“探秘HTTPS”,有修订和改动。

1、引言

对于IM开发者来说,IM里最常用的通信技术就是Socket长连接和HTTP短连接(通常一个主流im会是这两种通信手段的结合)。从通信安全的角度来说,Socket长连接的安全性,就是基于SSL/TLS加密的TCP协议来实现的(比如微信的mmtls,见《微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解);而对于HTTP短连接的安全性,也就是HTTPS了。

到底什么是HTTPS?为什么要用HTTPS?今天就借此机会,跟大家一起深入学习一下HTTPS的相关知识,包括HTTP的发展历程、HTTP遇到的问题、对称与非对称加密算法、数字签名、第三方证书颁发机构等概念。

学习交流:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK 

本文已同步发布于:http://www.52im.net/thread-3897-1-1.html

2、系列文章

本文是IM通讯安全知识系列文章中的第9篇,此系列总目录如下:

3、写在前面

说到HTTPS,那就得回到HTTP协议。

对于HTTP协议,大家肯定都熟得不能再熟了。那么HTTPS和HTTP的区别大家了解吗?

对于这个经典的面试题,大部分人会这么回答:

  • 1)HTTPS比HTTP多了一个S(Secure):也就是说HTTPS是安全版的HTTP;
  • 2)端口号不同:HTTP使用80端口,HTTPS使用443端口;
  • 3)加密算法:HTTPS用的是非对称加密算法。

上面的回答能给几分?等看完本文我们可以再回头来看下这个回答。

那么,HTTPS是如何实现安全的短连接数据传输呢?想彻底搞明白这个问题,还是要从HTTP的发展历程说起 ......

4、HTTP协议回顾

4.1 基础常识

HTTP是Hypertext Transfer Protocal 的缩写,中文全称是超文本传输协议(详见《深入浅出,全面理解HTTP协议)。

通俗了解释就是:

  • 1)超文本是指包含但不限于文本外的图片、音频、视频等多媒体资源;
  • 2)协议是通信双方约定好的数据传输格式以及通信规则。

HTTP是TCP/IP协议簇的最高层——应用层协议:

▲ 上图引用自《深入浅出,全面理解HTTP协议

浏览器和服务器在使用HTTP协议相互传递超文本数据时,将数据放入报文体内,同时填充首部(请求头或响应头)构成完整HTTP报文并交到下层传输层,之后每一层加上相应的首部(控制部分)便一层层的下发,最终由物理层将二进制数据以电信号的形式发送出去。

HTTP的请求如下图所示:

▲ 上图引用自《深入浅出,全面理解HTTP协议

HTTP报文结构如下:

4.2 发展历程

HTTP的发展历程如下:

由HTTP的发展历程来看,最开始版本的HTTP(HTTP1.0)在每次建立TCP连接后只能发起一次HTTP请求,请求完毕就释放TCP连接。

我们都知道TCP连接的建立需要经过三次握手的过程,而每次发送HTTP请求都需要重新建立TCP连接,毫无疑问是很低效的。所以HTTP1.1改善了这一点,使用长连接的机制,也就是“一次TCP连接,N次HTTP请求”。

HTTP协议的长连接和短连接,实质上是 TCP 协议的长连接和短连接。

在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接需要客户端和服务端都支持长连接。

PS:对于IM开发者来说,为了与Socket长连接通道区分,通常认为HTTP就是“短连接”虽然这个“短连接”不一定真的“短”)。

HTTP1.0若要开启长连接,需要加上Connection: keep-alive请求头。有关HTTP协议的详细发展历程可阅读《一文读懂HTTP协议的历史演变和设计思路》一文。

4.3 安全问题

随着HTTP越来越广泛的使用,HTTP的安全性问题也逐渐暴露。

回忆一下多年前遍地都是的运营商劫持,当你访问一个本来很正常的网页,但页面上却莫名其妙出现了一些广告标签、跳转脚本、欺骗性的红包按钮,甚至有时候本来要下载一个文件,最后下载下来却变成了另外一个完全不同的东西,这些都是被运营商劫持了HTTP明文数据的现象。

下图就是似曾相识的运营商劫持效果图:

PS:关于运营商劫持问题,可以详细阅读《全面了解移动端DNS域名劫持等杂症:原理、根源、HttpDNS解决方案等》。

HTTP主要有以下3点安全性问题:

 

归纳一下就是:

  • 1)数据保密性问题:因为HTTP无状态,而且又是明文传输,所有数据内容都在网络中裸奔,包用户括身份信息、支付账号与密码。这些敏感信息极易泄露造成安全隐患;
  • 2)数据完整性问题:HTTP数据包在到达目的主机前会经过很多转发设备,每一个设备节点都可能会篡改或调包信息,无法验证数据的完整性;
  • 3)身份校验问题:有可能遭受中间人攻击,我们无法验证通信的另一方就是我们的目标对象。

因此,为了保证数据传输的安全性,必须要对HTTP数据进行加密。

5、常见的加密方式

5.1 基本情况

常见的加密方式分为三种:

  • 1)对称加密;
  • 2)非对称加密;
  • 3)数字摘要。

前两种适合数据传输加密,而数字摘要不可逆的特性常被用于数字签名。

接下来,我们逐一简要学习一下这三种常见的加密方法。

5.2 对称加密

对称加密也称为密钥加密或单向加密,就是使用同一套密钥来进行加密和解密。密钥可以理解为加密算法。

对称加密图示如下:

广泛使用的对称加密有:

对称加密算法的优缺点和适用场景:

  • 1)优点:算法公开、简单,加密解密容易,加密速度快,效率高;
  • 2)缺点:相对来说不算特别安全,只有一把钥匙,密文如果被拦截,且密钥也被劫持,那么,信息很容易被破译;
  • 3)适用场景:加解密速度快、效率高,因此适用于大量数据的加密场景。由于如何传输密钥是较为头痛的问题,因此适用于无需进行密钥交换的场景,如内部系统,事先就可以直接确定密钥。

PS:可以在线体验对称加密算法,链接是:http://www.jsons.cn/textencrypt/

小知识:base64编码也属于对称加密哦!

5.3 非对称加密

非对称加密使用一对密钥(公钥和私钥)进行加密和解密。

非对称加密可以在不直接传递密钥的情况下,完成解密,具体步骤如下:

  • 1)乙方生成两把密钥(公钥和私钥)。公钥是公开的,任何人都可以获得,私钥则是保密的;
  • 2)甲方获取乙方的公钥,然后用它对信息加密;
  • 3)乙方得到加密后的信息,用私钥解密。

以最典型的非对称加密算法RSA为例,举个例子:

想要彻底搞懂RSA,需要了解数论的知识,全部推导过程RSA加密算法。简单介绍思路:使用两个超大质数以及其乘积作为生成公钥和私钥的材料,想要从公钥推算出私钥是非常困难的(需要对超大数因式分解为两个很大质数的乘积)。目前被破解的最长RSA密钥是768个二进制位。也就是说,长度超过768位的密钥,还无法破解(至少没人公开宣布)。因此可以认为,1024位的RSA密钥基本安全,2048位的密钥极其安全。

非对称加密算法的优缺点和适用场景:

  • 1)优点:强度高、安全性强于对称加密算法、无需传递私钥导致没有密钥泄露风险;
  • 2)缺点:计算量大、速度慢;
  • 3)适用场景:适用于需要密钥交换的场景,如互联网应用,无法事先约定密钥。

实践应用过程中,其实可以与对称加密算法结合:

  • 1)利用非对称加密算法安全性较好的特点来传递对称加密算法的密钥。
  • 2)利用对称加密算法加解密速度快的特点,进行数据内容比较大的加密场景的加密(如HTTPS)。

PS:对于IM开发者来说,《探讨组合加密算法在IM中的应用》一文值得一读。

5.4 如何选择?

1)如果选择对称加密:

HTTP请求方使用对称算法加密数据,那么为了接收方能够解密,发送方还需要把密钥一同传递到接收方。在传递密钥的过程中还是可能遭到嗅探攻击,攻击者窃取密钥后依然可以解密从而得到发送的数据,所以这种方案不可行。

2)如果选择非对称加密:

接收方保留私钥,把公钥传递给发送方。发送方用公钥来加密数据,接收方使用私钥解密数据。攻击者虽然不能直接获取这些数据(因为没有私钥),但是可以通过拦截传递的公钥,然后把自己的公钥传给发送方,再用自己的私钥对发送方发送数据进行解密。

整个过程通信双方都不知道中间人的存在,但是中间人能够获得完整的数据信息。

3)两种加密方法的混合:

先使用非对称加密算法加密并传递对称加密的密钥,然后双方通过对称加密方式加密要发送的数据。看起来没什么问题,但事实是这样吗?

中间人依然可以拦截公钥的传递,并以自己的公钥作为替换,治标不治本。

想要治本,就要找到一个第三方公证人来证明公钥没有被替换,因此就引出了数字证书的概念,这也是下一节将分享的内容。

6、数字证书

6.1 CA机构

CA就是 Certificate Authority,即颁发数字证书的机构。

作为受信任的第三方,CA承担公钥体系中公钥的合法性检验的责任。

证书就是源服务器向可信任的第三方机构申请的数据文件。这个证书除了表明这个域名是属于谁的,颁发日期等,还包括了第三方证书的私钥。

服务器将公钥放在数字证书中,只要证书是可信的,公钥就是可信的。

下面两图是飞书域名的证书中部分内容的信:

6.2 数字签名

摘要算法:一般用哈希函数来实现,可以理解成一种定长的压缩算法,它能把任意长度的数据压缩到固定长度。这好比是给数据加了一把锁,对数据有任何微小的改动都会使摘要变得截然不同。

通常情况下:数字证书的申请人(服务器)将生成由私钥和公钥以及证书请求文件(Certificate Signing Request,CSR)组成的密钥对。CSR是一个编码的文本文件,其中包含公钥和其他将包含在证书中的信息(例如:域名、组织、电子邮件地址等)。密钥对和CSR生成通常在将要安装证书的服务器上完成,并且 CSR 中包含的信息类型取决于证书的验证级别。与公钥不同,申请人的私钥是安全的,永远不要向 CA(或其他任何人)展示。

生成 CSR 后:申请人将其发送给 CA,CA 会验证其包含的信息是否正确,如果正确,则使用颁发的私钥对证书进行数字签名,然后将签名放在证书内随证书一起发送给申请人。

在SSL握手阶段:浏览器在收到服务器的证书后,使用CA的公钥进行解密,取出证书中的数据、数字签名以及服务器的公钥。如果解密成功,则可验证服务器身份真实。之后浏览器再对数据做Hash运算,将结果与数字签名作对比,如果一致则可以认为内容没有收到篡改。

对称加密和非对称加密是公钥加密、私钥解密, 而数字签名正好相反——是私钥加密(签名)、公钥解密(验证),如下图所示。

限于篇幅,关于数字证书的内容本文就不再赘述,想详细了解的可以阅读:

7、为什么要使用HTTPS

图解HTTP》一书中提到HTTPS就是身披SSL外壳的HTTP。

7.1 SSL

SSL 在1999年被更名为TLS

所以说:HTTPS 并不是一项新的应用层协议,只是 HTTP 通信接口部分由 SSL 和 TLS 替代而已。

具体就是:HTTP 会先直接和 TCP 进行通信,而HTTPS 会演变为先和 SSL 进行通信,然后再由 SSL 和 TCP 进行通信。

SSL是一个独立的协议,不只有 HTTP 可以使用,其他应用层协议也可以使用,比如FTP、SMTP都可以使用SSL来加密。

7.2 HTTPS请求流程

HTTPS请求全流程如下图:

如上图所示:

  • 1)用户在浏览器发起HTTPS请求,默认使用服务端的443端口进行连接;
  • 2)HTTPS需要使用一套CA 数字证书,证书内会附带一个服务器的公钥Pub,而与之对应的私钥Private保留在服务端不公开;
  • 3)服务端收到请求,返回配置好的包含公钥Pub的证书给客户端;
  • 4)客户端收到证书,校验合法性,主要包括是否在有效期内、证书的域名与请求的域名是否匹配,上一级证书是否有效(递归判断,直到判断到系统内置或浏览器配置好的根证书),如果不通过,则显示HTTPS警告信息,如果通过则继续;
  • 5)客户端生成一个用于对称加密的随机Key,并用证书内的公钥Pub进行加密,发送给服务端;
  • 6)服务端收到随机Key的密文,使用与公钥Pub配对的私钥Private进行解密,得到客户端真正想发送的随机Key;
  • 7)服务端使用客户端发送过来的随机Key对要传输的HTTP数据进行对称加密,将密文返回客户端;
  • 8)客户端使用随机Key对称解密密文,得到HTTP数据明文;
  • 9)后续HTTPS请求使用之前交换好的随机Key进行对称加解密。

7.3 HTTPS到底解决了什么问题

HTTPS确实解决了HTTP的三个安全性问题:

  • 1) 保密性:结合非对称加密和对称加密实现保密性。用非对称加密方式加密对称加密的秘钥,再用对称加密方式加密数据;
  • 2) 完整性:通过第三方CA的数字签名解决完整性问题;
  • 3) 身份校验:通过第三方CA的数字证书验证服务器的身份。

7.4 HTTPS优缺点

最后我们总结一下HTTPS的优缺点:

可以看到:HTTPS的确是当今安全传输HTTP的最优解,但他并不是完美的,仍会有漏洞。

8、参考资料

[1] 深入浅出,全面理解HTTP协议

[2] HTTP协议必知必会的一些知识

[3] 从数据传输层深度解密HTTP

[4] 一文读懂HTTP协议的历史演变和设计思路

[5] 你知道一个TCP连接上能发起多少个HTTP请求吗?

[6] 如果这样来理解HTTPS,一篇就够了

[7] 一分钟理解 HTTPS 到底解决了什么问题

[8] 你知道,HTTPS用的是对称加密还是非对称加密?

[9] HTTPS时代已来,打算更新你的HTTP服务了吗?

[10] 一篇读懂HTTPS:加密原理、安全逻辑、数字证书等

[11] 全面了解移动端DNS域名劫持等杂症:原理、根源、HttpDNS解决方案等

(本文已同步发布于:http://www.52im.net/thread-3897-1-1.html

posted @ 2022-05-13 16:27 Jack Jiang 阅读(142) | 评论 (0)编辑 收藏

关于MobileIMSDK

MobileIMSDK 是一套专为移动端开发的原创开源IM通信层框架:

  • 历经8年、久经考验;
  • 超轻量级、高度提炼,lib包50KB以内;
  • 精心封装,一套API同时支持UDP、TCP、WebSocket三种协议(可能是全网唯一开源的);
  • 客户端支持 iOSAndroid标准JavaH5小程序(开发中..)、Uniapp(开发中..);
  • 服务端基于Netty,性能卓越、易于扩展;:point_left:
  • 可与姊妹工程 MobileIMSDK-Web 无缝互通实现网页端聊天或推送等;:point_left:
  • 可应用于跨设备、跨网络的聊天APP、企业OA、消息推送等各种场景。

关于RainbowChat

► 详细产品介绍:http://www.52im.net/thread-19-1-1.html
► 版本更新记录:http://www.52im.net/thread-1217-1-1.html
► 全部运行截图:Android端iOS端
► 在线体验下载:专业版(TCP协议)专业版(UDP协议)      (关于 iOS 端,请:点此查看

 

RainbowChat是一套基于开源IM聊天框架 MobileIMSDK 的产品级移动端IM系统。RainbowChat源于真实运营的产品,解决了大量的屏幕适配、细节优化、机器兼容问题(可自行下载体验:专业版下载安装)。

* RainbowChat可能是市面上提供im即时通讯聊天源码的,唯一一款同时支持TCP、UDP两种通信协议的IM产品(通信层基于开源IM聊天框架  MobileIMSDK 实现)。

v8.1 版更新内容

此版更新内容:

(1)Android端主要更新内容【新增“扫一扫”等功能及优化!】:

  • 1)[新增]“扫一扫”界面及完整功能(支持扫码加好友、加群);
  • 2)[新增]“我的二维码”界面及完整功能;
  • 3)[新增]“群聊二维码”界面及完整功能;
  • 4)[升级]升级okhttp库至4.9.3;
  • 5)[优化]其它小优化。

(2)服务端主要更新内容:

  • 1)[优化]针对扫码加群等功能的相关修改。

此版主要新增功能运行截图更多截图点此查看):

posted @ 2022-05-11 17:49 Jack Jiang 阅读(144) | 评论 (0)编辑 收藏

     摘要: 一、前言MobileIMSDK 是什么?MobileIMSDK  是一套专门为移动端开发的开源IM即时通讯框架,超轻量级、高度提炼,一套API优雅支持UDP 、TCP 、WebSocket 三种协议,支持iOS、Android、H5、标准Java平台,服务端基于Netty编写。工程地址是:1)Gitee码云地址:https://www.oschina.net/p/mobilei...  阅读全文

posted @ 2022-05-05 15:15 Jack Jiang 阅读(197) | 评论 (0)编辑 收藏

本文由融云技术团队原创分享,原题“IM 消息数据存储结构设计”,内容有修订。

1、引言

在如今的移动互联网时代,IM类产品已是我们生活中不可或缺的组成部分。像微信、钉钉、QQ等是典型的以 IM 为核心功能的社交产品。另外也有一些应用虽然IM功能不是核心,但IM能力也是其整个应用极其重要的组成部分,比如在线游戏、电商直播等应用。

在IM技术应用场景越来越广泛的前提下,对即时通讯IM技术的学习和掌握就显的越来越有必要。

在IM庞大的技术体系中,消息系统无疑是最核心的,而消息系统中,最关键的部分是消息的分发和存储,而离线消息和历史消息又是这个关键环节中不可回避的技术要点。

本文将基于IM消息系统的技术实践,分享关于离线消息和历史消息的正确理解,以及具体的技术配合和实践,希望能为你的离线消息和历史消息技术设计带来最佳实践灵感。

学习交流:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK 

本文同步发布于:http://www.52im.net/thread-3887-1-1.html

2、相关文章

技术相关文章:

  1. 什么是IM系统的可靠性?
  2. 闲鱼IM的在线、离线聊天数据同步机制优化实践
  3. 闲鱼亿级IM消息系统的可靠投递优化实践
  4. 一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等
  5. IM消息送达保证机制实现(二):保证离线消息的可靠投递
  6. 我是如何解决大量离线消息导致客户端卡顿的

融云技术团队分享的其它文章:

  1. 融云安卓端IM产品的网络链路保活技术实践
  2. 全面揭秘亿级IM消息的可靠投递机制
  3. 解密融云IM产品的聊天消息ID生成策略
  4. 万人群聊消息投递方案的思考和实践
  5. 基于WebRTC的实时音视频首帧显示时间优化实践
  6. 融云IM技术分享:万人群聊消息投递方案的思考和实践

3、IM消息投递的一般做法

在通常的IM消息系统中,对于实时消息、离线消息、历史消息大概都是下面这样的技术思路。

对于在线用户:消息会直接实时发送到在线的接收方,消息发送完成后,服务器端并不会对消息进行落地存储。

而对于离线的用户:服务器端会将消息存入到离线库,当用户登录后,从离线库中将离线消息拉走,然后服务器端将离线消息删除。

这样实现的缺点就是消息不持久化,导致消息无法支持消息漫游,降低了消息的可靠性。

PS:实际上,这其实也不能算是缺点,因为一些场景下存储历史消息并不是必须的,所谓的消息漫游能力也不是必备的,比如微信。

而在我们设计的消息系统中,服务器只要接收到了发送方发上来的消息,在转发给接收方的同时也会在离线数据库及历史消息库中进行消息的落地存储,而历史消息的落地也就能支持消息漫游等相关功能了。

4、什么是离线消息和历史消息?

关于离线消息和历史消息,在技术上,我们是这样定义。

1)离线消息:

离线消息就是用户(即接收方)在离线过程中收到的消息,这些消息大多是用户比较关心的消息,具有一定的时效性。

以我们的系统经验来说,我们的离线消息默认只保存最近七天的消息。

用户(即接收方)在下次登录后会全量获取这些离线消息,然后在客户端根据聊天会话进行离线消息的UI展示(比如显示一个未读消息气泡等)。

PS:用户离线的可能性在技术上其实是由很多种情况组成的,比如对方不在线、对方网络断掉了、对方手机崩溃了、服务器发送时出错了等等,严格来讲——只要无法实时发送成的消息,都算“离线消息”。

2)历史消息:

历史消息存储了用户所有的聊天消息,这些消息包括发出的消息以及接收到的消息。

在客户端获取历史消息时,通常是按照会话进行分页获取的。

以我们的系统经验来说,历史消息的存储时间我们设计默认为半年,当然这个时间可以按实际的产品运营规则来定,没有硬性规定。

5、IM消息的发送及存储流程

以下是我们系统整体的消息发送及存储流程:

 如上图所示:当用户发送聊天消息到服务器端后,首先会进入到消息系统中,消息系统会对消息进行分发以及存储。

这个过程中:对于在线的接收方,会选择直接推送消息。但是遇到接收方不在线或者是消息推送失败的情况下,也会有另外的消息获取方式,比如接收方会主动向服务器拉取未收到的消息。但是接收方何时来服务器拉取消息以及从哪里拉取是未知的,所以消息存入到离线库的意义也就在这里。

消息系统存储离线的过程中,为了不影响整个系统的更为平稳,我们使用了MQ消息队列进行IO解偶,所以聊天消息实际上是异步存入到离线库中的(通过MQ进行慢IO解偶,这其实也是惯常做法)。

在分发完消息后:消息服务会同步一份消息数据到历史消息服务中,历史消息服务同样会对消息进行落地存储。

对于新的客户端设备:会有同步消息的需求(所谓的消息漫游能力),而这也正是历史消息的主要作用。在历史消息库中,客户端是可以拉取任意会话的全量历史消息的。

6、IM离线消息、历史消息在存储逻辑上的区别

6.1 概述

通过上面的图中能清晰的看到:

  • 1)离线消息我们存储介质选用的是 Redis
  • 2)历史消息我们选用的是 HBase

对于为什么选用不同的存储介质,其实我们考虑的是离线消息和历史消息不同的业务场景和读写模式。

下面我们重点介绍一下离线消息和历史消息存储的区别。

6.2 离线消息存储模式——“扩散写”

离线消息的存储模式我们用的是扩散写。

如上图所示:每个用户都有自己单独的收件箱和发件箱:

  • 1)收件箱存放的是需要向这个接收端同步的所有消息;
  • 2)发件箱里存放的是发送端发出的所有消息。

以单聊为例:聊天中的两人会话中,消息会产生两次写,即发送者的发件箱和接收端的收件箱。

而在群的场景下:写入会被更加的放大(扩散),如果群里有 N 个人,那一条群消息就会被扩散写 N 次。

小结一下:

  • 1)扩散写的优点是:接收端的逻辑会非常清晰简单,只需要从收件箱里读取一次即可,大大降低了同步消息所需的读的压力;
  • 2)扩散写的缺点是:写入会被成指数地放大,特别是针对群这种场景。

6.3 历史消息存储模式——“扩散读”

历史消息的存储模式我们用的是扩散读。

因为历史消息中,每个会话都保存了整个会话的全量消息。在扩散读这种模式下,每个会话的消息只保存一次。

对比扩散写模式,扩散读的优点和缺点如下:

  • 1)优点是:写入次数大大降低,特别是针对群消息,只需要存一次即可;
  • 2)缺点是:接收端接收消息非常的复杂和低效,因为这种模式客户端想拉取到所有消息就只能每个会话同步一次,读就会被放大,而且可能会产生很多次无效的读,因为有些会话可能根本没有新消息。

6.4 小结

在 IM 这种应用场景下,通常会用到扩散写这种消息同步模型,一条消息产生一条,但是可能会被读多次,是典型的读多写少的场景。

一个优化好的IM系统,必须从设计上平衡读写压力,避免读或者写任意一个维度达到天花板。

当然扩散写这种模式也有其弊端,比如万人群,会导致一条消息,写入了一万次。

综合来讲:我们需要根据自己的业务场景做相应设计选择,以我们的IM系统为例,就是是根据了离线和历史消息的不同场景选择了写扩散和读扩散的组合模式。适合的才是最好的,没有必要死搬硬套理论。

7、IM客户端的拉取消息逻辑

7.1 离线消息拉取逻辑

对于IM客户端而言,离线消息的获取针对的是自己的整个离线消息,包括所有的会话(直白了说,就是上线时拉取此次离线过程中的所有未收取的离线消息)。

离线消息的获取是自上而下的方式(按时间序),我们的经验是一次获取 200 条(PS:如果离线消息过多,会分页多次拉取,拉取1“次”可以理解为拉取1“页”)。

在客户端拉取离线消息的信令中,需要带上当前客户端缓存的消息的最大时间戳。

通过上节的图我们应该知道,离线消息我们存储的是一个线性结构(指的是按时间顺序),Server 会根据这个时间戳向下查找离线消息。当重装或者新安装 App 时,客户端的“当前客户端缓存的消息的最大时间戳”可以传 0 上来。

Server 也会缓存客户端拉取到的最后一条消息的时间戳,然后根据业务场景,客户端类型等因素来决定从哪里开始拉取,如果没有拉取完 Server 会在拉取消息的应答中带相应的标记位,告诉客户端继续拉取,客户端循环拉取,直到所有离线消息拉完。

7.2 历史消息拉取逻辑

历史消息的获取通常针对的是单一会话。

在拉取过程中,需要向服务端提交两个参数:

  • 1)对方的 ID(如果是单聊的话就是对方的 UserID,如果是群则是群组ID);
  • 2)当前会话的最前面消息的时间戳(即当前会话最老一条消息的时间戳)。

Server据这两个参数,可以定位到这个客户端的此会话,然后一次获取 20 条历史消息。

消息的拉取时序上采用的是自下而上的方式(也就是时间序逆序),即从最后面往前翻。只要有消息,客户端可以一直向前翻,手动触发获取会话的历史消息。

上面的拉取逻辑,在IM界面功能上通常对应的是下拉或点击“加载更多”,比如这样:

8、本文小结

本文主要分享了IM中有关离线消息和历史消息的正确,主要包括离线消息和历史消息的区别,以及二者在存储、分发、拉取逻辑方面的最佳践等。如对文中内容有异议,欢迎留言讨论。

9、参考资料

[1] 一套海量在线用户的移动端IM架构设计实践分享(含详细图文)

[2] 一套原创分布式即时通讯(IM)系统理论架构方案

[3] 从零到卓越:京东客服即时通讯系统的技术架构演进历程

[4] 一套亿级用户的IM架构技术干货(上篇):整体架构、服务拆分等

[5] 闲鱼亿级IM消息系统的架构演进之路

[6] 闲鱼亿级IM消息系统的可靠投递优化实践

[7] 闲鱼亿级IM消息系统的及时性优化实践

[8] 基于实践:一套百万消息量小规模IM系统技术要点总结

[9] IM消息送达保证机制实现(一):保证在线实时消息的可靠投递

[10] 理解IM消息“可靠性”和“一致性”问题,以及解决方案探讨

[11] 零基础IM开发入门(一):什么是IM系统?

本文同步发布于:http://www.52im.net/thread-3887-1-1.html

posted @ 2022-04-19 14:56 Jack Jiang 阅读(244) | 评论 (0)编辑 收藏

本文由网易云信技术团队分享,原题“如何保障一场千万级大型直播?”,有修订和改动。

1、引言

本文以TFBOYS“日光旅行”七周年这场直播演唱会为案例,为你分享大型直播系统后端架构设计的方方面面,包括:基本架构、稳定性保障、安全性障、监控报警、应急预案等技术范畴。

案例中的这次演唱会采用了在线实时互动及演唱会现场的多场景导播切换,提供了主机位和三个艺人专属机位流,同时每个机位流实时转码四个清晰度档位,用户可以根据喜好选择自己想看的内容。这场演唱会最高同时在线人数达78.6万,打破线上付费演唱会世界记录。

学习交流:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK 

本文同步发布于:http://www.52im.net/thread-3875-1-1.html

2、本文作者

费曼:网易智企服务端开发工程师。硕士毕业于华中科技大学电信系,2016年加入网易云信,热衷于大规模分布式系统和音视频相关技术,爱好文学、体育和电影。

3、架构方面

3.1 基本

 

上图是该次TFBOYS在线演唱会的直播媒体架构简图。

可以看出一场大型活动直播涵盖的技术方案点非常庞杂,本节接下来的内容我们将以推拉流链路、全局智能调度、流量精准调度以及单元化部署,对这套直播方案做一个展开介绍。

3.2 推拉流链路

 

如上图所示,直播技术架构,分为几大部分:

  • 1)视频直播中心(LMS——Live Manage Service):负责直播流的逻辑管理和操作控制,包括存储和下发实时转码、加密等媒体处理的配置信息;
  • 2)实时互动直播服:由连麦互动和直播两部分组成,主播和连麦者的音视频数据在互动直播高性能服务器合成为一道流后推流到直播流媒体服务器;
  • 3)直播源站服务(LSS——Live Source Service):网易云信自建的直播流媒体服务器节点,结合全局智能调度系统,提供第一公里的最佳链路选择,同时融合支持接入多家CDN厂商;
  • 4)媒体处理服务(MPS——Media Processing Service):提供实时水印、实时转码、媒体数据加密等强大的流媒体处理能力;
  • 5)融合CDN与全局智能调度(GSLB——Golabal Server Load Balancing):提供敏捷智能的CDN调度策略和分配算法,结合全链路、端到端的流媒体控制,来达到最终端侧优良的用户体验;
  • 6)客户端SDK:提供推流、拉流以及上下行的调度能力,便于用户快速接入使用网易云信平台一站式的音视频解决方案。

3.3 融合CDN与智能调度

这是一个端到端的服务,通过平台的SDK执行一个类似HTTPDNS的调度,来做到真正根据用户IP做就近的接入。

针对国内相对复杂的运营商网络环境,在直播上行方面通过BGP网络以及与相关运营商在网络接入方面的合作,能够更加精准地控制网络链路的选择。

而对于下行,也提供了播放端的SDK接入,通过端到端的调度策略就近选择合适的下行链路。

调度的准确性以及最终效果,依赖及时准确的数据支撑。

我们有一个全链路、立体的数据监控体系,一方面利用CDN上的一些实时日志,另一方面结合自建节点、客户端侧上报收集链路上探测的数据,然后整合做一个实时计算来支撑整个调度的策略。

融合CDN方案,通过调度、监控、高可用等技术和手段来解决CDN网络方面的问题。但是对于技术人员来说,就和在使用一个传统的CDN网络一样没有大的差异,这些技术细节对技术人员透明无感知。

3.4 流量精准调度

大型演唱会直播活动,尤其是正式开播时的进场阶段,突发流量峰值会非常高,这就需要实时精准的智能调度策略。

融合CDN的智能调度包含两大部分:CDN分配调度和节点调度。

节点调度:比较常见的是DNS协议解析调度和IP调度(302/HTTPDNS)。前者由于DNS协议原因,调度生效时间较慢,而后者则可以做到请求级别的调度,也就是支持任意比例的负载均衡,更加及时精准。在我们的智能调度的场景里,正常情况下会遵循IP调度,在IP调度解析失败时,客户端上会启动loacl DNS解析逻辑,两者的结合确保了调度的精准和稳定可靠。

Don't put all your eggs in one basket.

“永远不要将鸡蛋放在同一个篮子里”。

从风险管控的角度来说:大型活动保障的CDN厂商资源,通常没法通过一家CDN资源进行满足。融合CDN方案则是将多家CDN厂商进行整合与流量分配调度。

通常在一次大型直播中,多家CDN厂商提供的容量(区域带宽、最高带宽)、质量会各不相同。我们则是通过动态调整调度比例,在确保不超过最大带宽的前提下,精确化按比例分配流量,以及尽可能地确保体验。

我们设计了一套针对CDN厂商的打分算法:影响因子包含当前带宽、保底带宽、最大带宽、带宽预测、带宽质量。

算法遵循以下原则:

  • 1)没超保底的带宽,比超过保底的带宽,得分更高;
  • 2)没超保底的时候,剩余保底和剩余总带宽越大,得分更高;
  • 3)超过保底的时候,剩余总带宽越大、质量越好,得分更高。

各CDN的分数之比决定了调度比例,CDN打分算法是在持续地迭代更新计算,最大化分配使用各家CDN的带宽,然后再分配各家CDN厂商的保障之外的资源。同时优先选择质量较好的厂家,避免单价CDN厂商超分配。

3.5 单元化部署

上面所说,在大型直播活动中,短时间大量涌入的用户请求,对以全局智能调度服务为主的相关非媒体流链路应用,也提出了更高的并发处理挑战。

除了上行的推流链路我们做了主备两个单元的部署,非媒体数据链路上的服务也采用了单元化的部署方案。

在此部署方案下,可用性做到任意单元机房故障,不影响整体可用性,即异地多活。

单元化部署遵循以下原则:

  • 1)单元化的依赖也必须单元化(核心业务);
  • 2)单元化粒度为应用,非api;
  • 3)单元化技术栈对应用尽量避免产生侵入性。

 

如上图所示:非单元化的业务部署在主机房,单元化的业务则部署在主机房和单元机房。

4、稳定性保障

4.1 上行链路稳定

超大型直播方案最核心的诉求就是直播稳定性,下面我们将以该次在线演唱会为案例,重点阐述一下直播的全链路稳定性架构。

上图是我们直播的媒体流链路示意简图:整体方案可以承受任何单节点、单线路、单机房网络出口的故障。

如直播源站部分:采用了多线策略收流,包含机房专线和4G背包方案,一主一备两个线路。同时每个单元的源站集群都有4层负载均衡,一台机器宕机不会影响整体可用性。LMS、LSS、MPS都是跨机房部署,所有服务模块都可配置专有资源池供使用,保证不会受其他租户影响。

整个推流链路:采用双路热流、互为主备,且部署上是互相独立的两个单元,能做到支持Rack级别的故障灾备。双路热流实现了自动主备切换,端上无需专门添加应用层的线路切换逻辑。当任何一个链路出现问题的时候,观众的直播流不会受到影响,端上平均卡顿感知时间在1s以内。

除了推流链路的整体主备单元容灾,每个单元的服务本身也会有容灾手段。比如UPS接入,可以接受30min的供电故障,比如当实时互动流出现问题时,导播台会推垫片流以保证链路数据不中断。

4.2 下行链路稳定

在访次直播活动中,全局智能调度服务会承受较大的峰值压力,在单元化部署的基础上,我们经过多轮压测和性能调优,模型上可支撑千万级用户在半分钟内全部进入直播间。

除了上述关于推流链路的高可用,下行链路也有相关的容灾策略。当GSLB智能调度服务整体不可用,在客户端SDK预埋了融合CDN的local DNS灾备逻辑与比例配置,将云端的全局智能调度fail-over到客户端的本地兜底调度,并保持大数据统计层面的各CDN厂商的流量分配均衡。

同时:客户端也会有播放体验方面的容灾策略,诸如清晰度降级、线路调整等。

5、安全性保障

除了直播全链路的稳定之外,直播安全也很重要。

该次直播活动中,为TFBOYS活动链路多环节都提供了安全保障机制(如防盗链鉴权、IP黑白名单、HTTPS等能力),以及地区、运营商等下行调度的动态限制,实现全链路安全保障。

在此基础上:此次活动采用了端到端的视频流数据加密。

直播场景的加密有几点基本要求:压缩比不变、实时性和低计算复杂度。

除此之外:在融合多cdn的方案背景下,视频流的加密必须考虑到CDN厂商的兼容性。

比如须满足以下要求:

  • 1)不破坏流媒体协议格式、视频容器格式;
  • 2)metadata/video/audio tag的header部分不加密;
  • 3)对于avcSequenceHeader和aacSequenceHeader tag整体不加密。

具体加密算法,可以采用一些流式加密算法,这里我们不再赘述。

 

6、监控与报警

6.1 概述

一场大型直播将会有大量的计算节点参与,除了媒体数据处理与分发的各个服务器节点,还有分布在国内外的海量客户端。

我们对网络链路、服务节点、设备端的健康与质量感知,都离不开数据监控系统。

同时:我们在现有系统无法自动fail-over的故障场景下,需要人工预案介入,而后者的决策判断,也强依赖于完善的全链路数据质量监控与报警系统。

6.2 全链路监控

整个直播链路的监控包含了:

  • 1)上行推流链路的流质量;
  • 2)媒体流实时转码处理;
  • 3)端上播放质量;
  • 4)智能调度系统的可用性;
  • 5)业务量水位等相关监控数据。

上行链路常见的QoS指标有:帧率、码率、RTT等,其维度包含主备线路、出口运营商、CDN厂商节点等。

端上的QoS指标则包含了:拉流成功率、首帧时长、卡顿率、httpdns缓存命中率,维度则覆盖包含CDN厂商、国家、省份、运营商、直播流、清晰度档位、客户端等。

此次直播中:内容上支持了多种机位流以及多个清晰度的转码输出流,同时通过多个CDN厂商进行分发,我们把上行链路中节点的码率、帧率,直观地通过N个指标卡集中展示在单个大盘页面上,并且通过增加预警值进行异常显示和弹窗消息告警。活动作战室现场,我们采用了多个大屏展示,非常直观地展现当前主备双推流链路的实时帧率、码率等情况,为现场地指挥保障提供了强大的数据决策支撑。

以下图为例:蓝色表示上行帧率,绿色表示正常的上行码率,红色表示码率值过低,N/A表示当前没有上行推流数据。

而在下行播放链路中,比较常用的指标就是卡顿率。

下面是我们对卡顿相关的描述:

  • 1)一次卡顿:播放器持续2s发生缓冲区空,即播放器2s没有拉到流;
  • 2)一分钟用户卡顿:1分钟窗口内,用户只要卡顿一次,则该用户计作卡顿用户;
  • 3)一分钟用户卡顿率:1分钟窗口内,卡顿用户数/总的用户数;
  • 4)一分钟用户零卡顿率:1分钟窗口内,(总的用户数 - 卡顿用户数)/总的用户数。

为什么会选择用户卡顿率这个指标,而不是使用整体的卡顿采样点/总采样数呢?

是因为:我们更想看到有多少用户没有出现过卡顿现象,这更能直观体现优质网络的整体占比。通过对各省份用户零卡顿率、用户数排行,以及各省用户卡顿率的观察,我们可以非常直观地找到卡顿严重的地区,以便重点关注,进行资源调度优化。

7、应急预案

任何一个系统,无论你号称它被设计得多么健壮,它仍然会有故障时间的存在。

硬件故障、软件bug、人为操作失误等等,这些都无可避免地存在着。他们未必是一个必须多少时间内将其彻底解决的问题,他们是我们必须认清并接受共存的一个事实。

所以:预案管理是大型直播活动保障中不可缺少的一环。

我们遵循以下的预案原则:

  • 1)预案信息明确:大盘自动监控不具备二义性,确保预案信息来源正确,触发执行预案的条件明确且有数值化约束;
  • 2)预案操作简洁:所有的预案操作都有有简洁明确(开关型)的操作输入;
  • 3)预案操作安全:所有预案要经过充分预演,同时预演操作本身需要有明确的确认机制,以确保在正常情况下不会被误触发;
  • 4)预案影响验证:明确理清预案操作的影响,QA在预演阶段需要对相关影响进行充分验证。

此次活动的前期筹备中,我们总计进行了3次直播全链路的拟真演练,以及2次联合互动现场、导播台现场的活动全流程级别的彩排,另外进行了大大小小总计数十次的各类风险预案演练。所有演练过程中发现的问题,都会进行专项解决。

风险预案这块,包含了各类资源故障、上下行链路质量、地区性网络故障、CDN异常流量水位等在内的场景应对。其中资源故障包含了机器宕机、机架整体断电、堆叠交换机宕机、机房外网出口不可用,我们均进行了风险预案演练覆盖。

下面列举几点直播解决方案中的部分预案机制:

  • 1)如果因为误操作等导致非正常解密等,可在推流不中断的情况下,动态中止流加密,客户端无任何感知影响;
  • 2)某家cdn在某地区运营商出现大面积故障瘫痪,该地区相应运营商线路的QoS指标会大幅度下降并触发报警,将故障cdn在该地区运营商进行黑名单处理,动态停止对其的调度,将流量调度至正常提供服务的cdn厂商;
  • 3)在两路热流均正常的情况下,但是正在分发的一路出现质量问题,方案可支持手动触发主备切换,让监控数据质量更好的另一路流参与分发,客户端感知时间在1s以内;
  • 4)因为一些不可抗因素,某机房出现大面积故障整体不可用,触发链路报警,此时我们会紧急将流切至另一机房,故障感知与恢复的时间在一分钟内。

8、相关文章

[1] 移动端实时音视频直播技术详解(一):开篇

[2] 移动端实时音视频直播技术详解(二):采集

[3] 移动端实时音视频直播技术详解(三):处理

[4] 移动端实时音视频直播技术详解(四):编码和封装

[5] 移动端实时音视频直播技术详解(五):推流和传输

[6] 移动端实时音视频直播技术详解(六):延迟优化

[7] 淘宝直播技术干货:高清、低延时的实时视频直播技术解密

[8] 爱奇艺技术分享:轻松诙谐,讲解视频编解码技术的过去、现在和将来

[9] 零基础入门:实时音视频技术基础知识全面盘点

[10] 实时音视频面视必备:快速掌握11个视频技术相关的基础概念

[11] 网易云信实时视频直播在TCP数据传输层的一些优化思路

[12] 浅谈实时音视频直播中直接影响用户体验的几项关键技术指标

[13] 首次披露:快手是如何做到百万观众同场看直播仍能秒开且不卡顿的?

[14] 直播系统聊天技术(一):百万在线的美拍直播弹幕系统的实时推送技术实践之路

[15] 直播系统聊天技术(二)阿里电商IM消息平台,在群聊、直播场景下的技术实践

[16] 直播系统聊天技术(三):微信直播聊天室单房间1500万在线的消息架构演进之路

[17] 直播系统聊天技术(四):百度直播的海量用户实时消息系统架构演进实践

[18] 直播系统聊天技术(五):微信小游戏直播在Android端的跨进程渲染推流实践

[19] 直播系统聊天技术(六):百万人在线的直播间实时聊天消息分发技术实践

[20] 直播系统聊天技术(七):直播间海量聊天消息的架构设计难点实践

本文同步发布于:http://www.52im.net/thread-3875-1-1.html

posted @ 2022-04-13 10:58 Jack Jiang 阅读(248) | 评论 (0)编辑 收藏

本文由小枣君分享,文案:小枣君、漫画:杨洋,来自鲜枣课堂,有少许改动,原文链接见文末。

1、引言

网络编程能力对于即时通讯技术开发者来说是基本功,而计算机网络又是网络编程的理论根基,因而深刻准确地理解计算机网络知识显然能夯实你的即时通讯应用的实践品质。

本文风格延续了社区里的《网络编程懒人入门》、《脑残式网络编程入门》两个系列,没有更多的理论堆砌,通俗而不失内涵,非常适合希望轻松快乐地学习计算机网络知识的网络编程爱好者们阅读,希望能给你带来不一样的网络知识入门视角。

本篇文章将利用简洁生动的文字,配上轻松幽默的漫画,助你从零开始快速建立起对IPv6技术的直观理解,非常适合入门者阅读。

学习交流:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK 

 (本文同步发布于:http://www.52im.net/thread-3868-1-1.html

2、系列文章

本文是该系列文章中的第3篇:

  1. 网络编程入门从未如此简单(一):假如你来设计网络,会怎么做?
  2. 网络编程入门从未如此简单(二):假如你来设计TCP协议,会怎么做?
  3. 网络编程入门从未如此简单(三):什么是IPv6?漫画式图文,一篇即懂!》(本文

本文是IPv6的轻松入门文章,希望你能喜欢。

* 推荐阅读:本文作者的另一篇也同样优秀:网络编程懒人入门(十一):一文读懂什么是IPv6,感兴趣的建议一并阅读 。

3、技术背景

随着移动网络的不断建设和普及,加速了我们迈入万物互联时代的步伐。

我们的整个互联网络,正在发生翻天覆地的变化。急剧增加的网络连接数和流量,对网络的承载和传送能力,提出了前所未有的挑战。

除了速率和带宽之外,5G在垂直行业的落地,也要求网络能够提供灵活的差异化定制服务能力。

也就是说,面对不同的行业应用场景,网络需要能够提供套餐式的服务,支持不同的QoS(Quality of Service,服务质量),支持端到端的切片。

4、IP协议

众所周知,我们现在形影不离的互联网,最早诞生于上世纪60年代。它的核心基础,就是大名鼎鼎的IP协议Internet Protocol,网际互连协议,见《技术往事:改变世界的TCP/IP协议(珍贵多图、手机慎点)。

如果没有IP协议,以及基于它的IP地址,我们就没办法刷剧、网购、吃鸡、聊微信。

说白了,互联网就是一套“快递系统”。IP地址是你的快递地址,而IP协议,则是快递公司的“工作流程和制度”。

所有我们需要传递的信息,包括文字、图片、音频、视频等,都需要被打包成一个个的“快递包裹”,然后经过快递系统的运输,送到最终目的地。

5、第一、第二代“快递系统”:IPv4

互联网诞生后,长期使用的是v4版本的IP协议,也就是大家熟知的IPv4。

我们可以把它理解为第一代快递系统,它为互联网的早期发展奠定了坚实基础。

后来,随着互联网的迅速发展扩张,原始的IPv4系统暴露出了很多的问题,进行了一些技术上的升级改进。尤其是MPLSMulti-Protocol Label Switching,多协议标签交换)技术的引入,将这个快递系统升级到了第二代。

到了最近这几年,因为前面我们提到的网络挑战,远远超过了第二代快递系统的能力范围。

6、第三代“快递系统”:IPv6

于是,IPv6以及IPv6+,作为第三代快递系统,正式闪亮登场。

IPv6,是v6版本的IP协议。而IPv6+,则是IPv6的升级加强版。

具体来说,IPv6+基于IPv6,实现了更多的创新。

这些创新,既包括以IPv6分段路由、网络切片、随流检测、新型组播和应用感知网络等协议为代表的协议创新,又包括以网络分析、自动调优、网络自愈等网络智能化为代表的技术创新。

凭借这些创新,IPv6+更适合行业用户,更能够有力支撑行业的数字化转型和发展。

接下来,我们仔细看看,IPv6+究竟带来了哪些变化和升级。

7、IPv6优势1:IP地址大幅增加

首先,IPv6最广为人知的优点就是IP地址的大幅增加。具体来说,IPv6的地址数量是IPv4的2的96次方倍(详见《一文读懂什么是IPv6》的第6节内容)。

这么说吧,如果采用IPv6,即便是给地球上的每粒沙子都赋予一个IP地址,都绰绰有余。

传统的IPv4快递系统,邮箱地址不够,快递员往往需要将快速送到门卫处或快递柜,然后再二次派送给用户(在IPv4时代,这就是NAT路由技术啦,详见《NAT详解——详细原理、P2P简介》、《什么是公网IP和内网IP?NAT转换又是什么鬼?)。

 

在IPv6快速系统下,每个用户都有属于自己的邮箱地址,快递员可以直接将快递送到用户手中。

很显然,这样不仅提升了快递的收发速度,也节省了门卫或快递柜的开支,简化了维护,减少了能耗,降低了成本。

其实,IP地址数量的压力,主要来自物联网场景。因为物的数量远远超过人的数量。而且,物联网的控制,更需要端到端的直达。这样才能有更低的时延,实现更精准的控制。

8、IPv6优势2:“快递包装”的升级

IPv6的第二个重大改进,在于“快递包装”的升级。IPv6的数据报文结构变得更加丰富,里面可以记录更多的内容和信息。

简单来说,就是运输快递的纸箱变得更高级了。

传统的快递系统,包装很简单,我们并不知道里面到底是什么物品。

IPv6的快递系统,纸箱上可以贴更多的标签,标识纸箱里的货物属性,例如重货、易碎品、紧急文件等。系统根据标签,可以快速判断这个快递包裹所需的服务,例如需要加急、需要小心轻放等。

这样一来,快递公司可以根据包裹显示的信息,为不同的客户提供更精细化的服务,采用差异化的收费标准。

快递公司还可以走精品路线,提供专属的快递通道,实现高端用户的资源独享。

IPv6+对数据包属性的精准识别,也可以帮助运营商更好地掌握整个网络中数据业务的流动趋势,更好地调动和分配资源。

例如,从A地到B地的视频大颗粒传输需求很多,那么,就可以建立视频大颗粒业务专线,更好地满足传输需求。

这就好像从A地到B地的海鲜运输需求很多,那快递公司就采购更多的冷链运输车,专门投入到这条线路上,赚取更多的利润。

9、IPv6优势3:升级了“导航能力”

传统快递系统的运输路径,是相对固定和死板的。运输车从起点到终点,经过每一个路口,都由路口指定下一步前进的方向。

 

而IPv6+的话,通过与SR(Segment Routing,分段路由)技术、SDN(Software Defined Network,软件定义网络)技术进行结合,具有更强的路径选择能力。

快递包裹在出发时,就已经从管理中心获得了从起点到终点的最佳路径。每一次选路,都按照规划进行,可以避开拥堵,也可以避免绕路。

换言之,IPv6+超强的路径编排能力,可以实现数据报文的一跳入云,大幅提升效率。

10、IPv6优势4:降低运维成本

因为网络的管理功能集中,可以更方便地将配置意图转换成脚本,自动部署给各个网络节点。

引入AI之后,更能够对故障现象进行自动分析,更快地找到原因。

甚至说,AI还可以根据对故障模型的学习,主动提前识别网络中潜在的故障风险,实现事故预防。

集中管理+AI管理,大幅降低了网络的维护难度,提升了运维效率,减少了维护成本。

11、IPv6优势5:更安全

IPv6+的安全防御能力相比IPv4有了很大的提升,真正实现了云、网、安一体化防御。

传统网络中,因为大量私网的存在,恶意行为很难溯源。也就是说,很多坏人躲在暗处,发出有问题的包裹,对快递系统造成破坏。

在IPv6+网络中,节点采用公网地址取代私网地址,这就意味着,在快递系统中运输的每一个包裹,都有真实可溯源的寄件人信息。失去了私网的伪装,破坏行为将无所遁形。

升级后的快递包装(数据报文结构),也大幅增加了破坏分子对包裹进行恶意伪造和窃听的难度,增强了包裹的安全性和私密性。

 

12、写在最后

总而言之,IPv6+是一个高速、高效、灵活、智能的先进“快递系统”。

它可以提供满足千行百业应用需求的差异化服务能力,适配不同行业的业务承载需求,支撑各个行业的数字化转型,助力消费互联网向产业互联网升级,推动整个社会数字经济的发展。

目前,IPv6在我国已经取得了显著的成果。截至今年8月,我国IPv6地址资源储备位居世界第一。IPv6活跃用户数达5.51亿,占我国全部网民数的54.52%。

IPv6+的黄金时代,已然到来!

 

13、参考资料

[1] TCP/IP详解 卷1 - 第3章 IP:网际协议

[2] 网络编程懒人入门(十一):一文读懂什么是IPv6

[3] IPv6技术详解:基本概念、应用现状、技术实践(上篇)

[4] IPv6技术详解:基本概念、应用现状、技术实践(下篇)

[5] Java对IPv6的支持详解:支持情况、相关API、演示代码等

[6] NAT详解——详细原理、P2P简介

[7] 什么是公网IP和内网IP?NAT转换又是什么鬼?

本文同步发布于:http://www.52im.net/thread-3868-1-1.html

posted @ 2022-03-30 12:56 Jack Jiang 阅读(318) | 评论 (0)编辑 收藏

本文由小米技术团队分享,原题“小爱接入层单机百万长连接演进”,有修订。

1、引言

小爱接入层是小爱云端负责设备接入的第一个服务,也是最重要的服务之一,本篇文章介绍了小米技术团队2020至2021年在这个服务上所做的一些优化和尝试,最终将单机可承载长连接数从30w提升至120w+,节省了机器30+台

提示:什么是“小爱”?

小爱(全名“小爱同学”)是小米旗下的人工智能语音交互引擎,搭载在小米手机、小米AI音箱、小米电视等设备中,在个人移动、智能家庭、智能穿戴、智能办公、儿童娱乐、智能出行、智慧酒店、智慧学习共八大类场景中使用。

(本文同步发布于:http://www.52im.net/thread-3860-1-1.html

2、专题目录

本文是专题系列文章的第7篇,总目录如下:

  1. 长连接网关技术专题(一):京东京麦的生产级TCP网关技术实践总结
  2. 长连接网关技术专题(二):知乎千万级并发的高性能长连接网关技术实践
  3. 长连接网关技术专题(三):手淘亿级移动端接入层网关的技术演进之路
  4. 长连接网关技术专题(四):爱奇艺WebSocket实时推送网关技术实践
  5. 长连接网关技术专题(五):喜马拉雅自研亿级API网关技术实践
  6. 长连接网关技术专题(六):石墨文档单机50万WebSocket长连接架构实践
  7. 长连接网关技术专题(七):小米小爱单机120万长连接接入层的架构演进》(* 本文

3、什么是小爱接入层

整个小爱的架构分层如下:

接入层主要的工作在鉴权授权层和传输层,它是所有小爱设备和小爱大脑交互的第一个服务。

由上图我们知道小爱接入层的重要功能有如下几个:

  • 1)安全传输和鉴权:维护设备和大脑的安全通道,保障身份认证有效和传输数据安全;
  • 2)维护长连接:维持设备和大脑的长连接(Websocket等),做好连接状态存储,心跳维护等工作;
  • 3)请求转发:针对每一次小爱设备的请求做好转发,保障每一次请求的稳定。

4、早期接入层的技术实现

小爱接入层最早的实现是基于AkkaPlay,我们使用它们搭建了第一个版本,该版本特点如下:

  • 1)基于Akka我们基本做到了初步的异步化,保障核心线程不被阻塞,性能尚可。
  • 2)Play框架天然支持Websocket,因此我们在有限的人力下能够快速搭建和实现,且能够保障协议实现的标准性。

5、早期接入层的技术问题

随着小爱长连接的数量突破千万大关,针对早期的接入层方案,我们发现了一些问题。

主要的问题如下:

1)长连接数量上来后,需要维护的内存数据越来越多,JVM的GC成为不可忽略的性能瓶颈,且一旦代码写的不好有GC风险。经过之前事故分析,Akka+Play版的接入层其单实例长连接数量的上限在28w左右。

2)老版本的接入层实现比较随意,其Akka Actor之间存在非常多的状态依赖而不是基于不可变的消息传递这样使得Actor之间的通信变成了函数调用,导致代码可读性差且维护很困难,没有发挥出Akka Actor在构建并发程序的优势。

3)作为接入层服务,老版本对协议的解析是有很强的依赖的,这导致它要随着版本变动而频繁上线,其上线会引起长连接重连,随时有雪崩的风险。

4)由于依赖Play框架,我们发现其长连接打点有不准确的问题(因为拿不到底层TCP连接的数据),这个会影响我们每日巡检对服务容量的评估,且依赖其他框架在长连接数量上来后我们没有办法做更细致的优化。

6、新版接入层的设计目标

基于早期接入层技术方案的种种问题,我们打算重构接入层。

对于新版接入层我们制定的目标是:

  • 1)足够稳定:上线尽可能不断连接且服务稳定;
  • 2)极致性能:目标单机至少100w长连接,最好不要受GC影响;
  • 3)最大限度可控:除了底层网络I/O的系统调用,其他所有代码都要是自己实现/或者内部实现的组件,这样我们有足够的自主权。

于是,我们开始了单机百万长连接的漫漫实践之路。。。

7、新版接入层的优化思路

7.1 接入层的依赖关系

接入层与外部服务的关系理清如下:

7.2 接入层的功能划分

接入层的主要功能划分如下:

  • 1)WebSocket解析:收到的客户端字节流,要按照WebSocket协议要求解析出数据;
  • 2)Socket状态保持:存储连接的基本状态信息;
  • 3)加密解密:与客户端通讯的所有数据都是加密过的,而与后端模块之间传输是json明文的;
  • 4)顺序化:同一个物理连接上,先后两个请求A、B到达服务器,后端服务中B可能先于A得到了应答,但是我们收到B不能立刻发送给客户端,必须等待A完成后,再按照A,B的顺序发给客户端;
  • 5)后端消息分发:接入层后面不止对接单个服务,可能根据不同的消息转发给不同的服务;
  • 6)鉴权:安全相关验证,身份验证等。

7.3 接入层的拆分思路

把之前的单一模块按照是否有状态,拆分为两个子模块。

具体如下:

  • 1)前端:有状态,功能最小化,尽量少上线;
  • 2)后端:无状态,功能最大化,上线可做到用户无感知。

所以,按照上面的原则,理论上我们会做出这样的功能划分,即前端很小、后端很大。示意图如下图所示。

8、新版接入层的技术实现

8.1 总览

模块拆分为前后端:

  • 1)前端有状态,后端无状态;
  • 2)前后端是独立进程,同机部署。

补充:前端负责建立与维护设备长连接的状态,为有状态服务;后端负责具体业务请求,为无状态服务。后端服务上线不会导致设备连接断开重连及鉴权调用,避免了长连接状态因版本升级或逻辑调整而引起的不必要抖动;

前端使用CPP实现:

  • 1)Websocket协议完全自己解析:可以从Socket层面获取所有信息,任何Bug都可以处理;
  • 2)更高的CPU利用率:没有任何额外JVM代价,无GC拖累性能;
  • 3)更高的内存利用率:连接数量变大后与连接相关的内存开销变大,自己管理可以极端优化。

后端暂时使用Scala实现:

  • 1)已实现的功能直接迁移,比重写代价要低得多;
  • 2)依赖的部分外部服务(比如鉴权)有可直接利用的Scala(Java)SDK库,而没有C++版本,若用C++重写代价非常大;
  • 3)全部功能无状态化改造,可以做到随时重启而用户无感知。

通讯使用ZeroMQ

进程间通讯最高效的方式是共享内存,ZeroMQ基于共享内存实现,速度没问题。

8.2 前端实现

整体架构:

 

如上图所示,由四个子模块组成:

  • 1)传输层:Websocket协议解析,XMD协议解析;
  • 2)分发层:屏蔽传输层的差异,不管传输层使用的什么接口,在分发层转化成统一的事件投递到状态机;
  • 3)状态机层:为了实现纯异步服务,使用自研的基于Actor模型的类Akka状态机框架XMFSM,这里面实现了单线程的Actor抽象;
  • 4)ZeroMQ通讯层:由于ZeroMQ接口是阻塞实现,这一层通过两个线程分别负责发送和接收。

8.2.1)传输层:

WebSocket 部分使用 C++ 和 ASIO 实现 websocket-lib。小爱长连接基于WebSocket协议,因此我们自己实现了一个WebSocket长连接库。

这个长连接库的特点是:

  • a. 无锁化设计,保障性能优异;
  • b. 基于BOOST ASIO 开发,保障底层网络性能。

压测显示该库的性能十分优异的:

这一层同时也承担了除原始WebSocket外,其他两种通道的的收发任务。

目前传输层一共支持以下3种不同的客户端接口:

  • a. websocket(tcp):简称ws;
  • b. 基于ssl的加密websocket(tcp):简称wss;
  • c. xmd(udp):简称xmd。

8.2.2)分发层:

把不同的传输层事件转化成统一事件投递到状态机,这一层起到适配器的作用,确保无论前面的传输层使用哪种类型,到达分发层变都变成一致的事件向状态机投递。

8.2.3)状态机处理层:

主要的处理逻辑都位于这一层中,这里非常重要的一个部分是对于发送通道的封装。

对于小爱应用层协议,不同的通道处理逻辑是完全一致的,但是在处理和安全相关逻辑上每个通道又有细节差异。

比如:

  • a. wss 收发不需要加解密,加解密由更前端的Nginx做了,而ws需要使用AES加密发送;
  • b. wss 在鉴权成功后不需要向客户端下发challenge文本,因为wss不需要做加解密;
  • c. xmd 发送的内容与其他两个不同,是基于protobuf封装的私有协议,且xmd需要处理发送失败后的逻辑,而ws/wss不用考虑发送失败的问题,由底层Tcp协议保证。

针对这种情况:我们使用C++的多态特性来处理,专门抽象了一个Channel接口,这个接口中提供的方法包含了一个请求处理的一些关键差异步骤,比如如何发送消息到客户端,如何stop连接,如何处理发送失败等等。对于3种(ws/wss/xmd)不同的发送通道,每个通道有自己的Channel实现。

客户端连接对象一创建,对应类型的具体Channel对象就立刻被实例化。这样状态机主逻辑中只实现业务层的公共逻辑即可,当在有差异逻辑调用时,直接调用Channel接口完成,这样一个简单的多态特性帮助我们分割了差异,确保代码整洁。

8.2.4)ZeroMQ 通讯层:

通过两个线程将ZeroMQ的读写操作异步化,同时负责若干私有指令的封装和解析。

8.3 后端实现

8.3.1)无状态化改造:

后端做的最重要改造之一就是将所有与连接状态相关的信息进行剔除。

整个服务以 Request(一次连接上可以传输N个Request)为核心进行各种转发和处理,每次请求与上一次请求没有任何关联。一个连接上的多次请求在后端模块被当做独立请求处理。

8.3.2)架构:

Scala 服务采用 Akka-Actor 架构实现了业务逻辑。

服务从 ZeroMQ 收到消息后,直接投递到 Dispatcher 中进行数据解析与请求处理,在 Dispatcher 中不同的请求会发送给对应的 RequestActor进行 Event 协议解析并分发给该 event 对应的业务 Actor 进行处理。最后将处理后的请求数据通过XmqActor 发送给后端 AIMS&XMQ 服务。

一个请求在后端多个 Actor 中的处理流程:

8.3.3)Dispatcher 请求分发:

前端与后端之间通过 Protobuf 进行交互,避免了Json 解析的性能消耗,同时使得协议更加规范化。

后端服务从 ZeroMQ 收到消息后,会在 DispatcherActor 中进行PB协议解析并根据不同的分类(简称CMD)进行数据处理,分类包括如下几种。

* BIND 命令:

鉴权功能,由于鉴权功能逻辑复杂,使用C++语言实现起来较为困难,目前依然放在 scala 业务层进行鉴权。该部分对设备端请求的 HTTP Headers 进行解析,提取其中的 token 进行鉴权,并将结果返回前端。

* LOGIN 命令:

设备登入,设备鉴权通过后当前连接已成功建立,此时会进行 Login 命令的执行,用于将该长连接信息发送至AIMS并记录于Varys服务中,方便后续的主动下推等功能。在 Login 过程中,服务首先将请求 Account 服务获取长连接的 uuid(用于连接过程中的路由寻址),然后将设备信息+uuid 发送至AIMS进行设备登入操作。

* LOGOUT 命令:

设备登出,设备在与服务端断开连接时需要进行 Logout 操作,用于从 Varys 服务中删除该长连接记录。

* UPDATE 与 PING 命令:

a. Update 命令,设备状态信息更新,用于更新该设备在数据库中保存的相关信息;

b. Ping 命令,连接保活,用于确认该设备处于在线连接状态。

* TEXT_MESSAGE 与 BINARY_MESSAGE:

文本消息与二进制消息,在收到文本消息或二进制消息时将根据 requestid 发送给该请求对应的RequestActor进行处理。

8.3.4)Request 请求解析:

针对收到的文本和二进制消息,DispatcherActor 会根据 requestId 将其发送给对应的RequestActor进行处理。

其中:文本消息将会被解析为Event请求,并根据其中的 namespace 和 name 将其分发给指定的业务Actor。二进制消息则会根据当前请求的业务场景被分发给对应的业务Actor。

8.4 其他优化

在完成新架构 1.0 调整过程中,我们也在不断压测长连接容量,总结几点对容量影响较大的点。

8.4.1)协议优化:

a. JSON替换为Protobuf: 早期的前后端通信使用的是 json 文本协议,后来发现 json 序列化、反序列化这部分对CPU的占用较大,改为了 protobuf 协议后,CPU占用率明显下降。

b. JSON支持部分解析:业务层的协议是基于json的,没有办法直接替换,我们通过"部分解析json"的方式,只解析很小的 header 部分拿到 namespace 和 name,然后将大部分直接转发的消息转发出去,只将少量 json 消息进行完整反序列化成对象。此种优化后CPU占用下降10%。

8.4.2)延长心跳时间:

在第一次测试20w连接时,我们发现在前后端收发的消息中,一种用来保持用户在线状态的心跳PING消息占了总消息量的75%,收发这个消息耗费了大量CPU。因此我们延长心跳时间也起到了降低CPU消耗的目的。

8.4.3)自研内网通讯库:

为了提高与后端服务通信的性能,我们使用自研的TCP通讯库,该库是基于Boost ASIO开发的一个纯异步的多线程TCP网络库,其卓越的性能帮助我们将连接数提升到120w+。

9、未来规划

经过新版架构1.0版的优化,验证了我们的拆分方向是正确的,因为预设的目标已经达到:

  • 1)单机承载的连接数 28w => 120w+(普通服务端机器 16G内存 40核 峰值请求QPS过万),接入层下线节省了50%+的机器成本;
  • 2)后端可以做到无损上线。

再重新审视下我们的理想目标,以这个为方向,我们就有了2.0版的雏形:

具体就是:

  • 1)后端模块使用C++重写,进一步提高性能和稳定性。同时将后端模块中无法使用C++重写的部分,作为独立服务模块运维,后端模块通过网络库调用;
  • 2)前端模块中非必要功能尝试迁移到后端,让前端功能更少,更稳定;
  • 3)如果改造后,前端与后端处理能力差异较大,考虑到ZeroMQ实际是性能过剩的,可以考虑使用网络库替换掉ZeroMQ,这样前后端可以从1:1单机部署变为1:N多机部署,更好的利用机器资源。

2.0版目标是:经过以上改造后,期望单前端模块可以达到200w+的连接处理能力。

10、参考资料

[1] 上一个10年,著名的C10K并发连接问题

[2] 下一个10年,是时候考虑C10M并发问题了

[3] 一文读懂高性能网络编程中的线程模型

[4] 深入操作系统,一文读懂进程、线程、协程

[5] Protobuf通信协议详解:代码演示、详细原理介绍等

[6] WebSocket从入门到精通,半小时就够!

[7] 如何让你的WebSocket断网重连更快速?

[8] 从100到1000万高并发的架构演进之路

学习交流:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK 

(本文同步发布于:http://www.52im.net/thread-3860-1-1.html

posted @ 2022-03-22 17:00 Jack Jiang 阅读(626) | 评论 (0)编辑 收藏

本文由阿里闲鱼技术团队书闲分享,原题“如何有效缩短闲鱼消息处理时长”,有修订和改动。

1、引言

闲鱼技术团队围绕IM这个技术范畴,已经分享了好几篇实践性总结文章,本篇将要分享的是闲鱼IM系统中在线和离线聊天消息数据的同步机制上所遇到的一些问题,以及实践性的解决方案。

学习交流:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK 

本文已同步发布于:http://www.52im.net/thread-3856-1-1.html

2、系列文章

本文是系列文章的第7篇,总目录如下:

  1. 阿里IM技术分享(一):企业级IM王者——钉钉在后端架构上的过人之处
  2. 阿里IM技术分享(二):闲鱼IM基于Flutter的移动端跨端改造实践
  3. 阿里IM技术分享(三):闲鱼亿级IM消息系统的架构演进之路
  4. 阿里IM技术分享(四):闲鱼亿级IM消息系统的可靠投递优化实践
  5. 阿里IM技术分享(五):闲鱼亿级IM消息系统的及时性优化实践
  6. 阿里IM技术分享(六):闲鱼亿级IM消息系统的离线推送到达率优化
  7. 阿里IM技术分享(七):闲鱼IM的在线、离线聊天数据同步机制优化实践》(* 本文

3、问题背景

随着用户数的快速增长,闲鱼IM系统也迎来了前所未有的挑战。

历经多年的业务迭代,客户端侧IM的代码已经因为多年的迭代层次结构不足够清晰,之前一些隐藏起来的聊天数据同步问题,也随着用户数的增大而被放大。

这里面的具体流程在于:后台需要同步到用户端侧的数据包,后台会根据数据包的业务类型划分成不同的数据域,数据包在对应域里面存在唯一且连续的编号,每一个数据包发送到端侧并且被成功消费后,端侧会记录当前每一个数据域已经同步过的版本编号,下一次数据同步就以本地数据域的编号开始,不断的同步到客户端。

当然用户不会一直在线等待消息,所以之前端侧采用了推拉结合的方式保证数据的同步。

具体就是:

  • 1)客户端在线时:使用ACCS实时的将最新的数据内容推送到客户端(ACCS是淘宝无线向开发者提供全双工、低延时、高安全的通道服务);
  • 2)客户端从离线状态启动后:根据本地的数据域编号,拉取不在线时候的数据差;
  • 3)当数据获取出现黑洞时:触发数据同步拉取(“黑洞”即指数据包Version不连续的状态)。

4、问题分析

当前的聊天数据同步策略确实是可以基本保障IM的数据同步的,但是也伴随着一些隐含的问题。

这些隐含的问题主要有:

  • 1)短时间密集数据推送时,会快速的触发多次数据域同步。域同步回来的数据如果存在问题,又会触发新一轮的同步,造成网络资源的浪费。冗余数据包/无效的数据内容会占用有效内容的处理资源,又对CPU和内存资源造成浪费;
  • 2)数据域中的数据包客户端是否正常消费,服务端侧无感知,只能被动地根据当前数据域信息返回数据;
  • 3)数据收取/消息数据体解析/存储落库逻辑拆分不够清晰,无法针对性的对某一层的代码拆分替换进行ABTest。

针对上述问题,我们对闲鱼IM进行了分层改造——即抽离数据同步层。这样优化,除了希望以后这个数据的同步内容可以用在IM之外,也希望随着稳定性的增加,赋能其他的业务场景。

接下来的内容,我们重点来看下解决客户端侧闲鱼IM聊天数据同步问题的一些实践思路。

5、优化思路

5.1 分层拆分

对于服务端来说:业务侧产出数据包后,会拼接上当前的数据域信息,然后通过数据同步层将数据推送到端侧。

对于客户端来说:接收到数据包后,会根据当前的数据域信息,来确定需要消费数据包的业务方,确保数据包在数据域内完整连续后,将数据体脱壳后交于业务侧消费,并且应答消费的状况。

数据同步层的抽取:把数据同步中的加壳、脱壳、校验、重试流程封装到一起,可以让上层业务只需要关心自己需要监听的数据域信息,然后当这些数据域更新数据的时候,可以获取到这些数据进行消费,而不再需要关心数据包是否完整。

这样做的话:

  • 1)业务侧只需要关心业务侧对接的协议;
  • 2)数据侧只需要关心数据侧包装的协议;
  • 3)网络层负责真实的数据传输。

整体的架构原理如下:

总结一下就是:

  • 1)对齐数据层数据传输协议、描述当前数据包体数据域信息;
  • 2)将消息的处理/合并/落库抽离成数据消费者;
  • 3)上下楼依赖抽象化,去除对于具体实现的依赖。

5.2 数据层结构模型

基于对于数据模型剥离和对当下遇见问题的解决方案规整,将数据同步层拆分为下图这样的架构。

具体的实施思路就是:

  • 1)App启动时建立ACCS长链接服务,保证推推送信道链接,并且根据当前本地数据域信息触发一次数据拉取;
  • 2)数据消费者注册消费者信息和需要监听的数据域信息,这里是一对多的关系;
  • 3)新的数据抵达端侧后,将数据包放到指定的数据域的缓冲池,批量数据归纳结束后,重新出发数据的读取;
  • 4)根据当前数据域优先级弹出最高优的数据包,判断数据域版本是否符合消费者要求,符合则将数据包脱壳后丢给消费者消费,不符合则根据上一次正确的数据包的域信息触发增量的数据域同步拉取;
  • 5)触发数据域同步拉取时,block数据读取,此时通过ACCS触达的数据依旧会在继续归纳到指定的数据域队列中,等待数据域同步拉取结果,将数据包进行排序、去重,合并到对应的数据域队列中。然后重新激活数据读取;
  • 6)数据包体被消费者正确消费后,更新域信息并且通过上行信道告知服务端已经正确处理的数据域信息。

* 数据域同步协议:

Region中携带的数据不必过多,但需将数据包的内容描述清楚,具体是:

  • 1)目标用户的ID,用以确定目标数据包是否正确;
  • 2)数据域ID和优先级信息;
  • 3)当前数据包的域优先级版本。

* 排序策略:

针对于域数据归纳,无论是在写入数据的时候进行排序还是在读取的时候进行查找都需要进行一次排序的操作,时间复杂度最优也是O(logn)级别的。

在实际coding中发现由于在一个数据域里面,数据包的Version信息是连续唯一并且不存在断层的,上一个稳定消费的数据体的Version信息自增就是下一个数据包的Version,所以这里采用了以Versio为主键的Map存储,既降低了时间复杂度,也使得唯一标识的数据包后抵达端侧的包内容可以覆盖之前的包内容。

6、新的问题及解决策略

6.1 多数据来源和唯一数据消费的平衡

每当产生一条针对于当前用户的数据包:

  • 1)如果当前ACCS长链接存在,就会通过ACCS将数据包推送到客户端;
  • 2)如果App切换到后台一段时间,或者直接被杀死,ACCS链接断开,那么只能通过离线推送到用户的通知面板。

所以:每当App切换到活跃状态,都需要根据当前本地存储的数据域信息从后台触发一次数据同步。

数据包触达到客户端侧的来源主要是ACCS长链接的推送和域同步时的拉取,但是数据包的消费是根据数据域的监听划分的唯一消费者,也就是同一时间内只能消费一个数据包。

在压力测试中:当后台短时间内密集的将数据包通过ACCS推送到端侧时,端侧接收到的数据包并不有序,不连续的数据包域版本又会触发新的数据域同步,导致同样的一份数据包会通过两个不同的渠道多次的触达到端侧,浪费了不必要的流量。

当数据域同步时:这个时间节点产生的新数据包也会推送到端侧,数据体有效,并且需要被正确的消费。

针对上述这些问题的解决策略:

即在数据消费和数据获取中间装载一个数据中间层,当触发数据域同步的时候block数据的读取并且ACCS推送下来的数据包会被存放在一个数据的中转站里面,当数据域同步拉取的数据回来后,对数据进行合并后再重启数据读取流程。

6.2 数据域优先级

需要推送到客户端侧的数据包,根据业务的不同优先级也有不同的划分。

用户和用户的聊天产生的数据包会比运营类的消息的数据包优先级要高一些,所以要当多优先级的数据包快速的抵达端侧时,高优先级数据域的数据包需要被优先消费,而数据域的优先级也是需要动态调整,需要不断变换的优先级策略。

针对这个问题的解决策略:

不同的数据域,产生不同的数据队列,高优队列里面的数据包会被优先读取消费。

每一个数据包体中带回的数据域信息,都可以标注当前的数据域优先级,当数据域优先级发生变化的时候,调整数据包消费优先级策略。

7、优化后的效果

除去结构上分层梳理,使得数据同步层和依赖的服务内容可便捷解耦/每一个环节可插拔之外,数据同步中对于消息消费时长/流量节省,压力测试场景下优化效果更加明显。

在“500ms内100条全乱序数据包推送”压力测试场景下:

  • 1)消息处理时长(接收-上屏)缩短 31%;
  • 2)流量损耗(最终拉取到端侧数据包累积大小)降低35%。

8、后续的优化计划

8.1 数据同步层能力提升

数据同步侧的目标,既要保证数据包完整的到达端侧,又要在保证稳定性的前提下尽可能的减少数据的拉取,使得每一次数据的获取都有效。

后续数据同步层会着手于有效数据率和到达率进行更进一步的优化。

针对不同的场景,动态智能调整数据同步的优先级策略。

阻塞式长链接推送,保证同一时间只存在推模式或者拉模式,进一步减少冗余数据包的推送。

8.2 IM端侧整体架构升级

升级数据同步层策略主要还是要提升IM的能力,将数据同步分层后,接下来就是将消息的处理流程化,对每一个流程都可监控可回溯,提升IM数据包的正确解析存储和落库率。

细化一下就是:

  • 1)在数据来源侧剥离开后,后续对IM的整改也会逐步的将消息的处理分层剥离;
  • 2)消息处理关键节点的流程式上报、建立完整的监控体系,让问题发现先于用户舆情;
  • 3)消息完整性的动态自检,最小化数据补偿补全。

9、参考资料

[1] IM单聊和群聊中的在线状态同步应该用“推”还是“拉”?

[2] IM群聊消息如此复杂,如何保证不丢不重?

[3] 一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践

[4] 一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等

[5] 从新手到专家:如何设计一套亿级消息量的分布式IM系统

[6] 融云技术分享:全面揭秘亿级IM消息的可靠投递机制

[7] 移动端IM中大规模群消息的推送如何保证效率、实时性?

[8] 现代IM系统中聊天消息的同步和存储方案探讨

[9] 新手入门一篇就够:从零开发移动端IM

[10] IM消息送达保证机制实现(一):保证在线实时消息的可靠投递

[11] IM消息送达保证机制实现(二):保证离线消息的可靠投递

[12] 零基础IM开发入门(四):什么是IM系统的消息时序一致性?

[13] IM开发干货分享:我是如何解决大量离线消息导致客户端卡顿的

(本文已同步发布于:http://www.52im.net/thread-3856-1-1.html

posted @ 2022-03-15 17:32 Jack Jiang 阅读(428) | 评论 (0)编辑 收藏

本文由作者小林coding分享,来自公号“小林coding”,有修订和改动。

1、引言

说到TCP协议,对于从事即时通讯/IM这方面应用的开发者们来说,再熟悉不过了。随着对TCP理解的越来越深入,很多曾今碰到过但没时间深入探究的TCP技术概念或疑问,现在是时候回头来恶补一下了。

本篇文章,我们就从系统层面深入地探讨一个有趣的TCP技术问题:拔掉网线后,再插上,原本的这条TCP连接还在吗?或者说它还“好”吗?

可能有的人会说:网线都被拔掉了,那说明物理层(也叫实体层)被断开了(关于网络协议分层模型请见《快速理解网络通信协议(上篇)》),那在物理层之上的传输层理应也会断开,所以原本的 TCP 连接就不会存在的了。就好像我们拨打有线电话的时候,如果某一方的电话线被拔了,那么本次通话就彻底断了。

答案真的是这样吗?可能并非你理解的这样哦,一起跟随笔者来深入探讨一下。

学习交流:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK 

本文同步发布于:http://www.52im.net/thread-3846-1-1.html

2、系列文章

本文是系列文章中的第14篇,本系列文章的大纲如下:

3、比较笼统的答案

3.1 答案

引言里我们说到:有人认为,网线都被拔掉了,那说明物理层被断开,那么物理层之上的传输层肯定也会断开,所以原来的 TCP 连接自然也就不存在了。(PS:计算机网络分层详解请见《史上最通俗计算机网络分层详解

上面这个逻辑是有问题的。

问题在于:错误的认为拔掉网线这个动作会影响传输层,事实上并不会影响!

实际上:TCP 连接在 Linux 内核中是一个名为 struct socket 的结构体,该结构体的内容包含 TCP 连接的状态等信息。

所以:当拔掉网线的时候,操作系统并不会变更该结构体的任何内容,所以 TCP 连接的状态也不会发生改变。

3.2 实验验证一下

我做了个小实验:我用 ssh 终端连接了我的云服务器,然后我通过断开 wifi 的方式来模拟拔掉网线的场景,此时查看 TCP 连接的状态没有发生变化,还是处于 ESTABLISHED 状态(如下图所示)。

通过上面实验结果可以验证我的结论:拔掉网线这个动作并不会影响 TCP 连接的状态。

不过,这个答案还是有点笼统。实际上,我们应该在更具体的场景中来看待这个问题,答案才更准确一些。

这个具体场景就是:

  • 1)当拔掉网线后,有数据传输时;
  • 2)当拔掉网线后,没有数据传输时。

针对上面这两种具体的场景,我来更具体地来分析一下。我们继续往下阅读。

4、具体场景1:拔掉网线后,有数据传输时

4.1 数据传输过程中,恰好又把网线插回去了

如果是客户端被拔掉网线后,服务端向客户端发送的数据报文会得不到任何的响应,在等待一定时长后,服务端就会触发TCP协议的超时重传机制(详见:《TCP/IP详解 - 第21章·TCP的超时与重传),然而此时重传并不能得到响应的数据报文。

如果在服务端重传报文的过程中,客户端恰好把网线插回去了,由于拔掉网线并不会改变客户端的 TCP 连接状态,并且还是处于 ESTABLISHED 状态,所以这时客户端是可以正常接收服务端发来的数据报文的,然后客户端就会回 ACK 响应报文。

此时:客户端和服务端的 TCP 连接将依然存在且工作状态不会受到影响,给应用层的感觉就像什么事情都没有发生。。。

4.2 数据传输过程中,网线一直没有插回去

上面这种情况下,如果在服务端TCP协议重传报文的过程中,客户端一直没有将网线插回去,那么服务端超时重传报文的次数达到一定阈值后,内核就会判定出该 TCP 有问题。然后就会通过 Socket 接口告诉应用程序该 TCP 连接出问题了,于是服务端的 TCP 连接就会断开。

接下来,如果客户端再插回网线,如果客户端向服务端发送了数据,由于服务端已经没有与客户端匹配的 TCP 连接信息了,因此服务端内核就会回复 RST 报文,客户端收到后就会释放该 TCP 连接。

此时:客户端和服务端的 TCP 连接已经明确被断开,原本的这个连接也就不存在了。

4.3 刨根问底:TCP数据报文到底重传几次?

本着知其然更应知其所以然的精神,我们来刨根问底一下:TCP 的数据报文到底有重传几次呢?

在 Linux 系统中,提供了一个叫 tcp_retries2 配置项,默认值是 15(如下图所示)。

如上图所示:这个内核参数是控制 TCP 连接建立的情况下,超时重传的最大次数。

不过 tcp_retries2 设置了 15 次,并不代表 TCP 超时重传了 15 次才会通知应用程序终止该 TCP 连接,内核还会基于“最大超时时间”来判定。

每一轮的超时时间都是倍数增长的,比如第一次触发超时重传是在 2s 后,第二次则是在 4s 后,第三次则是 8s 后,以此类推。

内核会根据 tcp_retries2 设置的值,计算出一个最大超时时间。

在重传报文且一直没有收到对方响应的情况时,先达到“最大重传次数”或者“最大超时时间”这两个的其中一个条件后,就会停止重传,然后就会断开 TCP 连接。

PS:有关TCP超时重传机制的详细情况,可以阅读浅析TCP协议中的疑难杂症(下篇)》。

5、具体场景2:拔掉网线后,有数据传输时

5.1 场景分析

针对拔掉网线后,没有数据传输的场景,还得具体看看是否开启了 TCP KeepAlive 机制 (详见《彻底搞懂TCP协议层的KeepAlive保活机制》)。

1)如果没有开启 TCP KeepAlive 机制:

在客户端拔掉网线后,并且双方都没有进行数据传输,那么客户端和服务端的 TCP 连接将会一直保持存在。

2)如果开启了 TCP KeepAlive 机制:

在客户端拔掉网线后,即使双方都没有进行数据传输,在持续一段时间后,TCP 就会发送KeepAlive探测报文。

根据KeepAlive探测报文响应情况,会有以下两种可能:

  • 1)如果对端正常工作:当探测报文被对端收到并正常响应, TCP 保活时间将被重置,等待下一个 TCP 保活时间的到来;
  • 2)如果对端主机崩溃或对端由于其他原因导致报文不可达:当探测报文发送给对端后,石沉大海、没有响应,连续几次,达到保活探测次数后,TCP 会报告该连接已经死亡。

所以:TCP 保活机制可以在双方没有数据交互的情况,通过TCP KeepAlive 机制的探测报文,来确定对方的 TCP 连接是否存活。

5.2 刨根问底:TCP KeepAlive 机制具体是什么样的?

TCP KeepAlive 机制的原理是这样的:

定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个探测报文。该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。

在 Linux 内核可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔。

以下是 Linux 中的默认值:

net.ipv4.tcp_keepalive_time=7200

net.ipv4.tcp_keepalive_intvl=75 

net.ipv4.tcp_keepalive_probes=9

解释一下:

  • 1)tcp_keepalive_time=7200:表示保活时间是 7200 秒(2小时),也就 2 小时内如果没有任何连接相关的活动,则会启动保活机制;
  • 2)tcp_keepalive_intvl=75:表示每次检测间隔 75 秒;
  • 3)tcp_keepalive_probes=9:表示检测 9 次无响应,认为对方是不可达的,从而中断本次的连接。

也就是说在 Linux 系统中,最少需要经过 2 小时 11 分 15 秒才可以发现一个“死亡”连接。

计算公式是:

注意:应用程序若想使用 TCP 保活机制需要通过 socket 接口设置 SO_KEEPALIVE 选项才能够生效,如果没有设置,那么就无法使用 TCP 保活机制。

PS:关于TCP协议的KeepAlive 机制详见《彻底搞懂TCP协议层的KeepAlive保活机制》、《一文读懂即时通讯应用中的网络心跳包机制:作用、原理、实现思路等》。

5.3 刨根问底:TCP KeepAlive 机制的探测时间也太长了吧?

没错,确实有点长。

TCP KeepAlive  机制是 TCP 层(内核态) 实现的,它是给所有基于 TCP 传输协议的程序一个兜底的方案。

实际上:我们通常在应用层自己实现一套探测机制,可以在较短的时间内,探测到对方是否存活。

比如:一般Web 服务器都会提供 keepalive_timeout 参数,用来指定 HTTP 长连接的超时时间。如果设置了 HTTP 长连接的超时时间是 60 秒,Web 服务软件就会启动一个定时器,如果客户端在完后一个 HTTP 请求后,在 60 秒内都没有再发起新的请求,定时器的时间一到,就会触发回调函数来释放该连接。

再比如:IM、消息推送系统里的心跳机制,通过应用层的心跳机制(由客户端发出,服务端回复响应包),来灵活控制和探测长连接的健康度。

为何基于TCP协议的移动端IM仍然需要心跳保活机制?》这篇文章解释了IM这类应用中应用层心跳保活的必要性,有兴趣可以读一读。

如果对应用层心跳的具体应用没什么概念,可以看看微信的这两篇文章:

  1. 微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)
  2. 移动端IM实践:实现Android版微信的智能心跳机制

下面有几个针对im这类应用的心跳实现代码,可以具体感受学习一下:

  1. 正确理解IM长连接的心跳及重连机制,并动手实现(有完整IM源码)
  2. 一种Android端IM智能心跳算法的设计与实现探讨(含样例代码)
  3. 自已开发IM有那么难吗?手把手教你自撸一个Andriod版简易IM (有源码)
  4. 手把手教你用Netty实现网络通信程序的心跳机制、断线重连机制

6、本文小结

下面简单总结一下文中的内容,本文开头的问题并不是简单一句话能够准确说清楚的,需要分情况对待。

也就是:客户端拔掉网线后,并不会直接影响 TCP 的连接状态。所以拔掉网线后,TCP 连接是否还会存在,关键要看拔掉网线之后,有没有进行数据传输。

1)有数据传输的情况:

在客户端拔掉网线后:如果服务端发送了数据报文,那么在服务端重传次数没有达到最大值之前,客户端恰好插回网线的话,那么双方原本的 TCP 连接还是能存在并正常工作,就好像什么事情都没有发生。

在客户端拔掉网线后:如果服务端发送了数据报文,在客户端插回网线之前,服务端重传次数达到了最大值时,服务端就会断开 TCP 连接。等到客户端插回网线后,向服务端发送了数据,因为服务端已经断开了与客户端相同四元组的 TCP 连接,所以就会回 RST 报文,客户端收到后就会断开 TCP 连接。至此, 双方的 TCP 连接都断开了。

2)没有数据传输的情况:

  • a. 如果双方都没有开启 TCP keepalive 机制,那么在客户端拔掉网线后,如果客户端一直不插回网线,那么客户端和服务端的 TCP 连接状态将会一直保持存在;
  • b. 如果双方都开启了 TCP keepalive 机制,那么在客户端拔掉网线后,如果客户端一直不插回网线,TCP keepalive 机制会探测到对方的 TCP 连接没有存活,于是就会断开 TCP 连接。而如果在 TCP 探测期间,客户端插回了网线,那么双方原本的 TCP 连接还是能正常存在。

除了客户端拔掉网线的场景,还有客户端“宕机和杀死进程”的两种场景。

第一个场景:客户端宕机这件事跟拔掉网线是一样无法被服务端的感知的,所以如果在没有数据传输,并且没有开启 TCP keepalive 机制时,,服务端的 TCP 连接将会一直处于 ESTABLISHED 连接状态,直到服务端重启进程。

所以:我们可以得知一个点——在没有使用 TCP 保活机制,且双方不传输数据的情况下,一方的 TCP 连接处在 ESTABLISHED 状态时,并不代表另一方的 TCP 连接还一定是正常的。

第二个场景:杀死客户端的进程后,客户端的内核就会向服务端发送 FIN 报文,与客户端进行四次挥手(见《跟着动画来学TCP三次握手和四次挥手》)。

所以:即使没有开启 TCP KeepAlive,且双方也没有数据交互的情况下,如果其中一方的进程发生了崩溃,这个过程操作系统是可以感知的到的,于是就会发送 FIN 报文给对方,然后与对方进行 TCP 四次挥手。

7、参考资料

[1] TCP/IP详解 - 第21章·TCP的超时与重传

[2] 通俗易懂-深入理解TCP协议(上):理论基础

[3] 网络编程懒人入门(三):快速理解TCP协议一篇就够

[4] 脑残式网络编程入门(一):跟着动画来学TCP三次握手和四次挥手

[5] 脑残式网络编程入门(七):面视必备,史上最通俗计算机网络分层详解

[6] 技术大牛陈硕的分享:由浅入深,网络编程学习经验干货总结

[7] 网络编程入门从未如此简单(二):假如你来设计TCP协议,会怎么做?

[8] 不为人知的网络编程(十):深入操作系统,从内核理解网络包的接收过程(Linux篇)

[9] 为何基于TCP协议的移动端IM仍然需要心跳保活机制?

[10] 一文读懂即时通讯应用中的网络心跳包机制:作用、原理、实现思路等

[11] Web端即时通讯实践干货:如何让你的WebSocket断网重连更快速?

本文同步发布于:http://www.52im.net/thread-3846-1-1.html

posted @ 2022-03-07 18:15 Jack Jiang 阅读(263) | 评论 (0)编辑 收藏

仅列出标题
共49页: First 上一页 18 19 20 21 22 23 24 25 26 下一页 Last 
Jack Jiang的 Mail: jb2011@163.com, 联系QQ: 413980957, 微信: hellojackjiang