专栏首页微信终端开发团队的专栏揭秘:微信如何用 libco 支撑8亿用户?
原创

揭秘:微信如何用 libco 支撑8亿用户?

作者:Leiffy

导语

正如 tony 所说"微信将会发展为数字社会的基础设置,做一个很好的产品,做一个很酷的产品,这个理念对于微信团队来说,已经不足够"。

在这里,微信的技术团队也希望更多的将我们的技术精神与积累分享出去。WeMobileDev 是一种尝试,tinker 是另外一种尝试。tinker 在一个月内获得 5000 的 star 是一个不错的开端,在未来微信终端将开源更多优秀的项目,例如 12 月底的跨平台组件 mars。

不仅仅微信终端,微信后台也开源了大量优秀的项目,phxsql、phxpaxos 以及文中的 libco。事实上,腾讯的开源也在大力发展中,当前也有超过 6 个项目正在审核的流程中。在不久的将来,我们可以在 https://github.com/tencent 发现更多优秀的项目。

libco 是微信后台大规模使用的 c/c 协程库,2013 年至今稳定运行在微信后台的数万台机器上。libco 在 2013 年的时候作为腾讯六大开源项目首次开源,我们最近做了一次较大的更新,同步更新在 https://github.com/tencent/libco 上。libco 支持后台敏捷的同步风格编程模式,同时提供系统的高并发能力。

libco 支持的特性

  • 无需侵入业务逻辑,把多进程、多线程服务改造成协程服务,并发能力得到百倍提升;
  • 支持 CGI 框架,轻松构建 web 服务(New);
  • 支持 gethostbyname、mysqlclient、ssl 等常用第三库(New);
  • 可选的共享栈模式,单机轻松接入千万连接(New);
  • 完善简洁的协程编程接口:
    • 类 _thread 接口设计,通过 co_create、co_resume 等简单清晰接口即可完成协程的创建与恢复;
    • 类 _thread 的协程私有变量、协程间通信的协程信号量 co_signal (New);
    • 非语言级别的 lambda 实现,结合协程原地编写并执行后台异步任务 (New);
    • 基于 epoll/kqueue 实现的小而轻的网络框架,基于时间轮盘实现的高性能定时器;

libco 产生的背景

早期微信后台因为业务需求复杂多变、产品要求快速迭代等需求,大部分模块都采用了半同步半异步模型。接入层为异步模型,业务逻辑层则是同步的多进程或多线程模型,业务逻辑的并发能力只有几十到几百。随着微信业务的增长,系统规模变得越来越庞大,每个模块很容易受到后端服务/网络抖动的影响。

异步化改造的选择

为了提升微信后台的并发能力,一般的做法是把现网的所有服务改成异步模型。这种做法工程量巨大,从框架到业务逻辑代码均需要做一次彻底的改造,耗时耗力而且风险巨大。于是我们开始考虑使用协程。

但使用协程会面临以下挑战:

1、 业界协程在 c/c 环境下没有大规模应用的经验; 2、 如何控制协程调度; 3、 如何处理同步风格的 API 调用,如 Socket、mysqlclient 等; 4、 如何处理已有全局变量、线程私有变量的使用;

最终我们通过 libco 解决了上述的所有问题,实现了对业务逻辑非侵入的异步化改造。我们使用 libco 对微信后台上百个模块进行了协程异步化改造,改造过程中业务逻辑代码基本无修改。至今,微信后台绝大部分服务都已是多进程或多线程协程模型,并发能力相比之前有了质的提升,而 libco 也成为了微信后台框架的基石。

libco 框架

libco 在框架分为三层,分别是接口层、系统函数 Hook 层以及事件驱动层。

同步风格 API 的处理

对于同步风格的 API,主要是同步的网络调用,libco 的首要任务是消除这些等待对资源的占用,提高系统的并发性能。一个常规的网络后台服务,我们可能会经历 connect、write、read 等步骤,完成一次完整的网络交互。当同步的调用这些 API 的时候,整个线程会因为等待网络交互而挂起。

虽然同步编程风格的并发性能并不好,但是它具有代码逻辑清晰、易于编写的优点,并可支持业务快速迭代敏捷开发。为了继续保持同步编程的优点,并且不需修改线上已有的业务逻辑代码,libco 创新地接管了网络调用接口(Hook),把协程的让出与恢复作为异步网络 IO 中的一次事件注册与回调。当业务处理遇到同步网络请求的时候,libco 层会把本次网络请求注册为异步事件,本协程让出 CPU 占用,CPU 交给其它协程执行。libco 会在网络事件发生或者超时的时候,自动的恢复协程执行。

