芝法酱躺平攻略(20)——SpringBoot下redis集群配置,使用docker-compose搭建

一、docker-compose介绍

在上一讲中,我们已经介绍了docker的基本操作。这就是为后面的redis集群和mysql集群做准备。但在生产环境中,我们通常使用更规范的docker-compose。

1.1 docker-compose的介绍

Docker Compose是Docker官方提供的一个工具,它可以用于定义和运行多个Docker容器应用程序。使用Docker Compose,你可以通过一个YAML文件来定义你的应用程序所需要的服务、网络和存储等资源,然后使用一个命令来启动、停止和重启你的应用程序。可以帮助开发人员更加轻松地管理和运行多个Docker容器应用程序。它提供了简单的配置、多容器管理、方便的操作和环境隔离等特点,可以大大提高开发和部署的效率。

1.2 docker-compose的安装

sudo apt-get update
sudo apt-get install docker-compose-plugin

1.3 docker-compose的基础语法

对于基础语法的学习,那一定是先去官网
我们常用的docker-compose配置主要是3个代码段,services,volumes,networks

1.3.1 version 代码段

该代码段定义Docker Compose文件版本号。关于docker-compose的各版本。关于各版本的信息,可以看官网

1.3.2 services 代码段

定义应用程序的各个服务,每个服务都可以包含以下几个参数:

  • build:构建镜像的上下文路径和Dockerfile路径。
  • image:使用的镜像名称。
  • volumes:定义容器与主机之间的共享目录。
  • networks: 定于服务的网络
    该字段相当于docker-cli中的docker connect 网络名:容器名
  • ports:定义容器的端口映射。
  • environment:定义容器中的环境变量。
  • depends_on:定义容器之间的依赖关系。

1.3.3 networks 代码段

定义容器之间的网络,每个网络可以包含以下几个参数:

  • driver:指定网络驱动程序。
  • name:指定网络的名称。
  • external:指定网络是否是外部网络。

1.3.4 volumes代码段

定义数据卷,用于在容器之间共享数据。

1.3.5 restart代码段

定义容器的重启策略。

1.3.6 environment代码段

定义全局环境变量。

1.3.7 secrets代码段

定义使用Docker Secrets机制的密钥和证书文件。

二、redis主从配置

2.1 redis主从读写分离的介绍

随着访问量增加,redis可能会变慢。但大多数情况下,redis缓存是查询量高,更新量低。那么,读写分离的方案就呼之欲出。
所谓读写分离,也叫redis的主从模式,一个主节点,只负责插入更新,然后再自动更新到从服务器,从服务器只负责查找。
redis主从复制逻辑大概是这样的:

  1. 从节点连接主节点并发送SYNC命令,请求同步数据。
  2. 主节点接收到SYNC命令后,执行BGSAVE命令生成RDB快照文件,并将RDB文件传输给从节点。
  3. 主节点将写操作记录到内存中的数据库,并将这些操作缓存到内存中的复制缓冲区中。
  4. 主节点将复制缓冲区中的写操作发送给从节点,并等待从节点的ACK确认。
  5. 从节点接收到主节点发送的复制缓冲区中的写操作后进行执行,并返回ACK确认。
  6. 当从节点接收到RDB文件时,首先会清空当前的数据,然后将RDB文件中的数据加载到自己的数据库中。
  7. 从节点将主节点发送的复制缓冲区中的写操作和RDB文件中的数据进行合并,并将合并后的数据持久化到磁盘中的AOF文件中。
    8.从节点通过与主节点保持长连接,接收主节点发送的复制缓冲区中的写操作。
    总结起来,就是连接时全量复制,之后主节点每次写入命令,都做增量执行。

2.2 文件夹准备

cd ~/DOCUMENTS/docker/redis
mkdir compose-master-slave
cd compose-master-slave
mkdir master/config master/data s1/config s1/data s2/config s2/data s3/config s3/data

2.3 配置文件修改

和上一期讲的一样,先去官网下载redis的配置文件
然后做如下修改:

logfile "/data/redis-log.log" #log文件存放位置
dir "/data/redis" #指定dump.rdb路径
dbfilename dump.rdb
requirepass 密码

对于从服务器,还需要做如下修改(主服务器不需要改)

replicaof master 6379
masterauth 密码
replica-read-only yes

2.4 docker-compose文件

version: "3.9"

name: "redis-master-slave"

