需求背景

这个需求的出现来源于我们的某业务接口优化中遇到的一个瓶颈:

初期接口在调用php提供的第三方接口时,遇到访问高峰的情况会出现该接口timeout的情况(如下图 平常时接口时延75ms,高峰时到达368ms ),同时服务器上会出现及大量的TIME_WAIT连接数(高峰时 2W左右)。

分析是由于大流量时, 每次接口调用都会调用一次rest api,继而产生大量的tcp短连接导致的。这种不稳定的因素对我们的集群产生了极大的压力,同时接口时延也不稳定的上升。当务之急是减少短连接数,取而代之使用长连接的方式去请求。

同时我也了解到,异构系统间采用http长连接的做法在公司内部php集群并不支持(现阶段公司内部只有少数业务有成熟的长连接方案),而又了解到java服务化方可以提供dubbo协议的接口请求。

因此就决定尝试使用dubbo协议跨过php这一层直接和java服务化做直连,这样既可以减少短连接数,同时也将调用链路变短了一层。

nodeJs直连dubbo的做法

调用dubbo服务的过程主要分以下几步:

首先需要连接上dubbo的zk配置中心(从java服务化那边得到地址),获取你需要的服务的dubbo节点连接信息(包括dubbo服务的host:port,服务名等等)

然后 使用sockets-pool(我们维护的)库 与获得的dubbo节点建立起起socket长连接,并维护在一个连接池队列中
在需要查询对应服务时,从连接池中获取可用连接,执行查询,然后释放该次连接到连接池。

demo代码如下:

var ZD = require('node-zoodubbo');
        var zd = new ZD({
                // config the addresses of zookeeper
                conn: '127.0.0.1:2181',
                dubbo: '2.8.0.2',
                root: 'dubbo'
        });

        // connect to zookeeperzd.connect();
        //zd.getProvider('com.member.query', '1.0.0', function(res, data){//      console.log(res, data);//});// get a invoker with a service path
        var invoker = zd.getInvoker('com.member.query', {
                version: '1.0.0',
                timeout: 2000,
                pool: {
                        max: 100,
                        min: 10,
                        maxWaitingClients: 500       
                }   
        });

        // excute a method with parametersvar method = 'memberInfo';
        var arg1 = {
                $class: 'com.member.memberRequestTO',
                $: {
                        uid: {
                                $class: 'java.lang.String',
                                $: '34625433'
                        }
                }
        };

        invoker.excute(method, [arg1])
        .then(res => console.log(res))
        .catch(e => console.log(e));

node-zoodubbo 连接包实现

查看包地址

关于整个包及连接实现点击这里

带来的效果

采用restapi方式调用黑名单接口时,该接口平常访问时延在27ms左右,高峰时接口时延159ms。

采用dubbo长连接后,黑名单接口请求时延平均在3ms左右,高峰时任然不变。同时由于我们把ip的接口请求也自己本地化实现了一套,接口时延从(平常时75ms,高峰时368ms)降到如下情况

平常时 25ms 提升 300%,高峰时 56ms 提升 660%

结合某业务接口优化谈一谈QPS的提升

优化前对某接口做了差不多5组压测,每波大概20min,集群当时是9台机器,每台机器4个接口进程,集群qps测到 1200就上不去了(单机 130)。当时与运维讨论了很久未果, 甚至怀疑是测试工具的问题。

在压测过程中,我的服务打点时间始终是正常的且只维持在30ms左右,开始怀疑是不是我们node框架koa2的问题,第二天我在单机使用ab测试了空koa框架发现单机可以到达3000qps,因此框架的qps是不成问题的。

然后对接口的ab结果最终单机真实qps为 240.

再次与运维讨论怀疑是负载均衡而非框架的问题。然后又去了解了我们的进程管理工具PM2其实也承担了对请求的负载分发的工作,虽然其底层采用RollIng算法并不是问题,但是 pm2对请求队列的消耗过程并非是 epoll 类似的做法。

之后为了验证这个猜想使用了nginx反向代理到本机的4个端口,每个端口只跑一个进程,这样等于绕开了pm2分发,只由nginx来做负载均衡,发现QPS可以到达


(最终单机真实qps为 437)

至此我们初步确定QPS的部分瓶颈在pm2的请求转发,采用直接走nginx的方式 直接带来了性能 182%的提升