Jack Jiang

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

本文来自网易云信团队的技术分享,原创发表于网易云信公众号,原文链接:mp.weixin.qq.com/s/LT2dASI7QVpcOVxDAsMeVg,收录时有改动。

1、引言

在不了解IM技术的人眼里,群聊是再平常不过的功能而已,万人群聊?应该也不难实现吧?!

确实,从前端功能界面上来看,群聊无非就是个循环向群员发送消息的一对多聊天消息分发模式而已,难在何处?

真实的情况是,群聊是IM系统中的高难度技术点之一。难在哪?难在服务端!从某种角度上说,群聊功能的架构设计和技术实现的品质,可以代表这款IM软件的技术水平。

群聊从后台的技术实现上说,至少有以下难点:

1)如何高效地进行大量群员消息的分发?

2)如何高效地管理群员的在线状态?

3)如何高效地读取群员的在线状态?

4)集群系统中,如何高效地保证群员消息的准确送达?

5)群聊消息该扩散写还是扩散读?

6)如何保证大量群聊消息分发的情况下不影响单聊消息体验?

7)如何应对大群突发事件下的性能负载?

.... ....

目前,市面上主流的IM产品中,微信群是500人上限,QQ群是3000人上限(3000人群是按年付费升级,很贵,不是为一般用户准备的)。一方面,从产品的定义上群成员数量不应过多,另一方面,技术成本也是个不可回避的因素。万人群这种超大规模群的技术难度,更是难已想象。

本文内容是网易云信团队为了响应万人群聊功能需求,在设计实现万人群聊技术方案中总结的技术实践,借此机会分享给各IM开发者同行。

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

学习交流:

- 即时通讯/推送技术开发交流5群:215477170[推荐]

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

2、概述

随着移动互联网的发展,即时通讯服务被广泛应用到各个行业,客户业务快速发展,传统百人或千人上限的群聊已经无法满足很多业务发展需求,因此网易云信IM推出万人群服务。

万人群场景需要解决以下问题:

1)消息需要按1:9999的比例进行转发投递,按常规消息处理流程将产生大量的子任务,对系统吞吐量的要求极高;

2)在微服务系统架构下,如果不采用一些优化方案,服务以及存储(DB、缓存等)之间的QPS和网络流量将非常高;

3)以群为单位的缓存(如群成员列表)内存存储开销较大(假设一个成员200Byte,万人群约2MB);

4)群成员登录后需要同步群离线消息,智能手机上App前后台切换产生的较多登录同步消息协议,因此需要优化消息同步方案。

为了解决以上问题,万人群技术方案采用了“聚合+分层/组+增量”的设计思路:

3、万人群消息的处理流程

1)按群维护在线群成员信息,主要包含两部分(可以理解为两个缓存集合):

a. 群成员在线信息:即用户在线状态变化(上线、下线)时,更新相应群的在线状态信息(即动态维护群有哪些成员在线);

b. 成员IM长连接信息:即用户新登录时,更新用户的Link信息(即登录所在Link的地址信息,消息转发时根据Link地址路由消息)。

2)IM Server收到群消息后,按群ID将消息路由到“群消息服务”模块;

3)群消息模块检查并预处理消息内容,然后通过“群成员在线状态”服务获取在线成员,完成消息转发的基础工作。为了减少群消息模块和群在线成员服务之间的网络流量,采用了“本地缓存+增量同步”的缓存策略,即本地缓存记录最后更新版本号和时间戳,每次同步群在线成员前先检查缓存版本号是否有变更,若有则按最后更新时间增量同步;

4)通过“群成员在线服务”获取在线群成员的Link链接信息,按Link分组路由消息(分组路由的原因:同一Link上的全部群成员只需要路由一条消息即可)。同样为了减少网络开销,成员Link信息也采用“本地缓存+增量同步”的方案;

5)群消息采用“漫游+历史”的存储方案,漫游的消息存储在分布式缓存中,历史消息异步写入HBase。用户登录后可以通过漫游快速的获取到最新消息,并可以通过拉取历史查看更早的消息。

4、万人群方案本地缓存增量同步策略

抛开群在线状态管理逻辑,群成员在线状态服务可以简单理解为分布式集中缓存。

增量同步技术方案如下:

如上图所示:

1)数据缓存是一个集合,其包含了多个缓存数据项,每一个数据项带有最后更新时间信息;另外缓存还有一个严格递增的版本号;

2)缓存数据变更(新增、修改、删除)后,需要增加版本号;