services:
  master:
    image: redis:7-alpine
    ports:
      - 6370:6379
    command: redis-server /etc/redis/redis.conf  #容器启动后,运行的命令
    volumes:
      - ./master/data:/data
      - ./master/config/redis.conf:/etc/redis/redis.conf
    networks:
      redis-master-slave:
        aliases:
          - master
    #restart: always

  slaver-1:
    image: redis:7-alpine
    ports: 
      - 6378:6379
    command: redis-server /etc/redis/redis.conf --slaveof master 6379  #容器启动后,运行的命令
    volumes:
      - ./s1/data:/data
      - ./s1/config/redis.conf:/etc/redis/redis.conf
    networks:
      redis-master-slave:
        aliases: 
        - s1
    #restart: always
    depends_on:
      - master
  slaver-2:
    image: redis:7-alpine
    ports:
      - 6377:6379
    command: redis-server /etc/redis/redis.conf --slaveof master 6379  #容器启动后,运行的命令
    volumes:
      - ./s2/data:/data
      - ./s2/config/redis.conf:/etc/redis/redis.conf
    networks:
      redis-master-slave:
        aliases:
          - s2
    #restart: always
    depends_on:
      - master
  slaver-3:
    image: redis:7-alpine
    ports:
      - 6376:6379
    command: redis-server /etc/redis/redis.conf --slaveof master 6379  #容器启动后,运行的命令
    volumes:
      - ./s3/data:/data
      - ./s3/config/redis.conf:/etc/redis/redis.conf
    networks:
      redis-master-slave:
        aliases:
          - s3
    #restart: always
    depends_on:
      - master
networks:
  redis-master-slave:	

2.5 运行容器

cd ~/DOCUMENTS/docker/redis/compose-master-slave
sudo docker compose -f redis-cpmpose.yml up -d

2.6 redis的可执行文件位置

在docker-compose文件中,我们看到有这样一段代码:

command: redis-server /etc/redis/redis.conf  #容器启动后,运行的命令

那redis-server 放在容器的哪里呢,还有哪些其他的命令呢?

sudo docker exec -it 1c263f059068 sh
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
cd /usr/local/bin
ls
docker-entrypoint.sh  redis-benchmark   redis-check-aof    redis-check-rdb    redis-cli    redis-sentinel   redis-server

我们可以看到,/usr/local/bin是实际redis-server的存放路径。其中还有一个重要的东西,就是redis-sentinel

三、redis哨兵配置

3.1 多主多从的需求

看过上面的介绍,我们自然会想到,有1主多从来解决查询能力的横向拓展,那我们能不能把主服务器也做个集群。一方面,主服务器做集群意味着我们可以存更多的内容;另一方面,这也提高了可用性,一台主服务器挂了,其他主服务器还能运行。
于是问题就来了,主服务器集群,我如何决定一个key到底存在哪个服务器上呢?

3.2 Redis Cluster(集群)

3.2.1 文件夹和文件准备

cd ~/DOCUMENTS/docker/redis
mkdir compose-cluster
cd compose-cluster/
mkdir node1 node2 node3 node4 node5 node6 node1/data node1/config node2/data node2/config node3/data node3/config
node4/data node4/config node5/data node5/config node6/data node6/config

redis配置文件如下:

bind 0.0.0.0
port 6379
dir /data
logfile ./redis.log
requirepass ilv0404@1314
masterauth ilv0404@1314
protected-mode yes
daemonize no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 192.168.149.132
cluster-announce-port 6001
cluster-announce-bus-port 16001

注意:cluster-announce-ip,cluster-announce-port,cluster-announce-bus-port是申明的公网的地址、端口和bus端口。在这里就是wsl的eth0。

而后和2.3一样,把redis的配置文件拷到对应位置,并做相应修改

3.2.2 docker-compose

version: '3.9'

name: "redis-cluster"

services:
  node1:
    image: redis:7-alpine
    ports:
      - 6001:6379
      - 16001:16379
    command: redis-server /etc/redis/redis.conf --cluster-enabled yes --cluster-node-timeout 5000
    volumes:
      - ./node1/data:/data
      - ./node1/config/redis.conf:/etc/redis/redis.conf
    networks:
      redis-cluster:
        ipv4_address: 172.20.0.31
        aliases:
          - node1
  node2:
    image: redis:7-alpine
    ports:
      - 6002:6379
      - 16002:16379
    command: redis-server /etc/redis/redis.conf --cluster-enabled yes --cluster-node-timeout 5000
    volumes:
      - ./node2/data:/data
      - ./node2/config/redis.conf:/etc/redis/redis.conf
    networks:
      redis-cluster:
        ipv4_address: 172.20.0.32
        aliases:
          - node2
  node3:
    image: redis:7-alpine
    ports:
      - 6003:6379
      - 16003:16379
    command: redis-server /etc/redis/redis.conf --cluster-enabled yes --cluster-node-timeout 5000
    volumes:
      - ./node3/data:/data
      - ./node3/config/redis.conf:/etc/redis/redis.conf
    networks:
      redis-cluster:
        ipv4_address: 172.20.0.33
        aliases:
          - node3
  node4:
    image: redis:7-alpine
    ports:
      - 6004:6379
      - 16004:16379
    command: redis-server /etc/redis/redis.conf --cluster-enabled yes --cluster-node-timeout 5000
    volumes:
      - ./node4/data:/data
      - ./node4/config/redis.conf:/etc/redis/redis.conf
    networks:
      redis-cluster:
        ipv4_address: 172.20.0.34
        aliases:
          - node4
  node5:
    image: redis:7-alpine
    ports:
      - 6005:6379
      - 16005:16379
    command: redis-server /etc/redis/redis.conf --cluster-enabled yes --cluster-node-timeout 5000
    volumes:
      - ./node5/data:/data
      - ./node5/config/redis.conf:/etc/redis/redis.conf
    networks:
      redis-cluster:
        ipv4_address: 172.20.0.35
        aliases:
          - node5
  node6:
    image: redis:7-alpine
    ports:
      - 6006:6379
      - 16006:16379
    command: redis-server /etc/redis/redis.conf --cluster-enabled yes --cluster-node-timeout 5000
    volumes:
      - ./node6/data:/data
      - ./node6/config/redis.conf:/etc/redis/redis.conf
    networks:
      redis-cluster:
        ipv4_address: 172.20.0.36
        aliases:
          - node6
  redis-cluster-cli:
    image: redis:7-alpine
    command: redis-cli -a ilv0404@1314 --cluster create 192.168.149.132:6001 192.168.149.132:6002 192.168.149.132:6003 192.168.149.132:6004 192.168.149.132:6005 192.168.149.132:6006 --cluster-replicas 1 --cluster-yes
    networks:
      redis-cluster:
    depends_on:
      - node1
      - node2
      - node3
      - node4
      - node5
      - node6      

