如何在Nestjs中使用docker和nginx实现Redis读写分离和负载均衡

在实际项目中,如果只依赖一台 Redis 服务器,一旦服务器出现硬件故障或服务宕机,可能会导致数据丢失或服务不可用,增加业务风险。随着业务的发展,数据量和访问量可能不断增长,单台服务器可能很难通过简单的升级来满足需求,而扩展到多台服务器可能需要重新设计架构。

为了解决这些问题,通常会采用集群、主从复制、持久化策略等多种方法来提高 Redis 的可靠性、可扩展性和性能。

在接下来的内容中,我们将来学习一下如何使用 docker 搭件一个 Redis 主从复制来实现读写分离,并通过 NGINX 实现负载均衡。

docker 配置

在使用 docker 之前,首先确保你已经安装了 docker 了,在这里我们首先要在项目的根目录中创建一个 docker-compose.yml 文件,并编写以下命令:

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 作为代理服务器来管理访问。

下面来了解一下这些服务组件都是在干嘛的:

  1. redis-master:

    • 作用:作为主节点,处理所有写操作,并将数据同步到从节点。
    • 配置:使用最新版的 Redis 镜像并将宿主机的 6379 端口映射到容器的 6379 端口。数据持久化到 redis-master-data 卷。启动时设置 Redis 为只追加模式,并设置访问密码。健康检查通过每 30 秒执行一次 redis-cli ping 来进行。
  2. redis-slave 和 redis-slave2:

    • 作用: 作为从节点,接收主节点同步的数据。在主节点不可用时,可以提供读服务或升级为新的主节点。
    • 配置: 使用最新版的 Redis 镜像分别将宿主机的 6380 和 6381 端口映射到容器的 6379 端口。启动时配置为主节点的从节点,并设置访问及授权密码标识依赖于 redis-master 服务。健康检查通过执行 redis-cli ping 来进行。
  3. NGINX:

    • 作用: 作为代理服务器,可能用于负载均衡和请求路由,提高系统的可用性和可扩展性。
    • 使用最新版的 Nginx 镜像将宿主机的 16379 端口映射到容器的 16379 端口。加载自定义的 Nginx 配置文件并依赖于两个从节点服务。

redis-master-data, redis-slave-data, redis-slave2-data: 这些卷用于数据持久化,确保即使容器重启,数据也不会丢失。

使用 redis-net 自定义桥接网络,确保所有服务都在同一个网络中,可以相互通信。

在完成 docker-compose 文件编写之后,我们继续在该文件的同级目录下创建一个 nginx.conf 文件并编写以下代码:

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 会自动将流量路由到其他健康的节点。同时,使用最少连接数策略可以尽量保持节点间负载的平衡。这种配置通常用于提高大型应用的性能和稳定性。

到这里的时候,我们所有的配置文件都已经完成了,接下来我们就可以在终端中执行以下命令来启动容器了:

bash 复制代码
docker-compose up -d

启动成功以后你会看见以下结果:

这个时候表示我们的服务已经启动成功了,我们再来看看每个 Redis 服务的详细信息:

这里表示我们已经配置完成了,接下来我们就可以在 Nestjs 中引入了。

如何在 Nestjs 中接入

创建基础的 nest 项目估计都会了吧,要想使用 Redis,首先我们要安装 ioredis:

bash 复制代码
pnpm add ioredis

安装完成之后,我们在 app.service.ts 文件目录下编写如下代码:

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 的健康检查确保即使部分组件失败,系统仍能继续提供服务,减少了单点故障的风险。

相关推荐
2601_949817727 小时前
Spring Boot3.3.X整合Mybatis-Plus
spring boot·后端·mybatis
oyzz1208 小时前
PHP操作redis
开发语言·redis·php
uNke DEPH8 小时前
Spring Boot的项目结构
java·spring boot·后端
zhenxin01228 小时前
Spring Boot 3.x 系列【3】Spring Initializr快速创建Spring Boot项目
spring boot·后端·spring
超级无敌暴龙兽8 小时前
和我一起刷面试题呀
前端·面试
wzl202612139 小时前
企业微信定时群发技术实现与实操指南(原生接口+工具落地)
java·运维·前端·企业微信
yolo_guo9 小时前
redis++使用: hmset 与 hmget
c++·redis
小码哥_常9 小时前
Robots.txt:互联网爬虫世界的“隐形规则”
前端
小码哥_常9 小时前
Android开发神器:AndroidAutoSize,轻松搞定屏幕适配
前端
前端一小卒9 小时前
前端工程师的全栈焦虑,我用 60 天治好了
前端·javascript·后端