3)本地线程通过缓存管理读取数据时,管理服务先检查本地版本号和分布式缓存中的版本号是否一致,若不一致则按本地最新时间戳增量同步新数据项,并更新本地的版本号和最后更新时间(为了避免分布式集中缓存中并发写入导致的增量时间戳不可靠问题,增量更新时可以将本地记录的最后更新时间戳向前推移,比如减少20ms);

4)为避免本地多线程并发读取相同数据项导致并发更新本地缓存的问题,可以按缓存数据合并更新请求,即解决并发问题还可以减少网络开销;

5)缓存数据由大量数据项构成,为了避免单个缓存数据太大,可以将数据项中的属性业务场景精简(冷热分离),低频次读写的属性额外缓存。

5、万人群水平扩容方案

万人群采用大量本地缓存的方案解决消息处理性能和网络流量的问题,因此本地存储空间成了方案的瓶颈点。因此我们设计了分组路由的技术方案。

消息按群ID和路由策略定向路由到指定分组(集群)上处理,分组由多个计算节点组成,因此方案上可以做到分组内和分组间的水平扩缩容。

6、作为“云”服务,网易云信是如何实现万人群所需的计算资源的?

由于万人群对计算和存储资源消耗比较高,在实施和运维方案上也有一定的特殊性,为了保证业务的可靠性和稳定性,网易云信是将万人大群的能力,仅提供给专属的云客户(普通公有云客户是无法使用的)。

之所以能从软硬件基础设施上为万人群提供保障,网易云信的IM专有云必须具备以下资源能力:

1)需要专属的独立计算资源:保持计算资源独立,且资源冗余度比公有云高,且需要保证不会受到公有云上其他客户业务的影响;

2)需要专属的独立运维服务:从而根据客户业务场景制定最佳的业务监控、弹性扩容、故障迁移等运维方案。

总之,万人群聊的实现,过硬的技术方案设计和技术实现只是一方面,基础计算设施资源和运维能力也是不可或缺。

所以,从今以后,不要随随便便就喊万人群聊,甚至十万人群聊,这不是想实现就能实现的哦!

附录:更多群聊相关技术文章

快速裂变:见证微信强大后台架构从0到1的演进历程(一)
如何保证IM实时消息的“时序性”与“一致性”?
IM单聊和群聊中的在线状态同步应该用“推”还是“拉”?
IM群聊消息如此复杂,如何保证不丢不重?
微信后台团队:微信后台异步消息队列的优化升级实践分享
移动端IM中大规模群消息的推送如何保证效率、实时性?
现代IM系统中聊天消息的同步和存储方案探讨
关于IM即时通讯群聊消息的乱序问题讨论
IM群聊消息的已读回执功能该怎么实现?
IM群聊消息究竟是存1份(即扩散读)还是存多份(即扩散写)?
一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践
[技术脑洞] 如果把14亿中国人拉到一个微信群里技术上能实现吗?
IM群聊机制,除了循环去发消息还有什么方式?如何优化?
网易云信技术分享:IM中的万人群聊技术方案实践总结
>> 更多同类文章 ……

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

posted @ 2019-08-14 10:07 Jack Jiang 阅读(144) | 评论 (0)编辑 收藏

     摘要: 本文原文由作者“张小方”原创发布于“高性能服务器开发”微信公众号,原题《心跳包机制设计详解》,即时通讯网收录时有改动。1、引言一般来说,没有真正动手做过网络通信应用的开发者,很难想象即时通讯应用中的心跳机制的作用。但不可否认,作为即时通讯应用,心跳机制是其网络通信技术底层中非常重要的一环,有没有心跳机制、心跳机制的算法实现好坏,都将直接影响即时通讯应...  阅读全文

posted @ 2019-08-08 12:01 Jack Jiang 阅读(247) | 评论 (0)编辑 收藏

     摘要: 本文由原作者松若章原创发布,作者主页:zhihu.com/people/hrsonion/posts,感谢原作者的无私分享。1、引言一道经典的面试题是:从 URL 在浏览器被被输入到页面展现的过程中发生了什么?大多数回答都是说请求响应之后 DOM 怎么被构建,被绘制出来。但是你有没有想过,收到的 HTML 如果包含几十个图片标签,这些图片是以什么方式、什么顺序、建立了多少连接、使用什么协议被下载下...  阅读全文

posted @ 2019-08-02 09:55 Jack Jiang 阅读(196) | 评论 (0)编辑 收藏

     摘要: 本文由百度技术团队“蔡锐”原创发表于“百度App技术”公众号,原题为《百度App网络深度优化系列《三》弱网优化》,感谢原作者的无私分享。一、前言网络优化解决的核心问题有三个,第一是安全问题,我们在《百度APP移动端网络深度优化实践分享(一):DNS优化篇》进行了详细的讲解。第二是速度问题,我们在《百度APP移动端网络深度优化实践分享(二):网络连接优...  阅读全文

