高可用高并发集群配置(Docker+Redis)

高可用高并发Redis集群配置搭建(Docker+Redis)

  • 高可用:24小时对外提供服务
  • 高并发:同一时间段能处理的请求数

1. 中心化和去中心化

1.1 中心化

意思是所有的节点都要有一个主节点

  • 特点:就是一个路由作用。

  • 缺点:中心挂了,服务就挂了,中心处理数据的能力有限,不能把节点性能发挥到最大。

1.2 去中心化

特点:去掉路由,我自己来路由

1.3 总结

  • 中心化:几个经过认证的嘉宾在‘讲话’,所有其他人在听。
  • 去中心化:每个人都可以‘讲话’,每个人都可以选择听或者讲。

2. Redis集群的执行流程分析

如何连接集群:连接集群中的任意一个主redis,都相当于连上了集群,因为该集群是去中心化的。

向集群中写数据的流程:

    1. 连上任意一个redis指定写入key与value;
    1. 拿到key的值,进行hash算法,得到一个随机数;
    1. 通过这个数字就可以匹配到集群中的某一个redis:
    • 3.1 将该数字对16384取余,余数一定是0~16383之间的某一个数字;
    • 3.2 将这个余数与每一个redis被分配的哈希槽进行匹配,匹配上哪一个redis就将这个数据添加到哪一个redis中。

2.1 哈希槽说明

Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。

当你往Redis Cluster中加入一个Key时,会根据crc16(key) mod 16384计算这个key应该分布到哪个hash slot中,一个hash slot中会有很多key和value。你可以理解成表的分区,使用单节点时的redis时只有一个表,所有的key都放在这个表里;改用Redis Cluster以后会自动为你生成16384个分区表,你insert数据时会根据上面的简单算法来决定你的key应该存在哪个分区,每个分区里有很多key。

2.2 执行流程分析

  • 假如redis集群里面能存放90个key,那么redis集群把90key平分到3个主机;
  • redis对每个主机里面30个存储位置都编号,当应用连接到主机1上面时,应该发送一个写的命令;
  • 主机使用crc16算出槽号:
    • 如果槽号在1-30 可以直接操作主机1;
    • 如果槽号在31-60那么redis会转发到主机2;
    • 如果应该再发一个命令set age 22,那么主机2使用crc16再算槽号再转发。

3. 集群的搭建

3.1 原理

去中心化

3.2 集群规则

机器编号ipport
147.105.128.1517000
247.105.128.1517001
347.105.128.1517002
447.105.128.1517003
547.105.128.1517004
647.105.128.1517005

3.3 搭建过程

    1. 准备一个目录用来存放集群中Redis的配置文件:

    mkdir redis-cluster

    1. 准备一个服务端程序redis-server,方便启动Redis服务
    1. 准备6个redis的配置文件:

      #redis-7000.conf  后面几个配置文件如法炮制,修改后面编号就行
      bind 0.0.0.0                  
      port 7000                      
      daemonize yes                 
      # 打开aof 持久化
      appendonly yes                 
      # 开启集群
      cluster-enabled yes            
      # 集群的配置文件,该文件自动生成     此处的编号也要修改
      cluster-config-file nodes-7000.conf  
      # 集群的超时时间
      cluster-node-timeout 5000
      
    1. 启动六个redis:

    /usr/local/redis/bin/redis-server /usr/local/redis/conf/credis-cluster/redis-7000.conf

    其他五个一样启动。

    1. 使用docker下载redis-trib的镜像运行

      • 安装docker(如果已安装可忽略)

      yum install docker

      • 启动docker

        systemctl start docker

      • 下载镜像

        docker pull inem0o/redis-trib

      • 开启集群(IP地址和端口号按上面的规则)

        docker run -it --net host inem0o/redis-trib create --replicas 1 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005

        -it是为了可以输入,--net host 是为了让docker容器能连接上本地的宿主机。

      • 测试集群环境

        ./redis-cli -c -h 127.0.0.1 -p 7000

      -c 表示连接集群

      至此,集群搭建完毕。

    注意

    • 搭建集群的所有redis不能有数据,因为集群中的每一个数据都应该有哈希槽,而提前放进去就刚好没有,所以不允许;
    • 配置文件中最好不要设置密码,如果设置了密码,则需要在创建集群的对应服务命令的端口后添加-a passwod ,如:docker run -it --net host inem0o/redis-trib create --replicas 1 127.0.0.1:7000 -a password 127.0.0.1:7001 -a password 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
  • 查询集群:

  • -c:表示以集群方式连接惹redis

    -h:指定IP地址

    -p:指定端口

    cluster nodes:查询集群节点信息

    cluster info: 查询集群状态信息

    查询集群节点信息:

    如:/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7001 cluster nodes

