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

在实际项目中,如果只依赖一台 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 作为代理服务器来管理访问。

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

  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 文件并编写以下代码:

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

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

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

这个时候表示我们的服务已经启动成功了,我们再来看看每个 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 进行负载均衡来进行流量分发。

我们定义了如下路由:

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

其中根路由是一个 Redis 的写功能而 m 路由是读功能:

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

访问根路由数据写入成功了,接下来我们访问 m 路由:

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

数据也读取成功了。

总结

本篇文章中我们通过 docker+nginx 实现了一个 Redis 主从复制的架构来实现读写分离,并使用 NGINX 来实现了负载均衡。

通过读写分离和负载均衡,可以显著提高响应速度和处理大量并发请求的能力。

主从复制和 Nginx 的健康检查确保即使部分组件失败,系统仍能继续提供服务,减少了单点故障的风险。

原文链接:https://juejin.cn/post/7328670028606046220 作者:Moment

(0)
上一篇 2024年1月29日 上午10:25
下一篇 2024年1月29日 上午10:36

相关推荐

发表回复

登录后才能评论