关于微信内部正在使用的网络层封装库Mars开源的消息,1个多月前就已满天飞(参见《微信Mars:微信内部正在使用的网络层封装库,即将开源》),不过微信团队没有失约,微信Mars 于2016年12月28日正式公开源码(源码地址:https://github.com/Tencent/mars,也可从本文文末的附件下载之,Android版演示程序能够从文末的附件中下载)。
以前不管是微信团队仍是手机QQ团队,都以腾讯公司的名义在Github开源了数个工程,但这些工程所受的关注度远不及Mars。之因此Mars广受关注的缘由,其实搞移动端IM或推送技术的开发者同行都明白,由于移动网络实在太不可靠、太复杂,以致于写出一个能用于大规模用户环境的稳定、省流量、省电、数据传输流畅、弱网络健壮、后台自动保活等技术指标的IM或推送是至关困难的。
更为重要的缘由是毕竟微信Mars通过微信团队多年积累并通过海量用户的测试和使用,是经受的住各类复杂移动端网络环境、各类乱七八糟型号智能手机的真实考验的。若Mars开源,必将为IM及相关技术应用领域的同行带来不少有价值的实践成果,毕竟微信的体量和应用规模决定了技术的高度,确实是值得同行学习和关注。
以前的文章,好比《微信移动端应对弱网络状况的探索和实践PPT》、《微信Mars:微信内部正在使用的网络层封装库,即将开源》,也都或多或少对Mars进行了初步介绍,但微信Mars究竟是个啥玩意,它能解决啥问题?
咱们简要的归纳一下,微信Mars解决了以下问题:html
以上特色,还不尽于归纳微信Mars的技术特征,建议对C++熟悉的IM或推送技术同行能够直接去看看Mars源码。
那么微信Mars到底有什么用呢?毫无疑问,微信Mars存在的前提就是为了更好的服务微信这个超级IM而存在,最适合干的活就是开发移动端IM了,固然因为提炼的很好,相信移动端推送技术等都是可使用微信Mars做为网络层lib来使用,从而以微信的成果为起点开发出拥有更加优秀网络体验的移动端富网络应用。
好了,言归正传,本文正文内容引用了微信开发团队的资料,请继续往下阅读。(本文同步发布于:http://www.52im.net/thread-684-1-1.html)前端
《移动端IM实践:实现Android版微信的智能心跳机制》
《微信Mars:微信内部正在使用的网络层封装库,即将开源》
《微信移动端应对弱网络状况的探索和实践PPT [附件下载]》
《微信异步化改造实践:8亿月活、单机千万链接背后的后台解决方案》android
2012 年中,微信支持包括 Android、iOS、Symbian 三个平台。但在各个平台上,微信客户端没有任何统一的基础模块。2012 年的微信正处于高速发展时期,各平台的迭代速度不1、使用的编程语言各异,后台架构也处在不断探索的过程当中。多种因素使得各个平台基础模块的实现出现了差别,致使出现屡次须要服务器作兼容的善后工做。网络做为微信的基础,重要性不言而喻。任何网络实现的 bug 均可能致使重大事故。例如微信的容灾实现,若是由于版本的实现差别,致使某些版本上没法进行容灾恢复,将会严重的影响用户体验,甚至形成用户的流失。咱们亟需一套统一的网络基础库,为微信的高速发展保驾护航。
刚好,这个时候塞班渐入日暮,微信对塞班的支持也逐渐减弱。老大从塞班组抽调人力,组成一个三人小 team 的初始团队,开始着手作通用的基础组件。这个基础组件最初就定位为:跨平台、跨业务的基础组件。如今看,这个组件除了解决了已有问题,还给微信的高速发展带来了不少优点,例如:git
通过四年多的发展,跨平台的基础组件已经包含了网络组件、日志组件在内的多个组件。回头看,这是一条开荒路。github
在基础模块的开发中,设计尤其重要。在设计上,微信基础组件以跨平台、跨业务为前提,听从高可用,高性能,负载均衡的设计原则。
可用是一个即时通信类 App 的立身之本。高可用又体如今多个层面上:网络的可用性、 App 的可用性、系统的可用性等。web
保障高可用并不表明能够牺牲性能,对于一个用户使用最频繁的应用,反而更要对使用的资源精打细算。例如在 Mars 信令传输超时设计中,多级超时的设计充分的考虑了可用性与高性能之间的平衡取舍。
若是说高可用高性能只是客户端自己的考虑的话,负载均衡就须要结合服务器端来考虑了,作一个客户端网络永远不能只把眼光放在客户端上。任何有关网络访问的决策都要考虑给服务器所带来的额外压力是多大。为了选用质量较好的 IP,曾经写了完整的客户端测速代码,后来删掉,其中一个缘由是由于不想给服务器带来额外的负担。Mars 的代码中,选择 IP 时用了大量的随机函数也是为了规避大量的用户同时访问同一台服务器而作的。
在这四年,我学到最多的就是简单和平衡。 把方案作的尽量简单,这样才不容易出错。设计方案时大多数时候都不可能知足全部想达到的条件,这个时候就须要去平衡各个因素。在组件中一个很好的例子就是长链接的链接频率(具体实现见longlink_connect_monitor.cc),这个链接频率就是综合耗电量,流量,网络高可用,用户行为等因素进行综合考虑的。算法
跨平台基础组件的需求起源于微信,首要目标固然是先承载起微信业务。为了避免局限于微信,知足跨平台、跨业务的设计目标,在设计上,网络组件定位为客户端与服务端之间的无状态网络信令通道,即交互方式主要包含一来一回、主动push两种方式。这使得基础组件无需考虑请求间的关联性、时序性,核心接口获得了极大的简化。同时,简洁的交互也使得业务逻辑的耦合极少。目前基础组件与业务的交互只包括:编解码、auth状态查询两部分(具体见stn_logic.h)。编程
void StartTask(...); int OnTaskEnd(...); void OnPush(...); bool Req2Buf(...); int Buf2Resp(...); bool MakeSureAuthed();
在线程模型的选择上,最先使用的是多线程模型。当须要异步作一个工做,就起一个线程。多线程势必少不了锁。但当灰度几回以后发现,想要规避死锁的四个必要条件并无想象中的那么容易。用户使用场景复杂,客户端的时序、状态的影响因素多,例如网络切换事件、先后台事件、定时器事件、网络事件、任务事件等,致使了很多的死锁现象和对象析构时序错乱致使的内存非法访问问题。
这时,咱们开始思考,多线程确实有它的优势:能够并发甚至并行提升运行速度。可是对于网络模块来讲,性能瓶颈主要是在网络耗时上,并不在于本地程序执行速度上。那为什么不把大部分程序执行改为串行的,这样就不会存在多线程临界区的问题,无锁天然就不会死锁。
所以,咱们目前使用了消息队列的方案(具体实现见 comm/messagequeue 目录),把绝大多数非阻塞操做放到消息队列里执行。而且规定,基础组件与调用方之间的交互必须1. 尽快完成,不进行任何阻塞操做;2. 单向调用,避免造成环状的复杂时序。消息队列的引入很好的改善了死锁问题,但消息队列的线程模型中,咱们仍是不能避免存在须要阻塞的调用,例如网络操做。在将来的尝试中,咱们计划引入协程的方式,将线程模型尽量的简化。
在其它技术选型上,有时甚至须要细节到API 的使用,好比考虑平台兼容性问题,舍弃了一些函数的线程安全版本,使用了 asctime、localtime、rand 等非线程安全的版本。安全
在屡次的灰度验证、数据比对下,微信各平台的网络逻辑顺利的过渡到了统一基础组件。为了有效的验证组件的效果,咱们开发了 smc 的统计监控组件,开始关注网络的各项指标,进行网络基础研究与优化,尤为是关注移动网络的特征。
基础组件全量上线微信后,以微信的用户量,固然也会遇到各类各样的“妖”。例如,写网络程序躲不开运营商。印象比较深入的某地的用户反馈链接 WiFi 时,微信不可用,后来 tcpdump 发现,当包的大小超过必定大小后就发不出去。解决方案:在 WiFi 网络下强制把 MSS 改成1400(代码见 unix_socket.cc)。
作移动客户端更避不开手机厂商。一次遇到了一个百思不得其解的 crash,堆栈以下:
#00 pc 0x43e50 /system/lib/libc.so (???)
#01 pc 0x3143 /system/vendor/lib/libvendorconn.so (handleDpmIpcReq+154) #02 pc 0x2f6d /system/vendor/lib/libvendorconn.so (send_ipc_req+276) #03 pc 0x30ff /system/vendor/lib/libcneconn.so (connect+438)
看堆栈结合程序 xlog 分析,非阻塞 socket 卡在了 connect 函数里超过了6 min, 被咱们自带的 anr 检测(代码见anr.cc)发现而后自杀。最后实在一筹莫展,联系厂商一块儿排查,最终查明缘由:为了省电,当手机锁屏时连的不是 WiFi 且又没有下行网络数据时,芯片 gate 会关闭,block 住全部网络请求,直到有下行数据或者超过 20min 才会放开。当手机有网络即便是手机网络的状况下,很难没有下行数据,因此基本不会触发组件自带的 anr 检测,但当手机没链接任何网络时,就很容易触发。解决方案:厂商修改代码逻辑,当没有任何网络时不 block 网络请求。
运营商和手机厂商对咱们来讲已是一个黑盒,但其实也遇到过更黑的黑盒。当手机长时间不重启,有极小几率不能继续使用微信,重启手机会恢复。但由于一直找不到一个愿意配合咱们又知足条件的用户,致使这个问题很长一段时间内都没有任何进展,最终偶然一个机会,在一台测试机器上重现了该问题,tcpdump 发如今三步握手阶段,服务器带回的客户端带过去的 tsval 字段被篡改,致使三步握手直接失败,并且这个篡改发生在离开服务器以后到达客户端以前。
这个问题是微信网络模块中排查时间最长也是花费精力最多的一个问题,不只由于很长一段时间内无案例可分析,也由于在重现后,联系了大量的同事和外部有关人的帮忙,想排查出罪魁祸首。但由于中间涉及的环节和运营商相关部门过多,没法继续排查下去,最终也没找到根本缘由。 解决办法:服务器更改 net.ipv4.tcp_timestamps = 0。
这段时间是痛并快乐着,见识到了各类极差的网络,才切肤感觉到移动网络环境的恶劣程度,但看着咱们的网络性能数据在稳步提高又有种知足感。截止到今天,已经不多有真正的网络问题须要跟进了。这也是咱们能有时间开始把这些代码开源出去的很大的一个缘由。
大概一年前(大约2015年10月),咱们开始有想法把基础组件开源出去,当时你们都在纠结叫什么名字好呢?此时恰逢《火星救援》正在热映,一位同事说干脆叫 Mars 吧,因而就定下来叫了 Mars。看了看代码,发现想要开源出去可能仍是须要作一些其余工做的。
首先,代码风格方面,由于最初咱们使用文件名、函数名、变量名的规则是内部定义的规则,为了能让其余人读起来更舒心,咱们决定把代码风格改成谷歌风格,好比:变量名一概小写, 单词之间用下划线链接;左大括号不换行等等。可是为了更好的区分访问空间,咱们又在谷歌代码风格进行了一些变通,好比:私有函数所有是”__”开头;函数参数所有以”_”开头 等等。
其次,虽然最初的设计一直是秉承着业务性无关的设计,但在实际开发过程当中仍然不免带上了微信的业务性相关代码,比较典型的就是 newdns 。为了 Mars 之后的维护以及保证开源出去代码的同源,在开源出去以前必须把这些业务性有关的代码抽离出来,抽离后的结构以下:
最后,为了接口更易用,对调用接口以及回调接口的参数也进行了反复思考与修改。
在 Mars以前,是直接给 Android 提供动态库(.so),由于代码逻辑都已经固定,不须要有可定制的部分。给 Apple 系平台提供静态库(.a),由于对外暴露的函数几乎不会改变,直接把相应的头文件放到相应的项目里就行。但对外开源就彻底不同了:日志的加密算法可能别人须要本身实现;长连或者短连的包头有人须要本身定制;对外接口的头文件咱们可能会修改……
为了让使用者可定制代码,对于编译 Android 平台咱们提供了两种选择:
编译出来静态库后,实现本身须要定制的代码后,执行 ndk-build 后便可编译出来动态库。 对于 Apple 系平台,把头文件所有收拢为 Mars 维护,直接编译出 Framework。
为了能让开发者快速的入门,咱们提供了 Android、iOS、OS X 平台的 demo(微信开源Mars的Demo源码点此进入),其余平台的编译和 demo 会在不久就加上支持。
▲ 成型的 Mars 结构图如上图所示
咱们作的一直都不是知足全部需求的组件,只是作了一个更适合咱们使用的组件,这里也列了下和同类型的开源代码的对比。
能够看出:
总的来讲,Mars 是一个结合移动 App 所设计的基于 socket 层的解决方案,在网络调优方面有更好的可控性,对于 HTTP 完整协议的支持,已经考虑后续版本会加入。
▲ 截止2016年12月28日微信Mars的开源工程代码结构
试用 Android Demo 请从文末的附件下载之,iOS sample 请经过 Mars的Github仓库 编译得到(或文末的附件下载源码)。
常常有朋友和我说:发现网络信号差的时候或者其余应用不能用的时候,微信仍然能发出去消息。不知不觉咱们好像什么都没作,回头看,原来咱们已经作了这么多。
我想,并非任何一行代码均可以经历日活跃5亿用户的考验,感谢微信给咱们提供了这么一个平台。如今咱们想把这些代码和大家分享,运营方式上 Mars 所开源出去的代码会和微信所用的代码保持同源,全部开源出去的代码也首先会在微信上验证经过后再公开。
开源并非结束,只是开始。咱们后续仍然会继续探索在移动互联网下的网络优化。Talk is cheap,show you our code。
(本文同步发布于:http://www.52im.net/thread-684-1-1.html,本文引用了微信团队原创文章:原始文字点此进入)
[1] 网络编程基础资料:
《TCP/IP详解 - 第11章·UDP:用户数据报协议》
《TCP/IP详解 - 第17章·TCP:传输控制协议》
《TCP/IP详解 - 第18章·TCP链接的创建与终止》
《TCP/IP详解 - 第21章·TCP的超时与重传》
《技术往事:改变世界的TCP/IP协议(珍贵多图、手机慎点)》
《通俗易懂-深刻理解TCP协议(上):理论基础》
《通俗易懂-深刻理解TCP协议(下):RTT、滑动窗口、拥塞处理》
《理论经典:TCP协议的3次握手与4次挥手过程详解》
《理论联系实际:Wireshark抓包分析TCP 3次握手、4次挥手过程》
《计算机网络通信协议关系图(中文珍藏版)》
《UDP中一个包的大小最大能多大?》
《Java新一代网络编程模型AIO原理及Linux系统AIO介绍》
《NIO框架入门(一):服务端基于Netty4的UDP双向通讯Demo演示》
《NIO框架入门(二):服务端基于MINA2的UDP双向通讯Demo演示》
《NIO框架入门(三):iOS与MINA二、Netty4的跨平台UDP双向通讯实战》
《NIO框架入门(四):Android与MINA二、Netty4的跨平台UDP双向通讯实战》
《P2P技术详解(一):NAT详解——详细原理、P2P简介》
《P2P技术详解(二):P2P中的NAT穿越(打洞)方案详解》
《P2P技术详解(三):P2P技术之STUN、TURN、ICE详解》
《高性能网络编程(一):单台服务器并发TCP链接数到底能够有多少》
《高性能网络编程(二):上一个10年,著名的C10K并发链接问题》
《高性能网络编程(三):下一个10年,是时候考虑C10M并发问题了》
《高性能网络编程(四):从C10K到C10M高性能网络应用的理论探索》
>> 更多同类文章 ……
[2] 有关IM/推送的通讯格式、协议的选择:
《为何QQ用的是UDP协议而不是TCP协议?》
《移动端即时通信协议选择:UDP仍是TCP?》
《如何选择即时通信应用的数据传输格式》
《强列建议将Protobuf做为你的即时通信应用数据传输格式》
《移动端IM开发须要面对的技术问题(含通讯协议选择)》
《简述移动端IM开发的那些坑:架构设计、通讯协议和客户端》
《理论联系实际:一套典型的IM通讯协议设计详解》
《58到家实时消息系统的协议设计等技术实践分享》
>> 更多同类文章 ……
[3] 有关IM/推送的心跳保活处理:
《Android进程保活详解:一篇文章解决你的全部疑问》
《Android端消息推送总结:实现原理、心跳保活、遇到的问题等》
《为什么基于TCP协议的移动端IM仍然须要心跳保活机制?》
《微信团队原创分享:Android版微信后台保活实战分享(进程保活篇)》
《微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)》
《移动端IM实践:实现Android版微信的智能心跳机制》
《移动端IM实践:WhatsApp、Line、微信的心跳策略分析》
>> 更多同类文章 ……
[4] 有关WEB端即时通信开发:
《新手入门贴:史上最全Web端即时通信技术原理详解》
《Web端即时通信技术盘点:短轮询、Comet、Websocket、SSE》
《SSE技术详解:一种全新的HTML5服务器推送事件技术》
《Comet技术详解:基于HTTP长链接的Web端实时通讯技术》
《WebSocket详解(一):初步认识WebSocket技术》
《socket.io实现消息推送的一点实践及思路》
《LinkedIn的Web端即时通信实践:实现单机几十万条长链接》
>> 更多同类文章 ……
[5] 有关IM架构设计:
《浅谈IM系统的架构设计》
《简述移动端IM开发的那些坑:架构设计、通讯协议和客户端》
《一套原创分布式即时通信(IM)系统理论架构方案》
《从零到卓越:京东客服即时通信系统的技术架构演进历程》
《蘑菇街即时通信/IM服务器开发之架构选择》
《腾讯QQ1.4亿在线用户的技术挑战和架构演进之路PPT》
《微信技术总监谈架构:微信之道——大道至简(演讲全文)》
《如何解读《微信技术总监谈架构:微信之道——大道至简》》
《快速裂变:见证微信强大后台架构从0到1的演进历程(一)》
《17年的实践:腾讯海量产品的技术方法论》
>> 更多同类文章 ……
[6] 有关IM安全的文章:
《即时通信安全篇(一):正确地理解和使用Android端加密算法》
《即时通信安全篇(二):探讨组合加密算法在IM中的应用》
《即时通信安全篇(三):经常使用加解密算法与通信安全讲解》
《即时通信安全篇(四):实例分析Android中密钥硬编码的风险》
《即时通信安全篇(五):对称加密技术在Android平台上的应用实践》
《即时通信安全篇(六):非对称加密技术的原理与应用实践》
《传输层安全协议SSL/TLS的Java平台实现简介和Demo演示》
《理论联系实际:一套典型的IM通讯协议设计详解(含安全层设计)》
《微信新一代通讯安全解决方案:基于TLS1.3的MMTLS详解》
《来自阿里OpenIM:打造安全可靠即时通信服务的技术实践分享》
>> 更多同类文章 ……
[7] 有关实时音视频开发:
《即时通信音视频开发(一):视频编解码之理论概述》
《即时通信音视频开发(二):视频编解码之数字视频介绍》
《即时通信音视频开发(三):视频编解码之编码基础》
《即时通信音视频开发(四):视频编解码之预测技术介绍》
《即时通信音视频开发(五):认识主流视频编码技术H.264》
《即时通信音视频开发(六):如何开始音频编解码技术的学习》
《即时通信音视频开发(七):音频基础及编码原理入门》
《即时通信音视频开发(八):常见的实时语音通信编码标准》
《即时通信音视频开发(九):实时语音通信的回音及回音消除概述》
《即时通信音视频开发(十):实时语音通信的回音消除技术详解》
《即时通信音视频开发(十一):实时语音通信丢包补偿技术详解》
《即时通信音视频开发(十二):多人实时音视频聊天架构探讨》
《即时通信音视频开发(十三):实时视频编码H.264的特色与优点》
《即时通信音视频开发(十四):实时音视频数据传输协议介绍》
《即时通信音视频开发(十五):聊聊P2P与实时音视频的应用状况》
《即时通信音视频开发(十六):移动端实时音视频开发的几个建议》
《即时通信音视频开发(十七):视频编码H.26四、V8的前世此生》
《网易视频云技术分享:音频处理与压缩技术快速入门》
《学习RFC3550:RTP/RTCP实时传输协议基础知识》
《简述开源实时音视频技术WebRTC的优缺点》
《良心分享:WebRTC 零基础开发者教程(中文)》
《开源实时音视频技术WebRTC中RTP/RTCP数据传输协议的应用》
《基于RTMP数据传输协议的实时流媒体技术研究(论文全文)》
《声网架构师谈实时音视频云的实现难点(视频采访)》
《浅谈开发实时视频直播平台的技术要点》
《还在靠“喂喂喂”测试实时语音通话质量?本文教你科学的评测方法!》
《实现延迟低于500毫秒的1080P实时音视频直播的实践分享》
《移动端实时视频直播技术实践:如何作到实时秒开、流畅不卡》
《如何用最简单的方法测试你的实时音视频方案》
《技术揭秘:支持百万级粉丝互动的Facebook实时视频直播》
>> 更多同类文章 ……
[8] IM开发综合文章:
《移动端IM开发须要面对的技术问题》
《开发IM是本身设计协议用字节流好仍是字符流好?》
《请问有人知道语音留言聊天的主流实现方式吗?》
《IM消息送达保证机制实现(一):保证在线实时消息的可靠投递》
《IM消息送达保证机制实现(二):保证离线消息的可靠投递》
《谈谈移动端 IM 开发中登陆请求的优化》
《彻底自已开发的IM该如何设计“失败重试”机制?》
《微信对网络影响的技术试验及分析(论文全文)》
《即时通信系统的原理、技术和应用(技术论文)》
《开源IM工程“蘑菇街TeamTalk”的现状:一场虎头蛇尾的开源秀》
《腾讯原创分享(一):如何大幅提高移动网络下手机QQ的图片传输速度和成功率》
《如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源》
>> 更多同类文章 ……
[9] 开源移动端IM技术框架资料:
《开源移动端IM技术框架MobileIMSDK:快速入门》
《开源移动端IM技术框架MobileIMSDK:常见问题解答》
《开源移动端IM技术框架MobileIMSDK:压力测试报告》
>> 更多同类文章 ……
[10] 有关推送技术的文章:
《iOS的推送服务APNs详解:设计思路、技术原理及缺陷等》
《Android端消息推送总结:实现原理、心跳保活、遇到的问题等》
《扫盲贴:认识MQTT通讯协议》
《一个基于MQTT通讯协议的完整Android推送Demo》
《IBM技术经理访谈:MQTT协议的制定历程、发展示状等》
《求教android消息推送:GCM、XMPP、MQTT三种方案的优劣》
《移动端实时消息推送技术浅析》
《扫盲贴:浅谈iOS和Android后台实时消息推送的原理和区别》
《绝对干货:基于Netty实现海量接入的推送服务技术要点》
《移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信)》
《为什么微信、QQ这样的IM工具不使用GCM服务推送消息?》
《极光推送系统大规模高并发架构的技术实践分享》
《从HTTP到MQTT:一个基于位置服务的APP数据通讯实践概述》
>> 更多同类文章 ……
[11] 更多即时通信技术好文分类:
http://www.52im.net/forum.php?mod=collection&op=all