对于传统的服务端程序 (PHP, FASTCGI等),大多都是通过产生一个请求,有一个进程与之相对应,请求处理结束后,进程销毁释放,所以一些语言都通过常驻进程,线程,线程池等降低资源开销。即使是资源占用最小线程,当并发量超过1K(单台)的时候,操作系统的处理就会开始明显下降,因为有太多的CPU时间都消耗在系统的上下文切换。因此,对于一些性能要求比较高,并发量较大的需求,就需要一套高性能的服务区去支撑。
GITHUB
NGINX+LUA
nginx的lua-nginx-module模块将lua嵌入到nginx,让nginx高效的执行lua脚本,高并发,非阻塞的处理各种请求。
Lua内建协程,这样就可以很好的将一步回调转换成顺序调用的形式。ngx_lua在lua中进行IO操作都会委托给Nginx事件模型,实现非阻塞调用。
每个NginxWorker进程持有一个Lua解释器或者LuaJIT实例,被这个Worker处理的所有请求共享这个实例。每个请求的Context会被Lua轻量级的协程分割,从而保证各个请求是独立的。Ngx_lua 采用“one-conroutine-pre-request”的处理模型,对于每个用户请求,ngx_lua 会唤醒用于执行用户代码处理请求,当请求处理完成这个写成会被销毁。每个协程都有一个独立的全局环境(变量空间),继承于全局共享的、只读的“comman data”。所以,被用户代码注入全局空间的任何变量都不会影响其他请求的处理,并且这些变量在请求处理完成后会被释放,这样就保证所有的用户代码都运行在一个“sandbox”(沙箱),这个沙箱与请求具有相同的生命周期。 得益于Lua协程的支持,ngx_lua在处理10000个并发请求时只需要很少的内存。根据测试,ngx_lua处理每个请求只需要2KB的内存,如果使用LuaJIT则会更少。所以ngx_lua非常适合用于实现可扩展的、高并发的服务。
OPENRESTY 安装
sudo yum-config-manager --add-repo https://openresty.org/yum/cn/centos/OpenResty.repo
sudo yum install openresty
没有 yum-config-manager
命令
执行 sudo yum -y install yum-utils
安装
执行 openresty -h
成功
REDIS 安装
yum install epel-release
yum install redis
// 修改redis 配置
redis-service path/redis.conf &
// 测试是否安装成功
redis-cli
初试-HelloWorld
// 根目录下 创建项目目录
mkdir ~/openresty ~/openresty/logs/ ~/openresty/conf/
// conf 目录 设置 nginx配置
worker_processes 2; #nginx worker 数量 核数2
error_log logs/error.log; #指定错误日志文件路径
pid logs/openresty.pid;
#access_log logs/access.log; #info 日志
events {
worker_connections 10240;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format main1 '[$time_local] $host $remote_addr "$request_uri" '
'$status "$bytes_sent" "$request_time" "$upstream_response_time" '
'"$http_user_agent" "$http_referer" "$http_x_forwarded_for" "$http_cookie" "$uid_set" "$uid_got"';
lua_package_path "lua/?.lua;;";
lua_package_cpath "lua/?.so;;";
#access_log logs/access.log main;
server {
access_log logs/access.log main1;
#监听端口,若你的6699端口已经被占用,则需要修改
listen 6699;
location ~ ^/api/([-_a-zA-Z0-9/]+) {
# 准入阶段 参数校验
#access_by_lua_file lua/access_check.lua;
# 逻辑
content_by_lua_file lua/redis/repos.lua;
}
location / {
default_type text/html;
content_by_lua '
ngx.say("HelloWorld") // helloworld
';
}
}
}
NGINX优化
worker_processes 设置 最好和cpu 逻辑核数一致 (避免上下文切换)
# 查看CPU 逻辑核数
cat /proc/cpuinfo |grep "processor"|wc -l
siege 压力测试
wget http://ftp.tu-chemnitz.de/pub/linux/dag/redhat/el6/en/x86_64/rpmforge/RPMS/siege-2.72-1.el6.rf.x86_64.rpm
sudo rpm -Uvh siege-2.72-1.el6.rf.x86_64.rpm
sudo yum install siege
# 压力测试
# -r 测试轮数
# -c 并发数 siege超过1000会报错
siege http://test.com/api/123 -r 20 -c 2000
并发超过1000 会出错
ab 压力测试
# 只安装 ab工具包
yum -y install httpd-tools
# 测试是否安装成功
ab -V
# 1W 并发 10W 请求
ab -n100000 -c10000 http://test.com/api/123
ab -r # 默认跳过报错
- helloworld接口
总次数 | 并发数 | 失败数 | 成功率 | 最大时长(ms) | 大概率时长(90%) |
---|---|---|---|---|---|
100000 | 100 | 0 | 100% | 17 | 5 |
100000 | 1000 | 0 | 100% | 3043 | 62 |
100000 | 5000 | 0 | 100% | 3342 | 419 |
100000 | 10000 | 0 | 100% | 4180 | 1490 |
100000 | 20000 | 0 | 100% | 5118 | 1650 |
- 读redis 接口
总次数 | 并发数 | 失败数 | 成功率 | 最大时长(ms) | 大概率时长(90%) |
---|---|---|---|---|---|
100000 | 100 | 0 | 100% | 29 | 12 |
100000 | 1000 | 0 | 100% | 8769 | 26 |
100000 | 5000 | 0 | 100% | 15779 | 1577 |
100000 | 10000 | 99 | 100% | 9231 | 3057 |
100000 | 10000 | 105 | 100% | 8803 | 2416 |
100000 | 10000 | 0 | 100% | 8388 | 2343 |
1000000 | 10000 | 1 | 100% | 17535 | 2723 |
100000 | 20000 | 0 | 100% | 21464 | 6463 |
100000 | 20000 | 0 | 100% | 14434 | 6916 |
1000000 | 20000 | 1 | 100% | 10494 | 8520 |
优化
worker_connections 被打满
NGINX最大并发max_clients计算
NGINX作为http服务器时:
max_clients = worker_process * worker_conections
NGINX作为反向代理时:
max_clients = worker_process * worker_conections / 4
由以上计算得,原来最大clients = 2 * 10240 = 20480
在测试并发2W以上,会报连接数被打满。修改worker_connnections为20480,解决此问题。-
lua tcp socket connect timed out
redis 连接超时- 修改方案1-将超时时间设置为20s(原来2s)
结果: 超时操作明显减少 - 修改方案2-使用连接池
两W并发 100W请求下 连接池对比
连接池数 超时数 100 4434 100 3777 1000 4295 1000 2579 10000 2252 10000 5106 发现,加大连接池数并没有减少超时,因此采用加大超时时间+设置100连接池(减少资源消耗)。
2W并发 100W请求 超时数为0. - 修改方案1-将超时时间设置为20s(原来2s)
-
优化后结果
总次数 并发数 失败数 成功率 最大时长(ms) 大概率时长 (90%) 165270 16527 0 100% 9051 4899 104490 10449 0 100% 16109 2515 90590 9059 0 100% 4759 2164 132460 13246 0 100% 8670 3210