networks:
  redis-cluster:
    #driver: host
    ipam:
      config:
      - subnet: 172.20.0.0/24
        gateway: 172.20.0.1

注意,这里有一个坑。关于cluster-bus 的端口映射,如果redis本身端口设置的是6379的话,bus实际上就是16379。
还有一个坑,redis-cluster-cli中,不得用虚拟地址如node1:6379,不然集群无法创建。具体原因我还不知道。

3.2.3 启动并创建集群

首先先把data文件夹清空

sudo rm -rf node1/data/*  node2/data/*  node3/data/*  node4/data/*  node5/data/*  node6/data/*

启动docker-compose

sudo docker compose -f redis-cluster-cpmpose.yml up -d

3.2.4 测试

redis-cli -c -p 6001 -a 密码
127.0.0.1:6001> set pig 1
-> Redirected to slot [13819] located at 172.20.0.33:6379
OK
172.20.0.33:6379> set cat 2
-> Redirected to slot [1075] located at 172.20.0.31:6379
OK
172.20.0.31:6379> set dog 3
OK

四、springboot的接入

其实关于SpringBoot的接入十分简单,只需要把yml中redis的相关配置改下就行了。

redis:
    host: localhost
    port: 6001
    password: ilv0404@1314
    # 连接超时时间(记得添加单位,Duration)
    timeout: 10000ms
    lettuce:
      shutdown-timeout: 100 # 关闭超时时间
      pool:
        max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
        max-idle: 8 # 连接池中的最大空闲连接
        max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)
        min-idle: 0 # 连接池中的最小空闲连接
    cluster:
      max-redirects: 3
      nodes: localhost:6001,localhost:6002,localhost:6003,localhost:6004,localhost:6005,localhost:6006

注意,我这里没有用虚拟机,linux机是windows的wsl,所以写的localhost

4.1 redisson连接redis-cluster时的jar包异常

这里还有个大坑,我查了好半天才解决。
在Idea中运行,redis的操作毫无问题,但jar包部署时,则会报一个类无法找到的BUG

org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: org/springframework/data/redis/connection/zset/Tuple
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1082)
......
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NoClassDefFoundError: org/springframework/data/redis/connection/zset/Tuple
        at org.redisson.spring.data.connection.RedissonConnection.<clinit>(RedissonConnection.java:2369)
        at org.redisson.spring.data.connection.RedissonConnectionFactory.getConnection(RedissonConnectionFactory.java:11
 1)
        at org.springframework.data.redis.core.RedisConnectionUtils.fetchConnection(RedisConnectionUtils.java:193)
......
at org.springframework.data.redis.core.DefaultValueOperations.set(DefaultValueOperations.java:236)
        at indi.zhifa.recipe.bailan.framework.redis.util.RedisUtil.set(RedisUtil.java:146)
 {
    "code": 500,
    "data": null,
    "message": "发生未知错误 org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: Could not initialize class org.redisson.spring.data.connection.RedissonClusterConnection"
}

解决方法呢,按如下配置pom

        <!-- *****************redisson*************************-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.redisson</groupId>
                    <artifactId>redisson-spring-data-30</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
		<!-- 我的SpringBoot用的是2.4。redisson.version是3.20.0-->
        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-data-24</artifactId>
            <version>${redisson.version}</version>
        </dependency>
		<!-- 这个引用是关键中的关键,redisson高版本把这个包搞掉了,还得再引用回来-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
         <dependency>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </dependency>
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值