模型部署笔记

模型部署

Git

分布式的版本控制系统

版本控制

git工作区域分类:工作区 版本库 远程仓

工作区 →(add)→ 暂存区 →(commit)→ 版本库 →(push)→ 远程仓

工作区:平时我们写代码,存放代码的地方,本地文件夹

版本库:

暂存区:存放文件的索引,保存的是文件的信息

master:真实存储的地方

远程仓:存储云端

初始化及基础操作

图形化页面操作:

Git在这里创建版本库

Git提交到'master'

通过小乌龟(TortoiseGit)对版本继进行操控:日志和日志引用

git命令操作:

点击 Git Bash Here 打开命令窗口

显示配置信息 git config -l

配置个人信息

复制代码
git config --global user.name 'aoiner'                      //配置用户名
git config --global user.email '1065346498@qq.com'          //配置邮箱
git config --global core.quotepath false                    //中文文件名正常显示
git config --global credential.helper store                 //记住用户名和邮箱(远程联动时用 免去每次 push 输密码)

标准流程:改代码 → git status 看状态 → git add 暂存 → git commit -m 提交

在当前目录创建版本库 git init

查看工作区状态 git status

提交一个文件 git add 文件名

提交所有文件 git add *

提交到本地版本库(一次存档)git commit -m '描述信息'

修改最后一次提交的描述信息 git commit --amend -m '新描述'

显示版本记录(显示日志) git log

显示引用记录 git reflog

HEAD 指向当前版本(前7位即可) 该指令用于在各个版本里穿梭 git reset --hard commit_id

远程仓库

推送前最好先 git pull 拉取远程最新,避免冲突

命令操作

拷贝仓库 git clone 地址

推送文件 git push origin 分支名

git push (默认使用master推送)

分支管理:

查看分支:git branch

创建分支:git branch 分支名

合并分支(先切换到master):git merge 分支名

删除分支:git branch -d 分支名

Linux

基础操作

快捷键

放大终端:ctrl shift +

缩小终端:ctrl -

补齐:TAB

中英文切换:shift

清屏:ctrl + l or clear

终端

开启终端的快捷方式:

当前已经打开的终端窗口里,新建一个标签页 Ctrl + Shift + T

直接新开一个独立的终端窗口:Ctrl + Shift + N

关闭终端:ctrl d

hq @ hq-jn : ~ $
用户名 主机名 当前路径 命令提示符
/ 根目录
~ 家目录
/home 存放用户信息
/bin 二进制文件,存放普通用户命令
/dev 设备文件
/etc 配置文件
/mnt 挂载目录
/root 超级用户

查看当前绝对路径:pwd

查看用户名:whoami

查看主机名:hostname

ls 和 权限修改

显示隐藏文件:ls -a

查看文件详细信息:ls -l

d rwxrwxr-x 2 hq hq 4096 10月 30 2025 work
文件类型 权限 链接数 用户名 组名 大小 最后一次修改的时间 文件名

文件类型:bsp-lcd

d 目录文件 (文件夹)
- 普通文件 (.c .h .py .txt .jpg)
b 块设备文件
s 套接字文件
p 管道文件
l 软链接文件
c 字符设备文件

文件权限:

复制代码
rwx r-x r-x
用户权限 组内成员权限 其他成员权限

读:r 写:w 执行:x 无权限:-

修改权限:chmod 权限值 文件名

权限值:111(对应读写执行位置) ---------> 4(读)+2(写)+1(执行) == 7

更改文件所有者:chown 所有者用户名:组名 文件名

cd 切换路径

cd 目标路径

绝对路径:从根目录开始一级级向下查找

相对路径:相对于当前位置开始索引查找

根目录:/

家目录:~

当前目录 . (可省略)

上一级 ..

上一次在的文件夹:-(cd -)

新建命令

新建文件夹:mkdir 目录文件名称(可连续创建多个文件夹)

创建有层级关系的文件夹 mkdir -p 目录文件/目录下文件

新建普通文件夹:touch 普通文件名称.后缀

更改时间戳:touch 已有文件名

删除命令

删除普通文件: rm 普通文件名

删除目录文件:rm -r 目录文件名

复制命令

复制普通文件: cp 目标文件 目标路径

复制目录文件:cp -r 目录文件 目标路径

另存为:cp 目标文件 目标路径/新名称

移动命令

将文件移动到新路径下:mv 文件名 目标路径

重命名:mv 源文件 目标文件

文件编辑和查看命令

vi编辑器

进入vi编辑器:vi 文件名

三种工作模式:

命令行模式 (一开始进入默认进入该模式) 复制(yy)、粘贴(p)、删除、剪切(dd)

插入模式 编辑文本

底行模式 保存(w)、退出(q) 、查找(/str)、替换(g /str hello)、取消高亮(set nohl)、行号(set nu)

复制代码
通过 i o a 三个按键从命令行模式进入插入模式
通过 esc 回到 命令行模式 
通过 shift  : 进入底行模式
在底行模式下 w为保存 q为退出 !强制执行 

解决 vi 方向键变成 ABCD、退格键失效:

复制代码
输入sudo vi /etc/vim/vimrc.tiny
进入 vi 后,按 i 进入插入模式
修改set compatible为set nocompatible
并下面新增一行set backspace=2
显示文件内容

cat

cat 文件名

head(前) / tail(后)

默认显示hello.txt的前10行 head hello.txt

显示hello.txt的前n行 head -n hello.txt

默认显示hello.txt的后10行 tail hello.txt

显示hello.txt的后n行 tail -n hello.txt

网络配置

ubuntu网络配置

桥接模式:虚拟机和宿主机在同一个局域网 ,独立IP,局域网内其他设备能直接访问虚拟机 同一网段下

nat模式:虚拟机借用宿主机IP上网,内网隔离 ,外部设备默认不能直接访问虚拟机 不在同一网段

ping

