本教程利用Python异步编程方法,基于FastAPI和uvicorn库来快速搭建并运行网站后台。由于教程偏向于入门,所以我将介绍网站完整建立过程,并对每个可能的疑惑点进行详细解释;如果你已经有自己的服务器,并且掌握SSH和FTP的连接方法,可以直接跳转至第三步。
根据业务需求的不同, 当然也可以选择租用国外vps,比如:
可能要绑定visa银行卡且收费较高,不过有免费试用额度, 访问外网资源速度很快;
当然也可选择国内云服务器,比如:
注意: 国内服务器和域名都需要进行备案
由于作者刚好有阿里云的学生优惠, 在申请页面按照官方的引导进行身份认证, 于是拿到了一个月免费的服务器;
请注意:服务器的系统镜像请选择发行版Linux,比如Ubuntu、CentOS、Debian等,本教程服务器是阿里云的系统 (应该是从CentOS魔改来的)
由于阿里云学生申请域名首年一块钱,秉持白嫖到底的态度 我也申请了一个,暂且称为:mydomain.top
在下单界面需要创建你的信息模版, 认证身份并进行备案,按照官方流程即可。
3.域名解析
当你创建好域名后,前往你的 域名控制台
在域名列表中,你会看到刚刚申请好的域名,此时点击 “解析” —> “新手引导” :
您的业务需求:
这里是根据服务器公网IP来确定的,一般都是xxx.xxx.xxx.xxx格式,即IPv4地址;这里我们选择"解析到IPv4地址"就好;
请选择网站域名:
如果选择设置@主机记录
,则通过http://mydomain.top
就能访问我们的网站;
如果选择设置WWW主机记录
,则通过http://www.mydomain.top
才能访问网站;
注: 设置成WWW
的好处是便于我们后续添加子域名, 比如api.mydomain.top
; 这里我们选择WWW记录
请输入网站IP:
填入之前创建的服务器实例的公网IP就好了
解析设置成功后, 我们可以在cmd中ping一下我们的域名:
ping www.mydomain.top
如果像图中一样能够Ping得通, 就代表我们配置成功了!
[Xshell和Xftp](家庭/学校免费 - NetSarang Website (xshell.com)) 这两个软件功能强大、简单易用,且都有家庭和学生免费许可证,比较适合本教程的使用。
Xshell功能: 通过SSH连接我们服务器的终端, 并执行相关命令
这里点击 “连接” 后,会提示你输入用户名和密码,这里根据你的云服务器的系统账号(一般是root
) 和密码, 对应输入即可;出现SSH密钥确认界面时点击保存。
Xftp功能: 用于本地与远程服务器之间的文件传输, 可以直接复制粘贴文件
(这里填写的信息也与Xshell中一致)
到这里为止, 我们已经成功申请了云服务器和域名, 配置好了Xshell与Xftp, 那么接下来开始配置我们的服务器环境。
查看系统python版本:
python --version
本教程使用的Python版本为3.9, 建议Python版本为3.7+
教程所使用的FastAPI模块使用了很多Python3.7+的特性,而由于笔者的服务器自带Python3.6版本太低,直接重装可能影响其他系统程序正常工作,故准备建立一个miniconda的虚拟环境。
接下来按照官方指引,在Xshell中依此执行下面命令:
# 在root/下新建"miniconda3"目录
mkdir -p ~/miniconda3
# 获取最新miniconda安装包
wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O ~/miniconda3/miniconda.sh
# 执行安装
bash ~/miniconda3/miniconda.sh -b -u -p ~/miniconda3
# 删除安装包
rm -rf ~/miniconda3/miniconda.sh
等待终端完成miniconda的安装后, 执行下面命令初始化conda:
~/miniconda3/bin/conda init bash
接下来创建虚拟环境并命名为myenv
, 同时安装Python3.9:
conda create --name myenv python=3.9
等待环境创建完毕, 我们启动这个虚拟环境:
conda activate myenv
此时我们的用户名前缀变成了"(myenv)", 这就代表虚拟环境启动成功了! 这个环境是独立于系统的Python环境的,我们可以在这个环境下安装所需的fastapi和uvicorn模块。
# 用conda安装
conda install fastapi
conda install uvicorn
# 或者pip安装
pip install fastapi
pip install uvicorn
/
新建一个 mysite 目录, 用于存放我们的网站文件: mkdir /mysite
在你自己的电脑里, 新建一个文件"server.py", 这将作为网站的后台 放在/mysite目录下;
使用Vscode等代码编辑器打开"server.py"并导入我们需要用到的库,比如数据存储的json、异步操作的asyncio 和处理客户端请求的fastapi,以及运行ASGI应用程序的uvicorn:
#encoding=utf-8
import json
import asyncio
import uvicorn
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
提示:
ASGI
: 是一种异步 Web 服务器和应用程序之间通信的标准接口, fastapi和uvicorn都是基于此标准的模块;app
: 我们用fastapi实际上是构建了一个ASGI 应用程序, 并将其部署在uvicorn上运行; 注: 考虑到实际开发中网站需要能处理并发的请求, 故我们将采用异步的方式编写后台程序。
# 处理GET请求
@app.get("/")
async def read_root():
# 读取本目录下的"data.json"文件,将内容返回给客户端
with open("./data.json", "r", encoding="utf-8") as f:
data = json.load(f)
return data
提示:
@app.get("/")
:是一个Python中的修饰器, 用于捕获对server.py所在路径的GET
请求;换言之,read_root()
这个函数就与路径 /mysite 绑定在一起了,任何GET请求都会经过这个函数的处理。在这里我们简单实现了一个读取服务器资源并返回给客户端的方法。async
: 是python的关键词, 标识read_root
是一个异步方法。编写处理“POST”请求的函数
这里简单实现了一个json文件上传功能。
# 处理POST请求
@app.post("/")
async def save_data(data: dict):
# 将客户端发送的内容存储到本目录下,并向客户端发送一条消息
with open("./data.json", "w", encoding="utf-8") as f:
json.dump(data, f)
return {"message": "Data received and saved successfully"}
注: Fastapi支持的请求方法不只GET和POST, 还有PUT,DELETE等, 实现原理相似, 这里就不给出具体方法了。
最后添加程序入口,并设置监听服务器本地的8000端口:
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
到这一步,我们的网站后台就写好了, 只需用Xftp将server.py
上传至服务器的/mysite
目录下即可:
我们知道:http协议走80端口,而https走443端口,但我们的网站后台只监听8000端口;为了将其他端口的请求转发到8000端口上, 我们还需要配置Nginx进行反向代理。
# Ubuntu
sudo apt update
sudo apt install nginx
# CentOS
sudo yum update
sudo yum install nginx
#验证是否安装成功
nginx -v
/etc/nginx/nginx.conf
: sudo vim /etc/nginx/nginx.conf
找到文件内的server
块, 并添加location/
字段,完成后如下:
server {
listen 80;
listen [::]:80;
server_name _;
root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
location / {
proxy_pass http://localhost:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
proxy_pass
选项用于转发请求至本地的8000端口, 要与我们在server.py
中填写的端口一致;注意: 点击键盘的"Insert键才能开始编辑文件;完成后点"ESC"键退出编辑模式,然后按":“键输入"wq”,回车后保存并退出文件。
回到终端界面, 我们来启动nginx :
# 测试 Nginx 配置是否有误
sudo nginx -t
# 启动nginx服务
sudo systemctl start nginx
# 切换到/mysite路径下
cd /mysite
# 启动网站后台
uvicorn server:app --host 0.0.0.0 --port 8000
这里的`server:app`其实就是代指我们在`server.py`内定义的`app`程序。
当出现下图时就代表网站后台成功启动了 !
#encoding=utf-8
import requests
data = {"message":"success"}
url = "http://www.mydomain.top"
# 向网站发送一个POST请求
r = requests.post(url,data)
print(r.json())
# 输出: {"message": "Data received and saved successfully"}
# 向网站发送一个GET请求
r = requests.get(url)
print(r.json())
# 输出: {"message":"success"}
在浏览器地址栏填入"http://www.mydomain.top",检查是否能打开;
注意: 如果在国内服务器上搭建了网站, 还需要进行ICP备案
其实到这里,我们的所有工作都差不多完成了。不过我突然想到, 要是在某段时间内出现大量异常请求怎么办呢? 为了保证网站正常运行, 我决定为server.py
添加新功能——增加一个预处理请求的中间件,对IP在一段时间内的访问次数进行限制。
思路:用异步方法实现一个非阻塞计时器,并在达到阈值时对已保存的IP字典进行清除
代码如下:
app = FastAPI()
# 记录IP访问次数的字典
ip_access_count = {}
# 异步任务,用于定期重置IP访问次数
async def reset_ip_access_count():
while True:
await asyncio.sleep(3600) # 设置每小时清除一次
ip_access_count.clear()
# 启动重置IP访问次数的异步任务,添加一个协程
asyncio.create_task(reset_ip_access_count())
# 处理IP限制中间件
@app.middleware("http")
async def ip_limit_middleware(request: Request, call_next):
client_ip = request.client.host # 获取请求IP
# 在IP字典中将该IP访问次数+1
ip_access_count[client_ip] = ip_access_count.get(client_ip, 0) + 1
if ip_access_count[client_ip] > 100: # 当该IP请求次数大于设定阈值时返回403
return JSONResponse(status_code=403, content={"msg": "Access denied for this IP"})
response = await call_next(request) # 将请求交给后面处理(这是Fastapi内置方法)
return response
import json
import asyncio
import uvicorn
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
app = FastAPI()
# 记录IP访问次数的字典
ip_access_count = {}
# 异步任务,用于定期重置IP访问次数
async def reset_ip_access_count():
while True:
await asyncio.sleep(10)
ip_access_count.clear()
# 启动重置IP访问次数的异步任务
asyncio.create_task(reset_ip_access_count())
# 处理IP限制中间件
@app.middleware("http")
async def ip_limit_middleware(request: Request, call_next):
client_ip = request.client.host # 获取请求IP
# 在IP字典中将该IP访问次数+1
ip_access_count[client_ip] = ip_access_count.get(client_ip, 0) + 1
if ip_access_count[client_ip] > 100: # 当该IP请求次数大于设定阈值时返回403
return JSONResponse(status_code=403, content={"msg": "Access denied for this IP"})
response = await call_next(request) # 将请求交给后面处理(这是Fastapi内置方法)
return response
# 处理GET请求
@app.get("/")
async def read_root():
with open("./data.json", "r", encoding="utf-8") as f:
data = json.load(f)
return data
# 处理POST请求
@app.post("/")
async def save_data(data: dict):
with open("./data.json", "w", encoding="utf-8") as f:
json.dump(data, f)
return {"message": "Data received and saved successfully"}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
阅读至此, 若有不解或有错之处, 欢迎大家留言喔 !