1. Web 应用层典型架构示例
前面我们介绍了负载均衡层,负载均衡层代理的后端服务构架如下:
架构 1:负载均衡器 + 后端 Nginx + 应用服务
特点:
- 每台后端服务器用 Nginx 处理静态文件或转发动态请求,负载均衡器仅做流量分发。
- 对接的负载均衡可以在四层也可以在七层。
架构 2:负载均衡器 + 直接应用服务
特点:
- 后端服务直接暴露 HTTP 端口,无需 Nginx。
- 对接的负载均衡只能在七层。
2. PHP 程序部署
PHP 程序请求链路:
客户端 → Nginx → php-fpm → php 程序 → 数据库
注意:php-fpm 服务不支持 http 协议,所以必须使用 nginx 等服务来转发请求。
相关通信协议:
客户端 → Nginx:http
Nginx → php-fpm:FastCGI
TeX2.1. 相关协议介绍
2.1.1. CGI(Common Gateway Interface)协议
- 定义:CGI 是一种标准协议,定义了 Web 服务器(如 Apache/Nginx)如何与外部程序(如 PHP 脚本)通信,用于动态生成网页内容。
- 工作原理:
- 请求到达:用户访问
example.com/index.php
。 - 进程创建:Web 服务器为每个请求启动一个新的 CGI 进程(如 PHP 解释器)。
- 执行脚本:CGI 进程执行
index.php
,生成 HTML。 - 返回结果:结果通过标准输出(stdout)返回给 Web 服务器。
- 进程销毁:CGI 进程立即退出。
- 请求到达:用户访问
- 每个请求都创建/销毁进程,性能极差。现在仅用于学习或特殊场景。
2.1.2. FastCGI(Fast Common Gateway Interface)协议
- 定义:FastCGI 是 CGI 的改进版,通过 长驻进程(Persistent Process) 复用处理多个请求,显著提升性能。
- 工作原理:
- 进程启动:Web 服务器启动时,预加载 FastCGI 进程管理器(如
php-fpm
)。 - 请求处理:
- Web 服务器通过 FastCGI 协议(二进制)将请求转发给空闲的 FastCGI 进程。
- 进程处理请求后不退出,继续等待下一个请求。
- 结果返回:响应通过 FastCGI 协议传回 Web 服务器。
- 进程启动:Web 服务器启动时,预加载 FastCGI 进程管理器(如
2.2. PHP-FPM 服务
PHP-FPM(PHP FastCGI Process Manager)是用于管理 PHP 进程管理器,当上游请求到来时,启动 PHP 解释器运行 PHP 脚本。
2.3. 部署 PHP 程序相关组件配置示例
配置 Nginx 将动态请求转发到 PHP-FPM:
server {
listen 80;
server_name localhost;
# 非 .php 结尾请求从root指定的目录里直接拿,例如 jpg、css 等静态文件
location / {
root /usr/share/nginx/html;
index index.php index.html;
}
# 以 .php 结尾的请求,交给 fastcgi 程序处理
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000; # php-fpm 监听地址
fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name;
# SCRIPT_FILENAME 参数,告诉 PHP-FPM 要执行哪个脚本
# /usr/share/nginx/html 是网站的根目录,也可以使用$document_root来表示,例如:$document_root$fastcgi_script_name
# $fastcgi_script_name 是请求的 PHP 脚本路径
# /usr/share/nginx/html$fastcgi_script_name表示完整文件系统路径,
include fastcgi_params;
}
BashPHP-FPM 配置示例(php-fpm.conf):
[www]
; 监听方式(TCP或Unix Socket)
listen = 127.0.0.1:9000
; 或 listen = /var/run/php-fpm.sock
; 进程管理方式
pm = dynamic ; 动态调整进程数
pm.max_children = 50 ; 最大Worker进程数
pm.start_servers = 10 ; 启动时的进程数
pm.min_spare_servers = 5 ; 空闲时最少进程数
pm.max_spare_servers = 20 ; 空闲时最多进程数
; 慢请求日志(单位:秒)
request_slowlog_timeout = 5
slowlog = /var/log/php-fpm/slow.log
Bash2. Python 程序部署
Python 程序请求链路:
1、daphne 服务:
方案一:客户端 → Nginx → daphne 服务 → Python 程序 → 数据库
相关通信协议:
客户端 → Nginx:http
Nginx → daphne:http
daphne 服务 → Python 程序:asgi
方案二:客户端 → daphne 服务 → Python 程序 → 数据库
相关通信协议:
客户端 → daphne:http
daphne 服务 → Python 程序:asgi
2、uwsgi 服务:
方案一:客户端 → Nginx → uwsgi 服务 → Python 程序 → 数据库
相关通信协议:
客户端 → Nginx:http
Nginx → uwsgi:http、uwsgi
uwsgi 服务 → Python 程序:wsgi
方案二:客户端 → uwsgi 服务 → Python 程序 → 数据库
相关通信协议:
客户端 → uwsgi:http
uwsgi 服务 → Python 程序:wsgi
TeX上述几种方案中最常用的就是 uwsgi 服务的方案一,下面介绍一下这个方案的配置方式。
2.1. 部署 Python 程序相关组件配置示例
Nginx 代理 uwsgi 服务配置:
server {
listen 80;
server_name localhost;
location / {
# 包含uwsgi的请求参数
include uwsgi_params;
# 转交请求给uwsgi
uwsgi_pass 127.0.0.1:8080; # uwsgi服务器的ip:port
}
}
# uwsgi 服务支持 http 协议,所以也可以使用 proxy_pass 127.0.0.1:8080;但一般不推荐。
Bashuwsgi 配置示例:
安装:
pip3 install uwsgi
Bash配置文件(uwsgi.ini):
[uwsgi]
# 监听 uwsgi 的端口
socket = :8080
# 监听 http 的端口
# http = :8081
# 启动后切换到该目录下作为项目根目录
chdir = /blog/DjangoBlog-master/
# 完整查找路径:/blog/DjangoBlog-master/djangoblog/wsgi.py文件里的application对象
module = djangoblog.wsgi:application
# wsgi-file = djangoblog/wsgi.py # 与上面的配置等价
# 启动的工作进程数量
processes = 4
# 每个进程开启的线程数量
threads = 2
master=True
pidfile=/tmp/project-master.pid
vacuum=True
max-requests=5000
daemonize=/var/log/uwsgi.log
Bash启动:
uwsgi --ini uwsgi.ini
# uwsgi 日志
cat /var/log/uwsgi.log
# 查看 uwsgi 监听的端口
netstat -an | grep 8080
Bash3. Java 程序部署
3.1. 打包 Java 应用程序
Java 应用程序主要有两种打包格式:JAR (Java Archive) 和 WAR (Web Application Archive)。
- JAR 包
- 文件扩展名:
.jar
- 用途:通用的 Java 打包格式,可用于任何 Java 应用程序。
- 包内置了 tomcat 服务
- 文件扩展名:
- WAR 包
- 文件扩展名:
.war
- 用途:专门用于 Web 应用程序
- 需要结合 tomcat 等服务部署
- 文件扩展名:
3.2. Java 打包工具
Apache Maven 是 Java 生态中最流行的项目管理和构建工具之一,它通过项目对象模型(POM)概念来管理项目的构建、报告和文档。可以同时导出 JAR 和 WAR 包。
安装步骤
Maven 官网下载地址:https://maven.apache.org/download.cgi
wget https://dlcdn.apache.org/maven/maven-3/3.9.7/binaries/apache-maven-3.9.7-bin.tar.gz --no-check-certificate
# 解压到指定目录
tar xf apache-maven-3.9.7-bin.tar.gz -C /test/
# 添加环境变量
cat >> /etc/profile <<'EOF'
PATH=/test/apache-maven-3.9.7/bin:$PATH
export PATH
EOF
source /etc/profile
Bash导出标准 JAR 包
pom.xml 配置;
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.example.MainClass</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
XML基本命令:
mvn package
# 默认生成普通 JAR 包(不包含依赖)
# 输出路径:target/<artifactId>-<version>.jar
Bash导出包含依赖的 Fat/Uber JAR
使用 maven-assembly-plugin:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>com.example.MainClass</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
XML执行命令:
mvn clean package
# 生成文件:target/<artifactId>-<version>-jar-with-dependencies.jar
Bash使用 maven-shade-plugin (推荐):
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.MainClass</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
XML执行命令:
mvn clean package
# 生成文件:target/<artifactId>-<version>.jar (已包含依赖)
Bash导出 WAR 包
基本配置:
修改 pom.xml
的打包类型:
<packaging>war</packaging>
XML使用 maven-war-plugin:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
<configuration>
<warSourceDirectory>src/main/webapp</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
<!-- 可选:指定生成的WAR文件名 -->
<warName>myapplication</warName>
<!-- 包含/排除特定文件 -->
<packagingIncludes>WEB-INF/**,META-INF/**</packagingIncludes>
</configuration>
</plugin>
</plugins>
</build>
XMLSpringBoot 框架去除内置 Tomcat:
<!-- 多模块排除内置tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
XML执行命令:
mvn clean package
# 生成文件:target/<artifactId>-<version>.war 或 target/<warName>.war
Bash3.3. 部署 Java 程序
安装 JDK
# yum 安装 JDK1.8.0
yum -y install java-1.8.0-openjdk*
# 验证安装
java -version
openjdk version "1.8.0_402"
OpenJDK Runtime Environment (build 1.8.0_402-b06)
OpenJDK 64-Bit Server VM (build 25.402-b06, mixed mode)
Bash启动 Java 程序
JAR 包:
java -jar xxx.jar
# JAR 包内置 Tomcat
BashAWR 包:
安装 Tomcat :
Tomcat官网:https://tomcat.apache.org
# 下载
wget https://downloads.apache.org/tomcat/tomcat-10/v10.0.23/bin/apache-tomcat-10.0.23.tar.gz
# 解压文件
tar -xvzf apache-tomcat-10.0.23.tar.gz
sudo mv apache-tomcat-10.0.23 /opt/tomcat
# 设置权限
sudo useradd -r -m -U -d /opt/tomcat -s /bin/false tomcat
sudo chown -R tomcat: /opt/tomcat
sudo chmod +x /opt/tomcat/bin/*.sh
# 创建 systemd 服务
# 创建 /etc/systemd/system/tomcat.service 文件:
[Unit]
Description=Apache Tomcat Web Application Container
After=network.target
[Service]
Type=forking
User=tomcat
Group=tomcat
Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk-amd64"
Environment="CATALINA_PID=/opt/tomcat/temp/tomcat.pid"
Environment="CATALINA_HOME=/opt/tomcat"
Environment="CATALINA_BASE=/opt/tomcat"
ExecStart=/opt/tomcat/bin/startup.sh
ExecStop=/opt/tomcat/bin/shutdown.sh
RestartSec=10
Restart=always
[Install]
WantedBy=multi-user.target
# 启动 Tomcat
sudo systemctl daemon-reload
sudo systemctl start tomcat
sudo systemctl enable tomcat
Bash部署 AWR 包:
# 将打包好的 AWR 包复制到 /opt/tomcat/webapps/ 目录下即可
cp xxx.war /opt/tomcat/webapps/ # 根据tomcat的安装目录调整
Bash部署文档参考:https:/doc.ruoyi.vip/ruoyi/document/hjbs.html
4. Go 程序部署
Go 语言编写的程序部署相对简单,因为所有应用代码都被打包成一个二进制文件(视图模板、静态资源和配置文件等非Go代码除外),并且不需要依赖其他库(如 PHP 需要安装各种扩展);不需要部署额外的 HTTP 服务器(如 PHP还需要再启动 PHP-FPM 处理请求)。
对于包含了静态资源文件(CSS、JavaScript、图片)的项目可以在 Go Web 应用之前前置一个 Nginx 服务器处理静态资源请求,然后通过反向代理处理动态资源请求;对于那些不包含静态资源和视图模板的纯API项目,通常只需要打包一份二进制文件部署到服务器即可,更加便捷。
4.1. 运行环境配置
wget https://go.dev/dl/go1.22.1.linux-amd64.tar.gz
tar xf go1.22.1.linux-amd64.tar.gz
mv go /usr/local/go
vim /etc/profile
PATH=/usr/local/go/bin:$PATH
export PATH
Bash4.2. 部署
最简单的方式是直接部署编译好的二进制文件:
# 编译 (默认会生成与当前系统对应的可执行文件)
go build -o myapp
# 在 Linux 上交叉编译 Windows 可执行文件
GOOS=windows GOARCH=amd64 go build -o myapp.exe
# 然后只需将可执行文件复制到目标机器运行
./myapp
Bash使用 Docker 部署:
构建镜像:
# 使用多阶段构建减小镜像大小
# 第一阶段:构建
FROM golang:1.21 as builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp
# 第二阶段:运行
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/myapp .
CMD ["./myapp"]
Bash构建和运行:
docker build -t myapp .
docker run -d -p 8080:8080 myapp
Bash4.3. Go Web 程序配置 Nginx 反向代理
server {
listen 80;
server_name chitchat.test www.chitchat.test;
location /static {
root /go_pro/chitchat-master/public;
expires 1d; # 静态资源缓存一天
add_header Cache-Control public;
access_log off;
try_files $uri @goweb; # try_files指令会先查找路径/go_pro/chitchat-master/public/$uri是否存在,存在则返回,否则交给@goweb处理
}
location / {
try_files /_not_exists_ @goweb; # /_not_exists路径肯定不存在,使得所有请求都会转向名为@goweb的location块
}
location @goweb { # 定义一个名为@goweb的localtion块,用于处理动态请求
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Scheme $scheme;
proxy_redirect off;
proxy_pass http://127.0.0.1:8080;
}
error_log /var/log/nginx/chitchat_error.log;
access_log /var/log/nginx/chitchat_access.log;
}
Bash4.4. 使用 systemd 管理程序
创建服务文件 /etc/systemd/system/myapp.service:
[Unit]
Description=My Go Application
After=network.target
[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/myapp
Restart=always
[Install]
WantedBy=multi-user.target
Bash然后启用并启动服务:
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp
Bash4.5. 使用 Supervisor 管理程序
Supervisor 是一个用 Python 编写的 进程管理工具,用于监控和控制进程,确保它们在崩溃或服务器重启后自动恢复运行。它特别适合管理后台服务、守护进程(daemons)和开发环境中的长期运行任务。它可以很方便的监听、启动、停止、重启一个或多个进程。
用 Supervisor 管理的进程,当一个进程意外被杀死,supervisor 监听到进程死后,会自动将它重新拉起,很方便的做到进程自动恢复的功能,不再需要自己写 shell 脚本来控制。
安装:
yum install supervisor -y
Bash安装 Supervisor 后配置 /etc/supervisor/conf.d/myapp
.conf
:
[program:myapp]
process_name=%(program_name)s # 设置进程名称为程序名称(即 "myapp"),%(program_name)s 是一个变量,会被替换为 [program:] 后面定义的名称
command=/opt/myapp/myapp # 指定要运行的命令,这里是你的 Go 程序的可执行文件路径
directory=/opt/myapp # 设置工作目录,程序会在这个目录下运行
user=appuser # 指定运行该程序的用户(建议使用非 root 用户)
autostart=true # 当 Supervisor 启动时自动启动这个程序
autorestart=true # 如果程序意外退出,自动重新启动它。可选值:true(总是重启),false(不自动重启),unexpected(只在意外退出时重启)。
redirect_stderr=true # 将标准错误(stderr)重定向到标准输出(stdout),方便统一日志管理
stderr_logfile=/var/log/myapp.err.log # 标准错误日志文件路径(当 redirect_stderr=false 时有效)
stdout_logfile=/var/log/myapp.out.log # 标准输出日志文件路径,程序的所有输出会记录到这里
Bash