测试能否联网 ping [www.baidu.com](https://www.baidu.com)

检测能否与主机通信 ping 主机ip地址

结束 ctrl c

ifconfig

显示网络配置接口,可以查看IP地址

ipconfig 查看windowsIP地址

netstat

查看所有网络连接状态 netstat -a

查看tcp的网络连接状态 netstat -at

查看udp的网络连接状态 netstat -au

curl传输数据的工具

curl发送一个基本的get请求,提供url即可

curl http://example.com :获取http://example.com的内容

通过-X POST 选项发送一个post请求,可以使用-d选项添加发送的数据

curl -X POST -d "param1=value1&param2=value2" http://example.com/resource

wget

用于从网络下载文件,需要指定url

下载file.zip wget http://example.com/file.zip

Linux 安装工具

软件包安装
软件包的管理机制
常见的软件包后缀
windows .exe
android .apk
ubuntu .deb
redhat .rpm

debian: deb软件包管理机制

redhat: rpm软件包管理机制(Red Hat Package Manager)

源 查看:sudo vi /etc/apt/sources.list

换源就是更新该文件中的网址:

复制代码
换源:(推荐官方源或其他可用源)
步骤 1:备份当前源列表
sudo  cp  /etc/apt/sources.list  /etc/apt/sources.list.bak
步骤 2:编辑源列表文件
sudo  vi  /etc/apt/sources.list
步骤 3:替换为可用源(以 Ubuntu 18.04 LTS 为例)
将文件内容替换为 Ubuntu 官方源(或其他地区的镜像源,如阿里云、网易源),例如官方源:
deb http://archive.ubuntu.com/ubuntu/ bionic main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ bionic-updates main restricted universe multiverse
deb http://archive.ubuntu.com/ubuntu/ bionic-backports main restricted universe multiverse
deb http://security.ubuntu.com/ubuntu/ bionic-security main restricted universe multiverse
如果官方源速度慢,也可以尝试阿里云源:
deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
步骤 4:保存并退出
esc 退回命令行模式,shift : 回到底行模式,wq保存并退出
软件包分类

二进制软件包:xx.deb

源码包: xx.dsc 源码、二进制文件、配置文件、说明文档

软件管理工具

在Debian的linux发行版本下,采用dpkg和apt两种包管理工具。

dpkg :离线安装,安装本地包,不需要网络;与仓库内的软件没有关系;不能检查包的依赖关系

apt :在线安装,通过仓库获取软件包资源;解决依赖关系,把依赖包一起安装;不能安装本地包

dpkg工具

离线安装软件包:dpkg -i 软件包全称

相关操作

复制代码
sudo dpkg -s 软件包名:显示软件的安装状态  
运行:直接写软件名
sudo dpkg -l 软件包名:查看版本名
sudo dpkg -L 软件包名:显示软件各种信息的安装路径
sudo dpkg -r 软件包名:卸载软件
sudo dpkg -P 软件包名:完全卸载软件,重新下载不会保留上一次配置。
查看软件使用:man  sl

dpkg安装出现报错:dpkg: 错误: 另外一个进程已经为 dpkg frontend 加锁

解决方法:执行以下命令

sudo rm /var/lib/dpkg/lock-frontend 或者sudo rm /var/lib/dpkg/lock

apt工具
apt-get

在线安装软件包:sudo apt-get install 软件包名

默认下载的包存放路径:/var/cache/apt/archives/

相关操作

复制代码
sudo apt-get remove 软件包名:卸载软件
sudo apt-get --purge  remove 软件包: 完全卸载软件包(包括配置文件)
sudo apt-get update :下载更新软件包列表信息
sudo apt-get upgrade : 将系统中所有软件包升级到最新的版本
sudo apt-get upgrade  软件包名:将系统中指定的软件包升级到最新的版本
apt-cache
复制代码
sudo apt-cache show 软件包名:获取二进制软件包的详细描述信息
sudo apt-cache policy 软件包名:获取软件包当前的安装状态
sudo apt-cache depends 软件包名:获取该软件包的依赖信息
sudo apt-cache rdepends 软件包名:获取所有依赖于该软件包的软件包

系统信息和控制

top

top动态显示系统进程和资源使用情况

退出:q

复制代码
PR:优先级
VIRT:占用虚拟内存
RES:真正占用内存
SHR:共享内存

PR = NI + 20
NI值(nice值)的范围:-20 ~ 19,NI值越小优先级越高

sudo  nice  -n  NI值 启动进程命令
sudo  nice  -n	5     bash

sudo  renice [NI值]  PID:改变正在运行的进程的优先级
sudo  renice   6    2712
ps

显示所有用户的进程的详细信息:ps -aux

可以显示所有用户的进程号及父进程号:ps -ef

表头 含义 你图里标注
USER 进程所属用户 用户
PID Process ID,进程编号 进程号
%CPU CPU 占用百分比 (图里漏写,CPU 使用率)
%MEM 物理内存占用百分比 (图里漏写,内存占比)
VSZ Virtual Set Size,虚拟内存总大小 虚拟内存
RSS Resident Set Size,常驻物理内存(真正占用的物理内存) 实际物理内存
TTY 关联终端 终端
STAT 进程状态(R/S/Sl/Z 等) 进程状态
START 进程启动时间 开启时间
TIME 进程累计占用 CPU 总时间 CPU 运行时长
COMMAND 启动这个进程的命令 命令
kill

本质是发送信号的命令

查看信号:kill -l

给PID进程发送num信号:kill -num PID

杀死PID进程,默认发送15号结束进程信号:kill PID

查看磁盘空间使用情况

df -Th

其他命令

grep 搜索文件中的文本

在file.txt中查找包含hello的字符串:grep "hello" file.txt

精确查找:grep "\<要查找的内容\>" 文件名

find 查找文件

find 路径 -name 文件名

示例:find ~/work -name "*.txt"

注意:?为只匹配任意一个字符,*匹配所有

zip

zip命令用于将一个文件或多个文件压缩成单一的压缩文件

unzip命令用于解压

1)具有归档功能,并进行压缩

2)可以压缩文件夹,后缀为.zip

3)压缩或者解压后源文件依然存在

tar 解压和压缩文件

1)具有归档功能,并通过选项可以进行压缩或解压

2)压缩或解压后源文件存在

3)需要写全压缩或解压的文件名格式

选项:

复制代码
-x : 释放归档文件,解压
-c : 创建一个新的归档文件,压缩
-v : 显示归档和释放的过程信息
-f : 用户指定归档文件的文件名,否则使用默认名称
-j : 由tar生成归档,然后由bzip2压缩 .tar.bz2
-z : 由tar生成归档,然后由gzip压缩 .tar.gz

