本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/58203cfcd149ba305c5ccf85

作者:Leiffy

导语

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

libco支持的特性

  • 无需侵入业务逻辑,把多进程、多线程服务改造成协程服务,并发能力得到百倍提升;

  • 支持CGI框架,轻松构建web服务(New);

  • 支持gethostbyname、mysqlclient、ssl等常用第三库(New);

  • 可选的共享栈模式,单机轻松接入千万连接(New);

  • 完善简洁的协程编程接口

-- 类pthread接口设计,通过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。

协程私有变量

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

因为协程实质上是线程内串行执行的,所以当我们定义了一个线程私有变量的时候,可能会有重入的问题。比如我们定义了一个__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中,我们定义了协程信号量co_signal用于处理协程间的并发需求,一个协程可以通过co_cond_signal与co_cond_broadcast来决定通知一个等待的协程或者唤醒所有等待协程。

总结

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


更多精彩内容欢迎关注bugly的微信公众账号:

腾讯 Bugly是一款专为移动开发者打造的质量监控工具,帮助开发者快速,便捷的定位线上应用崩溃的情况以及解决方案。智能合并功能帮助开发同学把每天上报的数千条 Crash 根据根因合并分类,每日日报会列出影响用户数最多的崩溃,精准定位功能帮助开发同学定位到出问题的代码行,实时上报可以在发布后快速的了解应用的质量情况,适配最新的 iOS, Android 官方操作系统,鹅厂的工程师都在使用,快来加入我们吧!

【腾讯Bugly干货分享】揭秘:微信是如何用libco支撑8亿用户的的更多相关文章

  1. 【腾讯Bugly干货分享】微信iOS SQLite源码优化实践

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57b58022433221be01499480 作者:张三华 前言 随着微信iO ...

  2. 【腾讯Bugly干货分享】微信热补丁Tinker的实践演进之路

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57ad7a70eaed47bb2699e68e Dev Club 是一个交流移动 ...

  3. 【腾讯Bugly干货分享】微信终端跨平台组件 Mars 系列 - 我们如约而至

    导语 昨天上午,微信在广州举办了微信公开课Pro.于是,精神哥这两天的朋友圈被小龙的"八不做"刷屏了.小伙伴们可能不知道,下午,微信公开课专门开设了技术分论坛.在分论坛中,微信开源 ...

  4. 【腾讯Bugly干货分享】微信mars 的高性能日志模块 xlog

    本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/581c2c46bef1702a2db3ae53 Dev Club 是一个交流移动 ...

  5. 【腾讯Bugly干货分享】微信Tinker的一切都在这里,包括源码(一)

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/57ecdf2d98250b4631ae034b 最近半年以来,Android热补 ...

  6. 【腾讯Bugly干货分享】微信读书iOS性能优化

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/578c93ca9644bd524bfcabe8 “8小时内拼工作,8小时外拼成长 ...

  7. 【腾讯bugly干货分享】微信Android热补丁实践演进之路

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1264& ...

  8. 【腾讯Bugly干货分享】微信终端跨平台组件 mars 系列(二) - 信令传输超时设计

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:http://mp.weixin.qq.com/s/9DJxipJaaBC8yC-buHgnTQ 作者简介: ...

  9. 【腾讯Bugly干货分享】微信小程序开发思考总结——腾讯“信用卡还款”项目实践

    本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/58212d0fa7a7574c4f4cc3c5 作者:peggy 小程序概述 1 ...

随机推荐

  1. PHP的文件格式应该以UTF-8无BOM编码

    前一段时间写PHP,经常在解析文件之前就要对数据进行处理判断,并以header()的方式进行页面跳转.然而后来将文件放到 linux 服务器时常碰到header()解析出错的情况,而在 windows ...

  2. MVC 之 Code First

    在我们的工作和学习当中,经常会遇到中小型项目,这些项目除了业务上的区别较大 外,对于底层和数据访问,其实都差不多.记得以前做项目时,每次都要重复的写底层操作数据库的代码,不仅浪费时间,也无太大意思,后 ...

  3. Apizza可以进行本地调试,也可以进行跨域调试,但是需要chrome插件

    装Apizza Chrome插件   1 1. 进入Apizza官方网站.注册账号,进入控制台,可以看到提示安装chrome插件 2. 进入google app商店,搜索apiano 找到对于插件安装 ...

  4. CentOS6系统openssl生成证书和自签证书

    CentOS6系统openssl生成证书和自签证书的过程,记录一下,本文基于CentOS 6 64bit.$ yum install openssl openssl-devel 1,生成服务器端的私钥 ...

  5. ZooKeeper事务日志记录器SyncRequestProcessor

    SyncRequestProcessor作为一个ZooKeeper中的一个关键线程(ZooKeeperCriticalThread),是ZooKeeper请求处理链中的事务日志记录处理器,其主要用来将 ...

  6. NABCD

    1) N (Need 需求) 随着科学技术的进步和计算机行业的迅速发展,人们的工作效率得到大大提高.计算机信息处理系统的引进已彻底改变了许多系统的经营管理. 图书管理系统是学校管理机制中的重要组成部分 ...

  7. C#中的using和yield return混合使用

    最近写代码为了为了省事儿用了几个yield return,因为我不想New一个List<T>或者T[]对象再往里放元素,就直接返回IEnumerable<T>了.我的代码里还有 ...

  8. SQLServer数据库中创建临时表

    IF object_id('tempdb..#jimmy') is not NULL BEGIN DROP TABLE #jimmy; END IF object_id('tempdb..#jimmy ...

  9. 【洛谷P2866】Bad Hair Day

    单调栈版子 #include<cstdio> #include<cstring> using namespace std; ; ,zh[N]; int read(){ ; ch ...

  10. nexus2.1.2的配置

    最近在学习maven,逐渐接触到私服的搭建,也就着手学习使用nexus了,在http://www.sonatype.org/nexus/go网站上nexus最新版本的是,不过版本要同jvm的版本匹对, ...