开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石
微信于2013年开源的ibco库,是微信后台大规模使用的c/c++协程库,2013年至今稳定运行在微信后台的数万台机器上。libco在2013年的时候作为腾讯六大开源项目首次开源,ibco支持后台敏捷的同步风格编程模式,同时提供系统的高并发能力。
有关开发libco的背后故事,请见文章《微信异步化改造实践:8亿月活、单机千万连接背后的后台解决方案》。
libco支持的特性
libco主要特性:
- 无需侵入业务逻辑,把多进程、多线程服务改造成协程服务,并发能力得到百倍提升;
- 支持CGI框架,轻松构建web服务(New);
- 支持gethostbyname、mysqlclient、ssl等常用第三库(New);
- 可选的共享栈模式,单机轻松接入千万连接(New)。
libco完善简洁的协程编程接口:
- 类pthread接口设计,通过co_create、co_resume等简单清晰接口即可完成协程的创建与恢复;
- 类__thread的协程私有变量、协程间通信的协程信号量co_signal (New);
- 非语言级别的lambda实现,结合协程原地编写并执行后台异步任务 (New);
- 基于epoll/kqueue实现的小而轻的网络框架,基于时间轮盘实现的高性能定时器。
libco产生的背景
早期微信后台因为业务需求复杂多变、产品要求快速迭代等需求,大部分模块都采用了半同步半异步模型。接入层为异步模型,业务逻辑层则是同步的多进程或多线程模型,业务逻辑的并发能力只有几十到几百。随着微信业务的增长,系统规模变得越来越庞大,每个模块很容易受到后端服务/网络抖动的影响。
异步化改造的选择
为了提升微信后台的并发能力,一般的做法是把现网的所有服务改成异步模型。这种做法工程量巨大,从框架到业务逻辑代码均需要做一次彻底的改造,耗时耗力而且风险巨大。于是我们开始考虑使用协程。
但使用协程会面临以下挑战:
- 业界协程在c/c++环境下没有大规模应用的经验;
- 如何控制协程调度;
- 如何处理同步风格的API调用,如Socket、mysqlclient等;
- 如何处理已有全局变量、线程私有变量的使用。
最终我们通过libco解决了上述的所有问题,实现了对业务逻辑非侵入的异步化改造。我们使用libco对微信后台上百个模块进行了协程异步化改造,改造过程中业务逻辑代码基本无修改。至今,微信后台绝大部分服务都已是多进程或多线程协程模型,并发能力相比之前有了质的提升,而libco也成为了微信后台框架的基石。
libco框架技术架构图
libco在框架分为三层,分别是接口层、系统函数Hook层以及事件驱动层。
<ignore_js_op>
libco对同步风格API的创新处理
对于同步风格的API,主要是同步的网络调用,libco的首要任务是消除这些等待对资源的占用,提高系统的并发性能。一个常规的网络后台服务,我们可能会经历connect、write、read等步骤,完成一次完整的网络交互。当同步的调用这些API的时候,整个线程会因为等待网络交互而挂起。
虽然同步编程风格的并发性能并不好,但是它具有代码逻辑清晰、易于编写的优点,并可支持业务快速迭代敏捷开发。为了继续保持同步编程的优点,并且不需修改线上已有的业务逻辑代码,libco创新地接管了网络调用接口(Hook),把协程的让出与恢复作为异步网络IO中的一次事件注册与回调。当业务处理遇到同步网络请求的时候,libco层会把本次网络请求注册为异步事件,本协程让出CPU占用,CPU交给其它协程执行。libco会在网络事件发生或者超时的时候,自动的恢复协程执行。
大部分同步风格的API我们都通过Hook的方法来接管了,libco会在恰当的时机调度协程恢复执行。
libco达到千万级协程的支持
libco默认是每一个协程独享一个运行栈,在协程创建的时候,从堆内存分配一个固定大小的内存作为该协程的运行栈。如果我们用一个协程处理前端的一个接入连接,那对于一个海量接入服务来说,我们的服务的并发上限就很容易受限于内存。为此,libco也提供了stackless的协程共享栈模式,可以设置若干个协程共享同一个运行栈。同一个共享栈下的协程间切换的时候,需要把当前的运行栈内容拷贝到协程的私有内存中。为了减少这种内存拷贝次数,共享栈的内存拷贝只发生在不同协程间的切换。当共享栈的占用者一直没有改变的时候,则不需要拷贝运行栈。
<ignore_js_op>
libco协程的共享协程栈模式使得单机很容易接入千万连接,只需创建足够多的协程即可。我们通过libco共享栈模式创建1千万的协程(E5-2670 v3 @ 2.30GHz * 2, 128G内存),每10万个协程共享的使用128k内存,整个稳定echo服务的时候总内存消耗大概为66G。
libco协程级私有变量
多进程程序改造为多线程程序时候,我们可以用__thread来对全局变量进行快速修改,而在协程环境下,我们创造了协程变量ROUTINE_VAR,极大简化了协程的改造工作量。
因为协程实质上是线程内串行执行的,所以当我们定义了一个线程私有变量的时候,可能会有重入的问题。比如我们定义了一个__thread的线程私有变量,原本是希望每一个执行逻辑独享这个变量的。但当我们的执行环境迁移到协程了之后,同一个线程私有变量,可能会有多个协程会操作它,这就导致了变量冲入的问题。为此,我们在做libco异步化改造的时候,把大部分的线程私有变量改成了协程级私有变量。协程私有变量具有这样的特性:当代码运行在多线程非协程环境下时,该变量是线程私有的;当代码运行在协程环境的时候,此变量是协程私有的。底层的协程私有变量会自动完成运行环境的判断并正确返回所需的值。
协程私有变量对于现有环境同步到异步化改造起了举足轻重的作用,同时我们定义了一个非常简单方便的方法定义协程私有变量,简单到只需一行声明代码即可。
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的异步化。
libco定义了协程信号量的概念
在多线程环境下,我们会有线程间同步的需求,比如一个线程的执行需要等待另一个线程的信号,对于这种需求,我们通常是使用pthread_signal 来解决的。在libco中,我们定义了协程信号量co_signal用于处理协程间的并发需求,一个协程可以通过co_cond_signal与co_cond_broadcast来决定通知一个等待的协程或者唤醒所有等待协程。
libco技术小结
libco是一个高效的c/c++协程库,提供了完善的协程编程接口、常用的Socket族函数Hook等,使得业务可用同步编程模型快速迭代开发。随着几年来的稳定运行,libco作为微信后台框架的基石发挥了举足轻重的作用。
源码打包下载
<ignore_js_op> libco-20161207snapshot(52im.net).zip (42.39 KB, 下载次数: 0, 售价: 1 金币)
最新源码地址
http://www.52im.net/thread-623-1-1.html
开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石的更多相关文章
- 单机千万级MQTT连接服务器测试报告
目标:测试创建1000万客户端连接到服务器端,服务器操作系统 Linux(任意一款发行版服务器版本).分别在两台硬件一样的服务器,其中一台用于服务器端运行,另一台用于创建千万客户端连接客户端机器.在硬 ...
- [教程]微信官方开源UI库-WeUI使用方法【申明:来源于网络】
[教程]微信官方开源UI库-WeUI使用方法 [ 教程]微信官方开源UI库-WeUI使用方法 地址:http://www.weui.org.cn/?/article/1 微信公众号开发-WeUI使用说 ...
- 【腾讯Bugly干货分享】揭秘:微信是如何用libco支撑8亿用户的
本文来自于腾讯bugly开发者社区,未经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/58203cfcd149ba305c5ccf85 作者:Leiffy 导语 lib ...
- 小程序-文章:微信小程序常见的UI框架/组件库总结
ylbtech-小程序-文章:微信小程序常见的UI框架/组件库总结 1.返回顶部 1. 想要开发出一套高质量的小程序,运用框架,组件库是省时省力省心必不可少一部分,随着小程序日渐火爆,各种不同类型的小 ...
- 推荐3个小程序开源组件库——Vant、iView、ColorUI
推荐3个小程序开源组件库 在进行小程序开发时,经常会遇到编写组件方面的阻碍,这让我们花费大量的时间在页面以及 CSS 样式编写上.因此可以使用开源组件库,有些复杂的组件可以直接拿来使用,节省开发时间, ...
- iOS编译集成linux开源c库的一些记录
最近一个iOS项目需要使用一些Linux下面的开源c库,说是Linux的其实是跨平台的,各种Unix系统都有支持.理论上iOS来自MacOS,而MacOS其实是一种兼容的Unix系统,所以这些库应该也 ...
- 单机百万连接调优和Netty应用级别调优
作者:Grey 原文地址:单机百万连接调优和Netty应用级别调优 说明 本文为深度解析Netty源码的学习笔记. 单机百万连接调优 准备两台Linux服务器,一个充当服务端,一个充当客户端. 服务端 ...
- Pugixml一种快速解析XML文件的开源解析库
Pugixml是一个轻量级的C++ XML开源解析库,DOM形式的解析器.接口和丰富的遍历和修改操作,快速的解析,此外支持XPath1.0实现数据查询,支持unicode编码: 使用Pugixml可通 ...
- iOS流行的开源代码库
本文介绍一些流行的iOS的开源代码库 1.AFNetworking 更新频率高的轻量级的第三方网络库,基于NSURL和NSOperation,支持iOS和OSX.https://github.com/ ...
随机推荐
- 解决Request method 'GET' not supported问题
博主最近遇到了这个问题,解决情况如下 第一种情况:前台页面的表单在一些情况下没有指定POST方法: Ajax没有指定POST方法: 后台方法在一定情况下需要指定POST方法: 第二种情况:前端参数类型 ...
- 【BZOJ 1022】 [SHOI2008]小约翰的游戏John
[题目链接]:http://www.lydsy.com/JudgeOnline/problem.php?id=1022 [题意] [题解] 和这题类似http://blog.csdn.net/harl ...
- hadoop 3.x org.apache.hadoop.security.AccessControlException: Permission denied: user=Administrator, access=WRITE, inode="/":tele:supergroup:drwxr-xr-x
权限不足,上传文件时应当使用启动hadoop的账户,即在获取FileSystem时就应当指定用户 修改后的代码 public class Demo1 { public static void main ...
- CSS拾遗(二)
接CSS拾遗(一). 4. 不透明度 opacity: 0.8; filter: alpha(opacity=80); opacity: 0.8是标准的写法:filter: alpha(opacity ...
- Android 光标位置设置
EditText edit =(EditText) findViewById(R.id.etTest); 1.设置光标在EditText中的指定位置 edit.setSelection(1); 需要注 ...
- 左右RAC CRS 自己主动启动
左右CRS自己主动重新启动实验 一.检验ASM [root@rac1 ~]# /etc/init.d/oracleasm status Checking if ASM is loaded: yes C ...
- 微信公众平台消息接口开发(24)图片识别之人脸识别API
微信公众平台开发模式 微信 公众平台 消息接口 开发模式 企业微信公众平台 图片识别 人脸识别 API 作者:方倍工作室 原文:http://www.cnblogs.com/txw1958/archi ...
- C# WPF QQ新消息托盘悬浮窗效果实现
原文:C# WPF QQ新消息托盘悬浮窗效果实现 今天在做一个项目的时候需要这么一个效果,但是网上找了一会发现并没有现成的给我参考(复制),但是呢,我千(到)辛(处)万(抄)苦(袭)想(复)破(制)头 ...
- [OpenGL]OpenGL坐标系和坐标变换
OpenGL通过摄像机的模拟.要实现一个三维计算机图形重大转变,这是几何变换(模型转换-查看转型(两者统称为几何变换)).投影.作物转型.口变换等.同一时候,OpenGL还实现了矩阵堆栈等.理解掌握了 ...
- 【甘道夫】基于Mahout0.9+CDH5.2执行分布式ItemCF推荐算法
环境: hadoop-2.5.0-cdh5.2.0 mahout-0.9-cdh5.2.0 引言 尽管Mahout已经宣布不再继续基于Mapreduce开发,迁移到Spark.可是实际面临的情况是公司 ...