JB3-8-Nginx

Java道经第3卷 - 第8阶 - Nginx

心法:本章使用 Maven 父子结构项目进行练习

练习项目结构如下

|_ v3-8-ssm-nginx
	|_ 13801 nginx-a
	|_ 13802 nginx-b
	|_ 13803 nginx-web

武技:搭建练习项目结构

  1. 创建父项目 v3-8-ssm-nginx,删除 src 目录。
  2. 在父项目中管理依赖:
<dependencyManagement>
    <dependencies>
        <!--spring-boot-starter-parent-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.2.5</version>
            <!--声明该项目是一个POM类型的父项目,不参与打包,只用于传递依赖-->
            <type>pom</type>
            <!--导入该项目的 `<dependencyManagement>` 到本项目-->
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
  1. 在父项目中添加依赖:
<dependencies>
    <!--junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>${junit.version}</version>
        <scope>test</scope>
    </dependency>
    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>${lombok.version}</version>
        <scope>provided</scope>
    </dependency>
    <!--hutool-all-->
    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>${hutool-all.version}</version>
    </dependency>
</dependencies>

S01. Nginx基础入门

E01. 基础概念入门

心法:Nginx 是一款开源的服务器和反向代理 Web 服务器,邮件服务器,支持很多第三方的模块扩展。

Nginx 服务端启动模式:Nginx 服务端采取多进程模式,启动 Nginx 服务时,后台会至少启动 2 个进程:

  • 守护进程 Master:负责向 Worker 转发外界信号,负责监控 Worker 状态,当 Worker 异常时会自动重启新的 Worker 进程。
  • 工作进程 Worker:负责监听端口和处理用户请求,Worker 之间相互隔离独立,Worker 数量建议配置与 CPU 核数一致。

1. 正向代理模式

心法:正向代理是客户端的代理,对客户端负责,客户端发送请求到代理服务器,代理服务器转发请求到具体服务器,响应也是原路返回。

正向代理优势如下

  • 破解限制:某服务器限制客户端直接访问,则可以找个可访问的代理服务器来访问该服务器,比如 KX 上网。
  • 加速访问:电信客户端访问联通服务器太慢,则可以找个电信联通都能访问的代理服务器调节,比如游戏加速器。
  • 缓存数据:每次请求获取的数据都可以缓存到代理服务器中,后续相同的请求可以直接从缓存中获取。
  • 保护客户端隐私:服务端仅知道请求来自于哪个代理服务器,但并不知道请求具体来自于哪个客户端。

2. 反向代理模式

心法:反向代理是服务端的代理,对服务端负责,代理服务器接收到请求之后,按一定规则分发给某个具体的服务器,响应也是原路返回。

反向代理的优势如下

  • 负载均衡:代理服务器可以使用多种负载均衡策略分发请求,分摊服务器集群的压力 。
  • 保护服务端隐私:客户端仅知道请求发送给了哪个代理服务器,但并不知道请求具体由哪台服务器进行处理。

3. Nginx服务器安装

心法:Windows 系统中 Nginx 服务器启动和核心是 logs/nginx.pid 文件,后文简称 PID 文件。

PID 文件须知

  1. 使用 CMD 命令启动 nginx 服务时,会自动生成 PID 文件,该文件用于记录 nginx 服务中 master 进程的 ID 号。
  2. 使用 CMD 命令关闭 nginx 服务时,会根据 PID 文件内容来结束 nginx 所有后台进程,然后自动删除 PID 文件。
  3. 多次启动 nginx 服务不会端口占用,但会覆盖 PID 文件中的内容,进而导致之前的 nginx 进程失联并泄露,不建议。
  4. 双击 nginx.exe 也可以启动 nginx 服务,但不会生成 PID 文件,此时只能在任务管理器中手动结束相关进程,不建议。

武技:在 Windows 系统中安装 Nginx 服务器

  1. 下载 nginx for windows 服务器的压缩包 nginx-1.20.2.zip,然后加压缩到 D:\nginx 即可。
  2. 启动nginx服务:
# 切换到nginx家目录
cd D:\nginx

# 启动nginx服务并生成PID文件
start nginx

# 查看nginx服务启动的两个nginx.exe后台进程
tasklist | findstr "nginx*"

# 确认nginx默认的80端口是否正在被worker进程监听
netstat -ano | findstr "80"

# 强制结束某个后台进程
taskkill /pid 进程号 /f