选项组合:

复制代码
-cjf		以bz2格式压缩文件
-czf		以gz格式压缩文件
-xvf        解压压缩包

注意 : jz 在f 前; 压缩后源文件依然存在,解压后压缩包依然存在

示例:

tar -cjf xxx.tar.bz2 文件

tar -czf xxx.tar.gz 文件

tar -xvf xxx.tar.gz 或者 xxx.tar.bz2

history 显示命令历史记录

在家目录下的隐藏文件,通过vi打开:

.bash_history:默认存放2000条历史记录命令的文件

.bashrc :定义HISTSIZE(使用history时默认显示的记录个数)

HISTFILESIZE(.bash_history文件可以存放命令的个数)

man

man 章节 内容

man man:查看man手册

q 退出

复制代码
1 Executable programs or shell commands 可执行程序或shell命令
2 System calls (functions provided by the kernel) 系统调用 
3 Library calls (functions within program libraries) 库函数 
sudo

sudo是 Linux 和其他类 Unix 操作系统中的一个非常重要的命令,用于以另一个用 户的身份(通常是 root 用户)执行命令。

sudo的全称是 "superuser do",它允许 普通用户在临时提升权限的情况下执行需要更高权限的操作。

eg: sudo command

切换到root用户

sudo su

MQTT

基础介绍

基于TCP/IP实现的一种应用层协议,同时兼具TCP和UDP的协议特点:有tcp的可靠性特点,也有udp分包、灵活的特点

适合小数据量传输的场景:比如物联网

三种身份:

1)broker 服务器 ,只负责消息转发,不处理,一般用现成的库搭建,不会做二次开发

2)发布者 往某个主题发消息

3)订阅者 订阅某个主题,接收来自这个主题的信息

客户端可以通过paho库来实现

消息

主题: topic 可以是任意的字符串,双方协定

消息体:字节流

服务质量QoS(Quality of Service)

1) 最多一次(QoS 0)这一级别会发生消息丢失,消息发布依赖于底层的TCP/IP 网络,适用于环境监测或快速数据采集

2) 至少一次(QoS 1)这一级别会确保消息到达,但是消息可能会重复,适用于智能家居控制或设备状态更新

3) 只有一次(QoS 2)确保消息只有一次到达,适用于财务交易、关键任务控制系统等

不同质量所付出的代价也不同 选择合适的QoS

搭建MQTT服务器(Broker)

windows版本:

安装mosquitto-1.6.9-install-windows-x64.exe,全部按默认安装即可,路径可以自己修改。

安装后在安装路径会有一个名为mosquitto.conf的配置文件,需要修改下配置文件才能让任意主机访问。

复制代码
打开listener配置并允许任意IP访问,默认端口为1883
374行 listener 1883

修改allow_anonymous为true以允许主机匿名访问
651行 allow_anonymous true

如果想查看调试信息,那么就把log_type的配置项打开
600行 log_type debug

Linux版本:

获取离线压缩包mosquitto-1.6.3.tar.gz后

执行以下指令

复制代码
tar -xf mosquitto-1.6.3.tar.gz
cd mosquitto-1.6.3/
make
sudo make install

代码实现客户端的接口

流程

创建MQTT实例 连接服务器(代理) 订阅主题 / 接收消息 / 发布消息

相关接口
Client
复制代码
Client(client_id="", clean_session=True, userdata=None, protocol=MQTTv311, transport="tcp")

功能:创建MQTT客户端实例 参数: client_id :连接到代理时使用的唯一客户端ID字符串。 如果client_id长度为零或无,则会随机生成一个 clean_session :一个决定客户端类型的布尔值。 如果为True,那么代理将在其断开连接时删除有关此客户端的所有信息。 如果为False,则客户端是持久客户端,当客户端断开连接时,订阅信息和排队消息将被保留 userdata :用户定义的任何类型的数据作为userdata参数传递给回调函数。 它可能会在稍后使用user_data_set()函数进行更新。 protocol :用于此客户端的MQTT协议的版本。 可以是MQTTv31或MQTTv311 transport :设置为"websockets"通过WebSockets发送MQTT。 保留默认的"tcp"使用原始TCP 返回值:创建的实例

示例:

复制代码
import paho.mqtt.client as mqtt
client = mqtt.Client()
connect
复制代码
connect(host, port=1883, keepalive=60)

功能:将客户端连接到远程代码

参数 : host : 代理的IP地址或主机名 port :要连接的服务器主机的端口号,默认1883 keepalive :与代理通信之间允许的最长时间段(以秒为单位)。 如果没有其他消息正在交换,则它将控制客户端向代理发送ping消息的速率

on_connect:connect

功能:当代理响应连接请求时调用

复制代码
on_connect(client,userdata,flags,rc)

rc: rc的值决定是否连接成功 0 为连接成功 1 协议版本错误 2 无效的客户端标识 3 服务器无法使用 4 错误的用户名和密码 5 未经授权

subscribe
复制代码
subscrible(topic,qos=0)

功能:订阅主题

参数 :topic:一个字符串,指定要订阅的订阅主图

qos:期望的服务质量,默认0

on_message
复制代码
on_message(client,userdata,massage)

功能:当收到关于客户端订阅的主题的消息是调用

message:是一个描述所有参数的MQTTMessage

message.topic 主题

message.payload 负载,消息的内容(字节类型,通常要**.decode()**转成字符串)

publish
复制代码
publish(topic,payload)

功能:从客户端发送消息给代理

参数 :topic:该消息发布的主题

payload:要发送的实际消息。如果没有给出,或设置为无,则将使用零长度消息。

返回值 :rc:

内容 含义
MQTT_ERR_SUCCESS 成功
MQTT_ERR_NO_CONN 客户端当前未连接
MQTT_ERR_QUEUE_SIZE 当使用max_queued_messages_set来指示消息既不排队也不发送。

代码实现订阅和发布

订阅

复制代码
# 模块导入
import paho.mqtt.client as mqtt


# 定义连接回调函数
def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("连接成功")
        client.subscribe("up")
        client.subscribe("nb")
    else:
        print("连接失败")


# 定义消息到达后的回调函数
def on_message(client, userdata, message):
    print(f"收到主题{message.topic},内容为{message.payload.decode()}")