myself:表示当前所查的服务节点;

master:表示服务,最后的数字范围表示哈希槽范围;

slave:表示服务。

查询集群节点信息:

如:/usr/local/redis/bin/redis-cli -c -h 127.0.0.1 -p 7001 cluster info

4. 使用Docker搭建Redis集群

4.1 创建内网

为防止地址端口冲突,以及宿主机为docker重启后分配地址变化的情况发生,可以使用docker创建一个专用的网卡,命令如下:

docker network create 网卡名 --subnet 子网掩码(如:172.38.0.0/16)

通过命令docker network ls可以查看到目前已知的docker网络,

也可以通过docker network inspect 网卡名查看网络的详细信息

4.2 创建配置文件

创建六个redis启动所需要的配置文件,命令创建,路径可自定义,

cluster相关的配置一定要有,port 就是之后启动容器时必须要映射的端口,这里不要配置daemonize yes,会影响docker的启动,默认为no,这是受docker的机制影响的,如果将其设置为yes,也会导致docker容器启动失败。

mkdir -p /usr/local/redis/docker/node-${port}/conf
touch /usr/local/redis/docker/node-${port}/conf/redis.conf
cat << EOF >/usr/local/redis/docker/node-${port}/conf/redis.conf
port 6379 
bind 0.0.0.0
cluster-enabled yes 
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done

4.3 通过挂载数据卷的方式启动容器

挂载刚才创建好的配置文件,并使用其配置文件启动redis:

docker run -p 7001:6379 -p 17001:16379 --privileged=true --name redis-1 \
> -v /usr/local/redis/docker/node-1/data:/data \
> -v /usr/local/redis/docker/node-1/conf/redis.conf:/etc/redis/redis.conf \
> -d --net redis --ip 172.38.0.11 redis:5.0.14 redis-server /etc/redis/redis.conf

注意:

    1. 防火墙可以关闭,也可以打卡对应端口号,我这里打开了7001~7006,以及总集线端口(端口号+10000)17001~17006
    1. --privileged=true是为了防止Centos将启动停止,Centos7的安全机制;
    1. 当发现启动的容器自动退出,无法正常运行时,将-d取消掉重新运行,就能看到实际的错误,比如我出现了这个错误:

      意思是我的权限不够,可以试着添加第二点里的命令,还是不行就将配置文件的权限修改为"777";

    1. --ip一定是刚才自己建的网卡ip地址,每个容器对应一个;

输入docker ps显示如下信息表示启动成功并正常运行:

输入docker ps -a显示如下信息,表示异常退出:

补充知识:docker有一个机制,后台运行的程序必须有一个前台程序,否则这个docker容器就会觉得自己无事可做而自杀。

4.4 创建集群节点

当六个Redis都正常运行时,就可以创建节点了,进入任意一个redis服务:

docker exec -it 容器名 bash

创建节点:

redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1

ip号一定要和创建容器时的ip对应,端口也要和redis配置文件中的端口对应。

遇到询问,输入yes即可

到此,搭建工作完成

4.5 测试集群

进入集群一定注意输入:redis-cli -c -p 端口号而不是原来的单体redis-cli -p 端口号,默认端口是6379

输入cluster info查看该集群节点信息;

输入cluster nodes查看所有节点信息,master表示主机,slave表示从机,myself表示当前所在节点

