Swoole-rpc实践小结

4次阅读

共计 2051 个字符,预计需要花费 6 分钟才能阅读完成。

  1. 背景介绍
    公司内部基础服务,基本上每个业务,每个页面都会调用的接口。随着业务的增加,在 rpc 服务提供之前,http 接口每天的调用量有 1 亿次。原先有 4 台阿里云服务器(8C+32G),3 台 nginx+ 1 台 crontab,后来增加到 5 台 nginx。业务高峰时负载较高,时常超过运维规定的报警峰值。
    http 接口:php 5.6.7 + yaf 2.3.5 + redis + MySQL

    以其中标签业务为例:
    最短响应 (ms) <10ms <50ms
    2.5 99.9% 0.1%
    7.5 73% 27%
  2. RPC 接口
    php 7.1.9 + swoole 2.0.8 + zookeeper + redis + MySQL 等
    基于 Swoole,实现 dubbo 的主要功能:服务注册 + 服务发现 + 监控 + 服务提供 + 服务消费, 大致结构如下:

    数据流:
  3. 效果

    服务器 配置 数量
    http 8C+32G 5
    rpc 8C+32G 2

    注:redis 与 mysql 资源都是一样的。

    使用 rpc 接口后数据与效果对比:
    数据量 最短响应 (ms) <1ms <10ms <50ms
    http 读 n 2.5 0% 99.9% 0.1%
    http 写 m 4.0 0% 74% 26%
    rpc 读 40n 0.3 98% 1.9% 0.1%
    rpc 写 m 0.9 28% 62.5% 9.5%

    rpc 读接口详细详细时间分布

    服务器负载均降到 1 以内,业务高峰期不超过 2

    线上运行情况:

  4. 踩过的坑
    4.1. 请求丢失(万分之几,周期性)
    这个在开发初期,模拟线上业务长期压测的过程中出现。总是有周期性的丢包。联系过 swoole 作者,想让韩大帮忙看看解决,后来自己解决了。因为在 worker 进程中加了 SwooleTimer::tick(),想做心跳,检查 worker 状态 + 维护 tcp 连接,去掉了就没有丢过包。
    4.2. 心跳如何保持
    worker 进程里加心跳貌似会影响 swoole 的运行机制,通过对单起注册进程,定期对 worker 发送消息,worker 收到消息后检查维护。
    4.3. 变量如何传递($_SERVER, 调用链追溯)
    专门设置一个环境变量的参数,传递到下一级请求。
    4.4. 连接数增加(worker_num* 数据库数量)
    这是个大问题。我们的 php-fpm 调用 mysql 采用的短连接,每次请求完了会释放。rpc 使用的是 swoole 常驻进程,连接 mysql 使用的是长连接,连接数 =worker_num* 数据库数量,结果 mysql 服务器的连接数暴增,后通过 mycat 解决。
    4.5. 状态监控 (worker_num, active_worker_num)
    通过对单起注册进程,定期对 worker 发送消息维护。
  5. 对比与探索
    现在比较流行的 PHP 运行模式是 LNMP, 其中 PHP 以 PHP-FPM 模式运行。PHP-FPM 是多进程模式,master 进程管理 worker 进程,worker 进行实际运行代码处理请求。
    5.1. PHP-FPM 模式的几个问题:
    5.1.1. 没有连接池:PHP-FPM 模式下,注定一个请求的生命周期只有 1 次。也就是说,从 FPM 请求到请求,解析 PHP 脚本,FPM 的 Zend 虚拟机分配资源执行,到最后的处理结束,PHP-FPM 会回收这次请求的所有资源。所以高并发业务下,网络的处理会有劣势。
    5.1.2. 对单个进程没有多线程的程序来说,如果频繁的有 http 接口的请求,服务容易被 block。
    5.2. 基于 swoole 的 RPC 方案,可以有效避免 FPM 模式的两个缺陷。
    5.2.1. 维护连接池: worker 常驻进程,可以保持对资源连接的连接池,并通过定期心跳维护连接保持。
    5.2.2. 进程内使用协程,对每个资源的连接保存在 SwooleChannel(高性能内存队列)内,不必考虑对资源的过多连接,协程处理请求又不相互影响。不同的资源连接在不同的资源池,不会相互影响,其中一个请求到 IO 时,底层会将 IO 事件注册到 EventLoop,并让出执行权。接口请求并不会在进程里占用 timeout 时间。
    5.2.3. 常驻进程,配置文件及程序初始化加载均可以在初始化阶段(onWorkerStart)完成,省去了请求开始的脚本初始化及请求结束的资源释放,能更快的响应请求。
    5.3. swoole 协程解决的问题:
    5.3.1. 如果遇见 FPM 模式下接口 A 响应时间超时,不影响下一个请求的处理,不影响 worker 的处理能力;
    5.3.2. 资源类的 IO 都是通过协程出让 worker 的主动权,能提高整体的 QPS 能力;
  6. 总结
    基于 swoole 的 tcp/rpc 接口对短平快的接口有更好的表现。http 接口调用 php-fpm 初始化消耗一些时间,使用 swoole 常驻进程可以节省。http 接口请求频繁打开关闭的连接,对服务器造成压力,也可以通过 swoole 常驻进程解决。
  7. TODO
    7.1 swoole 协程对资源调用的 IO 操作有更好的处理,下一步在项目中验证。
    7.2 使用协程抢占式调度器,解决单次请求响应时间过长 / 超时问题。

(刚看了一眼 git tag 记录,第一次上线时间是 2018 年 5 月 22 日,至今上线已有一年有余,至今稳定运行。)

最后,欢迎路过的各路大神指点指正,互相交流。

正文完
 0