# 创建实例
client = mqtt.Client()

# 绑定
client.on_connect = on_connect
client.on_message = on_message

# 连接
client.connect("192.168.50.98", 1883, 60)

# 阻塞,自动处理接收到的数据
client.loop_forever()

发布

复制代码
# 模块导入
import paho.mqtt.client as mqtt
import time


# 定义连接回调函数
def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("连接成功")
        client.publish("up", "Hello")
    else:
        print("连接失败")


# 创建实例
client = mqtt.Client()

# 绑定回调函数
client.on_connect = on_connect

# 连接
client.connect("192.168.50.98", 1883, 60)

# 阻塞,自动处理接收数据
# client.loop_forever()

# 启动网络循环
client.loop_start()
# 等待一下,防止程序退出太快(测试用)
time.sleep(2)
# 停止循环
client.loop_stop()
# 断开连接
client.disconnect()

HTTP

简介

HTTP是一个基于TCP通信协议传输来传递数据 用于 B/S 架构

HTTP默认端口号为80,但是你也可以改为8080或者其他端口

是用于Web Browser(浏览器)到 Web Server(服务器)进行数据交互的传输协议

浏览器 发送请求 服务器 响应请求

特点

无连接:每次连接只处理一个请求

媒体独立 :任何数据都可以通过http发送 客户端以及服务器指定使用适合的MIME-type内容类型

无状态:对于事务处理没有记忆能力

协议格式

请求协议格式 响应协议格式
请求行 状态行
请求头部 响应头部
空行 空行
请求数据 响应正文
请求行

请求行 是由请求方法字段、url字段、http协议版本字段3个部分组成。请求行定义了本次请求的方式,格式如下:GET /example.html HTTP/1.1(CRLF)

请求方法有:OPTIONS、HEAD、GET、POST、PUT、DELETE、TRACE、CONNECT

常用的也就是 get 和 post

GET和POST区别
GET POST
GET请求的数据会附加在URL之后,以?分割URL和传输数据,多个参数用&连接(GET请求的数据会暴露在地址栏) 把请求的数据放置在HTTP请求包的包体中
在使用GET请求时,传输数据会受到URL长度的限制 理论上是不会受限制的,但是实际上各个服务器会规定对POST提交数据大小进行限制
返回的内容可以被浏览器缓存起来 不会缓存POST请求返回的内容
只接受ASCII字符 没有限制
请求头

也被称作消息报头,请求头是由一些键值对组成,每行一对,关键字和值用英文冒号":"分隔。允许客户端向服务器发送一些附加信息或者客户端自身的信息,典型的请求头如下:

Accept:作用:描述客户端希望接收的 响应body 数据类型;示例:Accept:text/html

Accept-Charset:作用:浏览器可以接受的字符编码集;示例:Accept-Charset:utf-8

Accept-Language:作用:浏览器可接受的语言;示例:Accept-Language:en

Connection:作用:表示是否需要持久连接,注意HTTP1.1默认进行持久连接;示例:Connection:close

Content-Length:作用:请求的内容长度:示例:Content-Length:348

Content-Type:作用:描述客户端发送的 body 数据类型

请求数据

请求数据:请求数据不在GET方法中使用,而是在POST方法中使用。POST方法适用于需要客户填写表单的场合。与请求数据相关的最常使用的请求头是Content-Type和Content-Length。

状态行

由三部分组成,HTTP协议的版本号、状态码、以及对状态码的文本描述。例如:HTTP/1.1 200 OK (CRLF) 。(200表示请求已经成功)

常用状态码

复制代码
● 200 OK:请求成功,服务器成功处理了请求并返回所请求的资源。
● 301 Moved Permanently:请求的资源已永久移动到新的URL,客户端应更新其链接。
● 302 Found:请求的资源暂时移动到新的URL,客户端应继续使用原始URL。
● 400 Bad Request:服务器无法理解请求的语法,通常是由于客户端发送的请求不正确导致的。
● 401 Unauthorized:请求要求身份验证,客户端需要提供有效的身份凭证。
● 403 Forbidden:服务器拒绝请求,客户端没有访问所请求资源的权限。
● 404 Not Found:请求的资源不存在,服务器无法找到所请求的资源。
● 500 Internal Server Error:服务器内部错误,无法完成请求的处理。
● 503 Service Unavailable:服务器当前无法处理请求,通常是由于服务器过载或维护导致的。
http和https的区别
对比项 http https
默认端口 80 443
传输方式 明文 加密
安全性 可被窃听 加密+完整性校验
身份认证 无 CA证书验证服务器身份
性能 快 稍慢
URL前缀 http:// https://
协议栈 HTTP->TCP HTTP->TLS->TCP

Flask

Flask 是一个非常小的 Python Web 框架 ,被称为微型框架:只提供了一个稳健的核心,其他功能全部通过扩展实现

Flask 的特点

特点 说明
轻量级和简洁 微框架,提供最基本功能,不强制使用任何特定工具或库
灵活性 没有强制性的项目布局或组件,开发者可自行定制
可扩展性 通过插件和扩展添加功能(表单、数据库、用户认证等)
内置开发服务器 方便本地调试和测试

安装:pip install flask

第一个 Flask 应用

复制代码
# 导入Flask类
from flask import Flask

# 创建Flask应用实例
app = Flask(__name__)

# 装饰器,用于告诉Flask哪个URL应该触发下面的函数
@app.route('/')
# 当用户访问根URL时,会调用以下函数
def hello_world():
    return 'hello world!'

if __name__ == '__main__':
    # 启动Flask内置开发服务器
    app.run(debug=True)

运行后打开浏览器访问 127.0.0.1:5000/ 即可看到结果。

默认端口 5000 ;debug=True 开启调试模式,改代码后自动重启

路由

路由是 URL 到 Python 函数的映射:当用户访问特定 URL 时,Flask 调用对应的视图函数处理请求。

复制代码
@app.route('/')
def hello_world():
    return 'hello world!'

# 将 /about URL映射到 about 函数
@app.route('/about')
def about():
    return 'This is about page'

第一个位置参数为路由分发的请求路径,分两类:

1)静态参数路由 ------ 路径写死

复制代码
@app.route('/')
@app.route('/about')
@app.route('/index')