posted @ 2019-07-29 10:29 Jack Jiang 阅读(145) | 评论 (0)编辑 收藏

     摘要: 本文引用自马蜂窝公众号,由马蜂窝技术团队原创分享。一、引言今天,越来越多的用户被马蜂窝持续积累的笔记、攻略、嗡嗡等优质的分享内容所吸引,在这里激发了去旅行的热情,同时也拉动了马蜂窝交易的增长。在帮助用户做出旅行决策、完成交易的过程中,IM 系统起到了重要的作用。IM 系统为用户与商家建立了直接沟通的渠道,帮助用户解答购买旅行产品中的问题,既促成了订单交易,也帮用户打消了疑虑,促成用户旅行愿望的实现...  阅读全文

posted @ 2019-07-24 21:44 Jack Jiang 阅读(141) | 评论 (0)编辑 收藏

     摘要: 本文由作者FreddyChen原创分享,为了更好的体现文章价值,引用时有少许改动,感谢原作者。1、写在前面一直想写一篇关于im即时通讯分享的文章,无奈工作太忙,很难抽出时间。今天终于从公司离职了,打算好好休息几天再重新找工作,趁时间空闲,决定静下心来写一篇文章,毕竟从前辈那里学到了很多东西。工作了五年半,这三四年来一直在做社交相关的项目,有直播、即时通讯、短视频分享、社区论坛等产品,深知即时通讯技...  阅读全文

posted @ 2019-07-22 12:48 Jack Jiang 阅读(439) | 评论 (0)编辑 收藏

     摘要: 1、引言本文以设计淘宝网的后台架构为例,介绍从一百个并发到千万级并发情况下服务端的架构的14次演进过程,同时列举出每个演进阶段会遇到的相关技术,让大家对架构的演进有一个整体的认知。文章最后汇总了一些架构设计的原则。(本文同步发布于:http://www.52im.net/thread-2665-1-1.html)2、关于作者huashiou:广东工业大学计算机科学与技术硕士毕业,大数据开发工程师。...  阅读全文

posted @ 2019-07-17 23:57 Jack Jiang 阅读(356) | 评论 (0)编辑 收藏

     摘要: 本文由DCloud 公司创始人王安原创发布于CSDN,原题《小程序技术演进史》,即时通讯网收录时有改动,感谢原作者。1、引言微信的成功,并非特定于某个具体的功能,微信的成功实际上是一大批创新技术和体验的成功合集,这也是它为何如此难此被超越的根本原因。作为微信这个超级社交应用中最为亮眼的技术之一——微信小程序,俨然已成历移动端小程序的代名词,很多人一提起“小程序&...  阅读全文

posted @ 2019-07-04 12:02 Jack Jiang 阅读(160) | 评论 (0)编辑 收藏

     摘要: 本文原题“《NIO 入门》,作者为“Gregory M. Travis”,他是《JDK 1.4 Tutorial》等书籍的作者。1、引言Java NIO是Java 1.4版加入的新特性,虽然Java技术日新月异,但历经10年,NIO依然为Java技术领域里最为重要的基础技术栈,而且依据现实的应用趋势,在可以预见的未来,它仍将继续在Java技术领域占据重要位置。网...  阅读全文

posted @ 2019-06-29 22:17 Jack Jiang 阅读(732) | 评论 (0)编辑 收藏

1、引言

很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO?

在本文中,将尝试用简明扼要的文字,阐明Java NIO和经典IO之间的差异、典型用例,以及这些差异如何影响我们的网络编程或数据传输代码的设计和实现的。

本文没有复杂理论,也没有像网上基它文章一样千篇一律的复制粘贴,有的只是接地气的通俗易懂,希望能给你带来帮助。

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

2、相关文章

3、Java NIO和IO的主要区别

下表总结了Java NIO和IO之间的主要区别。我将在表格后面的部分中详细介绍每个区别。

3.1 Stream Oriented vs. Buffer Oriented

Java NIO和IO之间的第一个重要区别是IO是面向流的,其中NIO是面向缓冲区的。那么,这意味着什么?

面向流的Java IO意味着您可以从流中一次读取一个或多个字节。你对读取的字节做什么取决于你。它们不会缓存在任何地方。此外,您无法在流中的数据中前后移动。如果需要在从流中读取的数据中前后移动,则需要先将其缓存在缓冲区中。

Java NIO的面向缓冲区的方法略有不同。数据被读入缓冲区,稍后处理该缓冲区。你可以根据需要在缓冲区中前后移动。这使你在处理过程中具有更大的灵活性。但是,你还需要检查缓冲区是否包含完整处理所需的所有数据。并且,你需要确保在将更多数据读入缓冲区时,不要覆盖尚未处理的缓冲区中的数据。

3.2 Blocking vs. Non-blocking IO

Java IO的各种流都是blocking的。这意味着,当线程调用read()或write()时,该线程将被阻塞,直到有一些数据要读取,或者数据被完全写入,在此期间,该线程无法执行任何其他操作。

Java NIO的非阻塞模式允许线程请求从通道读取数据,并且只获取当前可用的内容,或者根本没有数据,如果当前没有数据可用。线程可以继续使用其他内容,而不是在数据可供读取之前保持阻塞状态。

非阻塞写入也是如此,线程可以请求将某些数据写入通道,但不要等待它完全写入。然后线程可以继续并在同一时间做其他事情。

线程在IO调用中没有阻塞时花费空闲时间,通常在此期间在其他通道上执行IO。也就是说,单个线程现在可以管理多个输入和输出通道。

4、Selectors

Java NIO的选择器允许单个线程监视多个输入通道。你可以使用选择器注册多个通道,然后使用单个线程“选择”具有可用于处理的输入的通道,或者选择准备写入的通道。这种选择器机制使单个线程可以轻松管理多个通道。

5、NIO和经典IO如何影响应用程序的设计?

选择NIO或IO作为IO工具包可能会影响应用程序设计的以下方面:

1)API调用NIO或IO类;

