nginx与uWSGI踩坑记录
Kale

踩坑了好久,这一次算是大大加深了对nginx配置的理解.

WebSocket转发

之前已经解决了nginxuWSGIWebSocket的转发问题,但其实还留着一些坑没有解决,就是在前端发送心跳包之后,后端收到之后会立即返回心跳包的内容heartBeat,但是连接就会马上断开,然后重连.这个Bug找了好久,后来发现是前端的问题,因为后端对心跳包做出回复之后,前端的socket对象会触发onMessage函数,但是之前的onMessage函数写得有点问题,接收到之后会对消息进行反序列化,本来是想对另外一种消息进行反序列化,这样是不会出错的,但是接收到心跳包的回复后也会触发这个函数,报错,就触发了onError函数,然后重新连接.

将函数改过来,加上

1
2
3
4
5
6
7
8
try {
if (msg.data === 'heartBeat') {
this.reset();
return;
}
} catch (e) {
console.log(e);
}

但是还是没有解决问题,而是直接收不到后端的心跳消息了

Avatar

由于测试的时候就算不走nginx,也是无法正常回复,于是查看uWSGI的日志,发现有几行是这样的:

1
2
3
4
Thu Apr 23 13:47:45 2020 - ASYNC call without async mode !!!
Thu Apr 23 13:48:00 2020 - ASYNC call without async mode !!!
Thu Apr 23 13:48:15 2020 - ASYNC call without async mode !!!
Thu Apr 23 13:48:27 2020 - ASYNC call without async mode !!!

无异步模式的异步调用.
推断应该是建立连接之后,根据代码段的执行顺序,接收到心跳包之后,再回复(由于现在已经解决了问题,所以记不太清之前到底能不能回复了),然后代码执行结束,由于uWSGI并非是异步模式,所以连接自动断开了,然后前端再次重连…

uWSGI官网上查看异步开启方案,官网上是这样写的:

它们简单实现了协程(coroutine)/绿色线程(green thread)技术。它们并无事件引擎,因此,你必须使用由uWSGI提供的。一个事件引擎通常是一个为平台无关的非阻塞I/O(libevent, libev, libuv等等)导出基元的库。使用 —async 选项启用uWSGI事件引擎。目前,uWSGI发布版本包含了以下挂起/恢复引擎:

  • uGreen - Unbit的绿色线程实现 (基于swapcontext())
  • Greenlet - Python greenlet模块
  • Stackless - Stackless Python
  • Fiber - Ruby 1.9 fibers

所谓绿色线程(Green threads),则是程序里面的线程不会真正映射到操作系统的线程,而是由语言运行平台自身来调度。而操作系统线程(Native Thread)的意思就是,程序里面的线程会真正映射到操作系统的线程,线程的运行和调度都是由操作系统控制的.

绿色线程实际上是一种模拟的线程,优点是完全由语言运行平台管理,所以易于创建和销毁.

uwsgi --ini uwsgi.ini --async 100 --ugreen开启异步,WebSocket连接成功,并且可以成功发送和接收消息,不会断开.

Avatar

nginx代理

前后端分离代理

之前的代理方式是前端将将代码编译进/dist后,django设置里将静态文件路径改成前端编译后的/dist文件夹,然后python manage.py collectstatic,将静态文件收集到根目录下面的/static文件夹,然后用nginx代理后端应用,这样用vue.js写的组件走的还是index.html的路由,而这个路由是由vue.router指定的,所以用起来和写前端的时候没有区别,并且可以搜集admin的静态文件.

这种代理方式其实还是和后端应用同端口的,不会产生跨域问题,前后端并没有分别代理.并且前后端之间的请求走的都是公网,而不是直接内部转发.

而前后端分别代理,需要重新配置nginx.conf文件.

对前端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen 80;
server_name 公网ip或者域名; # 域名
access_log /var/log/nginx/access.log;
charset utf-8;
gzip on;
gzip_types text/plain application/x-javascript text/css text/javascript application/x-httpd-php application/json text>

error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;

location / {
root /*/*/*/dist/;
try_files $uri $uri/ /index.html;
}

location /message/api/ {
proxy_pass http://127.0.0.1:82/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

对后端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
server {
listen 82;
server_name 127.0.0.1; # 域名
access_log /var/log/nginx/access.log;
charset utf-8;
gzip on;
gzip_types text/plain application/x-javascript text/css text/javascript application/x-httpd-php application/json text/js>

error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
# 指定项目路径uwsgi
location / {
include uwsgi_params;
uwsgi_connect_timeout 30;
uwsgi_pass unix:/*/*/*/uwsgi.sock;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# 指定静态文件路径
location /static/ {
alias /*/*/*/static/;
index index.html index.htm;
}
}

分析前端配置文件,在80端口,由nginx代理前端的静态文件,并且将/message/api/路径指向本机的82端口,分析后端配置文件,监听本机的82端口,将默认路径指向uwsgi.sock文件,与uWSGI建立关联,将动态请求转发给uWSGI,下面的location /static/其实可以去掉,这是直接访问82端口的时候也能正常访问设置的.

按上述配置,前后端就分离代理了,服务器安全组关闭82端口,只开放80端口,后端应用跑在的本机的8000端口,而nginx监听82端口,前端在80端口接收请求,然后对动态请求进行分发.

这样部署的好处是更加安全了,不会暴露过多端口,并且前后端解耦,但是一个缺点就是无法使用djangoadmin了,因为admin是由后端渲染的,这样部署就是后端只提供接口,而不参与渲染页面.

同端口代理不同端口

上面是在80端口前后端分离部署的方式部署了一个项目,但是如果想要多个项目都部署到80端口,配置就要发生改变了.

首先在80端口,加上配置项:

1
2
3
4
5

location /hospital/ {
proxy_pass http://127.0.0.1:81/;
proxy_http_version 1.1;
}

这样配置就将路径为hospital的请求转发到本机的81端口.

然后新建*_front.conf文件,写上配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen 81;
server_name 127.0.0.1; # 域名
access_log /var/log/nginx/access.log;
charset utf-8;
gzip on;
gzip_types text/plain application/x-javascript text/css text/javascript application/x-httpd-php application/json text/js>

error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
# 指定项目路径uwsgi
location / {
root /*/*/*/dist/;
try_files $uri $uri/ /index.html;
}

location /api/ {
proxy_pass http://127.0.0.1:83/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}

然后新建*_backend.conf文件,写上配置项:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
server {
listen 83;
server_name 127.0.0.1; # 域名
access_log /var/log/nginx/access.log;
charset utf-8;
gzip on;
gzip_types text/plain application/x-javascript text/css text/javascript application/x-httpd-php application/json text/js>

error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
# 指定项目路径uwsgi
location / {
include uwsgi_params;
uwsgi_connect_timeout 30;
uwsgi_pass unix:/*/*/*/uwsgi.sock;
proxy_http_version 1.1;
}
}

完整的请求流程是,前端发送请求到80端口,路径为/hospital/,然后nginx将请求转发到本机的81端口,81端口收到后返回静态文件,然后将动态请求转发给/api/接口,即83端口,83端口收到后将请求转发给uWSGI处理,然后将结果渲染到页面.

其实可以写得更简单一点,不过我觉得这样配置更加清晰一点,管理起来也容易.

不过还是上面的缺点,不能访问djangoadmin..


踩了好多坑,感受到自己深深的菜….

  • 本文标题:nginx与uWSGI踩坑记录
  • 本文作者:Kale
  • 创建时间:2020-04-23 13:16:46
  • 本文链接:https://kalew515.com/2020/04/23/nginx与uWSGI踩坑记录/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!