2)动态参数路由 ------ 路径里用 <变量名>,接收字符串和数字类型

复制代码
@app.route('/index/<name>')
def index(name):
    return f"hello {name}"

可以指定 int 型 (如 /index/<int:id>),视图函数中必须有同名的形参来接收:

复制代码
@app.route('/index/<int:id>')
def index(id):
    return f"hello {id}"

只写了 <name> 的话,/index/123 进来拿到的是字符串 "123";指定 <int:id> 才会转成整数。

视图函数、请求对象、响应对象

视图函数:处理请求并返回响应的 Python 函数。通常接收请求对象作为参数,返回响应对象,或直接返回字符串、HTML 等内容。

请求对象(request):包含客户端发来的请求信息------请求方法、URL、请求头、表单数据等。

复制代码
from flask import request

结合 curl 指令与服务器进行数据传输:

复制代码
curl -X POST -d "username=John" 127.0.0.1:5000/submit

获取 GET 请求的数据用 request.args.get():

复制代码
@app.route('/search')
def search():
    keyword = request.args.get('key')
    return f"搜索的关键词是 {keyword}"

访问 /search?key=python → 返回 搜索的关键词是 python。

响应对象:包含发送给客户端的响应信息------状态码、响应头、响应体。Flask 默认把字符串、HTML 直接作为响应体。

复制代码
from flask import make_response

@app.route('/custom_response')
def custom_response():
    response = make_response('This is a custom_response')
    return response

模板(Templates)

Flask 使用 Jinja2 模板引擎渲染 HTML 模板,把 Python 代码嵌入 HTML,动态生成网页内容。

复制代码
from flask import Flask, render_template

@app.route('/hello/<name>')
def hello(name):
    return render_template('hello.html', name=name)

目录要求 :在当前 Python 文件同级目录 下新建 templates 文件夹,模板放里面。

⚠️ 文件夹名必须 是 templates(不能是 template 或别的名字),否则 Flask 找不到模板。

templates/hello.html:

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1> hello {{name}} </h1>
</body>
</html>

条件判断(Jinja2 语法):

复制代码
{% if 条件 %}
...
{% else %}
...
{% endif %}
复制代码
@app.route('/hello/<name>')
def hello(name):
    # 假设 lisi、Alice 是 VIP 用户,其他人是普通用户
    is_vip = False
    if name in ['lisi', 'Alice']:
        is_vip = True
    return render_template('hello.html', name=name, is_vip=is_vip)
复制代码
<body>
    <h1> hello {{name}} </h1>
    {% if is_vip %}
        恭喜!您是尊贵的VIP用户!
    {% else %}
        欢迎体验!
    {% endif %}
</body>

常用扩展

复制代码
# request  全局请求对象,包含当前 HTTP 请求的所有信息
# send_file  允许从服务器向客户端发送文件
# jsonify  创建 JSON 响应的便捷函数
from flask import Flask, request, send_file, jsonify

# Flask-Compress:压缩响应数据(gzip/deflate/brotli/zstandard)
# 显著减少传输体积,加快客户端加载
from flask_compress import Compress

# CORS 是一种安全机制,用于控制不同域之间的资源访问
# 当前端访问与自己不同域(协议/域名/端口不同)的 API 时就需要 CORS
from flask_cors import CORS

# secure_filename:安全处理用户上传的文件名
# 移除有害字符、防目录遍历攻击、转为安全的 ASCII 文件名
from werkzeug.utils import secure_filename

HTML

简介

HTML (Hyper Text Markup Language,超文本标记语言):用来描述网页的一种语言。

  • 所谓"超文本":可以加入图片、声音、动画、多媒体等内容,还可以从一个文件跳转到另一个文件。

  • HTML 不是编程语言 ,而是一种标记语言(markup language)。

  • Web 浏览器 的作用是读取 HTML 文档并以网页形式显示;浏览器不会显示 HTML 标签,而是用标签解释页面内容。

文档结构与核心标签

标签 作用
<html> 所有标签的根节点,最大的标签
<head> 文档头部,描述文档的属性信息(title、meta、base、style、script、link)
<title> 文档标题,让页面拥有自己的标题(head 里必须设置的就是它)
<body> 文档主体,页面内容基本都放这里(a、p、h、div、span、img...)

标签格式

  1. 用尖括号包围的关键字,如 <html>

  2. 通常成对存在,如 <body></body>

  3. 前面是开始标签,后面是结束标签

标签分类

  • 单标签 (空标签):<标签名>,如 <br>

  • 双标签 :<标签名> 内容 </标签名>

常用标签

1)h1~h6 标题标签

复制代码
<h1>这是标题标签</h1>
<h2>这是标题标签</h2>
<!-- ... 一直到 h6 -->

2)p 段落标签:段落会根据浏览器窗口大小自动换行

复制代码
<p> 文本内容 </p>

3)br 换行标签(单标签)

复制代码
<br>

4)div 标签:块级元素,可把文档分割为独立的、不同的部分,也能嵌套标签

复制代码
<div class="news">
    <h2>News headline 1</h2>
    <p>some text. some text. some text...</p>
</div>

div 可以设置 class 或 id,通过选择器设置属性,内部成员就具有相同属性。

style 与 class

复制代码
<style>
    h1 {color:red;}
    p  {color:blue;}
</style>

class 属性规定元素的类名,主要用于指向样式表中的类:

复制代码
<!-- head -->
<style>
    .intro{ color:blue; }
    .test { color:red;  }
</style>

<!-- body -->
<div class="test">
    <h2>News headline</h2>
    <p>some text. some text. some text...</p>
</div>

表单标签

<form> 标签用于创建 HTML 表单来接收用户输入。

属性 取值 作用
method get / post 规定发送表单数据时使用的 HTTP 方法
action URL 规定提交表单时将数据发送到哪里
复制代码
<form method="post" action="/submit"></form>

input :输入标签,单标签 ,属性很多,重点掌握 text 和 number。

复制代码
<!-- 学生姓名输入框:单行文本,name=username,默认提示"学生姓名" -->
<input type="text" name="usrname" placeholder="学生姓名" required>

type="number" 用于输入数字,可用以下属性限制:

