设计一个基于flask的高并发高可用的查询ip的http服务
文章目录
结构设计
基础架构为flask+gunicorn+负载均衡,负载均衡分为阿里云硬件负载均衡服务和软负载nginx。gunicorn使用supervisor进行管理。
使用nginx软件负载结构图
使用阿里云硬件负载均衡服务结构图
因为flask app需要在内存中保存ip树以及国家、省份、城市相关的字典,因此占用内存较高。gunicorn的1个worker需要占用300M内存,nginx的4个worker内存占用较小(不到100M),因此占用1.3G的内存(即需要一个2G内存的服务器)。当gunicorn任意一个节点挂断或者升级时,另外一个节点仍然在使用,不会影响整体服务
ip数据库
IP库(也叫IP地址数据库),是由专业技术人员经过长时间通过多种技术手段收集而来的,并且长期有专业人员进行更新、维护、补充。
ip数据库解析查询代码
基于二叉查找树实现
|
|
http请求
提供ip查询服务的GET请求和POST请求
|
|
POST请求需要在请求体中包含类似下面的json字段
|
|
GET请求的形式如:http://127.0.0.1:5000/api/ip_query?ip=165.118.213.9
服务部署
安装依赖库
依赖的库requirements.txt如下:
|
|
安装方法:pip install -r requirements.txt
配置supervisor
vim /etc/supervisor/conf.d/ip_query_http_service.conf
,内容如下
|
|
内容添加完成之后,需要创建stdout_logfile和stderr_logfile这两个目录,否则supervisor启动会报错。然后更新supervisor启动ip_query_http_service进程。
|
|
关于supervisor的常用操作参见最后面的参考资料。
安装nginx
如果是软负载的形式需要安装nginx,编译安装nginx的方法参见最后面的参考资料。
配置nginx
vim /usr/local/nginx/nginx.conf
,修改配置文件内容如下:
|
|
压力测试
做压力测试,选择正确的工具是前提。以下工具中,jmeter运行在windows机器较多,其他工具建议都运行在*nix
机器上。
压力测试工具选择
工具名称 | 优缺点 | 建议 |
---|---|---|
ApacheBench(ab) | 命令使用简单,效率高,统计信息完善,施压机器内存压力小 | 推荐 |
locust | python编写,效率低,受限于GIL,需要编写python测试脚本 | 不推荐 |
wrk | 命令使用简单,效率高,统计信息精炼,坑少,少报错 | 最推荐 |
jmeter | 基于java,Apache开源,图形化界面,操作简便 | 推荐 |
webbench | 使用简单,但是不支持POST请求 | 一般 |
tsung | erlang编写,配置模板较多,较复杂 | 不推荐 |
上述六种工具全部亲身使用过,下面选择ab、wrk、jmeter三种工具简单说明安装使用方法,其他工具的使用方法如有需要,自行google
ab
安装
|
|
常见options
option | 含义 |
---|---|
-r | 当接收到socket错误的时候ab不退出 |
-t | 发送请求的最长时间 |
-c | 并发数,一次构造的请求数量 |
-n | 发送的请求数量 |
-p | postfile,指定包含post数据的文件 |
-T | content-type,指定post和put发送请求时请求体的类型 |
使用
测试GET请求
|
|
测试POST请求
|
|
其中/tmp/post_data.txt
文件的内容为待发送的-T指定格式的数据,在此处为json格式
|
|
wrk
http://www.restran.net/2016/09/27/wrk-http-benchmark/
安装
|
|
常见options
option | 含义 |
---|---|
-c | 打开的连接数,即并发数 |
-d | 压力测试时间:发送请求的最长时间 |
-t | 施压机器使用的线程数量 |
-s | 指定要加载的lua脚本 |
–latency | 打印延迟统计信息 |
使用
测试GET请求
|
|
测试POST请求
|
|
其中/tmp/wrk_post.lua
文件的内容为待加载的lua脚本,指定post的path,header,body
|
|
jmeter
安装
安装jmeter前需要先安装jdk1.8。然后在Apache官网可以下载jmeter,点此下载
使用
以上图片来自一个测试大牛,非常详细,完整的xmind文件下载见:jmeter-张蓓.xmind
jmeter的入门级使用也可以参考最后面的参考资料部分:使用Apache Jmeter进行并发压力测试
压力测试结果分析
wrk GET请求压测结果
|
|
ab GET请求压测结果
|
|
jmeter GET请求压测结果
结果分析
以上三个工具的压测结果大体相同,RPS(Requests per second)大致在3000左右,此时机器配置为4核4G内存,并且gunicorn开了10个worker,内存占用3.2G。单台机器只有3000并发,对于此配置的机器来说,需要进一步分析原因。后续再弄一台机器,负载均衡后能达到5000以上才能满足使用要求。
压力测试注意事项
文件打开数
压力测试时对施压机器的文件打开数一般有要求,远不止1024个open files,需要增加linux系统的文件打开数,增加方法:
|
|
SYN洪水攻击保护
linux系统中有一个参数:/etc/sysctl.conf
配置文件中的net.ipv4.tcp_syncookies
字段。这个字段值默认为1,表示系统会检测SYN洪水攻击,并开启保护。因此压测时,如果发送大量重复性数据的请求,受压机器SYN队列溢出之后启用SYN cookie,导致会有大量请求超时失败。阿里云的负载均衡是有SYN洪水攻击检测和DDos攻击检测功能的,因此在做压力测试时需要注意两点:
- 测试时适当关闭负载均衡机器的 net.ipv4.tcp_syncookies 字段
- 造数据时应该尽量避免大量重复性数据,以免被识别为攻击。
gunicorn简介及调优
关于gunicorn的选择可以参考测试报告:Python WSGI Server 性能分析
在选定gunicorn作为WSGI server之后,需要根据机器选择相应的worker数量以及每个worker的worker-class。
worker数量选择
每一个worker都是作为一个单独的子进程来运行,都持有一份独立的内存数据,每增加或减少一个worker,系统内存明显的成倍数的改变。最初单台机器gunicorn开启3个worker,系统只支持1000RPS的并发。当把worker扩展为9个之后,系统支持3000RPS的并发。因此在内存足够的时候,可以适当增加worker数量。
worker-class选择
可以参考尾部的参考资料中的gunicorn常用settings和Gunicorn 几种 Worker class 性能测试比较这两篇文章。
将gunicorn启动时的worker-class从默认的sync改成gevent之后,系统RPS直接翻倍。
worker-class | worker数量 | ab测试的RPS |
---|---|---|
sync | 3 | 573.90 |
gevent | 3 | 1011.84 |
gevent依赖:gevent >= 0.13。因此需要先使用pip安装。对应的gunicorn启动flask应用的命令需要修改为:
|
|
改进点
改进ip数据库准确性
损失效率换取准确性:使用单一ip数据库会存在一些ip无法查询出结果的情况,并且国外ip一般只能精确到国家。可以平衡几家ip数据库的准确度和覆盖率,当无法查询出准确的地址信息时去查询另外几个ip数据库。
提高单台机器并发量
从发起请求,到WSGI服务器处理,到应用接口,到ip查询每个过程都需要单独分析每秒可执行量,进而分析系统瓶颈,从根本上提高单机并发量。
参考资料
文章作者 Suncle
上次更新 2017-08-16