参加比赛初期, 我对其评测方式有些不理解, 之后看过了benchmark源码, 有了大概的了解, 以后自己写的程序也是要测试性能的, 趁这个机会把评测工具的代码看看吧, 也许能用的上.

所以这篇帖子的重点不是讨论怎么优化程序的, 重点是阅读了benchmark程序后与大家分享一下. 最后也把我这些天自己的优化与大家分享下, 提供个思路吧.

缺少朋友组队, 你若是有兴趣, 何不一起(估计大佬都组完了).

大赛概述

因为各个程序的部署形式不同, 不能只单独看源码, 一定要结合真实的架构来看. 下面 是架构图:

system architecture

这个图中只有我们自己的程序, 外界请求时, 使用Spring Cloud Consumer Service的 接口进行调用.

图中的4个Agent是用来进行协调的, 左侧的Agent用来选择合适的合适的服务提供者, 假如Consumer Service侧不是瓶颈的话, 可以通过增加右侧机器来快速提高吞吐量.

在大赛中, 我们需要做的就是重写Agent, 使其能够保证高并发.

benchmark程序

benchmark程序中, 主要使用了Python以及Shell脚本, 大概有这么几个功能:

  1. 部署及运行etcd, 以及service(使用docker)
  2. 运行wrk评测性能
  3. 日志文件的收集, 打包.

运行benchmark的程序并不是真正的评测机器, 真正的评测机器, 需要ssh进行登录, 远程操作.

1
2
3
4
5
def __run_remote_script(self, script):  # 远程执行指令, 值得借鉴.
ssh = 'ssh -T -o StrictHostKeyChecking=no {}@{}'.format(
self.workspace.remote.user, self.workspace.remote.hostname)
bash = split(ssh) + ['bash']
return self.__run_script(bash, script)

不论是docker pull操作, 还是docker run操作, 都是在远程的服务器上被调用.

benchmark程序启动流程

读取配置文件 => 请求mock服务器获取待评测队伍的镜像 => docker pull构建etcdservice => 启动etcd以及service => 使用wrk压力测试

docker run到底做了什么

这要从我们要提交的镜像开始说起. TL;DR 没兴趣的直接跳到下一部分吧.

我们需要提交的镜像如何构建. 以agent-demo的Dockerfile为例

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
# Builder container
FROM registry.cn-hangzhou.aliyuncs.com/aliware2018/services AS builder

COPY . /root/workspace/agent
WORKDIR /root/workspace/agent
RUN set -ex && mvn clean package

# Runner container
FROM registry.cn-hangzhou.aliyuncs.com/aliware2018/debian-jdk8

# 从service中拷贝Service程序
COPY --from=builder /root/workspace/services/mesh-provider/target/mesh-provider-1.0-SNAPSHOT.jar /root/dists/mesh-provider.jar
COPY --from=builder /root/workspace/services/mesh-consumer/target/mesh-consumer-1.0-SNAPSHOT.jar /root/dists/mesh-consumer.jar

# 其实是由上面的mvn package操作生成的.
COPY --from=builder /root/workspace/agent/mesh-agent/target/mesh-agent-1.0-SNAPSHOT.jar /root/dists/mesh-agent.jar

COPY --from=builder /usr/local/bin/docker-entrypoint.sh /usr/local/bin
COPY start-agent.sh /usr/local/bin

RUN set -ex \
&& chmod a+x /usr/local/bin/start-agent.sh \
&& mkdir -p /root/logs

EXPOSE 8087

# 来自service中, 启动service之后, 调用start-agent.sh, 启动对应的agent
ENTRYPOINT ["docker-entrypoint.sh"]

由此Dockerfile构建的其实是包含了consumer, provier, 以及agent-demo的一个 镜像, 凭借传递的不同参数进行启动. 例如docker run <image> provider-small来启动 provider-samll服务, 以及provider-small对应的agent服务.

评测时, 我们通过consumer, provider-small, provider-medium, provider-large进行程序的启动.

Wrk的原理及使用

我记得今年2月份, 姐姐过来找我有没有什么压力测试软件, 当时就找到了wrk, 代码量不多, 适合阅读完之后和面试官吹, 哈哈.

https://github.com/wg/wrk

主要的逻辑代码在src/wrk.c里面, 里面初始化多个线程, 每个线程用一个epoll或是 select来监听多个socket连接.

请求的脚本使用lua写的, 由C语言来调用, 示例脚本在scripts目录里.

C语言调用Lua脚本: Simple Lua Api Example

使用epoll做压力测试能有效提高并发

某政务网站性能优化

这是个简单例子, 感觉可以参考他的测试方案和测量指标.

我所做的一些优化

使用Nginx作为consumer agent

从功能上看, consumer-agent的定位就是一个简单的反向代理工具. 完全可以用nginx 进行替代, 最终我设置了不同的upstream权重. 简单进行反向代理.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
upstream cluster {  # 真实的ip并没有固定, 需要在nginx启动前完善.
server <ip>:<port> weight=1;
server <ip>:<port> weight=2;
server <ip>:<port> weight=3;
}

server {
listen 20000;
server_name localhost;
# listen [::]:80 default_server;

access_log /root/logs/access.log;
error_log /root/logs/error.log;

location / {
proxy_pass http://cluster;
}
}

使用golang提高provider agent的并发

这个优化就谈不上什么很特别了. 借助了golang的高并发特性, 虽然提高了并发, 但是需要自己写dubbo的相关接口, 目前我的dubbo接口采用的是短连接形式, 每次调用服务 都要建立连接, 十分的慢, 当前成绩也就200多分吧(希望大佬们不要笑话).

对比赛尊重一点, 我的代码等结束后再开源吧.

6-3 使用长连接进行dubbo接口的调用

分数到达了3000, 目前基本进入了调参阶段 6-3结果

6-10 在Nginx的反向代理中使用长连接

主要是在Nginx反向代理中增加了长连接的使用, 参考了如何保持Nginx反向代理时的连接, 成绩到达了4500, 还是一点点进步吧.

6-10结果

我的代码

go-agent