属性 说明
max 允许的最大值
min 允许的最小值
step 合法的数字间隔
value 默认值
复制代码
<input type="number" name="score" placeholder="考试成绩(0-100)"
       max="100" min="0" step="0.5" required>

button 的 type:

值 描述
button 可点击的按钮
submit 提交按钮(提交表单数据)
reset 重置按钮(重置为初始值)
复制代码
<button type="button">点击</button>

综合示例:Flask + HTML 表单

HTML(templates/hello.html)

复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        p {color:blue;}
        .intro{ color:red; }
        .test { color:blue; }
    </style>
</head>
<body>
    <h1>学生管理系统</h1>
    <form method="post" action="/submit">
        请输入学生姓名:
        <input type="text" name="username" placeholder="学生姓名" required>
        <br>
        请输入成绩:
        <input type="number" name="score" placeholder="考试成绩(0-100)"
               max="100" min="0" step="0.5" required>
        <button type="submit"> 提交成绩 </button>
    </form>
</body>
</html>

Flask(app.py)

复制代码
@app.route('/')
def hello_world():
    return render_template('hello.html')

@app.route('/submit', methods=['post'])
def submit():
    username = request.form.get('username')
    score = request.form.get('score')
    return f" {username}: {score}分 提交成功!"

流程 :输入 ip 出现 hello.html 页面 → 在页面填写表单 → 点击提交(向 /submit 发送 POST 请求)→ 服务器接收并返回一个网页。

request.form.get() 取的是 POST 表单 数据,request.args.get() 取的是 GET URL 参数,别混


Postman

Postman 是接口测试工具,不用写前端页面就能给后端发请求、看响应。老师课上用它测 Flask 写的接口。

基本用法(需先运行 Flask 代码)

  1. 新建请求 → 选择方法(GET / POST)

  2. 填 URL,如 http://127.0.0.1:5000/search?key=python

  3. GET:参数直接写在 URL 或 Params 里

  4. POST :切到 Body → 选 form-data 或 x-www-form-urlencoded,填键值对

  5. 点 Send → 下方看响应体、状态码、响应头

场景 方法 参数位置
查询/搜索 GET URL 后 ?key=value(Params)
提交表单 POST Body(form-data)

Postman 里看到的 Content-Type 请求头,就是 HTTP 章节里说的"描述客户端发送的 body 数据类型"------用 form-data 时会自动带上 multipart/form-data。


Docker

是什么

Docker 是一种开源的容器化平台 ,允许开发者把应用程序及其所有依赖项(库、配置文件、环境变量等)打包到一个标准化的、轻量级、可移植的单元中,称为容器(Container)。

这些容器可以在任何支持 Docker 的环境中快速部署和运行,实现**"一次构建,处处运行"** 。容器完全使用沙箱机制 ,相互之间不会有任何接口,而且性能开销极低。

为什么需要 Docker

问题根源:应用部署的环境问题。 大型项目组件多、运行环境复杂:

  • 依赖关系复杂,容易出现兼容性问题

  • 开发、测试、生产环境有差异

例如一个项目部署时要依赖 node.js、Redis、RabbitMQ、MySQL 等,它们需要的函数库、依赖项各不相同,甚至冲突。

Docker 的两个手段

  1. 将应用的 Libs(函数库)、Deps(依赖)、配置 与应用一起打包

  2. 将每个应用放到隔离容器中运行,避免互相干扰

为什么不同系统上的库会不兼容? Ubuntu 和 CentOS 都基于 Linux 内核,无非系统应用不同、提供的函数库有差异。把 Ubuntu 版的 MySQL 装到 CentOS 上,调用 Ubuntu 函数库时就会找不到或不匹配而报错。

Docker 的三层解法

问题 Docker 的解法
不同系统环境 把用户程序与所需调用的系统(如 Ubuntu)函数库一起打包 ;运行时借助宿主机的 Linux 内核直接跑
依赖关系复杂/冲突 应用+依赖+函数库+配置一起打包成可移植镜像 ;容器用沙箱机制相互隔离
开发/测试/生产环境差异 镜像中包含完整运行环境(含系统函数库),仅依赖 Linux 内核,可在任意 Linux 上运行

Docker 的优势

  1. 把程序及其依赖、运行环境一起打包为镜像,可迁移到任意 Linux 操作系统

  2. 运行时利用沙箱机制形成隔离容器,各应用互不干扰

  3. 启动、移除都可以通过一行命令完成,方便快捷

Docker 与虚拟机的区别

对比项 Docker 容器 虚拟机
架构层级 操作系统级别 硬件级别
启动速度 秒级启动 分钟级启动
资源占用 很小,MB 级别 较大,GB 级别
隔离性 进程级别隔离(较轻) 完全隔离(更安全)
镜像大小 几十到几百 MB 包含整个操作系统,很大
运行效率 高 低

结论:Docker 更轻量、更快速,更适合微服务和持续集成;虚拟机隔离性更强,适合运行不同内核或对安全性要求极高的场景。

🌱 墨补(老师课上留的思考题,给个参考答案)

  • Docker 和直接部署到 Linux 的区别 :直接部署要把依赖一个个装在宿主机上,环境互相污染、换机器就得重装;Docker 把环境和应用封进镜像,换机器只跑一条 docker run。

  • Docker 和 K8s 的区别 :Docker 是容器引擎/运行时 ,管单机上容器的构建与运行;K8s 是容器编排平台 ,管的是一整个集群里成百上千个容器的调度、扩缩容、自愈和负载均衡。两者是"零件"和"调度系统"的关系。

  • root 模式 vs rootless 模式 :默认 Docker 守护进程以 root 运行,容器逃逸风险较高;rootless 让守护进程和容器都以非 root 用户运行,安全性更高,代价是少量性能与功能限制。

  • Docker 镜像 vs 系统镜像 :系统镜像是整机备份 (含整个操作系统内核),用于装系统;Docker 镜像是应用及其运行环境的分层打包(不含内核,共享宿主内核),用于跑应用。

四个核心概念

概念 说明
镜像(Image) 容器的静态模板 ,包含应用运行所需的所有依赖和文件,定义运行环境。镜像是不可变的
容器(Container) 镜像的一个运行实例 ,有自己的文件系统、进程、网络,是动态的。从镜像启动,运行时保持可变
Dockerfile 文本文件,描述如何自动构建镜像(指定基础镜像、安装软件、复制文件等)
仓库(Registry) 存储和分发镜像的平台,如 Docker Hub(官方公共仓库)或私有仓库(如 Harbor)