大部分同步风格的 API 我们都通过 Hook 的方法来接管了,libco 会在恰当的时机调度协程恢复执行。

千万级协程支持

libco 默认是每一个协程独享一个运行栈,在协程创建的时候,从堆内存分配一个固定大小的内存作为该协程的运行栈。如果我们用一个协程处理前端的一个接入连接,那对于一个海量接入服务来说,我们的服务的并发上限就很容易受限于内存。为此,libco 也提供了 stackless 的协程共享栈模式,可以设置若干个协程共享同一个运行栈。同一个共享栈下的协程间切换的时候,需要把当前的运行栈内容拷贝到协程的私有内存中。为了减少这种内存拷贝次数,共享栈的内存拷贝只发生在不同协程间的切换。当共享栈的占用者一直没有改变的时候,则不需要拷贝运行栈。

libco 协程的共享协程栈模式使得单机很容易接入千万连接,只需创建足够多的协程即可。我们通过 libco 共享栈模式创建 1 千万的协程(E5-2670 v3 @ 2.30GHz * 2, 128G 内存),每 10 万个协程共享的使用 128k 内存,整个稳定 echo 服务的时候总内存消耗大概为 66G,qps 可达到 210W /s;

协程私有变量

多进程程序改造为多线程程序时候,我们可以用_thread 来对全局变量进行快速修改,而在协程环境下,我们创造了协程变量 ROUTINEVAR,极大简化了协程的改造工作量。

因为协程实质上是线程内串行执行的,所以当我们定义了一个线程私有变量的时候,可能会有重入的问题。比如我们定义了一个_thread 的线程私有变量,原本是希望每一个执行逻辑独享这个变量的。但当我们的执行环境迁移到协程了之后,同一个线程私有变量,可能会有多个协程会操作它,这就导致了变量重入的问题。为此,我们在做 libco 异步化改造的时候,把大部分的线程私有变量改成了协程级私有变量。协程私有变量具有这样的特性:当代码运行在多线程非协程环境下时,该变量是线程私有的;当代码运行在协程环境的时候,此变量是协程私有的。底层的协程私有变量会自动完成运行环境的判断并正确返回所需的值。

协程私有变量对于现有环境同步到异步化改造起了举足轻重的作用,同时我们定义了一个非常简单方便的方法定义协程私有变量,简单到只需一行声明代码即可。

gethostbyname 的 Hook 方法

对于现网服务,有可能需要通过系统的 gethostbyname API 接口去查询 DNS 获取真实地址。我们在协程化改造的时候,发现我们 hook 的 socket 族函数对 gethostbyname 不适用,当一个协程调用了 gethostbyname 时会同步等待结果,这就导致了同线程内的其它协程被延时执行。我们对 glibc 的 gethostbyname 源码进行了研究,发现 hook 不生效主要是由于 glibc 内部是定义了_poll 方法来等待事件,而不是通用的 poll 方法;同时 glibc 还定义了一个线程私有变量,不同协程的切换可能会重入导致数据不准确。最终 gethostbyname 协程异步化是通过 Hook _poll 方法以及定义协程私有变量解决的。

gethostbyname 是 glibc 提供的同步查询 dns 接口,业界还有很多优秀的 gethostbyname 的异步化解决方案,但是这些实现都需要引入一个第三方库并且要求底层提供异步回调通知机制。libco 通过 hook 方法,在不修改 glibc 源码的前提下实现了的 gethostbyname 的异步化。

协程信号量

在多线程环境下,我们会有线程间同步的需求,比如一个线程的执行需要等待另一个线程的信号,对于这种需求,我们通常是使用 pthread signal 来解决的。在 libco 中,我们定义了协程信号量 cosignal 用于处理协程间的并发需求,一个协程可以通过 cocondsignal 与 cocondbroadcast 来决定通知一个等待的协程或者唤醒所有等待协程。

总结

libco 是一个高效的 c/c 协程库,提供了完善的协程编程接口、常用的 Socket 族函数 Hook 等,使得业务可用同步编程模型快速迭代开发。随着几年来的稳定运行,libco 作为微信后台框架的基石发挥了举足轻重的作用。

本文来源于:WeMobileDev 微信公众号

原创声明,本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

登录 后参与评论
0 条评论