向主机中写入数据,发现根据哈希算法计算,得到**[15495]**所以自动插入到3号主机的哈希槽中

测试故障转移:

故意关掉三号主机:docker stop redis-3,获取刚才插入的值

发现已经转移到4号redis上,并且可以发现4号机已经自动升级成了master,说明转移成功。


自此,搭建完成!!!
参考视频:狂神说java[Docker搭建Redis集群部署实战]

5. 外界访问集群的搭建方式

新的风暴已经出现…

按着前面搭建集群的方法确实可以搭建成功,但是有个问题,那就是只能在自己的虚拟机中访问,通过外界工具无法访问,外部连接此集群会存在无法连接的情况,这不符和我的要求。。。

4.6.1 问题分析

初步分析,竟然内部能操作集群,但是外部不能访问,猜测大概是网络的问题,前面创建reids容器时,是让它在自己创建的dcokerredis网络中运行,外部无法访问,之前我的理解是,宿主机可以直接通过端口映射就能访问集群了,没考虑过是否与网络有关。

4.6.2 尝试解决

之前创建容器时使用的redis网络,那么这次我就直接使用docker的host网络,和宿主机关联;redis的启动配置文件没做改动,创建容器的命令只需要修改网络部分,将--net redis改为--net host,为了能看到启动失败时的错误信息,我没有选择后台运行:

docker run -p 7001:6379 -p 17001:16379 --privileged=true --name redis-1 \
> -v /usr/local/redis/docker/node-1/data:/data \
> -v /usr/local/redis/docker/node-1/conf/redis.conf:/etc/redis/redis.conf \
> --net host redis:5.0.14 redis-server /etc/redis/redis.conf

果然,启动失败,,,

原因是我的6379端口被占用,看了一下我的6379端口是我宿主机的redis在用;那么问题来了,前面创建docker内部集群时,映射的端口全是6379和16379;为什么这次会报被占用的错呢?估计是因为我把网络改成了本地宿主机,docker映射时直接映射在了宿主机上,所以才产生了冲突;尝试着修改配置文件再试试,其中一个修改如下:

#每个配置文件的端口都要不一样 我的是7001~7006
port 7006
bind 0.0.0.0
#这里的配置是在官网看到的,redis的一种安全保护机制,3.2开始的,防止外部网络访问,这里我直接禁用掉
protected-mode no
cluster-enabled yes 
cluster-config-file nodes.conf
cluster-node-timeout 5000
#修改
cluster-announce-port 7006  
#修改
cluster-announce-bus-port 17006   
appendonly yes

protected-mode详细说明

重新启动,修改启动中的端口映射:

docker run -p 7001:7001 -p 17001:17001 --privileged=true --name redis-1 \
> -v /usr/local/redis/docker/node-1/data:/data \
> -v /usr/local/redis/docker/node-1/conf/redis.conf:/etc/redis/redis.conf \
> -d --net host redis:5.0.14 redis-server /etc/redis/redis.conf

启动成功!!!

依次启动其余五个容器,

再通过命令建立集群,记得修改每个容器的ip和端口:

docker run -it --net host inem0o/redis-trib create --replicas 1 宿主机ip:7001 宿主机ip:7002 宿主机ip:7003 宿主机ip:7004 宿主机ip:7005 宿主机ip:7006

之后的测试和前面的一样,另外这时也可以通过外界访问了!!!

4.6.3 最终测试

通过管理工具存入"hello world"键值对:

在集群中查看:

我的最终目的是在java程序中也能访问集群,尝试通过java程序访问:

	@Test
    public void testClusters(){
        //创建集群服务
        Set<HostAndPort> hostAndPorts = new HashSet<>();
        hostAndPorts.add(new HostAndPort("192.168.**.**",7001));//这里是自己宿主机的ip
        hostAndPorts.add(new HostAndPort("192.168.**.**",7002));
        hostAndPorts.add(new HostAndPort("192.168.**.**",7003));
        hostAndPorts.add(new HostAndPort("192.168.**.**",7004));
        hostAndPorts.add(new HostAndPort("192.168.**.**",7005));
        hostAndPorts.add(new HostAndPort("192.168.**.**",7006));

        //创建集群
        JedisCluster cluster = new JedisCluster(hostAndPorts);
        cluster.set("JAVA", "NO1");
        System.out.println("set = " + cluster.get("JAVA"));
        cluster.close();
    }

