Django学习笔记-部署Nginx与对接AcApp

  1. 1. 增加容器的映射端口80与443
  2. 2. 安装Nginx
  3. 3. 写入AcWing配置信息
  4. 4. 修改Django项目的配置
  5. 5. 配置uWSGI
  6. 6. 部分BUG修复

本节内容是通过 Nginx 与 uWSGI 将项目部署至 AcWing 平台上,同时修复在 AcApp 小窗口上存在的部分 BUG。

现在我们需要将之前在网页上运行的项目部署至 AcWing 上,让其前后端分离,一个后端可以对应多个前端。

如果要将网站修改为 HTTPS 协议,需要先购买一个域名,然后申请证书,还需要进行备案,非常麻烦。在 AcWing 上线 App 已具备域名和证书。

1. 增加容器的映射端口80与443

由于创建后的容器不方便增加新的端口映射,因此我们先将原容器保存成镜像后再生成一个新的容器。

首先需要将容器中正在运行的任务全部关闭,然后登录运行容器的服务器,执行以下命令:

1
2
3
4
5
docker commit ubuntu_django ubuntu_django:1.0  # 将容器ubuntu_django打包成镜像ubuntu_django,版本号为1.0
docker images # 查看现有镜像
docker stop ubuntu_django # 停止容器
docker rm ubuntu_django # 删除容器
docker run -p 20000:22 -p 8000:8000 -p 80:80 -p 443:443 --name ubuntu_django -itd ubuntu_django:1.0 # 使用保存的镜像重新创建容器

此时如果无法远程连接容器可以按照 Django 学习笔记-配置 Docker、Git 环境与项目创建文章创建一个 hosts.allow 文件。

接着去云服务器的控制台,在安全组配置中开放80和443端口。

2. 安装Nginx

(1)安装依赖包(安装均在根用户 root 下进行):

1
apt install curl gnupg2 ca-certificates lsb-release ubuntu-keyring

(2)安装 Nginx:

1
2
apt-get update
apt-get install nginx

(3)查看版本号:

1
nginx -v

(4)启动 Nginx 以及查看是否在运行中:

1
2
systemctl start nginx
systemctl status nginx

如果报错:System has not been booted with systemd as init system (PID 1). Can't operate. Failed to connect to bus: Host is down,可以参考这篇文章:System has not been booted with systemd as init system (PID 1). Can‘t operate. 问题解决方法,简而言之就是用以下指令代替:

1
2
service nginx start
service nginx status

3. 写入AcWing配置信息

配置信息在 AcWing 平台上可以查看,按以下步骤将信息复制到项目的配置文件中即可:

  • nginx.conf 中的内容写入服务器 /etc/nginx/nginx.conf 文件中。如果 Django 项目路径与配置文件中不同,注意修改路径。
  • acapp.key 中的内容写入服务器 /etc/nginx/cert/acapp.key 文件中(cert 目录需要自己创建)。
  • acapp.pem 中的内容写入服务器 /etc/nginx/cert/acapp.pem 文件中。

然后启动 Nginx 服务:

1
sudo /etc/init.d/nginx start

如果启动不成功可以重新加载一下 Nginx 的配置文件即可看到错误在哪:

1
sudo nginx -s reload

4. 修改Django项目的配置

  • 打开 settings.py 文件:
    • 将分配的域名添加到 ALLOWED_HOSTS 列表中。注意只需要添加 https:// 后面的部分。
    • DEBUG = False
  • 归档 static 文件:
    • 在项目根目录下执行:python3 manage.py collectstatic,执行完后可以看到生成了一份 game 中的 static 目录。

5. 配置uWSGI

WSGI(Web Server Gateway Interface)是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接口。网关(Gateway)的作用就是在协议之间进行转换。很多框架都自带了 WSGI Server,比如 Flask、Django 等。当然性能都不好,自带的 Web Server 更多的是测试用途,发布时则使用生产环境的 WSGI Server 或者是联合 Nginx 做 uWSGI。

uWSGI 是一个 Web 服务器,它实现了 WSGI、uwsgi、HTTP 等协议。Nginx 中 HttpUwsgiModule 的作用是与 uWSGI 服务器进行交换。

要注意 WSGI、uwsgi、uWSGI 这三个概念的区分:

  • WSGI 的内容之前已经讲过了,是一种通信协议。
  • uwsgi 同 WSGI 一样是一种通信协议。
  • uWSGI 则是实现了 uwsgi 和 WSGI 两种协议的 Web 服务器。

uwsgi 协议是一个 uWSGI 服务器自有的协议,它用于定义传输信息的类型(type of information),每一个 uwsgi packet4byte 为传输信息类型描述,它与 WSGI 相比是两样东西。

为什么有了 uWSGI 还需要 Nginx?因为 Nginx 具备优秀的静态内容处理能力,然后将动态内容转发给 uWSGI 服务器,这样可以达到很好的客户端响应。

现在用户通过80/443端口访问 Nginx,而 Nginx 与 Django 项目之间还需要一个桥梁就是 uWSGI。

在 Django 项目中添加 uWSGI 的配置文件:scripts/uwsgi.ini,内容如下:

1
2
3
4
5
6
7
8
[uwsgi]
socket = 127.0.0.1:8000
chdir = /home/asanosaki/djangoapp
wsgi-file = djangoapp/wsgi.py
master = true
processes = 2
threads = 5
vacuum = true

