首先讲讲我的配置清单🧾
宿主机 OS: CentOS 7
Nginx 版本:1.16.1
Docker 版本: 19.03.13
Docker 容器系统: CentOS 7
Python 虚拟环境管理: Anaconda
Python 版本: Python 3.7
选用的后端框架: Fastapi
Python 操作 PostgreSQL: Psycopg2 (毋容置疑)
数据库: PostgreSQL 12(毋容置疑 + 1)
宿主机中需要做的配置
1. 安装 & 配置 Nginx
大部分内容出自此链接🔗:juejin.im/post/684490…
# 安装 Nginx
yum -y install nginx
# 卸载 Nginx
yum remove nginx
# 设置开机启动,建议打开
systemctl enable nginx
# 启动 nginx 服务,安装后需运行
systemcrl start nginx
# 停止 nginx 服务
systemcrl stop nginx
# 重启 nginx 服务
systemcrl restart nginx
我的 Nginx 配置,以下仅为部分配置,可下载一键脚本进行配置。
如果不知道自己的 Nginx 配置的文件位置在哪,可使用 systemctl status nginx 查看。
# 编辑的文件
# 我的文件地址是
# vi /etc/nginx/conf.d/default.conf
server {
listen 80;
server_name www.xxx.xxx;
rewrite ^(.*)$ https://$host$1 permanent;
}
server {
listen 443 ssl http2;
server_name www.xxx.xxx;
root /etc/nginx/html;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$args;
}
# fastapi main
location /helloworld {
proxy_pass http://127.0.0.1:8011;
}
# fastapi docs
location /docs {
proxy_pass http://127.0.0.1:8011/api/docs;
}
}
2. 安装 & 配置 Docker
# 安装 Docker
yum -y install docker
# 启动 Docker 后台服务,安装完后需运行
systemctl start docker
# 开机自启动 Dokcer,建议开启
systemctl enable docker
# 查看 Dokcer 运行信息
systemctl status docker
# 重启 Docker 服务
systemctl restart docker
# 检测 Docker 是否正常安装,尝试跑一个叫 hello-word 的镜像
docker run hello-world
# 查看是存在 hello-world 镜像,如果存在,则成功
docker images
# 拉取 CentOS 镜像,注意':'后面带的是系统版本,
# 同样的 docker images 可以查看是否存在 centos 这个镜像
docker pull centos:7.8.2003
# 运行 docker centos 的时候,
# 一定要注意开放端口,以方便容器内的 Fastapi 使用
docker run -ti -d -p 80:80 -p 8011:8011 --privileged=true centos:7.8.2003 /usr/sbin/init
# 查看所有镜像的信息,方便使用 container id 进入
docker ps -a
# 先执行 attach 命令,以获得 systemctl 权限
docker attach docker_container_id
# 下次进入 docker 就不是上面那句 attach 命令了,而是
docker exec -it docker_container_id /bin/bash
# 所以需要 docker ps -a 查看 container id
!重要!:之后进入这个容器就不能用 docker attach docker_container_id 了,但为了保险,先 attach 一次,以获得容器内 CentOS 7 执行 systemctl 命令的权限
# 然后关掉终端或命令行,开启新的终端或命令行,这次换另外一条命令进入 虚拟机
docker exec -it docker_container_id /bin/bash
关于 CentOS 7 安装 Docker 更详细的信息:www.jianshu.com/p/3a4cd73e3…
关于 Docker 安装 CentOS 7 及基本配置:victorzhong.github.io/2018/01/15/…
容器内虚拟机需要做的配置
1. 安装 & 配置PostgreSQL 12
记得在 sql shell 里面的命令,结尾都要加上 ; 分号。
# 先安装 yum 的下载源,即安装 epel-release,
# 下载之后,就可以直接跳到 安装 PostgreSQl 12 那一步
#(还有其它的国内源请看:https://www.jianshu.com/p/541c737bc947)
yum -y install epel-release
# 如果选择不安装 yum 下载源 则使用下面的命令
yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm
# 安装 PostgreSQL 12
yum install -y postgresql12 postgresql12-server
# 初始化数据库
/usr/pgsql-12/bin/postgresql-12-setup initdb
# 启动PostgreSQL服务
systemctl start postgresql-12
# 设置 PostgreSQL 服务为开机启动
systemctl enable postgresql-12
# 查看 PostgreSQL 服务状态
systemctl status postgresql-12
# 进入 PostgreSQL 命令行
su postgres
# 启动SQL Shell
psql
!重要!,此时先不要退出 sql shell!接下来,可输入 \l 查看 DataBase 的 Encoding,假若 Encoding 不为 UTF8,插入中文值到数据库的时候,会出错!
请先不要急着卸载或执行破坏性命令(血与泪的教训),请按照顺序执行下面的命令,以更改 DataBase 的 Encoding。
这些命令全都要在 SQL Shell 里面执行!记得 ; 分号结尾,如果还是忘了,按 ctrl / command + c 可以退回到初始状态。
# 先切换 DataBase
\c template0;
# 将 template1 的 datistemplate 改为 false
update pg_database set datistemplate = FALSE where datname = 'template1';
# 删除 template1 DataBase
drop database template1;
# 重新创建 template1 DataBase 以 UTF8 编码
create database template1 with encoding = 'UTF8' LC_CTYPE = 'en_US.UTF-8' LC_COLLATE = 'en_US.UTF-8' template = template0;
# 重新将 template1 的 datistemplate 改为 true
update pg_database set datallowconn = TRUE where datname = 'template1';
# 接下来切换到 template1 Database
\c template1;
# 重复步骤,修改 template0 的 datistemplate
update pg_database set datistemplate = FALSE where datname = 'template0';
# 删除 template0 DataBase
drop database template0;
# 重新创建 template0 DataBase 以 UTF8 编码
create database template0 with encoding = 'UTF8' LC_CTYPE = 'en_US.UTF-8' LC_COLLATE = 'en_US.UTF-8' template = template1;
# 重新将 template0 的 datistemplate 改为 true
update pg_database set datallowconn = TRUE where datname = 'template0';
!重要!,切勿真的删除 template0 & template1 这两个数据库
# postgres 数据库,重复以上步骤,即可把 Encoding 改为 UTF8,不再赘述注释
\c template1;
update pg_database set datistemplate = FALSE where datname = 'postgres';
drop database template0;
create database postgres with encoding = 'UTF8' LC_CTYPE = 'en_US.UTF-8' LC_COLLATE = 'en_US.UTF-8' template = template0;
update pg_database set datallowconn = TRUE where datname = 'postgres';
\c postgres;
# 再次输入 \l 查看 Encoding,确保修改成功。
当执行了破坏性命令,例如:yum remove postgresql12 postgresql12-server。
并重新执行了yum install postgresql12 postgresql12-server后,执行初始化数据库命令时会出错,
即当在[root@xxx /]#状态下执行 /usr/pgsql-12/bin/postgresql-12-setup initdb 命令时会出现,该文件夹 📁 不为空的报错。
此时需执行:rm -rf /var/lib/pgsql/12/data/ 删除该文件夹。
然后重新执行 /usr/pgsql-12/bin/postgresql-12-setup initdb 命令即可。
# 修改 postgres 数据库密码
ALTER USER postgres WITH PASSWORD 'NewPassword';
!重要!,允许所有 IP 访问,我的配置
# 修改配置文件
vi /var/lib/pgsql/12/data/pg_hba.conf
# 将 ipv4 下面的内容改为
# IPv4 local connections:
host all all 127.0.0.1/32 trust
# 重启 postgresql-12 服务,注意⚠️一定要带上 -12
systemctl restart postgresql-12
# 如果需要退出 postgres=# 或 bash-4.2$ 的状态
直接输入 exit 按回车即可。
PostgreSQL 12 安装 & 配置完毕
PostgreSQL 修改数据库编码方式代码源自🔗:www.jianshu.com/p/62893363b…
更详细的信息可点击此链接🔗(大部分内容也源自此链接):ken.io/note/centos…
2. 安装 & 配置 Anaconda
以下内容大部分来自此链接🔗:juejin.im/post/685457…
# 下载 wget
yum -y install wget
# 配置 下载源
yum -y install perl
# 下载 Anaconda
wget https://repo.anaconda.com/archive/Anaconda3-2020.02-Linux-x86_64.sh
# 安装 Anaconda
bash Anaconda3-5.3.1-Linux-x86_64.sh
# 进入安装程序,提示输入“ENTER”继续:
Please, press ENTER to continue
>>> ENTER
# 输入yes确认接受许可协议
Do you accept the license terms? [yes|no]
[no] >>> yes
# 确认Anaconda的安装位置, 可改可不改
Anaconda3 will now be installed into this location:/root/anaconda3 -
Press ENTER to confirm the location - Press CTRL-C to abort the installation -
Or specify a different location below[/root/anaconda3] >>> /opt/anaconda3
# 安装完成后,出现询问是否在用户的.bashrc文件中初始化Anaconda3的相关内容,此处选 yes
Do you wish the installer to initialize Anaconda3by running conda init? [yes|no][no] >>> yes
安装完成 ✅ ,Anaconda 的一些基础命令:
# 执行下:source ~/.bashrc,
# 使用 conda 命令时就不会报 conda command not found 了
# 创建一个 Python3.7 版本的虚拟环境
conda create --name fastapienv python=3.7
# 删除虚拟环境命令
conda remove -n fastapienv --all
# 激活虚拟环境,可直接激活虚拟环境,无需先停用当前虚拟环境
conda activate fastapienv
# 停用虚拟环境
conda deactivate fastapienv
# 查看当前虚拟环境已安装的包
conda list
# conda 安装包命令,假如是安装 fastapi
conda install fastapi
# 当 conda install 无法安装某个包时,通过查询 pip 的路径,
# 如果有显示当前虚拟环境名,则可以使用 pip 安装 Python 包,看是否能装上
whereis pip
pip install fastapi
# 如果 pip install / coonda install 都无法安装,则需要下载源码包,使用命令解压安装。
解决每次进入虚拟机时,Anaconda 虚拟环境皆为 base 的问题
(源自此链接🔗:www.cnblogs.com/alphacode/p…
# 修改 ~/.bash_profile 文件,有时 ~/.bashrc 文件里也会有此配置
export PATH="~/anaconda/envs/your_env_name/bin:$PATH"
# your_env_name 是你自定义的环境名
# 修改 ~/.bashrc 文件
conda activate your_env_name
# "your_env_name"就是你的环境名
# 更新配置
source ~/.bashrc
source ~/.bash_profile
# 设置好后,返回到刚进入虚拟机的状态 [root@xxx /]#,
# 执行以下命令以停止 base 虚拟环境自启动
conda config --set auto_activate_base false
我的配置:
# vi ~/.bashrc
# >>> conda initialize >>>
# !! Contents within this block are managed by 'conda init' !!
__conda_setup="$('/root/anaconda3/bin/conda' 'shell.bash' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
eval "$__conda_setup"
else
if [ -f "/root/anaconda3/etc/profile.d/conda.sh" ]; then
. "/root/anaconda3/etc/profile.d/conda.sh"
else
export PATH="/root/anaconda3/bin:$PATH"
fi
fi
unset __conda_setup
conda activate fastapienv
# <<< conda initialize <<<
# vi ~/.bash_profile
# PATH=$PATH:$HOME/bin
PATH="~/anaconda3/envs/fastapienv/bin:$PATH"
export PATH
export LANG="en_US.UTF-8"
Anaconda 配置完毕
我的 Anaconda 虚拟环境 fastapienv 还需要安装的包
# 除了基本的包以外
还安装了:
fastapi
uvicorn
psycopg2
# 小程序解密用
pycryptodome
3. 安装 & 配置 & 启动一个 Fastapi 基础后端
fastapi官方中文链接🔗:fastapi.tiangolo.com/zh/
执行下面的步骤前,必须先安装好 fastapi & uvicorn & psycopg2
# 找个你喜欢的路径,(我喜欢 /tmp/) 创建 main.py 文件,键入以下代码:
from typing import Optional
from fastapi import FastAPI
# 跨域
from fastapi.middleware.cors import CORSMiddleware
import psycopg2
import psycopg2.extras
app = FastAPI()
# 跨域配置
origins = [
"http://localhost.tiangolo.com",
"https://localhost.tiangolo.com",
"http://localhost",
"http://localhost:8080",
"http://127.0.0.1",
"http://127.0.0.1:8080",
"*"
]
# 跨域配置
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"]
)
# 一般都是 5432 端口,下文会提及如何查询
conn = psycopg2.connect(database="postgres", user="postgres", password="Your_Pass_Word", host="127.0.0.1", port="5432")
# 这段代码很关键,下文会解释
cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor)
@app.get("/")
def read_root():
return {"Hello": "World"}
# 创建员工数据表
@app.get("/create/stafftable")
def create_staff_table():
sql = """
CREATE TABLE staff(
id varchar(11) PRIMARY KEY,
username varchar(20),
password varchar(50)
);
"""
try:
cursor.execute(sql)
print("staff table created successfully")
conn.commit()
except Exception as e:
conn.rollback()
else:
conn.commit()
# 创建员工
@app.get("/create/staff")
def create_staff(id: str, username: str, password: str):
idsql = """SELECT * FROM staff where id = %s"""
idparams = (id,)
cursor.execute(idsql, idparams)
conn.commit()
rows = cursor.fetchall()
if rows:
return -1
else:
sql = """INSERT INTO staff (id, username, password) VALUES (%(id)s,%(username)s, %(password)s)"""
params = {'id': id, 'username': username, 'password': password}
try:
cursor.execute(sql, params)
conn.commit()
except Exception as e:
conn.rollback()
else:
conn.commit()
# 删除员工
@app.get("/delete/staff")
def delete_staff(id: str):
sql = """delete from staff where id = %s """
params = (id,)
try:
cursor.execute(sql, params)
conn.commit()
except Exception as e:
conn.rollback()
else:
conn.commit()
# 查找所有员工
@app.get("/searchall/staff")
def search_all_staff():
sql = """SELECT * FROM staff;"""
try:
cursor.execute(sql)
rows = cursor.fetchall()
conn.commit()
return rows
except Exception as e:
conn.rollback()
else:
conn.commit()
# 关闭数据库连接
def close_database():
cursor = conn.cursor()
cursor.close()
conn.close()
!重要!,此时,我们进来 Dokcer 容器的那条命令就发挥作用了,我们进入的时候,映射了两个端口给虚拟机,分别是 80 & 8011。
如果你没有跑 docker run -ti -d -p 80:80 -p 8011:8011 --privileged=true centos:7.8.2003 /usr/sbin/init 这条命令就进入虚拟机,很抱歉,你需要回到创建 docker container 的位置重新创建...
写好 main.py 后,执行以下命令:
uvicorn main:app --host 0.0.0.0 --port 8011 --reload
# 按 ctrl / command + c 可停止运行
此时,你在浏览器的地址栏中输入,你的物理机IP,后面加上:8011,就能看到 hello world 的提示。
IP后输入 :8011/docs 你还能看到 fastapi 为我们准备的 交互式 API 文档,你可以在里面测试连接数据库是否存在问题。
:8011/docs 还能进行一定的接口测试,看看返回的 Data 是否符合自己的需要。
如键入中文值存在问题,请返回到上文,看看是否是,DataBase Encoding 的编码问题。
另外,如果你的 Uvicorn 在后台运作,而你希望重新启动,可使用以下命令让它停止运行
# 检查端口被哪个进程占用
netstat -lnp|grep 8011
# 如果无法使用 netstat 执行:
yum install net-tools
# 查看进程的详细信息
ps 11100
# 杀掉进程
kill -9 11100
# 通常,还需再使用 netstat -lnp|grep 8011 复查
更多详细信息:blog.csdn.net/u011389452/…
4. 可能存在的 Psycopg2 操作 PostgreSQL 数据库问题
如果没有 import psycopg2.extras & cursor = conn.cursor(cursor_factory=psycopg2.extras.RealDictCursor) 这段代码。
你的 /searchall/staff 请求的返回值,就不会以键值对的形式返回给前端。
以下内容你可以忽略
当初我为了这个问题,“学习了”下 PostgreSQL json & jsonb 数据类型的操作,jsonb 的读取性能更快(比 json 快很多很多倍,但是顺序会乱)
下面放一下 jsonb 的 数据库命令 操作,即,直接在 sql shell 中执行的命令
# postgresql jsonb 增删改查 知识点记录 📝 (注意!是jsonb):
# 在 sql shell 创建表:
CREATE table test_jsonb(id int, info jsonb);
# 增:
insert into test_jsonb values(2, '{"company":"sanzro design", "name":"sanzro", "career":"Full Stack Engineer"}');
# 删(暂时不知道如何通过jsonb去删除整个元素):
delete from test_jsonb where id = 2;
# 改:
update test_jsonb set info = info||'{"company":"sanzro design"}' WHERE id = 2;
# 查:
select * from test_jsonb where info @> '{"company":"sanzro design"}';
# 查询 id
select * from test_jsonb where id=2;
还有如果你需要 id 自增,则创建表的时候将 id 配置为 serial:
CREATE TABLE test_jsonb (
id SERIAL PRIMARY KEY,
info jsonb
);
当 id 为自增时,其它不变,插入数据变为:
insert into test_jsonb (info) values('{"company":"sanzro design", "name":"sanzro", "career":"Full Stack Engineer"}');
其它遇到的问题
后端解密小程序函数
!重要!,需要 pip install pycryptodome ,上文有提及
如果依旧无法使用,请参考此链接🔗:从 “警告:请勿使用 pycrypto” 看起:qastack.cn/programming…
# 示例代码
from fastapi.encoders import jsonable_encoder
@app.post('/code')
def user_wxlogin(appid, secret, code):
params = {
'appid': appid,
'secret': secret,
'js_code': code,
'grant_type': 'authorization_code'
}
url = 'https://api.weixin.qq.com/sns/jscode2session'
r = requests.get(url, params=params)
openid = r.json().get('openid', '')
session_key = r.json().get('session_key', '')
return {'openid': openid, 'session_key': session_key}
内容较长,感谢 🙏 你看到这里。
如有误,请提出,必纠正,谢谢。