一句话记住关系:镜像是容器的模板,容器是镜像的实例;Dockerfile 里写着构建镜像的指令。

Dockerfile 常用指令

1)FROM ------ 指定基础镜像,必须为第一个命令

复制代码
格式:
    FROM <image>
    FROM <image>:<tag>
    FROM <image>@<digest>
复制代码
FROM python:3.9-slim
部分 含义
<image> 基础镜像名称,如 ubuntu、python、nginx、alpine
<tag> 版本标签,如 3.9、slim、latest
<digest> 镜像唯一哈希值(更精准但不常用),如 sha256:xxxxxx,用于精确锁定内容

2)RUN ------ 构建镜像阶段的执行命令,每一条 RUN 都会生成一层镜像层

可做的事:安装软件包、下载并解压文件、编译安装软件、配置环境变量。

复制代码
RUN mkdir /bigproject

3)ADD 和 COPY

指令 核心功能
ADD ① 复制本地文件/目录到镜像内 ② 自动解压归档文件(tar/gzip/bzip2) ③ 可下载远程 URL(不推荐用于生产)
COPY 功能类似 ADD,但不会自动解压 ,也不能访问网络资源,推荐优先使用
复制代码
COPY src dest

4)WORKDIR ------ 设置容器的工作目录,类似 Linux 的 cd,目录不存在会自动创建

复制代码
WORKDIR /path/to/workdir

5)EXPOSE ------ 声明容器要暴露的端口

复制代码
EXPOSE 80

⚠️ EXPOSE 只是声明/文档用途 ,真正对外映射必须靠运行时加 -p 宿主机端口:容器端口:

复制代码
docker run -p 8080:80    # 把容器 80 端口映射到宿主机 8080 端口

6)ENTRYPOINT 和 CMD

CMD ------ 指定默认命令或参数 ,可以被 docker run 时附加的命令覆盖

复制代码
CMD ["executable", "param1", "param2"]
复制代码
FROM python:3.9-slim
CMD ["python", "app.py"]

ENTRYPOINT ------ 定义容器启动时始终运行的命令

复制代码
ENTRYPOINT ["executable", "param1", "param2"]

特性:

  • 支持附加参数 :docker run 的参数不会覆盖 ENTRYPOINT,而是作为它的附加参数

  • 不可轻易覆盖 :除非用 --entrypoint 明确指定新命令

  • 唯一性 :每个 Dockerfile 中只能有一个 ENTRYPOINT,定义多个只有最后一个生效

  • 主要用于定义不变的主命令

复制代码
FROM python:3.9-slim
ENTRYPOINT ["nginx", "-g", "daemon off;"]

两者配合使用 :ENTRYPOINT 定义主命令 (固定不变),CMD 定义默认参数 (可变);docker run 传入的参数会覆盖 CMD,但不会覆盖 ENTRYPOINT。

复制代码
FROM nginx
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
执行 等价于
docker run mynginx nginx -g daemon off;
docker run mynginx -s reload nginx -s reload(CMD 被覆盖)

实战写法(gunicorn 启动 Flask)

复制代码
ENTRYPOINT ["gunicorn", "app:app", "-c", "gunicorn.conf.py"]

gunicorn.conf.py 常用配置:

复制代码
bind = "0.0.0.0:80"                              # 绑定ip和端口号
workers = multiprocessing.cpu_count() * 2 + 1    # 工作进程数量
threads = 8                                      # 每个进程开启的线程数量
backlog = 2048                                   # 最大连接数量
worker_class = "gevent"
worker_connections = 1000                        # 最大连接数量
max_requests = 1000                              # 最大请求数量
daemon = False                                   # 是否后台运行
pidfile = '/bigproject/gunicorn.pid'             # pid文件存储路径
accesslog = '/bigproject/access.log'             # 访问日志文件
errorlog = '/bigproject/gunicorn.log'            # 错误日志文件
loglevel = 'info'                                # 日志级别

环境安装与配置(Ubuntu)

复制代码
# 1. 更新软件源
sudo apt update

# 2. 安装基本软件
sudo apt-get install apt-transport-https ca-certificates curl software-properties-common lrzsz -y

# 3. 指定使用阿里云镜像(添加 GPG 公钥 + 软件源)
sudo curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"

# 4. 安装 docker
sudo snap install docker

# 5. 查看版本,验证安装
docker version

$(lsb_release -cs) 会返回当前 Ubuntu 代号:20.04 → focal,22.04 → jammy。

配置国内镜像源(Docker Hub 在境外,直连慢或不稳)

复制代码
cd /etc/docker
sudo touch daemon.json
sudo chmod 777 daemon.json
sudo vi daemon.json        # 写入下面内容
复制代码
{
  "registry-mirrors": [
    "https://docker-0.unsee.tech",
    "https://docker-cf.registry.cyou",
    "https://docker.1panel.live"
  ]
}
复制代码
# 重启 docker 生效
sudo systemctl daemon-reload
sudo systemctl restart docker

# 测试:能正常下载镜像就说明配置成功
sudo docker pull alpine:latest

requirements.txt(放在项目文件夹中)

复制代码
Flask==2.3.2
Werkzeug==2.3.7
Jinja2==3.1.2
itsdangerous==2.1.2
click==8.1.3
gunicorn==20.1.0

部署常用命令

构建镜像

复制代码
sudo docker build -t 镜像名称:标签 .
sudo docker build -t ai_bigproject .          # 不写标签默认 latest

-t 给镜像打标签;末尾的 . 表示使用当前目录下的 Dockerfile 和构建上下文(别漏了)。

查看镜像 / 容器

复制代码
sudo docker images        # 查看构建的镜像
sudo docker ps            # 查看运行中的容器
sudo docker ps -a         # 查看所有容器(包括已关闭的)

启停 / 删除

复制代码
sudo docker stop  容器ID                      # 停止容器
sudo docker start 容器ID                      # 启动已存在但已停止的容器
sudo docker rm -f 容器ID                      # 删除容器
sudo docker rmi -f 镜像ID                     # 删除镜像

