在实际项目中,如果只依赖一台 Redis 服务器,一旦服务器出现硬件故障或服务宕机,可能会导致数据丢失或服务不可用,增加业务风险。随着业务的发展,数据量和访问量可能不断增长,单台服务器可能很难通过简单的升级来满足需求,而扩展到多台服务器可能需要重新设计架构。
为了解决这些问题,通常会采用集群、主从复制、持久化策略等多种方法来提高 Redis 的可靠性、可扩展性和性能。
在接下来的内容中,我们将来学习一下如何使用 docker 搭件一个 Redis 主从复制来实现读写分离,并通过 NGINX 实现负载均衡。
docker 配置
在使用 docker 之前,首先确保你已经安装了 docker 了,在这里我们首先要在项目的根目录中创建一个 docker-compose.yml 文件,并编写以下命令:
version: "3.9"
services:
redis-master:
image: redis:latest
ports:
- "6379:6379"
volumes:
- redis-master-data:/data
command: ["redis-server", "--appendonly", "yes", "--requirepass", "moment"]
networks:
- redis-net
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 3
redis-slave:
image: redis:latest
ports:
- "6380:6379"
volumes:
- redis-slave-data:/data
command:
[
"redis-server",
"--slaveof",
"redis-master",
"6379",
"--masterauth",
"moment",
"--requirepass",
"moment111",
]
depends_on:
- redis-master
networks:
- redis-net
healthcheck:
test: ["CMD", "redis-cli", "-h", "redis-slave", "ping"]
interval: 30s
timeout: 10s
retries: 3
redis-slave2:
image: redis:latest
ports:
- "6381:6379"
volumes:
- redis-slave2-data:/data
command:
[
"redis-server",
"--slaveof",
"redis-master",
"6379",
"--masterauth",
"moment",
"--requirepass",
"moment111",
]
depends_on:
- redis-master
networks:
- redis-net
healthcheck:
test: ["CMD", "redis-cli", "-h", "redis-slave2", "ping"]
interval: 30s
timeout: 10s
retries: 3
nginx:
image: nginx:latest
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- "16379:16379"
depends_on:
- redis-slave
- redis-slave2
networks:
- redis-net
volumes:
redis-master-data:
redis-slave-data:
redis-slave2-data:
networks:
redis-net:
driver: bridge
在上面的配置文件中,部署了一个 Redis 主从复制架构,并通过 Nginx 作为代理服务器来管理访问。
下面来了解一下这些服务组件都是在干嘛的:
-
redis-master:
- 作用:作为主节点,处理所有写操作,并将数据同步到从节点。
- 配置:使用最新版的 Redis 镜像并将宿主机的 6379 端口映射到容器的 6379 端口。数据持久化到 redis-master-data 卷。启动时设置 Redis 为只追加模式,并设置访问密码。健康检查通过每 30 秒执行一次 redis-cli ping 来进行。
-
redis-slave 和 redis-slave2:
- 作用: 作为从节点,接收主节点同步的数据。在主节点不可用时,可以提供读服务或升级为新的主节点。
- 配置: 使用最新版的 Redis 镜像分别将宿主机的 6380 和 6381 端口映射到容器的 6379 端口。启动时配置为主节点的从节点,并设置访问及授权密码标识依赖于 redis-master 服务。健康检查通过执行 redis-cli ping 来进行。
-
NGINX:
- 作用: 作为代理服务器,可能用于负载均衡和请求路由,提高系统的可用性和可扩展性。
- 使用最新版的 Nginx 镜像将宿主机的 16379 端口映射到容器的 16379 端口。加载自定义的 Nginx 配置文件并依赖于两个从节点服务。
redis-master-data, redis-slave-data, redis-slave2-data: 这些卷用于数据持久化,确保即使容器重启,数据也不会丢失。
使用 redis-net 自定义桥接网络,确保所有服务都在同一个网络中,可以相互通信。
在完成 docker-compose 文件编写之后,我们继续在该文件的同级目录下创建一个 nginx.conf 文件并编写以下代码:
events {}
stream {
upstream redis_slaves {
least_conn; # 使用最少连接数策略
server redis-slave:6380; # 第一个从节点的内部Docker服务名和端口
server redis-slave2:6381; # 第二个从节点的内部Docker服务名和端口
}
server {
listen 16379; # Nginx将在此端口上监听外部请求
proxy_pass redis_slaves; # 转发请求到上面定义的从节点组
}
}
这个配置文件让 Nginx 作为 TCP 负载均衡器,监听端口 16379,所有传入此端口的 Redis 连接请求都会被均衡地分发到两个 Redis 从节点上。这样做的好处是可以均衡负载和提高可用性,如果一个从节点失败,Nginx 会自动将流量路由到其他健康的节点。同时,使用最少连接数策略可以尽量保持节点间负载的平衡。这种配置通常用于提高大型应用的性能和稳定性。
到这里的时候,我们所有的配置文件都已经完成了,接下来我们就可以在终端中执行以下命令来启动容器了:
docker-compose up -d
启动成功以后你会看见以下结果:
这个时候表示我们的服务已经启动成功了,我们再来看看每个 Redis 服务的详细信息:
这里表示我们已经配置完成了,接下来我们就可以在 Nestjs 中引入了。
如何在 Nestjs 中接入
创建基础的 nest 项目估计都会了吧,要想使用 Redis,首先我们要安装 ioredis:
pnpm add ioredis
安装完成之后,我们在 app.service.ts 文件目录下编写如下代码:
import { Injectable } from "@nestjs/common";
import Redis from "ioredis";
@Injectable()
export class AppService {
public writer: Redis;
public reader: Redis;
public nginx: Redis;
public slaves: Redis[];
constructor() {
this.writer = new Redis({
port: 6379,
host: "127.0.0.1",
password: "moment",
});
// nginx负载均衡
this.nginx = new Redis({
port: 16379,
host: "127.0.0.1",
password: "moment111",
});
const slaveConfigs = [
{ host: "127.0.0.1", port: 6380, password: "moment111" },
{ host: "127.0.0.1", port: 6381, password: "moment111" },
];
this.slaves = slaveConfigs.map((config) => new Redis(config));
}
private getRandomSlave() {
const index = Math.floor(Math.random() * this.slaves.length);
return this.slaves[index];
}
async test() {
await this.writer.set("moments", "Redis牛逼");
return "写入成功";
}
async get(): Promise<string> {
return await this.nginx.get("moments");
}
}
在上面的这段代码中,Redis 的写功能我们使用的是主服务器,而读功能是使用多个从服务器,并且使用 nginx 进行负载均衡来进行流量分发。
我们定义了如下路由:
其中根路由是一个 Redis 的写功能而 m 路由是读功能:
访问根路由数据写入成功了,接下来我们访问 m 路由:
数据也读取成功了。
总结
本篇文章中我们通过 docker+nginx 实现了一个 Redis 主从复制的架构来实现读写分离,并使用 NGINX 来实现了负载均衡。
通过读写分离和负载均衡,可以显著提高响应速度和处理大量并发请求的能力。
主从复制和 Nginx 的健康检查确保即使部分组件失败,系统仍能继续提供服务,减少了单点故障的风险。
原文链接:https://juejin.cn/post/7328670028606046220 作者:Moment