0x0 了解uWSGI*
可能有的同学对UWSGI比较陌生,更多是知道NGINX,但是我们的服务器一开始的时候是先使用了uWSGI,后来才加上NGINX的。
uWSGI,是一个对WSGI协议的实现的应用,所以我们先来了解一下什么是WSGI。
WSGI,Web Server Gateway Interface,也叫做the Python Web Server Gateway Interface,显然是为Python所准备的web服务器网关接口,作用是在协议之间进行转换。目前很多框架都自带了uWSGI的实现,如Flash、Django、webPy等,但是自带的实现性能不敢恭维,多用作测试,发布的话需要自己重新配置。
简单点说,WSGI就是web服务器和web应用之间的桥梁:
其作用还可以用一副漫画说明:
WSGI的作用此时应该已经理解了,总结一下:
- WSGI有两方:“服务器”或“网关”一方,以及“应用程序”或“应用框架”一方。服务方调用应用方,提供环境信息,以及一个回调函数(提供给应用程序用来将消息头传递给服务器方),并接收Web内容作为返回值。
- 所谓的 WSGI中间件同时实现了API的两方,因此可以在WSGI服务和WSGI应用之间起调解作用:从WSGI服务器的角度来说,中间件扮演应用程序;而从应用程序的角度来说,中间件扮演服务器。
- “中间件”组件可以执行以下功能:
- 重写环境变量后,根据目标URL,将请求消息路由到不同的应用对象。
- 允许在一个进程中同时运行多个应用程序或应用框架。
- 负载均衡和远程处理,通过在网络上转发请求和响应消息。
- 进行内容后处理,例如应用XSLT样式表。
差不多说完WSGI,接下来引入一下uwsgi,可能这时候有人迷惑了,一下大写一下小写,实际上这两个是不同的:uwsgi与WSGI一样是一种通信协议,是uWSGI服务器的独占协议,用于定义传输信息的类型(type of information),每一个uwsgi packet前4byte为传输信息类型的描述,与WSGI协议是两种东西,据说该协议是fcgi协议的10倍快。我们的主角uWSGI是一个web服务器,实现了WSGI协议、uwsgi协议、http协议等。
0x1 部署uWSGI
uWSGI的部署非常简单,直接在python环境下安装即可:pip install uwsgi
新建 test.py:
def application(env, start_response):
start_response('200 OK', [('Content-Type','text/html')])
return [b"Hello World"]
运行uWSGI:uwsgi --http :8001 --wsgi-file test.py
此时在 http://127.0.0.1:8001 应该已经有Hello World的输出了,说明安装成功。
uWSGI可以配置多进程多线程,这个在后面一起讲。
0x2 了解NGINX
其实一开始的时候,我们并不想部署NGINX,觉得直接用python的runserver+uWSGI跑足以,但是随着访问数量的增多,甚至我们组内的测试使用都会出现卡顿,这时我们觉得是应该学习一下了。
NGINX大家非常熟悉,但是对于更多JAVA后端的同学来说,APACHE应该是早有耳闻,APACHE和NGINX一样是一种WEB服务器,找到一张对比图:
web服务功能 | NGINX | APACHE |
---|---|---|
方向代理 | 支持反向代理 | 好 |
Rewrite | 非常好 | 非常好 |
fastCHI(网关接口) | 好 | 差 |
热部署 | 支持 | 不支持 |
系统压力比较 | 很小 | 小 |
稳定性 | 非常好 | 好 |
安全性 | 一般 | 好 |
静态文件处理 | 非常好 | 一般 |
动态文件处理 | 好 | 非常好 |
虚拟主机 | 支持 | 支持 |
内存消耗 | 非常小 | 很大 |
扩展资料 | 很少 | 非常多 |
两者最核心的区别在于:
APACHE是同步多进程模型,一个连接对应一个进程,而NGINX是异步的,多个连接(上万级别)可以对应一个进程。
所以一般来说,需要性能的WEB服务,大多使用NGINX,而需要稳定的更考虑APACHE,后者的各种功能模块实现都比前者更加完善,配置更多。更加通用的方案是:前端NGINX抗并发,后端APACHE集群,组成配合。
像是淘宝、支付宝、迅雷、新浪博客等高并发的网站,都是用的是NGINX作为WEB服务器。
考虑到我们的实际情况:
- 服务器配置低:2C4G
- 静态资源请求多:加载登陆界面和个别界面需要加载背景图片
- 需要频繁启动:网站版本迭代更新快
- 对同时连接数量要求高
- 可能需要反向代理
根据上面几个特点,我们选择了NGINX作为web服务器。
0x3 部署NGINX
接下来以我们运行CentOS的aliyun的2C4G的ECS服务器为环境进行介绍。
首先,需要安装NGINX,在CentOS下直接 yum install -y nginx
即可。
NGINX的配置需要在/etc/nginx/nginx.conf
中修改,这个等下一起说。
0x4 NGINX和uWSGI建立连接
uWSGI对于动态资源的处理很好,NGINX对于静态资源的处理很好,所以通常django应用会结合NGINX和uWSGI一起作为web服务端。NGINX抗高并发,接收所有的请求,对于静态资源的请求直接自身处理,对于动态资源的请求再转发给uWSGI处理。接下来详述我们的NGINX配置:
user root; # 可能要给予NGINX进程以root执行的权限才能对socket之类的文件进行改写
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
# mysite_nginx.conf
events {
worker_connections 1024; ## Default: 1024
}
http
{
server_tokens off;
autoindex off;
access_log off;
include mime.types;
default_type application/octet-stream;
proxy_hide_header X-Powered-By;
# 开启gzip压缩更快传输静态资源
gzip on;
gzip_min_length 1k; # 对请求的静态资源的大小设置阈值,小于1k的资源经过压缩反而会变大
gzip_buffers 4 16k;
gzip_http_version 1.1;
gzip_comp_level 9;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/javascript application/json;
gzip_disable "MSIE [1-6].";
gzip_vary on;
include /etc/nginx/mime.types;
# the upstream component nginx needs to connect to
# 在这里 NGINX 负责连接的web服务端是django应用程序,是NGXIN的上游upstream
upstream django {
# for a file socket
# 这里的server使用的是sock文件,代替使用服务器端口
server unix:///your/path/to/backend.sock;
# server 127.0.0.1:8001; # for a web port socket (we'll use this first, but replace it later)
}
# configuration of the server
server {
# the port your site will be served on
listen 80;
listen [::]:80;
# the domain name it will serve for
server_name your.ip.address; # substitute your machine's IP address or FQDN
charset utf_8_sig; # utf_8_sig 对中文的支持最好
include /etc/nginx/default.d/*.conf;
# max upload size
client_max_body_size 75M; # adjust to taste
# Django media
location /media {
alias /your/path/to/media; # your Django project's media files - amend as required
}
location /static {
# 注意这里我们尝试使用root来定义,请求的静态资源是直接拼接在下面的路径下的,使用root表示所在的根目录,所以如果网站的静态资源都存在于/frontend/static中,下面的root路径应该写/frontend文件夹的路径而不是/static的路径,比如我们的登陆界面北京的连接为:http://website/static/img/bgd.cc940dc6.png,就是将/img/bgd.cc940dc6.png拼接到root后面进行请求
# 当然你也可以用上面的alias的别名方式,直接替换请求的路径
root /your/path/to/static‘s/parent; # your Django project's static files - amend as required
# 另一种写法
# alias /your/path/to/static;
break;
}
# Finally, send all non-media requests to the Django server.
location / {
uwsgi_pass django; # 设置将除静态资源外的请求发送给谁,这里的django是上面的upstream的django
uwsgi_read_timeout 360s; # 设置超时
uwsgi_send_timeout 360s; # 设置超时
include /etc/nginx/uwsgi_params; # the uwsgi_params file you installed
index index.html;
}
}
在 upstream django中,我们的server使用了一个套接字,实际上server还可以是服务器的一个端口,如:8001
端口,但是套接字socket是直接给予Unix的底层实现的,套接字文件的读写速度非常快,当并发高时,Unix Socket比TCP port要快一些。但是如果要使用复杂均衡,则必须要使用TCP port。
接下来设置负责处理动态资源请求的后端uWSGI,该文件名为uwsgi.ini,存放在django项目文件夹内:
[uwsgi]
# Django-related settings
# the base directory (full path)
chdir = /your/path/to/backend
# Django's wsgi file
# module = backend.wsgi
env = DJANGO_SETTINGS_MODULE=backend.settings
# the virtualenv (full path)
home = /your/path/to/venv
# where wsgi file locate, normaly in app/wsgi.py
wsgi-file = app/wsgi.py
# where the log file output
daemonize = nohup.out
# process-related settings
# master
master = true
# maximum number of worker processes
processes = 10
# the socket (use the full path to be safe
socket = /your/path/to/backend.sock
# ... with appropriate permissions - may be needed
chmod-socket = 664
# clear environment on exit
vacuum = true
harakiri=360 # harakiri(服务器响应时间)超时服务器停止计算
http-timeout=360 # 前后端断开链接时间,服务器继续计算,单独uwsgi时使用
socket-timeout=360 # 前后端断开链接时间,服务器继续计算,配合nginx时使用
# python-autoreload=1
# buffer-size=1024
max-requests = 1000
接下来看app应用内的wsgi的配置,该文件存放在django项目的app应用文件夹内:
"""
WSGI config for backend project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
application = get_wsgi_application()