更新内容

  • 架构上添加代理层,升级为应用服务集群架构
  • 容器编排上,后端服务程序数量提升为两个
  • 采用反向代理负载均衡技术,后端服务器均衡承载负载

什么是应用服务集群架构

当单台应用服务器出现了性能瓶颈之后,可以有如下两种扩展解决方案:

  1. 垂直扩展/纵向扩展ScaleUp。通过购买性能更优、价格更⾼的应⽤服务器来应对更多的流量。这种⽅案的优势在于完全不需要对系统软件做任何的调整;但劣势也很明显:硬件性能和价格的增⻓关系是⾮线性的,意味着选择性能2倍的硬件可能需要花费超过4倍的价格,其次硬件性能提升是有明显上限的。
  2. ⽔平扩展/横向扩展ScaleOut。通过调整软件架构,增加应⽤层硬件,将⽤⼾流量分担到不同的应⽤层服务器上,来提升系统的承载能⼒。这种⽅案的优势在于成本相对较低,并且提升的上限空间也很⼤。但劣势是带给系统更多的复杂性,需要技术团队有更丰富的经验

这里我们决定使用水平扩展,调整软件架构的方式提高服务法并发量。

但这需要引⼊⼀个新的组件⸺负载均衡:为了解决⽤⼾流量向哪台应⽤服务器分发的问题,需要⼀个专⻔的系统组件做流量分发。

关于流量分发的策略,有以下几种常见的策略

  1. Round-Robin轮询算法:即⾮常公平地将请求依次分给不同的应⽤服务器
  2. Weight-Round-Robin轮询算法:引入权重,为不同的服务器(⽐如性能不同)赋予不同的权重(weight),按权重分配流量
  3. ⼀致哈希散列算法:通过计算⽤⼾的特征值(⽐如IP地址)得到哈希值,根据哈希结果做分发,优点是确保来⾃相同⽤⼾的请求总是被分给指定的服务器。

本次具体更新内容

选用nginx

这里我们选用了nginx作为现成的反向代理服务器,并提供负载均衡服务,分发策略使用的是Weight-Round-Robin轮询算法,但是目前分配的权重是1比1的等权重。

为什么使用nginx

  • 连接处理效率高 (Efficient Connection Handling): Nginx 采用异步非阻塞 I/O 模型,能够高效地处理大量的并发连接,包括长连接(Keep-Alive)。它可以作为连接池,维护与后端应用服务器的少量持久连接,避免了应用服务器频繁地创建和销毁连接的开销。
  • 负载均衡 (Load Balancing): 当有多个应用服务器实例时,Nginx 可以将传入的请求分发到这些后端服务器上,实现请求的均衡分配。这不仅可以提高整个系统的吞吐量,还能避免单个服务器过载。Nginx 支持多种负载均衡算法,如轮询 (round-robin)、最少连接 (least-connected)、IP 哈希 (ip-hash) 等。
  • 健康检查与故障转移 (Health Checks & Failover): Nginx 可以配置对后端服务器进行健康检查,如果发现某个后端服务器宕机或响应异常,Nginx 会自动将其从负载均衡池中移除,不再转发请求给它,确保服务的高可用性。待该服务器恢复正常后,Nginx 会自动将其重新加入。
  • 水平扩展 (Horizontal Scaling): 当业务增长时,只需增加新的应用服务器实例,并在 Nginx 配置中加入这些新的后端服务器,即可轻松实现系统的水平扩展,提高处理能力
  • 统一日志入口 (Unified Log Entry Point): 所有进入系统的请求都会经过 Nginx,因此 Nginx 的访问日志可以提供一个统一的、全面的请求记录,便于进行流量分析、问题排查和安全审计。

引入nginx

当前服务端架构图为:

构建配置文件

我们创建一个nginx文件夹:

然后在里面新建一个配置文件sup.conf并写入如下内容,这样就能执行一个简单的负载均衡功能了。其中serverserver2都使用了docker提供的DNS服务,所以要保证它们都在同一个docker网络环境下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
upstream backend{
server server:80 weight=1;
server server2:80 weight=1;
}

server {
listen 80;
access_log off;

location / {
proxy_pass http://backend;
proxy_connect_timeout 3s;
proxy_read_timeout 10s;
}
}

构建Dockerfile

我们使用nginx:1.24.0官方镜像为基底,替换掉配置文件后重新构建一个自己的nginx镜像

1
2
3
4
5
6
FROM nginx:1.24.0
# 先删除默认配置
RUN rm /etc/nginx/conf.d/default.conf
COPY ./sup.conf /etc/nginx/conf.d
CMD ["nginx","-g","daemon off;"]
ENTRYPOINT [ "/docker-entrypoint.sh" ]

修改dockerfile.yml

对于容器编排,我们主要有以下几步要做

  1. 新增一个后端服务器容器server2
  2. 新增一个自定义nginx容器web
  3. 后端服务器不再暴露端口,注释端口映射代码
  4. nginx镜像添加端口映射,暴露容器的80端口

修改后的docker-compose-yml如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
services:
web:
image: mynginx
build:
context: ./nginx
ports:
- 30001:80
depends_on:
server:
condition: service_started
server2:
condition: service_started
networks: # 新增网络配置
- btygoose_net
server:
build:
context: ./server
image: btygoose
environment:
- TZ=Asia/Shanghai # 设置时区变量
- LANG=C.UTF-8 # 同步语言编码
# ======= 仅测试使用 ======
# privileged: true
# ======== end =========
# ports:
# - 30001:80
depends_on:
db:
condition: service_healthy
networks: # 新增网络配置
- btygoose_net

server2:
image: btygoose
environment:
- TZ=Asia/Shanghai # 设置时区变量
- LANG=C.UTF-8 # 同步语言编码
depends_on:
db:
condition: service_healthy
networks: # 新增网络配置
- btygoose_net
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: "Password!1"
volumes:
- ./mysql/varlib/:/var/lib/mysql
- ./mysql/initdb:/docker-entrypoint-initdb.d/
healthcheck:
test: mysql -uroot -pPassword!1 -e 'select 1'
interval: 10s
timeout: 5s
retries: 5
ports:
- 30006:3306
networks: # 新增网络配置
- btygoose_net

networks: # 新增网络定义
btygoose_net:
driver: bridge
ipam:
config:
- subnet: 172.28.0.0/16 # 自定义子网