🔥 面试题:Docker 查看容器的命令是什么? → docker ps(运行中的)、docker ps -a(全部)。

从镜像创建并运行容器

复制代码
sudo docker run -d --restart=always -p 18080:80 ai_bigproject:latest
部分 含义
docker run 从镜像创建并启动一个新容器
-d --detach,后台(守护)模式运行
--restart=always 重启策略:无论退出状态如何都自动重启。其他:no(默认不重启)、on-failure(非正常退出时重启)、unless-stopped(除非手动停止)
-p 18080:80 端口映射,格式 主机端口:容器端口;外部通过 http://主机IP:18080 访问
ai_bigproject:latest 要运行的镜像名称与标签

排查:容器无法正常启动怎么办?

复制代码
# 1. 查看容器日志(容器ID 取前 3 位就行)
sudo docker logs 容器ID

# 2. 从容器把内容复制出来修改
sudo docker cp 7d0:/bigproject/ ./

# 3. 查看 ip 地址(先装工具)
sudo apt install net-tools
ifconfig

# 4. 进入容器内部
sudo docker exec -it 容器ID /bin/bash
exit        # 退出

🔥 面试题:进入 docker 容器的指令? → docker exec -it 容器ID /bin/bash。

检查镜像内环境

复制代码
docker run -it --rm --entrypoint /bin/sh python:3.9
参数 含义
-it -i(--interactive)保持标准输入开启;-t(--tty)分配伪终端
--rm 容器停止时自动删除,避免产生无用容器
--entrypoint /bin/sh 覆盖镜像默认入口点,直接运行 shell
python:3.9 使用的镜像

测试接口(上传文件)

复制代码
sudo curl -X POST -F "file=@test.jpg" http://localhost:18080/upload --output file
参数 含义
-X POST 指定 HTTP 方法为 POST
-F "file=@test.jpg" 发送 multipart/form-data 表单(常用于文件上传)。file 是表单字段名,@ 表示从本地文件读取
--output 文件名 指定保存返回内容的文件名

也可以用 Postman 测试同一接口。

容器导出为镜像 / 镜像导出导入

复制代码
# 容器导出为镜像
sudo docker commit [容器ID或名称] [镜像名称]:[标签]

# 导出镜像到文件(tar 归档,便于跨机器传输)
sudo docker save -o ai.tar ai_bigproject:latest

# 导入镜像
sudo docker load -i ai.tar

示例一:Hello Docker(最小可用)

项目 hello_docker/ 下两个文件:app.py、Dockerfile

复制代码
# app.py
print("Hello Docker!")
复制代码
# Dockerfile
# 指定基础镜像
FROM python:3.9-slim

# 设置工作目录(容器内的目录)
WORKDIR /app

# 将当前目录的 app.py 复制到容器内的 /app 目录
COPY app.py .

# 运行 python 脚本
CMD ["python", "app.py"]
复制代码
# 进入 hello_docker 目录后构建
sudo docker build -t hello-docker:v1.0 .

# 构建成功会输出:
# Successfully built xxxxxxxx
# Successfully tagged hello-docker:v1.0

docker images              # 确认构建成功

# 运行
sudo docker run hello-docker:v1.0
# 终端显示:Hello Docker!

示例二:Flask + Docker(完整流程)

项目 flask_docker_demo/ 下两个文件:app.py、Dockerfile

复制代码
# app.py
from flask import Flask

# 创建 Flask 应用实例
app = Flask(__name__)

# 定义路由 /
@app.route('/')
def hello():
    return "Hello, Flask in Docker!"

# 启动应用(这行只在直接运行时生效,Docker 中不会直接用)
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

注意:app.run(...) 在 Docker 中不会直接使用(生产用 Gunicorn)。这里为了简单先用 Flask 自带服务器,仅用于教学演示。

复制代码
# 使用官方 Python 轻量镜像作为基础
FROM python:3.9-slim

# 设置工作目录(容器内的目录)
WORKDIR /app

# 将当前目录下的 app.py 复制到容器内的 /app 目录
COPY app.py .

# 安装 Flask(依赖多时建议用 requirements.txt)
RUN pip install --no-cache-dir flask

# 暴露容器运行时的端口(仅作文档用途,实际端口映射靠 docker run -p)
EXPOSE 5000

# 启动 Flask 应用(开发模式,仅演示,生产环境不推荐)
CMD ["python", "app.py"]
复制代码
# 1. 构建镜像(确保 app.py 和 Dockerfile 在同一目录)
docker build -t flask-demo .

# 2. 确认构建成功
docker images

# 3. 后台运行,宿主机 8080 → 容器 5000
docker run -d -p 8080:5000 flask-demo

# 4. 访问
curl -D 
# 或浏览器打开 http://localhost:8080

页面显示 Hello, Flask in Docker! 即成功。

⚠️ 在虚拟机内部 curl 可以直接用 127.0.0.1;在 Windows 浏览器 里要输入虚拟机的 IP:8080。

相关推荐
运维小文2 小时前
apache nifi 2.90部署+运维手册
大数据·运维·数据分析·apache·nifi
2603_965148112 小时前
终极清单:构建自动化选品比价系统的所有组件与资源
大数据·运维·数据库·数据挖掘·自动化
CNSSIRD数据库2 小时前
中国省地县乡四级行政建制及区划代码-2025年版
大数据·数据库·数据分析·论文笔记
我的愿望是成为富婆2 小时前
2027校招准备:普通本科数据相关专业,如何把考证、SQL与项目能力衔接起来?
大数据
重生之高冷汉堡包2 小时前
JavaScript 网络爬虫教程(2026)
大数据·网络·爬虫·安全
Data analyse4562 小时前
用户路径SQL按会话起止时间聚合的方法
大数据·服务器·前端·数据库·人工智能·sql·数据分析
程序员清风2 小时前
文本字段清洗:字符串、分类值与格式标准化
大数据·人工智能·分类
智慧物业老杨2 小时前
物业费催收难?数智化系统如何用技术重构“服务-收费“闭环
大数据·人工智能
Raas1002 小时前
AI网关怎么选?MAI Gateway(魔芋企业级AI网关)入选2026年企业级AI网关推荐清单
大数据·网络·人工智能·gateway·mai gateway