最近在研究libco,打算把这用到新模块看能不能提升开发效率,改造访问mysql的模块真的是简单封装下就可以了。不过libco没有协程池之类的东西,这个协程管理起来比较麻烦,所以需要自己写个协程池节省下资源。调研的时候是libco + hredis-vip sync api,写起来真的无脑,但是一个协程要分配一个redisClusterContext, 一个redisClusterContext 最少也有一条连接,1000并发就至少要1000个连接,这种写法意外的可怕,而且对比了hiredis async的api,发现sync模式的api带来的开销也非常大,零散的read write请求会消耗client和server大量的cpu。所以尝试了合并多个请求到一个连接上面,这个改动一举多得,于是啃了hiredis vip的源码,尝试开始改动,中间过程省略….

问题

1
2
3
4
5
6
7
(gdb) bt
#0 0x00007f351a2fb4c2 in _int_malloc () from /lib64/libc.so.6
#1 0x00007f351a2fdfbc in malloc () from /lib64/libc.so.6
#2 0x00007f351b786898 in co_swap (curr=0x0, pending_co=0x1) at co_routine.cpp:599
#3 0x00007f351b7869ae in co_swap (curr=0x11fa710, pending_co=0x12130d0) at co_routine.cpp:637
#4 0x00007f351b78677d in co_yield_env (env=0x12130d0) at co_routine.cpp:566
#5 0x00007f351b78773d in co_cond_signal (si=0x4126e0 <_start>) at co_routine.cpp:1080

malloc 里面Segmentation fault? wtf ? google了下 发现是heap corruption,引用msdn

This typically occurs when an application allocates a block of heap memory of a given size and then writes to memory addresses beyond the requested size of the heap block. Heap corruption can also occur when an application writes to block of memory that has already been freed.

free看了下内存发现足够的,所以就应该是使用了被free的内存了,由于我改动的只有几十行代码,于是不断的注释可疑代码然后运行,最后定位到了co_redisGetReply这个函数,发现只要加上这个函数,程序就会core,于是把相关的指针都printf出来,发现redisContext里面的fd突然为0了,初步怀疑是redisContext被free了,因为我还没做到free相关的东西,协程会一直跑的,所以往redisFree这个函数加了printf,果然发现在core之前这个redisContext已经被free了,然后又继续操作这块内存,等到malloc分配到这块内存的时候就core了。

所以只需要free的时候把连接关联的协程也停止就可以了,这个与libco的使用姿势有关,又废了一番小小的功夫,改完之后简单压了下,合并请求的效果出来了,redis server在8wqps的时候 cpu比以前少了40%。不过各种异常处理还没做,离可用还有距离,坑还是有待填