2)处理数据;

3)用于处理数据的线程数。

5.1 API调用

当然,使用NIO时的API调用看起来与使用IO时不同。这并不奇怪。而不是仅仅从例如InputStream读取字节的数据字节,必须首先将数据读入缓冲区,然后从那里进行处理。

5.2 数据处理

使用纯NIO设计与IO设计时,数据处理也会受到影响。

在IO设计中,您从InputStream或Reader中读取字节的数据字节。想象一下,您正在处理基于行的文本数据流。

例如:

Name: Anna

Age: 25

Email: [url=mailto:anna@mailserver.com]anna@mailserver.com[/url]

Phone: 1234567890

这个文本行流可以像这样处理:

InputStream input = ... ; // get the InputStream from the client socket


BufferedReader reader = newBufferedReader(newInputStreamReader(input));


String nameLine   = reader.readLine();

String ageLine    = reader.readLine();

String emailLine  = reader.readLine();

String phoneLine  = reader.readLine();

注意处理状态是如何,由程序执行的程度决定的。换句话说,一旦第一个reader.readLine()方法返回,您就确定已经读取了整行文本。readLine()会阻塞直到读取整行,这就是原因。您还知道此行包含名称。同样,当第二个readLine()调用返回时,您知道此行包含年龄等。

正如您所看到的,只有当有新数据要读取时,程序才会进行,并且对于每个步骤,您都知道该数据是什么。一旦执行的线程已经超过读取代码中的某个数据片段,该线程就不会在数据中向后移动(通常不会)。

此图中还说明了此原则:

▲ Java IO:从阻塞流中读取数据

NIO的实现看起来会有所不同,这是一个简化的例子:

ByteBuffer buffer = ByteBuffer.allocate(48);

intbytesRead = inChannel.read(buffer);

注意第二行从通道读取字节到ByteBuffer。当该方法调用返回时,您不知道所需的所有数据是否都在缓冲区内。你只知道缓冲区包含一些字节,这使得处理更加困难。

想象一下,在第一次读取(缓冲)调用之后,是否所有读入缓冲区的内容都是半行。例如,“姓名:An”。你能处理这些数据吗?并不是的。在完成任何数据的处理之前,您需要等待至少一整行数据进入缓冲区。

那么你怎么知道缓冲区是否包含足够的数据来处理它?好吧,你没有。找出的唯一方法是查看缓冲区中的数据。结果是,在您知道所有数据是否存在之前,您可能需要多次检查缓冲区中的数据。这既低效又可能在程序设计方面变得混乱。

例如:

ByteBuffer buffer = ByteBuffer.allocate(48);

intbytesRead = inChannel.read(buffer);

while(! bufferFull(bytesRead) ) {

    bytesRead = inChannel.read(buffer);

}

bufferFull()方法必须跟踪读入缓冲区的数据量,并返回true或false,具体取决于缓冲区是否已满。换句话说,如果缓冲区已准备好进行处理,则认为它已满。

