历史原因,一直使用 libev 作为服务底层;异步框架虽然性能比较高,但新人学习和使用门槛非常高,而且串行的逻辑被打散为状态机,这也会严重影响生产效率。

用同步方式实现异步功能,既保证了异步性能优势,又使得同步方式实现源码思路清晰,容易维护,这是协程的优势。带着这样的目的学习微信开源的一个轻量级网络协程库:libco 。

1. 概述

libco 是轻量级的协程库,看完下面几个帖子,应该能大致搞懂它的工作原理。

2. 问题

带着问题学习 libco:

搞清这几个概念:阻塞,非阻塞,同步,异步,锁。

协程是什么东西,与进程和线程有啥关系。

协程解决了什么问题。

协程在什么场景下使用。

协程切换原理。

协程切换时机。

协程需要上锁吗?

libco 主要有啥功能。(协程管理,epoll/kevent,hook)

3. libco 源码结构布局

将 libco 的源码结构展开,这样方便理清它的内部结构关系。

4. mysql 测试

测试目标:测试 libco 协程性能,以及是否能将 mysqlclient 同步接口进行异步改造。

测试系统:CentOS Linux release 7.7.1908 (Core)

测试源码:github。

4.1. 测试源码

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58/* 数据库信息。 */

typedef struct db_s {

std::string host;

int port;

std::string user;

std::string psw;

std::string charset;

} db_t;

/* 协程任务。 */

typedef struct task_s {

int id; /* 任务 id。 */

db_t* db; /* 数据库信息。 */

MYSQL* mysql; /* 数据库实例指针。 */

stCoRoutine_t* co; /* 协程指针。 */

} task_t;

/* 协程处理函数。 */

void* co_handler_mysql_query(void* arg) {

co_enable_hook_sys();

...

/* 同步方式写数据库访问代码。 */

for (i = 0; i < g_co_query_cnt; i++) {

g_cur_test_cnt++;

/* 读数据库 select。 */

query = "select * from mytest.test_async_mysql where id = 1;";

if (mysql_real_query(task->mysql, query, strlen(query))) {

show_error(task->mysql);

return nullptr;

}

res = mysql_store_result(task->mysql);

mysql_free_result(res);

}

...

}

int main(int argc, char** argv) {

...

/* 协程个数。 */

g_co_cnt = atoi(argv[1]);

/* 每个协程 mysql query 次数。 */

g_co_query_cnt = atoi(argv[2]);

/* 数据库信息。 */

db = new db_t{"127.0.0.1", 3306, "root", "123456", "utf8mb4"};

for (i = 0; i < g_co_cnt; i++) {

task = new task_t{i, db, nullptr, nullptr};

/* 创建协程。 */

co_create(&(task->co), NULL, co_handler_mysql_query, task);

/* 唤醒协程。 */

co_resume(task->co);

}

/* 循环处理协程事件逻辑。 */

co_eventloop(co_get_epoll_ct(), 0, 0);

...

}

5. hook

在 Centos 系统,查看 hook 是否成功,除了测试打印日志,其实还有其它比较直观的方法。

5.1. strace

用 strace 查看底层的调用,我们看到 mysql_real_connect 内部的 connect,被 hook 成功,connect 前,被替换为 libco 的 connect 了。socket 在 connect 前,被修改为 O_NONBLOCK 。

1

2

3

4

5# strace -s 512 -o /tmp/libco.log ./test_libco 1 1

socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 4

fcntl(4, F_GETFL) = 0x2 (flags O_RDWR)

fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0

connect(4, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EINPROGRESS (Operation now inprogress)

5.2. gdb

上神器 gdb,在 co_hook_sys_call.cpp 文件的 read 和 write 函数下断点。

命中断点,查看函数调用堆栈,libco 在 Centos 系统能成功 hook 住 mysqlclient 的阻塞接口。

1

2

3

4

5

6

7

8

9

10#0 read (fd=fd@entry=9, buf=buf@entry=0x71fc30, nbyte=nbyte@entry=19404) at co_hook_sys_call.cpp:299

#1 0x00007ffff762b30a in read (__nbytes=19404, __buf=0x71fc30, __fd=9) at /usr/include/bits/unistd.h:44

#2 my_read (Filedes=Filedes@entry=9, Buffer=Buffer@entry=0x71fc30 "", Count=Count@entry=19404, MyFlags=MyFlags@entry=0)

at /export/home/pb2/build/sb_0-37309218-1576675139.51/rpm/BUILD/mysql-5.7.29/mysql-5.7.29/mysys/my_read.c:64

#3 0x00007ffff7624966 in inline_mysql_file_read (

src_file=0x7ffff78424b0 "/export/home/pb2/build/sb_0-37309218-1576675139.51/rpm/BUILD/mysql-5.7.29/mysql-5.7.29/mysys/charset.c",

src_line=383, flags=0, count=19404, buffer=0x71fc30 "", file=9)