相关文章

  • 揭秘:微信是如何用libco支撑8亿用户的

    导语 ibco是微信后台大规模使用的c/c++协程库,2013年至今稳定运行在微信后台的数万台机器上。libco在2013年的时候作为腾讯六大开源项目首次开源,...

    腾讯Bugly
  • 2017微信数据报告:日活跃用户达9亿、日发消息380亿条

    2017年11月9日,微信团队在成都腾讯全球合作伙伴大会上为全球伙伴解读了最新的《2017微信数据报告》。微信每天有多少条消息被发送?目前有多少个行业已经在使用...

    JackJiang
  • 腾讯十大最受欢迎的开源项目!

    MMKV 是基于 mmap 内存映射的 key-value 组件,底层序列化/反序列化使用 protobuf 实现,性能高,稳定性强。从 2015 年中至今在微...

    肉眼品世界
  • 【开源公告】 微信后台Phx系列开源

    来自微信后台团队的话 微信后台团队是一支充满活力的团队,自2010年那天微信后台第一版发布开始,便满怀着代码令生活更美好的理想,一行一行地去改善着人们的移动生活...

    腾讯开源
  • 从0到10亿,微信后台架构及基础设施设计与实践!

    本文根据许家滔老师在2018年10月17日【第十届中国系统架构师大会(SACC2018)】现场演讲内容整理而成。 回顾微信发展历程

    数据和云
  • 微信libco协程库源码分析

    libco是微信后台开发和使用的协程库,同时应该也是极少数的将C/C++协程直接运用到如此大规模的生成环境中的案例了。

    cyhone
  • 微信团队分享:iOS版微信是如何防止特殊字符导致的炸群、APP崩溃的?

    相信大家都遇到过一段特殊文本可以让iOS设备所有app闪退的经历。前段时间大年初一,又出现某个印度语字符引起iOS11系统奔溃,所幸iOS版微信客户端做了保护并...

    JackJiang
  • 微服务在微信的架构实践

    微服务的理念与腾讯一直倡导的“大系统小做”有很多相通之处,本文将分享微信后台架构的服务发现、通信机制、集群管理等基础能力与其上层服务划分原则、代码管理规则等。

    Java架构师历程
  • 技术往事:微信估值已超5千亿,雷军曾有机会收编张小龙及其Foxmail

    据汇丰银行(HSBC)发布的报告显示,腾讯公司旗下手机通讯app微信市场价值估计高达836亿美元(约合人民币5344亿元),几乎是腾讯市值的一半。由此可见腾讯高...

    JackJiang
  • QQ和微信凶猛成长的背后:腾讯网络基础架构的这些年

    也许没有多少人记得2004年发生的事情。但对于老腾讯来说,14年前的那个日子,2004年6月16日永远难以忘怀。这一天,QQ诞生5年后的腾讯在香港联交所主板上市...

    JackJiang
  • 精选腾讯技术干货200+篇,云加社区全年沙龙PPT免费下载!

    “看一看”推荐模型揭秘!微信团队提出实时Look-alike算法,解决推荐系统多样性问题;

    风间琉璃
  • 微信团队披露:微信界面卡死超级bug“15。。。。”的来龙去脉

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

    JackJiang
  • 微信朋友圈千亿访问量背后的技术挑战和实践总结

    微信朋友圈包括图片和视频两套业务架构组成,朋友圈图片的特点是请求量大、消耗计算资源较多,视频则主要消耗带宽。

    JackJiang
  • 腾讯云开发者社区技术沙龙资料合集

    腾讯云开发者社区技术沙龙
  • 微信团队分享:iOS版微信的高性能通用key-value组件技术实践

    本文要分享的是iOS版微信内部正在推广和使用的一个高性能通用key-value 组件的技术实践过程,该组件在微信内部被命名为MMKV(以下简称MMKV)。

    JackJiang
  • 微信团队分享:微信每日亿次实时音视频聊天背后的技术解密

    2012 年 7 月,微信 4.2 版本首次加入了实时音视频聊天功能,如今已发展了 5 年,在面对亿级微信用户复杂多变的网络和设备环境,微信多媒体团队在每个技术...

    JackJiang
  • iOS后台唤醒实战:微信收款到账语音提醒技术总结

    微信为了解决小商户老板们在频繁交易中不方便核对、确认到账的功能痛点,产品MM提出了新版本需要支持收款到账语音提醒功能。本文借此总结了iOS平台上的APP后台唤醒...

    JackJiang

扫码关注腾讯云开发者

领取腾讯云代金券