由于使用 uwsgi 命令启动项目后原来的公网 IP 就无法访问了,因此需要对 web.html 中 CSS、JS 的链接地址进行修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{% load static %}

<head>
<link rel="stylesheet" href="{% static 'css/jquery-ui.min.css' %}">
<script src="{% static 'js/jquery-3.6.1.min.js' %}"></script>
<link rel="stylesheet" href="{% static 'css/game.css' %}">
</head>

<body style="margin: 0">
<div id="ac_game_1"></div>
<script type="module">
import {AcGame} from "{% static 'js/dist/game.js' %}";
$(document).ready(function() {
let ac_game = new AcGame("ac_game_1");
});
</script>
</body>

然后启动 uwsgi 服务(代替 python3 manage.py runserver 0.0.0.0:8000):

1
uwsgi --ini scripts/uwsgi.ini

在 AcWing 填写完应用的剩余信息即可发布。

6. 部分BUG修复

在 AcWing 中打开应用时可以发现鼠标右键点击的位置不对,这是因为之前我们默认游戏的画布在左上角,而开启小窗口后画布就不在左上角了,我们移动小球时用的 e.clientX/Y 表示的是整个屏幕的坐标,而小球的位置坐标本身是画布中的相对坐标。

Canvas 中的 getBoundingClientRect 函数可以获得当前视窗在浏览器中的位置以及自身占据的空间的大小,left 表示窗口左侧边框距离浏览器视窗左侧的距离,top 表示窗口顶侧边框距离浏览器视窗顶侧的距离,right 表示窗口右侧边框距离浏览器视窗左侧的距离,bottom 表示窗口底侧边框距离浏览器视窗顶侧的距离:

因此我们将鼠标点击的坐标分别减去 lefttop 即可映射到当前视窗内,在 Player 类中进行修改:

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
26
27
28
29
30
class Player extends AcGameObject {
...

add_listening_events() {
let outer = this;
this.playground.game_map.$canvas.on('contextmenu', function() {
return false;
}); // 取消右键的菜单功能
this.playground.game_map.$canvas.mousedown(function(e) {
const rect = outer.ctx.canvas.getBoundingClientRect();
if (e.which === 3) { // 1表示左键,2表示滚轮,3表示右键
outer.move_to(e.clientX - rect.left, e.clientY - rect.top); // e.clientX/Y为鼠标点击坐标
} else if (e.which === 1) {
if (outer.cur_skill === 'fireball') {
outer.shoot_fireball(e.clientX - rect.left, e.clientY - rect.top);
}

outer.cur_skill = null; // 释放完一次技能后还原
}
});
$(window).keydown(function(e) {
if (e.which === 81) { // Q键
outer.cur_skill = 'fireball';
return false;
}
});
}

...
}

注意现在我们的项目为发行版本,修改完静态文件且打包完后还需要去项目根目录执行:python3 manage.py collectstatic,此时会问你是否要覆盖,输入 yes 即可。我们同样也可以修改之前的打包脚本 compress_game_js.sh,在其后面添加一行:echo yes | python3 manage.py collectstatic

由于游戏有菜单界面,应该在从菜单进入游戏后才开始计算窗口大小,因此我们需要把 AcGamePlayground 的初始化放在 show 函数中执行:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class AcGamePlayground {
constructor(root) {
this.root = root;
this.$playground = $(`
<div class='ac_game_playground'>
</div>
`);
this.root.$ac_game.append(this.$playground);

this.start();
}

get_random_color() {
let colors = ['blue', 'red', 'pink', 'grey', 'green'];
return colors[Math.floor(Math.random() * 5)];
}

start() {
this.hide(); // 初始化时需要先关闭playground界面
}

// 显示playground界面
show() {
// 将界面的宽高先存下来
this.width = this.$playground.width();
this.height = this.$playground.height();

this.game_map = new GameMap(this); // 创建游戏画面

this.players = []; // 所有玩家
this.players.push(new Player(this, this.width / 2, this.height / 2, this.height * 0.05, 'white', this.height * 0.15, true)); // 创建自己

// 创建敌人
for (let i = 0; i < 8; i++) {
this.players.push(new Player(this, this.width / 2, this.height / 2, this.height * 0.05, this.get_random_color(), this.height * 0.15, false));
}

this.$playground.show();
}

// 关闭playground界面
hide() {
this.$playground.hide();
}
}

最后我们需要对菜单页面进行修改,在小窗口中的菜单页面不太美观,需要让其适应窗口大小,应该用小窗口的相对距离来表示,对 game.css 进行修改:

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
26
27
28
29
30
31
32
.ac_game_menu {
...
}

.ac_game_menu_btgroup {
width: 20vw;
position: relative;
top: 30%;
left: 20%;
}

.ac_game_menu_btgroup_bt {
height: 7vh;
width: 15vw;
color: white;
font-size: 3vh;
line-height: 7vh;
font-style: italic;
cursor: pointer;
text-align: center;
background-color: rgba(39, 21, 28, 0.6);
border-radius: 10px;
letter-spacing: 0.5vw;
}

.ac_game_menu_btgroup_bt:hover {
...
}

.ac_game_playground {
...
}

上一章:Django学习笔记-创建游戏界面

下一章:Django学习笔记-用户名密码登录