# 关闭nginx服务,结束所有相关进程,并删除PID文件(若PID文件不存在则报错)
nginx -s stop
  1. 访问 nginx 服务器 http://localhost:80(80 端口可省略)。

4. Nginx单机容器搭建

武技:在 Docker 中搭建单机 Nginx 容器

  1. 创建相关目录:
# 创建Nginx相关目录
mkdir -p /opt/nginx/conf;
mkdir -p /opt/nginx/logs;
mkdir -p /opt/nginx/static;
chmod -R 777 /opt/nginx;
  1. 开发配置文件:
# 拉取镜像(二选一)
docker pull nginx:1.25.2;
docker pull registry.cn-hangzhou.aliyuncs.com/joezhou/nginx:1.25.2;

# 创建并运行临时的容器: 该容器仅用于拷贝配置文件,用完即删
docker run -d --name tmp registry.cn-hangzhou.aliyuncs.com/joezhou/nginx:1.25.2;

# 拷贝主配文件,子配置目录和HTML目录到虚拟机
docker cp tmp:/etc/nginx/nginx.conf /opt/nginx/conf/;
docker cp tmp:/etc/nginx/conf.d /opt/nginx/;
docker cp tmp:/usr/share/nginx/html /opt/nginx/;

# 删除临时容器,为后续真正的容器让出端口
docker rm -f tmp
  1. 创建运行容器:
# 创建并运行Nginx容器: 额外暴露几个端口,用于测试Nginx的多server模型
docker run --name nginx -d --network my-net \
      -p 80:80 -p 81:81 -p 82:82 -p 83:83 -p 84:84 -p 85:85 -p 86:86 -p 87:87 -p 88:88 -p 89:89 \
      -v /opt/nginx/static:/opt \
      -v /opt/nginx/logs:/var/log/nginx \
      -v /opt/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \
      -v /opt/nginx/conf.d:/etc/nginx/conf.d \
      -v /opt/nginx/html:/usr/share/nginx/html \
      registry.cn-hangzhou.aliyuncs.com/joezhou/nginx:1.25.2;
   
# 查看Nginx容器
docker ps --format "{{.ID}}\t{{.Names}}\t{{.Ports}}"
docker logs nginx --tail 30

# 进入容器的内部并查看Nginx版本
docker exec -it nginx bash
nginx -version
  1. 访问单机容器:在 Windows 机器上访问 Nginx 服务器 http://192.168.40.77:80 (虚拟机默认开放了 80 端口)。

E02. Nginx配置文件

1. nginx.conf

心法:Nginx 主配置文件 /opt/nginx/conf/nginx.conf 包括全局配置,events 块和 http 块三部分的配置内容。

内容解析如下

# 启动Worker进程的用户和组
user nginx;
# 生成的Worker进程数: 设置为auto表示自动设置为机器的CPU核数
worker_processes auto;
# 错误日志文件: notice级别
error_log /var/log/nginx/error.log notice;
# PID文件,用于记录当前Master进程的ID值
pid /var/run/nginx.pid;

events {
    # 每个Worker进程能并发处理的最大连接数,包括前后端所有连接
    worker_connections  1024;
}

