将Nginx与Locust结合进行分布式压测,核心思路是让多个Locust worker节点向同一个Nginx入口发起请求,由Nginx将流量反向代理到后端服务,从而模拟更接近真实用户访问路径的压力场景。这种架构下,Locust master节点负责协调任务分配和统计结果,worker节点各自运行压测脚本,Nginx则作为统一接入层承担连接分发。接下来从脚本编写、节点配置到执行命令逐一展开。

一、Locust分布式压测脚本的核心写法
Locust的压测脚本通常是一个Python文件,继承HttpUser类并定义任务。分布式模式下,master节点不执行任务,只负责收集worker上报的数据,因此脚本需要保证所有worker加载的代码一致。下面是一个针对HTTP接口的Locustfile.py示例,包含用户行为模拟、等待时间和任务权重。
from locust import HttpUser, task, between
class ApiUser(HttpUser):
wait_time = between(1, 3)
@task(3)
def get_articles(self):
self.client.get("/api/articles", name="获取文章列表")
@task(1)
def create_order(self):
payload = {"product_id": 101, "quantity": 1}
self.client.post("/api/orders", json=payload, name="创建订单")
这段代码中,@task(3)表示get_articles被执行的权重是3,create_order权重是1,最终每个用户实例会更频繁地请求文章列表接口。wait_time控制两次任务之间的等待时间,这里设为1到3秒随机。name参数用于在统计报告中聚合不同的URL,避免路径参数不同导致结果分散。分布式执行时,每个worker会按照相同的权重比例生成请求,master汇总后得到总体吞吐量。
如果需要模拟登录态,可以在on_start方法中先调用登录接口并保存Cookie。由于Locust的HttpUser底层封装了requests.Session,Cookie会自动在后续请求中携带。编写时还要注意不要在脚本中使用全局可变状态,因为master和worker加载的是同一份代码,但进程空间独立,全局变量不会跨节点同步。可以通过os.environ或命令行参数来区分节点角色,不过Locust官方已经通过--master和--worker选项处理了角色分配,脚本不用额外判断。
二、Nginx作为统一入口的负载均衡配置
在分布式压测结构中,Nginx可以放在Locust与后端服务之间,也可以作为后端服务的前置代理。如果目标是压测Nginx本身或经过Nginx到达应用,那么Locust请求地址应指向Nginx监听端口。Nginx配置的关键是upstream定义和proxy_pass转发。下面给出一个简单示例,假设后端有两个应用实例,Nginx监听80端口并分发流量。
upstream backend_app {
server 192.168.1.10:8080;
server 192.168.1.11:8080;
keepalive 64;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://backend_app;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
这个配置将发往Nginx 80端口的请求轮询转发到两个后端节点,keepalive 64表示与后端保持64个长连接,降低TCP握手开销。对于压测场景,如果后端服务处理能力较强,而Nginx本身成为瓶颈,可以适当调整worker_processes和worker_connections。在压测开始前,建议先单独测试Nginx的静态页面返回能力,确认其不是误判的瓶颈点。
如果希望模拟不同路径的流量分配,可以利用Nginx的location匹配规则。例如将/api/articles转发到一组后端,将/api/orders转发到另一组,再配合Locust脚本中的不同任务权重,使压力更符合线上业务分布。需要注意的是,当使用proxy_pass时,默认会修改请求头中的Host为后端地址,这可能导致后端应用按错误域名处理。示例中的proxy_set_header Host $host就是保留原始Host,很多Web框架依赖该头进行路由匹配。配置完成后执行nginx -t检查语法,再执行nginx -s reload生效。
三、启动分布式Locust并执行压测
分布式Locust的启动分为master和worker两个角色。首先在控制机上启动master,命令如下。
python -m locust -f locustfile.py --master --host=http://nginx地址或IP
然后在每台worker机器上执行类似命令,指定master节点的IP地址。worker可以是多台物理机,也可以在同一台机器上开启多个进程,但要注意CPU核心数和网络带宽限制。
python -m locust -f locustfile.py --worker --master-host=192.168.1.100 --host=http://nginx地址或IP
启动成功后,打开Locust的Web界面,通常为http://master-ip:8089。在界面中设置并发用户数和每秒启动速率,点击开始即可。master会把任务参数下发给所有worker,worker执行脚本时客户端请求统一指向Nginx地址。压测过程中可以实时查看RPS、响应时间百分位数和错误率。如果某些worker连接master失败,需要检查防火墙是否放行5557和5558端口,以及master的--web-port是否冲突。
命令行无界面模式也常用于CI流程,例如通过--headless --run-time 10m --users 2000 --spawn-rate 100执行一次10分钟的压测。这种方式便于把结果导出为CSV或接入监控。另外,所有worker节点上的Locust版本必须一致,否则可能出现协议不兼容导致统计异常。压测结束后,可以下载报告或使用--csv参数自动保存统计数据。
四、常见问题与性能调优建议
第一个常见问题是worker节点资源打满但RPS上不去。此时应检查Nginx的worker_connections是否足够,以及后端应用是否已经达到处理上限。可以通过nginx -T查看生效配置,确认没有因为默认的1024连接数限制导致排队。增大Nginx的worker_processes为CPU核心数,并调整worker_connections到合适值,例如events { worker_connections 4096; }。同时,后端应用的连接池大小、数据库连接数也需要同步提升,避免刚过Nginx就卡在应用层。
第二个问题是Locust的RPS统计与后端实际日志不一致。这是因为Locust统计的是客户端发起请求到收到响应的时间,而Nginx和后端日志可能只记录接入时间。如果Nginx有重试或长连接复用,数量会有轻微差异。排查时可以在Nginx的access_log中记录request_time和upstream_response_time,对照Locust的报告定位延迟发生在哪一层。
第三个问题是压测脚本中使用了同步阻塞操作。Locust基于gevent协程,如果任务函数里包含time.sleep等阻塞调用,会阻塞整个worker的协程调度,导致并发能力急剧下降。应使用gevent.sleep或直接依赖wait_time。对于数据库查询等IO操作,可以考虑使用异步驱动或放到线程池中执行,但这会引入额外的复杂度,一般情况下HTTP压测不需要这样做。
还有一点是压测数据清理。分布式压测会产生大量真实或模拟数据,如果后端是生产环境,务必先确认数据隔离。测试环境也要在压测后清理测试订单、用户等记录,避免影响后续功能验证。对于只读接口,压测风险较低,但写接口要考虑幂等性和数据回滚。