输出台输出:

再查看Redis集群中:

目的达成,完结撒花!!!

4.6 总结

    1. 使用docker搭建redis集群走了很多弯路,遇到了最主要的两个问题就是创建集群的时候一直显示Waiting for cluster join...,主要原因是ip地址和端口不正确,无法找到节点,最好的方式就是自己为docker创建一个网卡,可以避免很多不必要的麻烦,但是这样外部就不能访问了,如果需要外部访问,还是得使用宿主机的ip地址,这时每个容器的端口也必须唯一,不能都是默认的6379了;
    1. 输入创建节点命令时,会出现not IP:端口 as a cluster nodes,这是因为创建容器时,没有将Redis中的cluster-enabled设置为yes
    1. 启动redis容器失败,始终异常退出,原因可能是:
        1. 命令错误,可以在启动时不加-d,就能发现错误原因;
        2. 配置文件权限没设置,chmod 777 redis.conf
        3. 关掉Centos的安全机制或者添加--privileged=true
        4. 数据卷挂载失败,容器启动时没有按照配置文件的内容启动,默认启动中cluster-enabledno,并且很多集群相关的配置信息都没有,所以要仔细检查数据卷的挂载路径宿主机路径:容器内路径,注意一个-v表示配置文件目录,另一个-v表示docker以后生成的数据文件存储目录,并且不能以/结束,redis的启动文件以docker内的路径为准,因为这时已经将路径映射到宿主机上了。

因为对网络部分的知识和redis不熟悉,走了很多弯路,此篇文章记录下自己的问题解决过程,加深印象。

《基于docker容器的高并发web系统架构设计与实现》随着互联网迅速发展,社交、媒体以及电商等web网站用户数量 越来越大,并发流量也越来越高,这对于传统web系统架构设计提出 新的挑战。本文基于docker容器虚拟化技术来设计实现高并发web系 统架构,实现web系统的高并发、易扩展以及提升系统资源利用均衡 率等功能。 本文基于docker容器以及Kubemetes容器集群技术,从负载均 衡、弹性伸缩以及资源调度等方面设计实现容器化高并发web系统架 构。设计实现基于工作负载特性的动态负载均衡策略,能够实现根据 不同负载类型以及容器集群资源利用率而实时调整容器集群服务的 权重;设计实现基于灰度模型短时间负载预测弹性伸缩策略,能够实 现高效容器集群弹性伸缩以及提升系统并发性能;设计实现基于蚁群 算法并行调度策略,能够有效提升容器集群整体调度效果,提高容器 服务集群的可用性以及改善系统的资源利用均衡率等。 基于docker容器的高并发web系统架构能够实现系统的高并发、 易扩展以及提升系统集群的资源利用率等功能。经过系统测试分析, 基于工作负载特性的动态负载均衡策略比传统轮询、加权轮询策略在 高并发流量下有更好的吞吐量以及响应时间等性能表现,基于灰度模 型预测的弹性伸缩机制比传统KubemetesHPA机制在短时间内具有 更好的集群伸缩特性以及基于蚁群算法的并行容器调度策略比传统 Kubemetesdefaults策略有更高的调度优势,能够有效提升容器的均 匀分发,实现系统高可用性以及提升系统的资源利用均衡率等。 基于docker容器技术高并发web系统架构设计,能够有效解决 当前高并发流量下web系统流量转发、集群伸缩以及资源调度等问 题,对于容器技术的具体应用研究以及高并发系统架构的设计具有一 定的实用价值。 关键词:高并发web系统,容器虚拟化,动态负载均衡,弹性伸 缩,并行资源调度
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值