前言
昨天突然之间想到,django 的请求耗时有点,有没有办法进行优化,并且提高其并发性呢,于是去网上一搜,发现好多都是基于 uWSGI + Nginx + Linux 内核的部分设置来进行优化的。所以今天我就写下其中的部署过程以及踩到的坑。
1. uWSGI
什么是WSGI?什么又是uWSGI?
通过搜索,可以知道,其实 WSGI(Web Server Gateway Interface) 是一个协议,这个协议是位于 Application 以及服务器之间,只要遵循这个协议,基于 WSGI 的应用可以跑在任何一种服务器上。而 uWSGI 这是基于 C 实现 WSGI 的服务器。注意这句话,后面遇到的坑就是跟 uWSGI 有关的。
WSGI 原理
在廖雪峰大神的网站上可以简单的了解到 WSGI 的原理以及流程是怎么样的,以下我再来简单说说: 首先一个应用要实现 WSGI 协议,主要就是应用在方法内要处理两个东西, 一个是 environ, 一个是回调函数 start_response,environ 是一个包含了所有 HTTP 请求信息的dict对象,而 start_response 则是发送 HTTP 响应的函数。来看下大神的例子
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return '<h1>Hello, web!</h1>'
这个 application 很简单, 就是直接传 response code 和 header 到 start_response 中,然后返回 response 的 body 内容。
那如果是 Django 的 application 呢,我查了下源码,他的 application 是一个 WSGIHandler 对象。整个处理流程也不长,源码如下:
class WSGIHandler(base.BaseHandler):
request_class = WSGIRequest
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.load_middleware()
def __call__(self, environ, start_response):
set_script_prefix(get_script_name(environ))
signals.request_started.send(sender=self.__class__, environ=environ)
request = self.request_class(environ)
response = self.get_response(request)
response._handler_class = self.__class__
status = '%d %s' % (response.status_code, response.reason_phrase)
response_headers = list(response.items())
for c in response.cookies.values():
response_headers.append(('Set-Cookie', c.output(header='')))
start_response(status, response_headers)
if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
response = environ['wsgi.file_wrapper'](response.file_to_stream)
return response
注意:内置函数 call , 在 WSGIHandler()(environ, start_response) 的时候调用
根据源码可以知道步骤如下:
- 根据 environ 设置脚本前缀
- singals 中的 request_started ( Signal 对象) 发送 environ
- 通过 request 获取 response
- 获取 response 的 status 以及 header , 调用 start_response 方法
- 返回 response
接着 server 要怎么处理呢,看下下面摘抄的代码。
# server.py
# 从wsgiref模块导入:
from wsgiref.simple_server import make_server
# 导入我们自己编写的application函数:
from hello import application
# 创建一个服务器,IP地址为空,端口是8000,处理函数是application:
httpd = make_server('', 8000, application)
print("Serving HTTP on port 8000...")
# 开始监听HTTP请求:
httpd.serve_forever()
创建 server 的时候, 在 make_server 方法内传入 server 的 ip, 端口, 处理的应用是啥就行了。
安装 uwsgi
Linux 下安装 uwsgi 比较简单,在有 pip 的情况下直接 pip install uwsgi 就行了
Windows 下比较麻烦, 因为 windows 下 python 的 os 模块 没有 uname() 方法 ,所以需要通过修改源码进行安装。再者 uwsgi 是用C语言写的,需要编译,要用 MinGW 来编译(注意了,当初我以为 git 里面的 MinGW64 能用,事实上是不能的,因为没有 GCC 编译器,只能用官网的方法来进行安装)。
先下载源码,然后找到 uwsgiconfig.py 文件中,首先 import platform 后,将 os.unam() 都改为 platform.uname()。 昨天我用 MinGW 来安装还是报错,今天上网查了下,发现原来是 MingGW 还是不支持 Windows ......
uwsgi 配置
要配合 nginx 使用的话,需要两个配置文件,一个是启动配置文件,一个是 uswgi_params 文件(用来建立 wsgi - nginx 的请求参数映射关系的,如果这个不存在的话,会出现 502 bad gateway 错误)。
启动文件的配置如下(我将它命名为uwsgi.ini)
[uwsgi]
# 设置这个值以后,可以用localhost:8888访问uwsgi
http = :9999
# socket 连接,指的是nginx反向代理的时候会用这个端口
socket = 127.0.0.1:8888
# Django 项目根目录
chdir = /home/ubuntu/web/mini-program-admin/simama_mall
# Django wsgi 文件
wsgi-file = simama_mall/wsgi.py
# 最大进程数
processes = 2
# 每个进程的最大线程数
threads = 2
# 监控台地址
status = 127.0.0.1:10000
# 是否清除环境
vacuum = true
# 日志地址
daemonize = /home/ubuntu/web/mini-program-admin/simama_mall/uwsgi.log
# pid文件 用于 uwsgi --stop uwsgi.pid (关闭服务或者重启,重启就是把 --stop 改为 --reload)
pidfile = /home/ubuntu/web/mini-program-admin/simama_mall/uwsgi.pid
uswgi_params 配置文件是这样的(文件名就是uswgi_param)
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param UWSGI_SCHEME $scheme;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
启动
启动命令: uwsgi --ini uwsgi.ini
重启:uwsgi --reload uwsgi.pid
停止:uwsgi --stop uwsgi.pid
2. nginx
在做了上述的 uwsgi 设置之后,我们还需要对 nginx.conf 进行修改,修改如下
worker_processes 2; #指定开启2个worker进程
worker_rlimit_nofile 20000; # 此指令的值将覆盖ulimit的值,用于修改当前worker运行用户的最大文件打开数值
events {
worker_connections 20000; #设置worker进程最大打开的连接数
multi_accept on;
}
http {
keepalive_timeout 0; # 高并发下设为0 上传文件等长连接要保持连接
upstream test-django {
# 对应 uwsgi.ini 的 socket 参数
server 127.0.0.1:8888;
}
server{
location / {
uwsgi_pass test-django;
uwsgi_connect_timeout 600;
uwsgi_read_timeout 600;
# 这里需要include uwsgi_params 文件,否则会有 502 错误
include /home/ubuntu/web/mini-program-admin/simama_mall/uwsgi_params;
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 就可以了
nginx.conf 位置: /etc/nginx/nginx.conf
重启命令:service nginx restart
3. Linux TCP 优化
有两种方法对 Linux 内核进行修改:
1. 逐个配置文件修改
# 修改允许等待中的监听
echo 50000 >/proc/sys/net/core/somaxconn
# 修改tcp连接快速回收
echo 1 >/proc/sys/net/ipv4/tcp_tw_recycle
# 修改tcp连接重用
echo 1 >/proc/sys/net/ipv4/tcp_tw_reuse
# 不抵御洪水攻击
echo 0 >/proc/sys/net/ipv4/tcp_syncookies
2. 修改 /etc/sysctl.conf,然后执行 sysctl -p,具体增加内容如下
net.core.somaxconn = 20000
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_syncookies = 0
4. ab 测试
进行了上面的修改之后,我用了apache的ab.exe 进行压力测试,结果........
Benchmarking test.ranking.pub (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests
Server Software: nginx/1.10.3
Server Hostname: test.ranking.pub
Server Port: 80
Document Path: /goods/consume
Document Length: 170 bytes
Concurrency Level: 100
Time taken for tests: 8.681 seconds
Complete requests: 1000
Failed requests: 0
Non-2xx responses: 1000
Total transferred: 387000 bytes
HTML transferred: 170000 bytes
Requests per second: 115.20 [#/sec] (mean)
Time per request: 868.081 [ms] (mean)
Time per request: 8.681 [ms] (mean, across all concurrent requests)
Transfer rate: 43.54 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 8 7.8 15 31
Processing: 0 817 148.7 860 882
Waiting: 0 449 247.0 453 875
Total: 16 825 148.6 860 891
Percentage of the requests served within a certain time (ms)
50% 860
66% 875
75% 875
80% 875
90% 876
95% 881
98% 881
99% 891
100% 891 (longest request)
测试结果并不是太好,感觉应该因为服务器性能太差(腾讯云的学生优惠服务器)
PS : 这是我的第一篇博客,上面的内容有部分参考网上的博客,如果写的不好,或者有什么疑问或者问题,大家可以在下方指出,谢谢大家。