Octavia API接口慢问题排查引发的思考

原创 ohhahali 360云计算 2020-07-08

女主宣言

文本梳理了Octavia API接口访问慢问题的排查过程和解决方案,并对排查过程中涉及到的相关知识点进行了梳理,希望日后遇到类似的问题可以有所借鉴和参考。

PS:丰富的一线技术、多元化的表现形式,尽在“360云计算”,点关注哦!

Octavia API接口慢问题排查引发的思考

1

问题背景

Octavia是为openstack集群提供高可用的负载均衡解决方案,它对外提供REST API来创建业务访问的VIP,后端采用Haproxy+LVS来提供负载均衡服务,将业务请求自动分发给真实服务器。


在我们实际的使用中,Octavia对外提供的REST API接口的访问响应时间存在0.2s-50s之间的较大波动,造成VIP可能无法正常创建和查询。

Octavia API接口慢问题排查引发的思考

Octavia服务架构


Octavia API接口慢问题排查引发的思考

上图是我们Octavia服务使用的部署模式,通过keepalived vrrp实现高可用,haproxy后端挂载多个Octavia API服务节点。

2

问题排查过程

抓包


API接口访问慢,首先需要知道请求的时间具体消耗在哪个步骤。对接口的访问请求进行抓包,并观察请求过程中数据包的传输情况。由于接口请求较多,通过添加自定义头部信息对请求进行区分,并使用http header过滤抓包结果。发现时间消耗较长的请求,存在数据包重传现象。

Octavia API接口慢问题排查引发的思考

分析


1)时间消耗

从抓包情况来看,client访问haproxy很快得到响应,时间消耗都在haproxy与octavia-api之间的交互,排除haproxy问题。

2)物理硬件/机器负载/网络抖动

查看服务器网卡不存在丢包和错包;haproxy与octavia-api都属于同一网段,也不存在网络抖动导致丢包;查看三台octavia-api机器负载不高,连接数等没有异常。

3)应用程序层面

从抓包现象上看,octavia-api没有响应syn或ack,查看9876端口连接情况。

netstat -s |grep -i listen  #发现两个数值都在增长
1173805 times the listen queue of a socket overflowed1175909 SYNs to LISTEN sockets dropped

增长的两个数值涉及到server端在连接建立过程中的两个队列:


半连接队列(syn queue):用来保存处于SYN_SENT和SYN_RECV状态的请求。

队列大小:max_qlen = 2^max_qlen_log(linux-3.10.0)


全连接队列(accept queue):用来保存处于established状态,但是应用层没有调用accept取走的请求。

队列大小:min(backlog, net.core.somaxconn)


当server端在建立连接过程中,收到client发出的syn或者ack包时,都要对accept队列是否溢出进行判断,溢出则会引起以上两个数值的增长。

部署环境接口压力并不大,为何会引起accept队列溢出呢?通过ss命令来查看当前server端对应accept队列的大小。

ss -ntlp|grep 9876
LISTEN     6     5     10.145.69.9:9876                     *:*                   users:(("octavia-api",pid=10209,fd=4))

6和5分别表示Recv-Q和Send-Q,Recv-Q表示accept队列等待用户调用accept的完成3次握手的socket,Send-Q表示accept队列实际的大小。从数值看出,server端的accept队列确实存在溢出情况。

队列溢出后,server端如何处理此时收到的syn或者ack呢?由以下内核参数来决定:

/proc/sys/net/ipv4/tcp_abort_on_overflow

0表示直接丢弃ack,client会进行重传,重传次数根据内核参数tcp_synack_retries决定;1则直接发送rst将连接断开。


在测试环境将该值改为1后,请求被立即断开,证明重传server端队列溢出有关。

3

解决方案

确定是server端连接队列溢出导致重传后,需要修改accept队列的大小来解决。

第2部分中我们看到accept队列长度等于5,somaxconn默认为128,这说明octavia-api 服务端listen创建时传入的backlog值为5。该值过小,导致accept队列很容易就会溢出。

那么Octavia的backlog值来源于哪里呢?我们需要查看它代码的具体实现来找到答案。

octavia-api的启动逻辑中调⽤了wsgiref库⾥的simple_server.py中的make_server⽅法来创建⼀个WSGI server。具体逻辑如下:

simple_server.make_server(host, port, app) -> WSGIServer() -> server_bind -> BaseHTTPServer.HTTPServer.server_bind -> SocketServer.TCPServer.server_bind

Octavia API接口慢问题排查引发的思考


图里看到SocketServer.TCPServer类中的server_activate方法listen函数传入的self.request_queue_size为固定值5,这就是octavia-api backlog值的来源。因此要改动该值,需要修改request_queue_size值的大小。

4

问题后续

经过一番排查确认并修改request_queue_size值大小后,查看octavia-api的backlog值已经为128。访问octavia-api发现数据包已无重传现象,accept队列无溢出,但访问速度仍然没有明显提升。


再次抓包发现连接正常,但octavia-api回包较慢,应该是应用层请求处理不过来导致。octavia-api只有单进程在处理请求,无法响应较多的接口调用。


结合openstack其他项目的处理方式,修改octavia-api WSGI server的创建方式。不再使用wsgiref库,改用openstack的oslo_service库来创建WSGI server,增加设置进程数的参数来设置octavia-api的进程个数(默认与cpu process个数相同),且oslo_service的WSGI server默认backlog值为128。经过此次修改后,接口响应速度提升至1s内。


另外,也可以使用httpd的mod_wsgi与Octavia配合部署提升其处理能力,毕竟wsgiref是官方给出的一个实现了WSGI标准用于演示用的简单Python内置库,并不建议在线上部署使用。


上一篇:iptables(下)规则


下一篇:python搜索关键词自动提交