bufferFull()方法扫描缓冲区,但必须使缓冲区保持与调用bufferFull()方法之前相同的状态。如果不是,则可能无法在正确的位置读入读入缓冲区的下一个数据。这不是不可能的,但这是另一个需要注意的问题。

如果缓冲区已满,则可以对其进行处理。如果它不满,您可能能够部分处理那里的任何数据,如果这在您的特定情况下是有意义的。在许多情况下,它没有。

这个图中说明了is-data-in-buffer-ready循环:

▲ Java NIO:从通道读取数据,直到所有需要的数据都在缓冲区中

6、什么时候该用NIO?什么时候该用经典IO?

NIO允许您仅使用一个(或几个)线程来管理多个通道(网络连接或文件),但成本是解析数据可能比从阻塞流中读取数据时更复杂。

如果您需要同时管理数千个打开的连接,每个只发送一些数据,例如聊天服务器,在NIO中实现服务器可能是一个优势。同样,如果您需要与其他计算机保持大量开放连接,例如在P2P网络中,使用单个线程来管理所有出站连接可能是一个优势。

此图中说明了这一个线程,多个连接设计:

▲ Java NIO:管理多个连接的单个线程

如果您拥有较少带宽的连接,一次发送大量数据,那么可能最经典的IO服务器实现可能是最合适的。

此图说明了经典的IO服务器设计:

▲ Java IO:经典的IO服务器设计 - 由一个线程处理的一个连接

7、更简化的理解

以众所周之的数据读取过程为例,我们来一个更简化的理解。

对于数据读取,就读取速度来说:CPU > 内存 > 硬盘。

I- 就是从硬盘到内存

O- 就是从内存到硬盘

第一种方式:从硬盘读取数据,然后程序一直等,数据读完后,继续你的操作。这种方式是最简单的,叫阻塞IO(也就是经典IO)。

第二种方式:从硬盘读取数据,然后程序继续向下执行,等数据读取完后,通知当前程序读取完成(对硬件来说叫中断,对程序来说叫回调),然后此程序可以立即处理读取的数据,也可以执行完当前操作后再对读取完的数据进行操作。

8、总而言之

还是以数据读取为例,操作系统是按块Block(块)从硬盘拿数据,就如同一个大脸盆,一下子就放入了一盆水。但是,当 Java 使用的时候,旧的 IO(经典IO)确实基于 流 Stream的,也就是虽然操作系统给我了一脸盆水,但是我得用吸管慢慢喝。

由于经典IO的重重落后理念,于是,NIO 横空出世。。。

附录:更多NIO异步网络编程资料

Java新一代网络编程模型AIO原理及Linux系统AIO介绍
有关“为何选择Netty”的11个疑问及解答
开源NIO框架八卦——到底是先有MINA还是先有Netty?
选Netty还是Mina:深入研究与对比(一)
选Netty还是Mina:深入研究与对比(二)
NIO框架入门(一):服务端基于Netty4的UDP双向通信Demo演示
NIO框架入门(二):服务端基于MINA2的UDP双向通信Demo演示
NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战
NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战
Netty 4.x学习(一):ByteBuf详解
Netty 4.x学习(二):Channel和Pipeline详解
Netty 4.x学习(三):线程模型详解
Apache Mina框架高级篇(一):IoFilter详解
Apache Mina框架高级篇(二):IoHandler详解
MINA2 线程原理总结(含简单测试实例)
Apache MINA2.0 开发指南(中文版)[附件下载]
MINA、Netty的源代码(在线阅读版)已整理发布
解决MINA数据传输中TCP的粘包、缺包问题(有源码)
解决Mina中多个同类型Filter实例共存的问题
实践总结:Netty3.x升级Netty4.x遇到的那些坑(线程篇)
实践总结:Netty3.x VS Netty4.x的线程模型
详解Netty的安全性:原理介绍、代码演示(上篇)
详解Netty的安全性:原理介绍、代码演示(下篇)
详解Netty的优雅退出机制和原理
NIO框架详解:Netty的高性能之道
Twitter:如何使用Netty 4来减少JVM的GC开销(译文)
绝对干货:基于Netty实现海量接入的推送服务技术要点
Netty干货分享:京东京麦的生产级TCP网关技术实践总结
新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析
写给初学者:Java高性能NIO框架Netty的学习方法和进阶策略
少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别
>> 更多同类文章 ……

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

posted @ 2019-06-25 16:32 Jack Jiang 阅读(553) | 评论 (0)编辑 收藏

仅列出标题
共47页: First 上一页 30 31 32 33 34 35 36 37 38 下一页 Last 
Jack Jiang的 Mail: jb2011@163.com, 联系QQ: 413980957, 微信: hellojackjiang