at /export/home/pb2/build/sb_0-37309218-1576675139.51/rpm/BUILD/mysql-5.7.29/mysql-5.7.29/include/mysql/psi/mysql_file.h:1129

#4 my_read_charset_file (loader=loader@entry=0x7ffff7ed7270, filename=filename@entry=0x7ffff7ed7320 "/usr/share/mysql/charsets/Index.xml",

myflags=myflags@entry=0) at /export/home/pb2/build/sb_0-37309218-1576675139.51/rpm/BUILD/mysql-5.7.29/mysql-5.7.29/mysys/charset.c:383

6. 压测结果

从测试结果看,单进程单线程,多个协程是“同时”进行的,“并发”量也随着协程个数增加而增加,跟测试预期一样。

这里只测试协程的”并发性”,实际应用应该是用户比较多,每个用户的 sql 命令比较少的。

1

2

3

4

5

6

7

8

9

10

11

12

13

14# ./test_libco 1 10000

id: 0, testcnt: 10000, cur spend time: 1.778823

total cnt: 10000, total time: 1.790962, avg: 5583.591448

# ./test_libco 2 10000

id: 0, testcnt: 10000, cur spend time: 2.328348

id: 1, testcnt: 10000, cur spend time: 2.360431

total cnt: 20000, total time: 2.373994, avg: 8424.620726

# ./test_libco 3 10000

id: 0, testcnt: 10000, cur spend time: 2.283759

id: 2, testcnt: 10000, cur spend time: 2.352147

id: 1, testcnt: 10000, cur spend time: 2.350272

total cnt: 30000, total time: 2.370038, avg: 12658.024719

7. mysql 连接池

用 libco 共享栈简单造了个连接池,在 Linux 压力测试单进程 10w 个协程,每个协程读 10 个 sql 命令(相当于 1000w 个包),并发处理能力 8k/s,在可接受范围内。

1

2# ./test_mysql_mgr r 100000 10

total cnt: 1000000, total time: 125.832877, avg: 7947.048692

压测源码(github)。

mysql 连接池简单实现(github)。

压测发现每个 mysql 连接只能独立运行在固定的协程里,否则大概率会出现问题。

libco hook 技术虽然将 mysqlclient 阻塞接口设置为非阻塞,但是每个 mysqlclient 连接,必须一次只能处理一个命令,像同步那样!非阻塞只是方便协程切换到其它空闲协程进行工作,充分利用原来阻塞等待的时间。而且 mysqlclient 本来就是按照同步的逻辑来写的,一个连接,一次只能处理一个包,不可能被你设置为非阻塞后,一次往 mysql server 发 N 个包,这样肯定会出现不可预料的问题。

libco 协程切换成本不高,主要是 mysqlclient 耗费性能,参考火焰图。

压测频繁地申请内存空间也耗费了不少性能(参考火焰图的 __brk),尝试添加 jemalloc 优化,发现 jemalloc 与 libco 一起用在 Linux 竟然出现死锁!!!

8. 小结

通过学习其他大神的帖子,走读源码,写测试代码,终于对协程有了比较清晰的认知。

测试 libco,Centos 功能正常,但 MacOS 下不能成功 Hook 住 mysqlclient 阻塞接口。

libco 是轻量级的,它主要应用于高并发的 IO 密集型场景,所以你看到它绑定了多路复用模型。

虽然测试效果不错,如果你考虑用 libco 去造一个 mysql 连接池,还有不少工作要做。

libco 很不错,所以我选择 golang

