记swoole数据库连接池消失问题的解决方式
问题背景:
物联网项目saas,为分布式+集群模式.swoole(worker+task+协程) + web + mysql(pool) + 客户server(记为cserver,设备的数据最终会往cserver发送一份).
有个法兰克福客户有三千台设备,想着该地区暂时没有新客户,所以只是单机部署,想着4核8g配置肯定够用了.
周五收到通知,客户的3k设备集体掉线,紧忙分析+处理,此处记下笔记!
分析结果:
根据日志很快速定位到问题所在,为客户所在cserver宕机,导致cserver不能正确处理swoole发送过来的数据.我们的swoole采用同步重发机制,保证数据是及时+稳定送达的.此机制导致短时间内,会有大量的重复信息在数据库中分析+分发,此过程耗尽了连接池中所有连接.而我们的每个数据库连接都为一个协程.
swoole在没有设置max_coroutine参数的情况下,默认每个进程创建协程数量为3k.这就会导致协程数过多而致使task进程异常退出!从而该task对应的数据库连接池也全部断开连接!
当task退出时,manager进程会重新拉起同样pid的task,此task会瞬间接受woker分配给刚才挂掉task所未能完成的包,这种瞬间式的堵塞导致task再次挂掉.manager重新拉起,task再挂掉,如此循环导致manager挂掉,当manager挂掉后,所有的task没有管理进程,当挂掉后无进程能够拉起,所有最后所有task挂掉,所有数据库连接断开.进而使得swoole的master守护进程依然在值守,却不再具备数据接收与处理的能力!即swoole变相挂掉,mysql也不能再重新拉起连接!
解决方案:
-
根据模拟场景的慢日志,添加一些平时未注意的索引
-
调整mysql max_connection参数,默认未224,我此前设置的为1000.当并发太大的时候,1k明显不够用了.
-
调整mysql innodb_buffer_pool_size参数(数据和索引缓存的地方),当你的server只做mysql时,此值设置为内存的70%~75%.因为我是单机设备,设置为40%.
-
适当加大了mysql innodb_log_file_size参数值
-
增大swoole中max_coroutine参数值,按常理,单worker能理想分配1g内存的时候,max_coroutine能设置成1w 乃至10w,因为一个coroutine默认为8k内存.
-
增大swoole中task_worker_num,理想情况下,tasknum可设置为1000.根据你的task处理速度看着调整,我此处设置为50来应对突如其来的高并发.
-
调整swoole中的worker_num,默认该参数为cpu核数,我平时也是这么设置的,明显,当我task_worker_num增大的时候,worker的压力也变大,所以我设置为cpu核数的2倍.
-
swoole_table的初始内存调整,这个设置不能在程序中动态调整,而每个连接id我这边都是在sw_tb中记录,所以初始化的大小需要大于未来几年设备最大量.
-
新增managerStart和managerStop的回调,使得我们能更直观地观测manager的生命周期.
-
对数据库连接池程序进行修改,尽量在抛出异常后妥善退出,而非导致worker/task的exit.
-
保证连接池程序内部参数以接口的形式暴露给swoole,从而我们在可视化界面了解每个task的连接池情况
-
对channel的errcode进行追踪,当errcode为-1的时候,打印error日志,-2的时候发送短信通知/微信魔板通知
-
swoole程序中对数据库连接池进行死链接排查
-
增大mysql的连接最长空闲时间,保证它大于swoole程序中连接池的最长空闲时间.
-
增加应急处理方案,即cserver宕机+swoole挂掉.此处我们的解决方案是,引导设备切换server到另一集群.(此处虽然是单机部署,但是也有个中央服务器是留作调控的. 不过周五此服务器未起作用是因为,中央服务器我这有个调控程序,使得符合条件在若干时间后自动切换到相应服务器x,而此时x不是挂了吗? 所以就有个循环,设备一直处于离线状态!后面我们进一步完善了这个机制)