http {
    # 导入扩展名与文件类型的映射表types文件
    include /etc/nginx/mime.types;
    # Nginx自动下载不识别的文件类型
    default_type application/octet-stream;
    # 日志格式: `客户端IP 用户 访问时间 请求信息/状态 内容大小 请求源头 浏览器`,命名为main
    log_format main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    # 访问日志文件,采用main日志格式
    access_log /var/log/nginx/access.log main;
    # 开启高效文件传输模式,关闭使用off,普通应用建议开启,下载相关的应用建议关闭
    sendfile on;
    # 开启sendfile的情况下,会将请求合并,然后统⼀发送给客⼾端
    tcp_nopush on;
    # HTTP连接超时时间,上传文件时设置大一些,避免超时断开连接而上传失败
    keepalive_timeout 65;
    # 对网络传输的数据内容进行压缩以提升传输效率
    gzip on;
    # 引入该目录下的所有Nginx子配置文件
    include /etc/nginx/conf.d/*.conf;
}

2. default.conf

心法:Nginx 子配置文件 /opt/nginx/conf.d/default.conf 主要包括的就是 server 块,可以存在多个,保证端口号和服务名不相同即可。

内容解析如下

server {
    # 配置服务器监听的端口号
    listen       80;
    listen  [::]:80;
    # 配置服务名
    server_name localhost;
    # 爆发50开头的错误时,执行名为 `/50x.html` 的 `location{}` 块
    error_page 500 502 503 504 /50x.html;
    # 当请求 `/50x.html` 时,响应 `/usr/share/nginx/html/50x.html` 页面
    location = /50x.html {
        # 请求资源在服务器上的真实页面所在的目录
        root /usr/share/nginx/html;
    }
    # 当请求 `/` 时,响应 `/usr/share/nginx/html/index.html` 页面
    location / {
        # 请求资源在服务器上的真实页面所在的目录
        root /usr/share/nginx/html;
        # 请求资源在服务器上的真实页面
        index index.html index.htm;
    }
}

3. location匹配

location 匹配原则

  1. location 所有匹配过程都会忽略请求末尾查询串。
  2. location 匹配顺序是首先精准匹配,然后正则匹配,最后前缀匹配。
  3. Windows 下的 Nginx 服务器对大小写不敏感,均可匹配成功。

location 精准匹配:一旦匹配成功则立刻停止,如 location = /a {}

  • http://IP:PORT/a:精准匹配,成功。
  • http://IP:PORT/A:Linux 下匹配失败,Windows 下忽略大小写匹配成功。
  • http://IP:PORT/a/:对末尾的 / 敏感,失败。
  • http://IP:PORT/b:不匹配,失败。

location 正则匹配:按照编写顺序依次匹配,一旦匹配成功则立刻停止,如 location ~ ^/a$ {}

  • http://IP:PORT/a:正则匹配,成功。

  • http://IP:PORT/A:Linux 下匹配失败,Windows下忽略大小写匹配成功。

  • http://IP:PORT/a/:对末尾的 / 敏感,失败。

  • http://IP:PORT/b:正则不匹配,失败。

  • 若将 ~ 符号替换为 !~ 表示取反操作。

  • 若将 ~ 符号替换为 ~* 表示忽略大小写正则匹配。

  • 若将 ~ 符号替换为 !~* 表示忽略大小写正则匹配的取反效果。

location 前缀匹配:按照编写顺序依次匹配,最终只有匹配度最高的那一个生效,如 location /a {}

  • http://IP:PORT/a:前缀匹配,成功。
  • http://IP:PORT/ab:前缀匹配,成功。
  • http://IP:PORT/a/:前缀匹配,成功。
  • http://IP:PORT/A:Linux 下匹配失败,Windows 下忽略大小写匹配成功。
  • http://IP:PORT/b:前缀不匹配,失败。
  • 若使用 ^~ 符号和前缀匹配效果一致,唯一不同的是匹配成功后立即停止继续匹配。

4. Nginx路径映射

武技:使用 Nginx 代理本地资源(一张本地图片)

  1. 拷贝一张图片到虚拟机的 /opt/nginx/static/image 中,如 avatar.png
  2. 开发子配置文件:
cp /opt/nginx/conf.d/default.conf /opt/nginx/conf.d/82.conf

文件内容如下

server {
	listen 82;
	listen [::]:82;
	server_name static-image;

	location /image {
	  # 需要指定Nginx容器内的静态资源地址
	  alias /opt/image/;
	}
}
  1. 重启 Nginx 服务使得配置生效:
docker restart nginx
  1. 在 Windows机器上访问静态资源 http://192.168.40.77:82/image/avatar.png

S02. Nginx项目部署

E01. 反向代理后端项目

武技:创建 nginx-a 和 nginx-b 两个子项目

1. 开发nginx-a项目

  1. 添加三方依赖:
<dependencies>
	<!--spring-boot-starter-web-->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<!--spring-boot-starter-test-->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
	</dependency>
</dependencies>
  1. 开发主配文件:
server:
  port: 13801 # 端口号
spring:
  application:
    name: nginx-a # 项目名
  1. 开发启动类:
package com.joezhou;

/** @author 周航宇 */
@SpringBootApplication
public class NginxA {
    public static void main(String[] args) {
        SpringApplication.run(NginxA.class, args);
    }
}
  1. 开发控制器:
package com.joezhou.controller;

/** @author 周航宇 */
@RestController
public class NginxController {

    @GetMapping("/hello")
    public Map<String, Object> hello(HttpSession httpSession) {
        Map<String, Object> resultMap = new HashMap<>(3);
        resultMap.put("applicationName", "nginx-a");
        resultMap.put("port", 13801);
        resultMap.put("session-id", httpSession.getId());
        return resultMap;
    }
}
  1. 访问子项目 http://localhost:13801/hello:关注sessionId是否共享。

2. 开发nginx-b项目

  1. 添加三方依赖:
<dependencies>
	<!--spring-boot-starter-web-->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<!--spring-boot-starter-test-->
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
	</dependency>
</dependencies>
  1. 开发主配文件:
server:
  port: 13802 # 端口号
spring:
  application:
    name: nginx-b # 项目名
  1. 开发启动类:
package com.joezhou;

/** @author 周航宇 */
@SpringBootApplication
public class NginxB {
    public static void main(String[] args) {
        SpringApplication.run(NginxB.class, args);
    }
}
  1. 开发控制器:
package com.joezhou.controller;

/** @author 周航宇 */
@RestController
public class NginxController {

    @GetMapping("/hello")
    public Map<String, Object> hello(HttpSession httpSession) {
        Map<String, Object> resultMap = new HashMap<>(3);
        resultMap.put("applicationName", "nginx-b");
        resultMap.put("port", 13802);
        resultMap.put("session-id", httpSession.getId());
        return resultMap;
    }
}
  1. 访问子项目 http://localhost:13802/hello:关注sessionId是否共享。

3. 共享Session会话

心法:默认情况下,多个 Nginx 节点之间的 Session 是不一致的,会导致负载均衡效果下出现各种各样的意外,在使用 Nginx 负载均衡时,建议使用 Redis 来共享 Session 配置。

使用 Redis 来共享 Session 配置要求

  1. 要求项目中引入 spring-boot-starter-data-redis 依赖。
  2. 要求项目中引入 spring-session-data-redis 依赖。
  3. 要求项目主配中正确配置了 Redis 服务地址和端口号等项。

武技:共享两个子项目的 Session 会话

  1. 分别在两个子项目中引入依赖:
<dependencies>
    <!--spring-boot-starter-data-redis-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!--spring-session-data-redis-->
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-data-redis</artifactId>
    </dependency>
</dependencies>
  1. 分别在两个子项目中添加 Redis 相关主配项:
 spring:   
  data:  
    redis:  
      host: 192.168.40.77  # Redis服务器IP  
      port: 6379  # Redis服务器端口
  1. 访问子项目:

4. Docker部署测试项目

武技:在 Docker 中部署两个子项目

  1. 创建相关目录:
# 创建相关目录
mkdir -p /opt/app/a;
mkdir -p /opt/app/b;
  1. 打包 SpringBoot 项目:
    • 将项目 nginx-a 打包为 a.jar 文件并拷贝到 /opt/app/a 目录中。
    • 将项目 nginx-b 打包为 b.jar 文件并拷贝到 /opt/app/b 目录中。
  2. 创建 a.jar 的 DockerFile 文件,内容如下:
# 开发 a.jar 项目节点的Dockerfile文件
cd /opt/app/a;
touch Dockerfile;
echo 'FROM registry.cn-hangzhou.aliyuncs.com/joezhou/jdk:17' >> Dockerfile;
echo 'MAINTAINER JoeZhou' >> Dockerfile;
echo 'ADD a.jar a.jar' >> Dockerfile;
echo 'EXPOSE 13801' >> Dockerfile;
echo 'CMD ["nohup", "java", "-jar", "a.jar", "&"]' >> Dockerfile;

# 根据Dockerfile文件制作镜像: 必须在Dockerfile文件同级目录下
docker build -t a:1.0 .

# 查看镜像
docker images
  1. 创建 b.jar 的 DockerFile 文件,内容如下:
# 开发 b.jar 项目节点的Dockerfile文件
cd /opt/app/b;
touch Dockerfile;
echo 'FROM registry.cn-hangzhou.aliyuncs.com/joezhou/jdk:17' >> Dockerfile;
echo 'MAINTAINER JoeZhou' >> Dockerfile;
echo 'ADD b.jar b.jar' >> Dockerfile;
echo 'EXPOSE 13802' >> Dockerfile;
echo 'CMD ["nohup", "java", "-jar", "b.jar", "&"]' >> Dockerfile;

# 根据Dockerfile文件制作镜像: 必须在Dockerfile文件同级目录下
docker build -t b:1.0 .

# 查看镜像
docker images
  1. 创建运行容器:
# 创建并运行 a 节点容器
docker run -itd --name a --network my-net -p 13801:13801 a:1.0;

# 创建并运行 b 节点容器
docker run -itd --name b --network my-net -p 13802:13802 b:1.0;

# 查看节点容器
docker ps --format "{{.ID}}\t{{.Names}}\t{{.Ports}}"

# 永久开放13801和13802端口
firewall-cmd --add-port=13801/tcp --permanent
firewall-cmd --add-port=13802/tcp --permanent
firewall-cmd --reload
  1. 访问项目容器:

5. Nginx代理测试项目

心法:Nginx 代理服务器可以使用基础轮询,最少连接,地址哈希,加权轮询等负载均衡策略分发请求,分摊服务器集群的压力。

负载均衡策略

负载均衡策略描述(如何将请求转发到目标服务器节点)配置(假设负载均衡器的名称为 x
基础轮询按顺序轮流转发:每个服务器节点均摊请求默认策略,无需配置
最少连接按空闲度转发:当前连接数越少,接收到的请求就越多upstream x {least_conn; 服务列表}
地址哈希按客户端IP地址转发:定向转发到服务器节点upstream x {ip_hash; 服务列表}
加权轮询按权重转发:配置的权重越高,接收到的请求就越多upstream x {服务 weight=1}

负载均衡器 http{} -> upstream apps {}

  1. 负载均衡器的名称随意,如 apps,但不能重复。
  2. 负载均衡器中使用 server IP:PORT; 设置节点,可设置多个。
  3. 负载均衡器中的每个Server节点都可以额外添加以下设置:

负载均衡器配置

  • weight=1:设置该节点的权重,权重越大,访问的机率越大,压力也就越大,数字从 1 开始。
  • down:设置该服务不参与负载均衡。
  • max_fails=2:设置该节点允许请求失败的最大次数,超出次数后报错。
  • fail_timeout=30s:设置该节点请求失败后暂停访问的超时时间。
  • backup:设置节点为备用节点,即当其它节点全忙或宕机时,该节点才会被启用。

武技:在 Nginx 服务器中反向代理两个子项目节点

  1. 在 Nginx 主配置文件的 http{} 块中添加负载均衡器:
# 负载均衡器默认使用轮询策略
upstream apps {
	server 192.168.40.77:6625;
	server 192.168.40.77:6626;
}
  1. 开发子配置文件:
cp /opt/nginx/conf.d/default.conf /opt/nginx/conf.d/83.conf

内容如下

server {
	listen 83;
	listen [::]:83;
	server_name app-server;
	
	location / {
		# 拦截全部请求并代理转发到主配中的负载均衡器 `apps{}` 中,并进行负载均衡调用
		proxy_pass http://apps;
	}
}
  1. 重启 Nginx 服务使得配置生效:
docker restart nginx
  1. 在Windows机器上多次访问项目节点 http://192.168.40.77:83/hello,查看轮询效果。

E02. 反向代理前端项目

武技:使用 Nginx 代理前端 Vue 项目 v3-8-ssm-nginx/nginx-web

  1. 将项目中的 localhost 全部替换为虚拟机IP地址 192.168.40.77
  2. 删除整个项目中的 console.log() 打印语句,尤其是请求拦截器中的打印语句(生产环境下不要使用打印语句,打印的内容无法回收,容易造成内存泄露,页面越来越卡)。
  3. 使用 npm run build 命令打包后端前台项目,打包后会生成一个 dist 目录。

1. 上传dist目录

  1. 将打包生成的 dist 目录,整体拷贝到 /opt/nginx/html/ 目录中。
# 进入Nginx的html目录
cd /opt/nginx/html

# 重命名dist目录为
mv dist nginx-web

2. 使用Nginx代理

  1. 开发子配置文件:
cp /opt/nginx/conf.d/default.conf /opt/nginx/conf.d/84.conf

内容如下

server {
	listen 84;
	listen [::]:84;
	server_name nginx-web;

	# Nginx首页所在目录  
    location / {  
	    root   /usr/share/nginx/html/nginx-web;  
	    index  index.html;  
    }
}
  1. 重启 Nginx 服务使得配置生效:
docker restart nginx
  1. 在 Windows 机器上访问前端项目 http://192.168.40.77:84

Java道经第3卷 - 第8阶 - Nginx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值