isql 测试mysql连接_[libco] 协程库学习,测试连接 mysql相关推荐

  1. libco协程库源码解读

    2019独角兽企业重金招聘Python工程师标准>>> 协程,又被称为用户级线程,是在应用层被调度,可以减少因为调用系统调用而阻塞的线程切换的时间.目前有很多协程的实现,由于微信内部 ...

  2. libco协程库上下文切换原理详解

    缘起 libco 协程库在单个线程中实现了多个协程的创建和切换.按照我们通常的编程思路,单个线程中的程序执行流程通常是顺序的,调用函数同样也是 "调用--返回",每次都是从函数的入 ...

  3. 微信 libco 协程库原理剖析

    作者:alexzmzheng 同 Go 语言一样,libco 也是提供了同步风格编程模式,同时还能保证系统的高并发能力,本文主要剖析 libco 中的协程原理. 简介 libco 是微信后台大规模使用 ...

  4. linux c++11高性能协程库netco

    目录 一.开源协程库调研 1.golang语言自带协程 2.云风的coroutine协程库 3.腾讯的libco协程库 4.魅族的libgo协程库 二.netco协程库概述 三.netco的实现 1. ...

  5. C/C++协程库libco:微信怎样漂亮地完成异步化改造

    如今,微信拥有月活跃用户8亿. 不可否认,当今的微信后台拥有着强大的并发能力. 不过, 正如罗马并非一日建成:微信的技术也曾经略显稚嫩. 微信诞生于2011年1月,当年用户规模为0.1亿左右:2013 ...

  6. 万字长文 | 漫谈libco协程设计及实现

    libco简介 libco是微信后台大规模使用的c/c++协程库,2013年至今稳定运行在微信后台的数万台机器上,使得微信后端服务能同时hold大量请求,被誉为微信服务器稳定性的基石.libco在20 ...

  7. Libco是一个C/C++协程库,在微信服务中广泛使用

    Table of Contents 协程简介 协程Libco库 libco的特性 PS:CGI框架 PS:Lambda表达式 协程简介 协程这个概念其实在<操作系统>系统里面应该有了解过, ...

  8. 漫谈微信libco协程设计及实现(万字长文)

    欢迎关注作者git博客 1.libco简介   libco是微信后台大规模使用的c/c++协程库,2013年至今稳定运行在微信后台的数万台机器上,使得微信后端服务能同时hold大量请求,被誉为微信服务 ...

  9. C++ 开源协程库 libco——原理及应用

    1 导论 使用 C++ 来编写高性能的网络服务器程序,从来都不是件很容易的事情.在没有应用任何网络框架,从 epoll/kqueue 直接码起的时候尤其如此.即便使用 libevent, libev这 ...

最新文章

  1. [HDOJ3308]LCIS(线段树,区间合并)
  2. docker部署django项目、mysql主从搭建、django实现读写分离
  3. 查看oracle监听服务状态,(总结)Oracle监听服务lsnrctl参数及查询状态详解
  4. 总线全称_一篇文章讲透I2C总线协议
  5. 树莓派入门教程 - 0 - 准备篇 - 0.2 树莓派SSH远程登陆,VNC远程桌面
  6. ArcGISEngine二次开发(5):添加矢量要素
  7. 如何把后缀为.mdf的文件打开
  8. Axure动态显示实时时间
  9. 《还珠格格》《大宅门》取景地将被拍卖:3.6亿起拍
  10. 英语拼读规则28条(必知)
  11. 基于富芮坤fr8016 蓝牙5.0 芯片设计的BLE HID Joystick 游戏摇杆设备
  12. 软件开发公司的提成制度【修订中】
  13. day06 代码实现邮件自动发送
  14. 关于cstring头文件里的函数
  15. 再见了《越狱》——永恒的经典!
  16. KNIME服务器安装配置
  17. DVD-R-01.我自己搜索收集的一些Delphi免杀远控软件源代码(永久免云杀变种)2013-12-14 11:53:19...
  18. 计网 ---第7章 广域网
  19. PL2303串口无法识别
  20. python和财务管理的区别与联系_财务会计、财务管理和管理会计有什么区别和联系?...

热门文章

  1. web开发项目,web前端CSS全局样式,面试必问
  2. 「一本通 6.4 例 4」曹冲养猪(CRT)
  3. Mysql常用命令(二)
  4. Leetcode: Counting Bits
  5. Node.js之HTPP URL
  6. WordPress 博客文章时间格式the_time()设置
  7. Bootstrap系列 -- 11. 基础表单
  8. .NET Framework 工具
  9. SQL 语句时间比较
  10. [转]项目失败的经验