Web 应用层

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
TeX

2.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 服务器。

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;
    }
Bash

PHP-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
Bash

2. 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;但一般不推荐。
Bash

uwsgi 配置示例:

安装:

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
Bash

3. 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)概念来管理项目的构建、报告和文档。可以同时导出 JARWAR 包。

安装步骤

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>
XML

SpringBoot 框架去除内置 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
Bash

3.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
Bash

AWR 包:

安装 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 
Bash

4.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
Bash

4.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;
}
Bash

4.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
Bash

4.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
上一篇
下一篇