集群 订阅
集群通信系统是一种用于集团调度指挥通信的移动通信系统,主要应用在专业移动通信领域。该系统具有的可用信道可为系统的全体用户共用,具有自动选择信道功能,它是共享资源、分担费用、共用信道设备及服务的多用途、高效能的无线调度通信系统。 展开全文
集群通信系统是一种用于集团调度指挥通信的移动通信系统,主要应用在专业移动通信领域。该系统具有的可用信道可为系统的全体用户共用,具有自动选择信道功能,它是共享资源、分担费用、共用信道设备及服务的多用途、高效能的无线调度通信系统。
信息
特    点
音通信采用PTT
类    型
移动通信系统
中文名
集群
功    能
共享资源、分担费用等
集群简介
集群通信的最大特点是话音通信采用PTT(Push To Talk),以一按即通的方式接续,被叫无需摘机即可接听,且接续速度较快,并能支持群组呼叫等功能,它的运作方式以单工、半双工为主,主要采用信道动态分配方式,并且用户具有不同的优先等级和特殊功能,通信时可以一呼百应。追溯到它的产生,集群的概念确实是从有线电话通信中的“中继”概念而来。1908年,E.C.Mo1ina发表的“中继”曲线的概念等级,证明了一群用户的若干中继线路的概率可以大大提高中继线的利用率。“集群”这一概念应用于无线电通信系统,把信道视为中继。“集群”的概念,还可从另一角度来认识,即与机电式(纵横制式)交换机类比,把有线的中继视为无线信道,把交换机的标志器视为集群系统的控制器,当中继为全利用度时,就可认为是集群的信道。集群系统控制器能把有限的信道动态地、自动地最佳分配给系统的所有用户,这实际上就是信道全利用度或我们经常使用的术语“信道共用”。
收起全文
精华内容
下载资源
问答
  • 集群
    万次阅读 多人点赞
    2021-02-01 03:09:27

    一、Redis集群介绍:

    1、为什么需要Redis集群?

            在讲Redis集群架构之前,我们先简单讲下Redis单实例的架构,从最开始的一主N从,到读写分离,再到Sentinel哨兵机制,单实例的Redis缓存足以应对大多数的使用场景,也能实现主从故障迁移。

    但是,在某些场景下,单实例存Redis缓存会存在的几个问题:

    (1)写并发:

            Redis单实例读写分离可以解决读操作的负载均衡,但对于写操作,仍然是全部落在了master节点上面,在海量数据高并发场景,一个节点写数据容易出现瓶颈,造成master节点的压力上升。

    (2)海量数据的存储压力:

            单实例Redis本质上只有一台Master作为存储,如果面对海量数据的存储,一台Redis的服务器就应付不过来了,而且数据量太大意味着持久化成本高,严重时可能会阻塞服务器,造成服务请求成功率下降,降低服务的稳定性。

    针对以上的问题,Redis集群提供了较为完善的方案,解决了存储能力受到单机限制,写操作无法负载均衡的问题。

    2、什么是Redis集群?

            Redis3.0加入了Redis的集群模式,实现了数据的分布式存储,对数据进行分片,将不同的数据存储在不同的master节点上面,从而解决了海量数据的存储问题。

            Redis集群采用去中心化的思想,没有中心节点的说法,对于客户端来说,整个集群可以看成一个整体,可以连接任意一个节点进行操作,就像操作单一Redis实例一样,不需要任何代理中间件,当客户端操作的key没有分配到该node上时,Redis会返回转向指令,指向正确的node。

            Redis也内置了高可用机制,支持N个master节点,每个master节点都可以挂载多个slave节点,当master节点挂掉时,集群会提升它的某个slave节点作为新的master节点。

            如上图所示,Redis集群可以看成多个主从架构组合起来的,每一个主从架构可以看成一个节点(其中,只有master节点具有处理请求的能力,slave节点主要是用于节点的高可用)

    二、Redis集群的数据分布算法:哈希槽算法

    1、什么是哈希槽算法?

            前面讲到,Redis集群通过分布式存储的方式解决了单节点的海量数据存储的问题,对于分布式存储,需要考虑的重点就是如何将数据进行拆分到不同的Redis服务器上。常见的分区算法有hash算法、一致性hash算法,关于这些算法这里就不多介绍。

    • 普通hash算法:将key使用hash算法计算之后,按照节点数量来取余,即hash(key)%N。优点就是比较简单,但是扩容或者摘除节点时需要重新根据映射关系计算,会导致数据重新迁移。
    • 一致性hash算法:为每一个节点分配一个token,构成一个哈希环;查找时先根据key计算hash值,然后顺时针找到第一个大于等于该哈希值的token节点。优点是在加入和删除节点时只影响相邻的两个节点,缺点是加减节点会造成部分数据无法命中,所以一般用于缓存,而且用于节点量大的情况下,扩容一般增加一倍节点保障数据负载均衡。

            Redis集群采用的算法是哈希槽分区算法。Redis集群中有16384个哈希槽(槽的范围是 0 -16383,哈希槽),将不同的哈希槽分布在不同的Redis节点上面进行管理,也就是说每个Redis节点只负责一部分的哈希槽。在对数据进行操作的时候,集群会对使用CRC16算法对key进行计算并对16384取模(slot = CRC16(key)%16383),得到的结果就是 Key-Value 所放入的槽,通过这个值,去找到对应的槽所对应的Redis节点,然后直接到这个对应的节点上进行存取操作。

            使用哈希槽的好处就在于可以方便的添加或者移除节点,并且无论是添加删除或者修改某一个节点,都不会造成集群不可用的状态。当需要增加节点时,只需要把其他节点的某些哈希槽挪到新节点就可以了;当需要移除节点时,只需要把移除节点上的哈希槽挪到其他节点就行了;哈希槽数据分区算法具有以下几种特点:

    • 解耦数据和节点之间的关系,简化了扩容和收缩难度;
    • 节点自身维护槽的映射关系,不需要客户端代理服务维护槽分区元数据
    • 支持节点、槽、键之间的映射查询,用于数据路由,在线伸缩等场景

    槽的迁移与指派命令:CLUSTER ADDSLOTS 0 1 2 3 4 ... 5000 

            默认情况下,redis集群的读和写都是到master上去执行的,不支持slave节点读和写,跟Redis主从复制下读写分离不一样,因为redis集群的核心的理念,主要是使用slave做数据的热备,以及master故障时的主备切换,实现高可用的。Redis的读写分离,是为了横向任意扩展slave节点去支撑更大的读吞吐量。而redis集群架构下,本身master就是可以任意扩展的,如果想要支撑更大的读或写的吞吐量,都可以直接对master进行横向扩展。

    2、Redis中哈希槽相关的数据结构:

    (1)clusterNode数据结构:保存节点的当前状态,比如节点的创建时间,节点的名字,节点当前的配置纪元,节点的IP和地址,等等。

    (2)clusterState数据结构:记录当前节点所认为的集群目前所处的状态。

    (3)节点的槽指派信息:

            clusterNode数据结构的slots属性和numslot属性记录了节点负责处理那些槽:slots属性是一个二进制位数组(bit array),这个数组的长度为16384/8=2048个字节,共包含16384个二进制位。Master节点用bit来标识对于某个槽自己是否拥有,时间复杂度为O(1)

    (4)集群所有槽的指派信息:

            当收到集群中其他节点发送的信息时,通过将节点槽的指派信息保存在本地的clusterState.slots数组里面,程序要检查槽i是否已经被指派,又或者取得负责处理槽i的节点,只需要访问clusterState.slots[i]的值即可,时间复杂度仅为O(1)

            如上图所示,ClusterState 中保存的 Slots 数组中每个下标对应一个槽,每个槽信息中对应一个 clusterNode 也就是缓存的节点。这些节点会对应一个实际存在的 Redis 缓存服务,包括 IP 和 Port 的信息。Redis Cluster 的通讯机制实际上保证了每个节点都有其他节点和槽数据的对应关系。无论Redis 的客户端访问集群中的哪个节点都可以路由到对应的节点上,因为每个节点都有一份 ClusterState,它记录了所有槽和节点的对应关系。

    3、集群的请求重定向:

            前面讲到,Redis集群在客户端层面没有采用代理,并且无论Redis 的客户端访问集群中的哪个节点都可以路由到对应的节点上,下面来看看 Redis 客户端是如何通过路由来调用缓存节点的:

    (1)MOVED请求:

            如上图所示,Redis 客户端通过 CRC16(key)%16383 计算出 Slot 的值,发现需要找“缓存节点1”进行数据操作,但是由于缓存数据迁移或者其他原因导致这个对应的 Slot 的数据被迁移到了“缓存节点2”上面。那么这个时候 Redis 客户端就无法从“缓存节点1”中获取数据了。但是由于“缓存节点1”中保存了所有集群中缓存节点的信息,因此它知道这个 Slot 的数据在“缓存节点2”中保存,因此向 Redis 客户端发送了一个 MOVED 的重定向请求。这个请求告诉其应该访问的“缓存节点2”的地址。Redis 客户端拿到这个地址,继续访问“缓存节点2”并且拿到数据。

    (2)ASK请求:

            上面的例子说明了,数据 Slot 从“缓存节点1”已经迁移到“缓存节点2”了,那么客户端可以直接找“缓存节点2”要数据。那么如果两个缓存节点正在做节点的数据迁移,此时客户端请求会如何处理呢?

            Redis 客户端向“缓存节点1”发出请求,此时“缓存节点1”正向“缓存节点 2”迁移数据,如果没有命中对应的 Slot,它会返回客户端一个 ASK 重定向请求并且告诉“缓存节点2”的地址。客户端向“缓存节点2”发送 Asking 命令,询问需要的数据是否在“缓存节点2”上,“缓存节点2”接到消息以后返回数据是否存在的结果。

    (3)频繁重定向造成的网络开销的处理:smart客户端

    ① 什么是 smart客户端:

            在大部分情况下,可能都会出现一次请求重定向才能找到正确的节点,这个重定向过程显然会增加集群的网络负担和单次请求耗时。所以大部分的客户端都是smart的。所谓 smart客户端,就是指客户端本地维护一份hashslot => node的映射表缓存,大部分情况下,直接走本地缓存就可以找到hashslot => node,不需要通过节点进行moved重定向,

    ② JedisCluster的工作原理:

    • 在JedisCluster初始化的时候,就会随机选择一个node,初始化hashslot => node映射表,同时为每个节点创建一个JedisPool连接池。
    • 每次基于JedisCluster执行操作时,首先会在本地计算key的hashslot,然后在本地映射表找到对应的节点node。
    • 如果那个node正好还是持有那个hashslot,那么就ok;如果进行了reshard操作,可能hashslot已经不在那个node上了,就会返回moved。
    • 如果JedisCluter API发现对应的节点返回moved,那么利用该节点返回的元数据,更新本地的hashslot => node映射表缓存
    • 重复上面几个步骤,直到找到对应的节点,如果重试超过5次,那么就报错,JedisClusterMaxRedirectionException

    ③ hashslot迁移和ask重定向:

            如果hashslot正在迁移,那么会返回ask重定向给客户端。客户端接收到ask重定向之后,会重新定位到目标节点去执行,但是因为ask发生在hashslot迁移过程中,所以JedisCluster API收到ask是不会更新hashslot本地缓存。

            虽然ASK与MOVED都是对客户端的重定向控制,但是有本质区别。ASK重定向说明集群正在进行slot数据迁移,客户端无法知道迁移什么时候完成,因此只能是临时性的重定向,客户端不会更新slots缓存。但是MOVED重定向说明键对应的槽已经明确指定到新的节点,客户端需要更新slots缓存。

    三、Redis集群中节点的通信机制:goosip协议

            redis集群的哈希槽算法解决的是数据的存取问题,不同的哈希槽位于不同的节点上,而不同的节点维护着一份它所认为的当前集群的状态,同时,Redis集群是去中心化的架构。那么,当集群的状态发生变化时,比如新节点加入、slot迁移、节点宕机、slave提升为新Master等等,我们希望这些变化尽快被其他节点发现,Redis是如何进行处理的呢?也就是说,Redis不同节点之间是如何进行通信进行维护集群的同步状态呢?

            在Redis集群中,不同的节点之间采用gossip协议进行通信,节点之间通讯的目的是为了维护节点之间的元数据信息。这些元数据就是每个节点包含哪些数据,是否出现故障,通过gossip协议,达到最终数据的一致性。

    gossip协议,是基于流行病传播方式的节点或者进程之间信息交换的协议。原理就是在不同的节点间不断地通信交换信息,一段时间后,所有的节点就都有了整个集群的完整信息,并且所有节点的状态都会达成一致。每个节点可能知道所有其他节点,也可能仅知道几个邻居节点,但只要这些节可以通过网络连通,最终他们的状态就会是一致的。Gossip协议最大的好处在于,即使集群节点的数量增加,每个节点的负载也不会增加很多,几乎是恒定的。

    Redis集群中节点的通信过程如下:

    • 集群中每个节点都会单独开一个TCP通道,用于节点间彼此通信。
    • 每个节点在固定周期内通过待定的规则选择几个节点发送ping消息
    • 接收到ping消息的节点用pong消息作为响应

            使用gossip协议的优点在于将元数据的更新分散在不同的节点上面,降低了压力;但是缺点就是元数据的更新有延时,可能导致集群中的一些操作会有一些滞后。另外,由于 gossip 协议对服务器时间的要求较高,时间戳不准确会影响节点判断消息的有效性。而且节点数量增多后的网络开销也会对服务器产生压力,同时结点数太多,意味着达到最终一致性的时间也相对变长,因此官方推荐最大节点数为1000左右。

    redis cluster架构下的每个redis都要开放两个端口号,比如一个是6379,另一个就是加1w的端口号16379。

    • 6379端口号就是redis服务器入口。
    • 16379端口号是用来进行节点间通信的,也就是 cluster bus 的东西,cluster bus 的通信,用来进行故障检测、配置更新、故障转移授权。cluster bus 用的是一种叫gossip 协议的二进制协议

    1、gossip协议的常见类型:

    gossip协议常见的消息类型包含: ping、pong、meet、fail等等。

    (1)meet:主要用于通知新节点加入到集群中,通过「cluster meet ip port」命令,已有集群的节点会向新的节点发送邀请,加入现有集群。

    (2)ping:用于交换节点的元数据。每个节点每秒会向集群中其他节点发送 ping 消息,消息中封装了自身节点状态还有其他部分节点的状态数据,也包括自身所管理的槽信息等等。

    • 因为发送ping命令时要携带一些元数据,如果很频繁,可能会加重网络负担。因此,一般每个节点每秒会执行 10 次 ping,每次会选择 5 个最久没有通信的其它节点。
    • 如果发现某个节点通信延时达到了 cluster_node_timeout / 2,那么立即发送 ping,避免数据交换延时过长导致信息严重滞后。比如说,两个节点之间都 10 分钟没有交换数据了,那么整个集群处于严重的元数据不一致的情况,就会有问题。所以 cluster_node_timeout 可以调节,如果调得比较大,那么会降低 ping 的频率。
    • 每次 ping,会带上自己节点的信息,还有就是带上 1/10 其它节点的信息,发送出去,进行交换。至少包含 3 个其它节点的信息,最多包含 (总节点数 - 2)个其它节点的信息。

    (3)pong:ping和meet消息的响应,同样包含了自身节点的状态和集群元数据信息。

    (4)fail:某个节点判断另一个节点 fail 之后,向集群所有节点广播该节点挂掉的消息,其他节点收到消息后标记已下线。

    由于Redis集群的去中心化以及gossip通信机制,Redis集群中的节点只能保证最终一致性。例如当加入新节点时(meet),只有邀请节点和被邀请节点知道这件事,其余节点要等待 ping 消息一层一层扩散。除了 Fail 是立即全网通知的,其他诸如新节点、节点重上线、从节点选举成为主节点、槽变化等,都需要等待被通知到,也就是Gossip协议是最终一致性的协议。

    2、meet命令的实现:

    (1)节点A会为节点B创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。

    (2)节点A根据CLUSTER MEET命令给定的IP地址和端口号,向节点B发送一条MEET消息。

    (3)节点B接收到节点A发送的MEET消息,节点B会为节点A创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。

    (4)节点B向节点A返回一条PONG消息。

    (5)节点A将受到节点B返回的PONG消息,通过这条PONG消息,节点A可以知道节点B已经成功的接收了自己发送的MEET消息。

    (6)之后,节点A将向节点B返回一条PING消息。

    (7)节点B将接收到的节点A返回的PING消息,通过这条PING消息节点B可以知道节点A已经成功的接收到了自己返回的PONG消息,握手完成。

    (8)之后,节点A会将节点B的信息通过Gossip协议传播给集群中的其他节点,让其他节点也与节点B进行握手,最终,经过一段时间后,节点B会被集群中的所有节点认识。

    四、集群的扩容与收缩:

            作为分布式部署的缓存节点总会遇到缓存扩容和缓存故障的问题。这就会导致缓存节点的上线和下线的问题。由于每个节点中保存着槽数据,因此当缓存节点数出现变动时,这些槽数据会根据对应的虚拟槽算法被迁移到其他的缓存节点上。所以对于redis集群,集群伸缩主要在于槽和数据在节点之间移动。

    1、扩容:

    • (1)启动新节点
    • (2)使用cluster meet命令将新节点加入到集群
    • (3)迁移槽和数据:添加新节点后,需要将一些槽和数据从旧节点迁移到新节点

            如上图所示,集群中本来存在“缓存节点1”和“缓存节点2”,此时“缓存节点3”上线了并且加入到集群中。此时根据虚拟槽的算法,“缓存节点1”和“缓存节点2”中对应槽的数据会应该新节点的加入被迁移到“缓存节点3”上面。

            新节点加入到集群的时候,作为孤儿节点是没有和其他节点进行通讯的。因此需要在集群中任意节点执行 cluster meet 命令让新节点加入进来。假设新节点是 192.168.1.1 5002,老节点是 192.168.1.1 5003,那么运行以下命令将新节点加入到集群中。

    192.168.1.1 5003> cluster meet 192.168.1.1 5002

            这个是由老节点发起的,有点老成员欢迎新成员加入的意思。新节点刚刚建立没有建立槽对应的数据,也就是说没有缓存任何数据。如果这个节点是主节点,需要对其进行槽数据的扩容;如果这个节点是从节点,就需要同步主节点上的数据。总之就是要同步数据。

    如上图所示,由客户端发起节点之间的槽数据迁移,数据从源节点往目标节点迁移。

    • (1)客户端对目标节点发起准备导入槽数据的命令,让目标节点准备好导入槽数据。使用命令:cluster setslot {slot} importing {sourceNodeId}
    • (2)之后对源节点发起送命令,让源节点准备迁出对应的槽数据。使用命令:cluster setslot {slot} migrating {targetNodeId}
    • (3)此时源节点准备迁移数据了,在迁移之前把要迁移的数据获取出来。通过命令 cluster getkeysinslot {slot} {count}。Count 表示迁移的 Slot 的个数。
    • (4)然后在源节点上执行,migrate {targetIP} {targetPort} “” 0 {timeout} keys {keys} 命令,把获取的键通过流水线批量迁移到目标节点。
    • (5)重复 3 和 4 两步不断将数据迁移到目标节点。
    • (6)完成数据迁移到目标节点以后,通过 cluster setslot {slot} node {targetNodeId} 命令通知对应的槽被分配到目标节点,并且广播这个信息给全网的其他主节点,更新自身的槽节点对应表。

    2、收缩:

    • 迁移槽。
    • 忘记节点。通过命令 cluster forget {downNodeId} 通知其他的节点

            为了安全删除节点,Redis集群只能下线没有负责槽的节点。因此如果要下线有负责槽的master节点,则需要先将它负责的槽迁移到其他节点。迁移的过程也与上线操作类似,不同的是下线的时候需要通知全网的其他节点忘记自己,此时通过命令 cluster forget {downNodeId} 通知其他的节点。

    五、集群的故障检测与故障转恢复机制:

    1、集群的故障检测:

            Redis集群的故障检测是基于gossip协议的,集群中的每个节点都会定期地向集群中的其他节点发送PING消息,以此交换各个节点状态信息,检测各个节点状态:在线状态、疑似下线状态PFAIL、已下线状态FAIL。

    (1)主观下线(pfail):当节点A检测到与节点B的通讯时间超过了cluster-node-timeout 的时候,就会更新本地节点状态,把节点B更新为主观下线。

    主观下线并不能代表某个节点真的下线了,有可能是节点A与节点B之间的网络断开了,但是其他的节点依旧可以和节点B进行通讯。

    (2)客观下线:

            由于集群内的节点会不断地与其他节点进行通讯,下线信息也会通过 Gossip 消息传遍所有节点,因此集群内的节点会不断收到下线报告。

            当半数以上的主节点标记了节点B是主观下线时,便会触发客观下线的流程(该流程只针对主节点,如果是从节点就会忽略)。将主观下线的报告保存到本地的 ClusterNode 的结构fail_reports链表中,并且对主观下线报告的时效性进行检查,如果超过 cluster-node-timeout*2 的时间,就忽略这个报告,否则就记录报告内容,将其标记为客观下线

            接着向集群广播一条主节点B的Fail 消息,所有收到消息的节点都会标记节点B为客观下线。

    2、集群地故障恢复:

            当故障节点下线后,如果是持有槽的主节点则需要在其从节点中找出一个替换它,从而保证高可用。此时下线主节点的所有从节点都担负着恢复义务,这些从节点会定时监测主节点是否进入客观下线状态,如果是,则触发故障恢复流程。故障恢复也就是选举一个节点充当新的master,选举的过程是基于Raft协议选举方式来实现的。

    2.1、从节点过滤:

            检查每个slave节点与master节点断开连接的时间,如果超过了cluster-node-timeout * cluster-slave-validity-factor,那么就没有资格切换成master

    2.2、投票选举:

    (1)节点排序:

            对通过过滤条件的所有从节点进行排序,按照priority、offset、run id排序,排序越靠前的节点,越优先进行选举。

    • priority的值越低,优先级越高
    • offset越大,表示从master节点复制的数据越多,选举时间越靠前,优先进行选举
    • 如果offset相同,run id越小,优先级越高

    (2)更新配置纪元:

            每个主节点会去更新配置纪元(clusterNode.configEpoch),这个值是不断增加的整数。这个值记录了每个节点的版本和整个集群的版本。每当发生重要事情的时候(例如:出现新节点,从节点精选)都会增加全局的配置纪元并且赋给相关的主节点,用来记录这个事件。更新这个值目的是,保证所有主节点对这件“大事”保持一致,大家都统一成一个配置纪元,表示大家都知道这个“大事”了。

    (3)发起选举:

            更新完配置纪元以后,从节点会向集群发起广播选举的消息(CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST),要求所有收到这条消息,并且具有投票权的主节点进行投票。每个从节点在一个纪元中只能发起一次选举。

    (4)选举投票:

            如果一个主节点具有投票权,并且这个主节点尚未投票给其他从节点,那么主节点将向要求投票的从节点返回一条CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示这个主节点支持从节点成为新的主节点。每个参与选举的从节点都会接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,并根据自己收到了多少条这种消息来统计自己获得了多少主节点的支持。

            如果超过(N/2 + 1)数量的master节点都投票给了某个从节点,那么选举通过,这个从节点可以切换成master,如果在 cluster-node-timeout*2 的时间内从节点没有获得足够数量的票数,本次选举作废,更新配置纪元,并进行第二轮选举,直到选出新的主节点为止。

    在第(1)步排序领先的从节点通常会获得更多的票,因为它触发选举的时间更早一些,获得票的机会更大

    2.3、替换主节点:

            当满足投票条件的从节点被选出来以后,会触发替换主节点的操作。删除原主节点负责的槽数据,把这些槽数据添加到自己节点上,并且广播让其他的节点都知道这件事情,新的主节点诞生了。

    (1)被选中的从节点执行SLAVEOF NO ONE命令,使其成为新的主节点

    (2)新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己

    (3)新的主节点对集群进行广播PONG消息,告知其他节点已经成为新的主节点

    (4)新的主节点开始接收和处理槽相关的请求

    备注:如果集群中某个节点的master和slave节点都宕机了,那么集群就会进入fail状态,因为集群的slot映射不完整。如果集群超过半数以上的master挂掉,无论是否有slave,集群都会进入fail状态。

    六、Redis集群的搭建:

    该部分可以参考这篇文章:https://juejin.cn/post/6922690589347545102#heading-1

    Redis集群的搭建可以分为以下几个部分:

    1、启动节点:将节点以集群模式启动,读取或者生成集群配置文件,此时节点是独立的。

    2、节点握手:节点通过gossip协议通信,将独立的节点连成网络,主要使用meet命令。

    3、槽指派:将16384个槽位分配给主节点,以达到分片保存数据库键值对的效果。

    七、Redis集群的运维:

    1、数据迁移问题:

    Redis集群可以进行节点的动态扩容缩容,这一过程目前还处于半自动状态,需要人工介入。在扩缩容的时候,需要进行数据迁移。而 Redis为了保证迁移的一致性,迁移所有操作都是同步操作,执行迁移时,两端的 Redis均会进入时长不等的阻塞状态,对于小Key,该时间可以忽略不计,但如果一旦Key的内存使用过大,严重的时候会接触发集群内的故障转移,造成不必要的切换。

    2、带宽消耗问题:

    Redis集群是无中心节点的集群架构,依靠Gossip协议协同自动化修复集群的状态,但goosip有消息延时和消息冗余的问题,在集群节点数量过多的时候,goosip协议通信会消耗大量的带宽,主要体现在以下几个方面:

    • 消息发送频率:跟cluster-node-timeout密切相关,当节点发现与其他节点的最后通信时间超过 cluster-node-timeout/2时会直接发送ping消息
    • 消息数据量:每个消息主要的数据占用包含:slots槽数组(2kb)和整个集群1/10的状态数据
    • 节点部署的机器规模:机器的带宽上限是固定的,因此相同规模的集群分布的机器越多,每台机器划分的节点越均匀,则整个集群内整体的可用带宽越高

    集群带宽消耗主要分为:读写命令消耗+Gossip消息消耗,因此搭建Redis集群需要根据业务数据规模和消息通信成本做出合理规划:

    • 在满足业务需求的情况下尽量避免大集群,同一个系统可以针对不同业务场景拆分使用若干个集群。
    • 适度提供cluster-node-timeout降低消息发送频率,但是cluster-node-timeout还影响故障转移的速度,因此需要根据自身业务场景兼顾二者平衡
    • 如果条件允许尽量均匀部署在更多机器上,避免集中部署。如果有60个节点的集群部署在3台机器上每台20个节点,这是机器的带宽消耗将非常严重

    3、Pub/Sub广播问题:

    集群模式下内部对所有publish命令都会向所有节点进行广播,加重带宽负担,所以集群应该避免频繁使用Pub/sub功能

    4、集群倾斜:

    集群倾斜是指不同节点之间数据量和请求量出现明显差异,这种情况将加大负载均衡和开发运维的难度。因此需要理解集群倾斜的原因

    (1)数据倾斜:

    • 节点和槽分配不均
    • 不同槽对应键数量差异过大
    • 集合对象包含大量元素
    • 内存相关配置不一致

    (2)请求倾斜:

    合理设计键,热点大集合对象做拆分或者使用hmget代替hgetall避免整体读取

    5、集群读写分离:

    集群模式下读写分离成本比较高,直接扩展主节点数量来提高集群性能是更好的选择。

    参考文章:https://baijiahao.baidu.com/s?id=1663270958212268352&wfr=spider&for=pc

    更多相关内容
  • RabbitMQ集群

    千次阅读 2022-03-15 16:13:41
    如何避免单点的MQ故障而导致的不可用问题,这个就要靠MQ的集群去实现了。

    目录

    1、集群分类

    1.1 普通集群

    1.1.1 集群结构和特征

    1.1.2 部署

    1.2 镜像集群

    1.2.1 集群结构和特征

    1.2.2 部署

    2、仲裁队列

    2.1 部署

    2.2 .Java代码创建仲裁队列

    2.3 SpringAMQP连接MQ集群


    今天我们来学习如何避免单点的MQ故障而导致的不可用问题,这个就要靠MQ的集群去实现了。

    1、集群分类

    RabbitMQ的是基于Erlang语言编写,而Erlang又是一个面向并发的语言,天然支持集群模式。RabbitMQ的集群有两种模式:

    1.1 普通集群

    是一种分布式集群,将队列分散到集群的各个节点,从而提高整个集群的并发能力。

    这种集群有一个问题,一旦集群中某个节点出现了故障,那这个节点上的队列,以及上面的消息就全都没了,所以它会存在一定的安全问题。

    1.1.1 集群结构和特征

    普通集群,或者叫标准集群(classic cluster),具备下列特征:

    1. 会在集群的各个节点间共享部分数据,包括:交换机、队列元信息(队列的描述信息,包括队列名字,队列节点,队列有什么消息)。不包含消息本身。

    2. 当访问集群某节点时,如果队列不在该节点,会从数据所在节点传递到当前节点并返回

    3. 队列所在节点宕机,队列中的消息就会丢失

    结构如图:

    1.1.2 部署

    我们的计划部署3节点的mq集群:

    这里三个主机就是三台机器,这里我用三个Docker代替了。

    集群中的节点标示默认都是:rabbit@[hostname],因此以上三个节点的名称分别为:

    • rabbit@mq1

    • rabbit@mq2

    • rabbit@mq3

    1、获取cookie

    RabbitMQ底层依赖于Erlang,而Erlang虚拟机就是一个面向分布式的语言,默认就支持集群模式。集群模式中的每个RabbitMQ 节点使用 cookie 来确定它们是否被允许相互通信。

    要使两个节点能够通信,它们必须具有相同的共享秘密,称为Erlang cookie。cookie 只是一串最多 255 个字符的字母数字字符。

    每个集群节点必须具有相同的 cookie。实例之间也需要它来相互通信。

    我们先启动一个mq容器中获取一个cookie值,作为集群的cookie。执行下面的命令:

    docker exec -it mq cat /var/lib/rabbitmq/.erlang.cookie

    我的之前的mq容器就叫mq。

    可以看到我的cookie值如下:

     接下来,停止并删除当前的mq容器,我们重新搭建集群(记得把cookie值复制保存一下)。

    docker rm -f mq

    2、准备集群配置

    在/tmp目录新建一个配置文件 rabbitmq.conf:

    cd /tmp

    创建文件

    touch rabbitmq.conf

     文件内容如下:

    loopback_users.guest = false
    listeners.tcp.default = 5672
    cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config
    cluster_formation.classic_config.nodes.1 = rabbit@mq1
    cluster_formation.classic_config.nodes.2 = rabbit@mq2
    cluster_formation.classic_config.nodes.3 = rabbit@mq3

    解析:

    loopback_users.guest =false:禁用guest用户,因为RabbitMQ客户端有一个guest用户,所以我们把它禁用掉,防止有些不法分子来访问我们。

    listeners.tcp.default = 5672:监听端口,用于消息通信

    最重要的是下面三个:

    cluster_formation.classic_config.nodes.1 = rabbit@mq1 cluster_formation.classic_config.nodes.2 = rabbit@mq2 cluster_formation.classic_config.nodes.3 = rabbit@mq3

    这里配置的分别集群中的节点信息。

    再创建一个文件,记录cookie。

    cd /tmp

    创建cookie文件。

    touch .erlang.cookie

    写入cookie

    echo "NEHXVEBVVLVHYDWCAFVH" > .erlang.cookie

    修改cookie文件的权限

    chmod 600 .erlang.cookie

    准备三个目录,mq1、mq2、mq3:

    cd /tmp
    

    创建目录

    mkdir mq1 mq2 mq3

    然后拷贝rabbitmq.conf、cookie文件到mq1、mq2、mq3:

    cd /tmp

    拷贝

    cp rabbitmq.conf mq1
    cp rabbitmq.conf mq2
    cp rabbitmq.conf mq3
    cp .erlang.cookie mq1
    cp .erlang.cookie mq2
    cp .erlang.cookie mq3

    3、启动集群

    创建一个网络:

    docker network create mq-net

    运行命令

    mq1:

    docker run -d --net mq-net \
    -v ${PWD}/mq1/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
    -v ${PWD}/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
    -e RABBITMQ_DEFAULT_USER=jie \
    -e RABBITMQ_DEFAULT_PASS=123456 \
    --name mq1 \
    --hostname mq1 \
    -p 8071:5672 \
    -p 8081:15672 \
    rabbitmq:3.8-management

    mq2:

    docker run -d --net mq-net \
    -v ${PWD}/mq2/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
    -v ${PWD}/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
    -e RABBITMQ_DEFAULT_USER=jie \
    -e RABBITMQ_DEFAULT_PASS=123456 \
    --name mq2 \
    --hostname mq2 \
    -p 8072:5672 \
    -p 8082:15672 \
    rabbitmq:3.8-management

    mq3:

    docker run -d --net mq-net \
    -v ${PWD}/mq3/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf \
    -v ${PWD}/.erlang.cookie:/var/lib/rabbitmq/.erlang.cookie \
    -e RABBITMQ_DEFAULT_USER=jie \
    -e RABBITMQ_DEFAULT_PASS=123456 \
    --name mq3 \
    --hostname mq3 \
    -p 8073:5672 \
    -p 8083:15672 \
    rabbitmq:3.8-management

    打开浏览器

    4、测试

    在mq1这个节点上添加一个队列:

     然后我们去mq2mq3那里也能看到这个队列。

    4.1 数据共享测试

    点击这个队列,进入管理页面:

     然后利用控制台发送一条消息到这个队列:

     结果在mq2、mq3上都能看到这条消息:

    4.2 可用性测试

    我们让其中一台节点mq1宕机:

    docker stop mq1

    然后登录mq2或mq3的控制台,发现simple.queue也不可用了:

    说明数据并没有拷贝到mq2mq3

    1.2 镜像集群

    在刚刚的案例中,一旦创建队列的主机宕机,队列就会不可用。不具备高可用能力。如果要解决这个问题,必须使用官方提供的镜像集群方案。

    官方文档地址:Classic Queue Mirroring — RabbitMQ

    镜像集群是一种主从集群,普通集群的基础上,添加了主从备份功能,提高集群的数据可用性。

    这种集群有一个问题,主从数据源要同步,要从主节点同步到从节点,但是这个主从同步它不是强一致的,存在一定的延迟,如果在主从同步期间出现了一点故障,就可能导致数据丢失

    因此在RabbitMQ3.8版本以后,推出了新的功能:仲裁队列来代替镜像集群,底层采用Raft协议确保主从的数据一致性。

    1.2.1 集群结构和特征

    • 交换机、队列、队列中的消息会在各个mq的镜像节点之间同步备份。

    • 创建队列的节点被称为该队列的主节点,备份到的其它节点叫做该队列的镜像节点。

    • 一个队列的主节点可能是另一个队列的镜像节点。

    • 不具备负载均衡功能,因为所有操作都是主节点完成,然后同步给镜像节点。

    • 主宕机后,镜像节点会替代成新的主节点。

    结构如图:

    1.2.2 部署

    镜像集群的配置有3种模式:

    这里我们以rabbitmqctl命令作为案例来讲解配置语法。

    语法示例:

    1、exactly模式

    首先进入任意一个节点

    docker exec -it mq1 bash

    rabbitmqctl set_policy ha-two "^two\." '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}'

    • rabbitmqctl set_policy`:固定写法

    • ha-two:策略名称,自定义

    • "^two\.":匹配队列的正则表达式,符合命名规则的队列才生效,这里是任何以two.开头的队列名称

    • '{"ha-mode":"exactly","ha-params":2,"ha-sync-mode":"automatic"}': 策略内容

      • "ha-mode":"exactly":策略模式,此处是exactly模式,指定副本数量

      • "ha-params":2:策略参数,这里是2,就是副本数量为2,1主1镜像

      • "ha-sync-mode":"automatic":同步策略,默认是manual,即新加入的镜像节点不会同步旧的消息。如果设置为automatic,则新加入的镜像节点会把主节点中所有消息都同步,会带来额外的网络开销

    然后退出 exit,我们进入浏览器查看。

     我们创建一个新的队列:

     在任意一个mq控制台查看队列:

     测试数据共享,给two.queue发送一条消息:

     然后在mq1、mq2、mq3的任意控制台查看消息:

     测试高可用,现在,我们让two.queue的主节点mq1宕机:

    docker stop mq1

    然后我们先看集群状态

     再看队列状态:

    发现依然是健康的!并且其主节点切换到了rabbit@mq2上。

    剩下的模式,大家可以自己去试试,大同小异。

    2、all模式

    rabbitmqctl set_policy ha-all "^all\." '{"ha-mode":"all"}'
    • ha-all:策略名称,自定义

    • "^all\.":匹配所有以all.开头的队列名

    • '{"ha-mode":"all"}':策略内容

      • "ha-mode":"all":策略模式,此处是all模式,即所有节点都会称为镜像节点

    3、nodes模式

    rabbitmqctl set_policy ha-nodes "^nodes\." '{"ha-mode":"nodes","ha-params":["rabbit@nodeA", "rabbit@nodeB"]}'
    • rabbitmqctl set_policy:固定写法

    • ha-nodes:策略名称,自定义

    • "^nodes\.":匹配队列的正则表达式,符合命名规则的队列才生效,这里是任何以nodes.开头的队列名称

    • '{"ha-mode":"nodes","ha-params":["rabbit@nodeA", "rabbit@nodeB"]}': 策略内容

      • "ha-mode":"nodes":策略模式,此处是nodes模式

      • "ha-params":["rabbit@mq1", "rabbit@mq2"]:策略参数,这里指定副本所在节点名称

    2、仲裁队列

    从RabbitMQ 3.8版本开始,引入了新的仲裁队列,他具备与镜像队里类似的功能,但使用更加方便,它具备以下特征。

    • 与镜像队列一样,都是主从模式,支持主从数据同步

    • 使用非常简单,没有复杂的配置

    • 主从同步基于Raft协议,强一致

    2.1 部署

    在任意控制台添加一个队列,一定要选择队列类型为Quorum类型。

     在任意控制台查看队列:

    可以看到,仲裁队列的 + 2字样。代表这个队列有2个镜像节点。

    因为仲裁队列默认的镜像数为5。如果你的集群有7个节点,那么镜像数肯定是5;而我们集群只有3个节点,因此镜像数量就是3.

    测试参考镜像集群的方式,效果是一样的。

    2.2 .Java代码创建仲裁队列

    要创建仲裁队列记得先去配置集群。

    @Bean
    public Queue quorumQueue() {
        return QueueBuilder
            .durable("quorum.queue") // 持久化
            .quorum() // 仲裁队列
            .build();
    }

    2.3 SpringAMQP连接MQ集群

    注意,这里用address来代替host、port方式

    spring:
      rabbitmq:
        addresses: 192.168.58.149:8071, 192.168.58.149:8072, 192.168.58.149:8073
        username: jie
        password: 123456
        virtual-host: /

    展开全文
  • zookeeper集群

    千次阅读 2021-07-20 17:23:24
    目录 一、Zookeeper定义 二、 zookeeper工作机制 三、zookeeper特点 四、Zookeeper数据结构 五、zookeeper应用场景 六、zookeeper选举机制 七、部署Zookeeper集群 一、Zookeeper定义 Zookeeper是一个开源的分布式的...

    一、Zookeeper定义

    • Zookeeper是一个开源的分布式的,为分布式框架提供协调服务的Apache项目。

    二、 zookeeper工作机制

    • Zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper就将负责通知已经在Zookeeper上注册的那些观察者做出相应的反应。也就是说 Zookeeper =文件系统+通知机制

    三、zookeeper特点

    在这里插入图片描述

    (1)Zookeeper:一个领导者(Leader),多个跟随者(Follower)组成的集群。
    (2)Zookeepe集群中只要有半数以上节点存活,Zookeeper集群就能正常服务。所以zookeeper适合安装奇数台服务器。
    (3)全局数据一致:每个server保存一份相同的数据副本,client无论连接到哪个Server,数据都是一致的。
    (4)更新请求顺序执行,来自同一个client的更新请求按其发送顺序依次执行,即先进先出。
    (5)数据更新原子性,一次数据更新要么成功,要么失败。
    (6)实时性,在一定时间范围内,client能读到最新数据。
    

    四、Zookeeper数据结构

    • ZooKeeper数据模型的结构与Linux文件系统很类似,整体上可以看作是一棵树,每个节点称做一个2Node。每一个2Node默认能够存储1MB的数据,每个ZNode都可以通过其路径唯一标识。

    在这里插入图片描述

    五、zookeeper应用场景

    提供的服务包括:统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、软负载均衡等。

    • 统一命名服务
      在分布式环境下,经常需要对应用/服务进行统一命名,便于识别。例如:IP不容易记住,而域名容易记住。

    • 统一配置管理
      (1)分布式环境下,配置文件同步非常常见。一般要求一个集群中,所有节点的配置信息是一致的,比如Kafka集群。对配置文件修改后,希望能够快速同步到各个节点上。
      (2)配置管理可交由ZooKeeper实现。可将配置信息写入ZooKeeper上的一个Znode。各个客户端服务器监听这个Znode。一旦Znode中的数据被修改,ZooKeeper将通知各个客户端服务器。

    • 统一集群管理
      (1)分布式环境中,实时掌握每个节点的状态是必要的。可根据节点实时状态做出一些调整。
      (2)ZooKeeper可以实现实时监控节点状态变化。可将节点信息写入ZooKeeper上的一个2Node。监听这个DMode可获取它的实时状态变化。

    • 服务器动态上下线
      客户端能实时洞察到服务器上下线的变化。

    • 软负教均衡
      在Zookeeper中记录每台服务器的访问数,让访问数最少的服务器去处理最新的客户端请求。


    在这里插入图片描述

    六、zookeeper选举机制

    在这里插入图片描述

    • 第一次启动选举机制

    (1)服务器1启动,发起一次选举。服务器1投自己一票。此时服务器1票数一票,不够半数以上(3票),选举无法完成,服务器1状态保持为LOOKING;

    (2)服务器2启动,再发起一次选举。服务器1和2分别投自己一票并交换选票信息:此时服务器1发现服务器2的myid比自己目前投票推举的(服务器1)大,更改选票为推举服务器2。此时服务器1票数0票,服务器2票数2票,没有半数以上结果,选举无法完成,服务器1,2状态保持LOOKING

    (3)服务器3启动,发起一次选举。此时服务器1和2都会更改选票为服务器3。此次投票结果:服务器1为0票,服务器2为0票,服务器3为3票。此时服务器3的票数已经超过半数,服务器3当选Leader。服务器1,2更改状态为FOLLOWING,服务器3更改状态为LEADING;

    (4)服务器4启动,发起一次选举。此时服务器1,2,3已经不是LooKING状态,不会更改选票信息。交换选票信息结果:服务器3为3票,服务器4为1票。此时服务器4服从多数,更改选票信息为服务器3,并更改状态为FOLOWING;

    (5) 服务器5启动,同4一样当小弟。

    在这里插入图片描述

    • 非第一次启动选举机制

    (1)当ZooKeeper集群中的一台服务器出现以下两种情况之一时,就会开始进入Leader选举:

    1. 服务器初始化启动。
    2. 服务器运行期间无法和Leader保持连接。

    (2)而当一台机器进入Leader选举流程时,当前集群也可能会处于以下两种状态:

    1. 集群中本来就己经存在一个Leader。
      对于已经存在Leader的情况,机器试图去选举Leader时,会被告知当前服务器的Leader信息,对于该机器来说,仅仅需要和Leader机器建立连接,并进行状态同步即可。
    2. 集群中确实不存在Leader。
      假设ZooKeeper由5台服务器组成,SID分别为1、2、3、4、5,ZXID分别为8、8、8、7、,并且此时sID为3的服务器是。一时刻,3和5服务器出现故障,因此开始进行Leader选举。

    选举Leader规则:

    1. EPOCH大的直接胜出
    2. EPOCH相同,事务id大的胜出
    3. 事务id相同,服务器id大的胜出

    SID:服务器ID。用来唯一标识一台ZooKeeper集群中的机器,每台机器不能重复,和myid一致。
    ZXID:事务ID。ZXID是一个事务ID,用来标识一次服务器状态的变更。在某一时刻,集群中的每台机器的zxID值不一定完全一致,这和ZooKeeper服务器对于客户端"更新请求"的处理逻辑速度有关。
    Bpoch:每个Leader任期的代号。没有Leader时同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加


    七、部署Zookeeper集群

    环境准备工作

    服务器类型系统和IP地址需要安装的组件
    Zookeeper服务器1CentOS7.4(64 位) 192.168.80.50jdk
    Zookeeper服务器2CentOS7.4(64 位) 192.168.80.60jdk
    Zookeeper服务器3CentOS7.4(64 位) 192.168.80.70jdk

    必要工作:关闭防火期和SElinux

    三台的zookeeper服务器的配置一样,仅展示一台
    1. 安装前准备
    //关闭防火墙
    systemctl stop firewalld
    systemctl disable firewalld
    setenforce 0
    
    //安装 JDK
    yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel
    java -version
    
    //下载安装包
    官方下载地址:https://archive.apache.org/dist/zookeeper/
    
    cd /opt
    wget https://archive.apache.org/dist/zookeeper/zookeeper-3.5.7/apache-zookeeper-3.5.7-bin.tar.gz
    
    1. 安装 Zookeeper
    cd /opt
    tar -zxvf apache-zookeeper-3.5.7-bin.tar.gz
    mv apache-zookeeper-3.5.7-bin /usr/local/zookeeper-3.5.7
    

    在这里插入图片描述
    在这里插入图片描述

    • 修改配置文件
    cd /usr/local/zookeeper-3.5.7/conf/
    cp zoo_sample.cfg zoo.cfg
    
    vim zoo.cfg
    tickTime=2000   #通信心跳时间,Zookeeper服务器与客户端心跳时间,单位毫秒
    initLimit=10    #Leader和Follower初始连接时能容忍的最多心跳数(tickTime的数量),这里表示为10*2s
    syncLimit=5     #Leader和Follower之间同步通信的超时时间,这里表示如果超过5*2s,Leader认为Follwer死掉,并从服务器列表中删除Follwer
    dataDir=/usr/local/zookeeper-3.5.7/data      ●修改,指定保存Zookeeper中的数据的目录,目录需要单独创建
    dataLogDir=/usr/local/zookeeper-3.5.7/logs   ●添加,指定存放日志的目录,目录需要单独创建
    clientPort=2181   #客户端连接端口
    

    在这里插入图片描述
    在这里插入图片描述

    • 添加集群信息
    vim zoo.cfg
    ------
    追加
    server.1=192.168.80.50:3188:3288
    server.2=192.168.80.60:3188:3288
    server.3=192.168.80.70:3188:3288
    

    server.A=B:C:D
    - A 是一个数字,表示这个是第几号服务器。集群模式下需要在zoo.cfg中dataDir指定的目录下创建一个文件myid,这个文件里面有一个数据就是A的值,Zookeeper启动时读取此文件,拿到里面的数据与zoo.cfg里面的配置信息比较从而判断到底是哪个server。
    - B 是这个服务器的地址。
    - C 是这个服务器Follower与集群中的Leader服务器交换信息的端口。
    - D 是万一集群中的Leader服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口。
    

    • 拷贝配置好的 Zookeeper 配置文件到其他机器上
    scp /usr/local/zookeeper-3.5.7/conf/zoo.cfg 192.168.80.60:/usr/local/zookeeper-3.5.7/conf/
    scp /usr/local/zookeeper-3.5.7/conf/zoo.cfg 192.168.80.70:/usr/local/zookeeper-3.5.7/conf/
    

    在这里插入图片描述

    • 在每个节点上创建数据目录和日志目录
    mkdir /usr/local/zookeeper-3.5.7/data
    mkdir /usr/local/zookeeper-3.5.7/logs
    

    在这里插入图片描述

    • 在每个节点的dataDir指定的目录下创建一个 myid 的文件
    echo 1 > /usr/local/zookeeper-3.5.7/data/myid
    echo 2 > /usr/local/zookeeper-3.5.7/data/myid
    echo 3 > /usr/local/zookeeper-3.5.7/data/myid
    

    在这里插入图片描述

    • 配置 Zookeeper 启动脚本
    vim /etc/init.d/zookeeper
    #!/bin/bash
    #chkconfig:2345 20 90
    #description:Zookeeper Service Control Script
    ZK_HOME='/usr/local/zookeeper-3.5.7'
    case $1 in
    start)
    	echo "---------- zookeeper 启动 ------------"
    	$ZK_HOME/bin/zkServer.sh start
    ;;
    stop)
    	echo "---------- zookeeper 停止 ------------"
    	$ZK_HOME/bin/zkServer.sh stop
    ;;
    restart)
    	echo "---------- zookeeper 重启 ------------"
    	$ZK_HOME/bin/zkServer.sh restart
    ;;
    status)
    	echo "---------- zookeeper 状态 ------------"
    	$ZK_HOME/bin/zkServer.sh status
    ;;
    *)
        echo "Usage: $0 {start|stop|restart|status}"
    esac
    

    在这里插入图片描述

    • 设置开机自启
    chmod +x /etc/init.d/zookeeper
    chkconfig --add zookeeper
    

    在这里插入图片描述

    • 分别启动 Zookeeper
    service zookeeper start
    

    在这里插入图片描述

    • 查看当前状态
    service zookeeper status
    

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    总结:

    1. 集群最少要几台机器,集群规则是怎样的?集群中有 3 台服务器,其中一个节点宕机,这个时候 Zookeeper 还可以使用吗?

    集群规则为 2N+1 台,N>0,即 3 台。可以继续使用,单数服务器只要没超过一半的服务器宕机就可以继续使用。

    1. Zookeeper 都有哪些功能?
    • 集群管理:监控节点存活状态、运行请求等;

    • 主节点选举:主节点挂掉了之后可以从备用的节点开始新一轮选主,主节点选举说的就是这个选举的过程,使用 Zookeeper可以协助完成这个过程;

    • 分布式锁:Zookeeper
      提供两种锁:独占锁、共享锁。独占锁即一次只能有一个线程使用资源,共享锁是读锁共享,读写互斥,即可以有多线线程同时读同一个资源,如果要使用写锁也只能有一个线程使用。Zookeeper可以对分布式锁进行控制。

    • 命名服务:在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息。

    1. zookeeper 节点宕机如何处理?
    • Zookeeper 本身也是集群,推荐配置不少于 3 个服务器。Zookeeper 自身也要保证当一个节点宕机时,其他节点会继续提供服务。

    • 如果是一个 Follower 宕机,还有 2 台服务器提供访问,因为 Zookeeper 上的数据是有多个副本的,数据并不会丢失;

    • 如果是一个 Leader 宕机,Zookeeper 会选举出新的 Leader。

    • ZK 集群的机制是只要超过半数的节点正常,集群就能正常提供服务。只有在 ZK节点挂得太多,只剩一半或不到一半节点能工作,集群才失效。

    所以

    • 3 个节点的 cluster 可以挂掉 1 个节点(leader 可以得到 2 票>1.5)

    • 2 个节点的 cluster 就不能挂掉任何 1 个节点了(leader 可以得到 1 票<=1)

    1. ZooKeeper 本身就是一个分布式程序(只要半数以上节点存活,ZooKeeper 就能正常服务)。

    2. ZooKeeper 将数据保存在内存中,这也就保证了 高吞吐量和低延迟(但是内存限制了能够存储的容量不太大,此限制也是保持 Znode 中存储的数据量较小的进一步原因)。

    3. ZooKeeper 底层其实只提供了两个功能:①管理(存储、读取)用户程序提交的数据;②为用户程序提交数据节点监听服务。

    展开全文
  • 手把手教你搭建 RabbitMQ 集群

    千次阅读 2021-12-12 16:55:00
    两种模式1.1 普通集群1.2 镜像集群1.3 节点类型2. 搭建普通集群2.1 预备知识2.2 开始搭建2.3 代码测试2.4 反向测试3. 搭建镜像集群3.1 网页配置镜像队列3.2 命令行配置镜像队列4. 小结 单个的 RabbitMQ 肯定无法...


    单个的 RabbitMQ 肯定无法实现高可用,要想高可用,还得上集群。

    今天松哥就来和大家聊一聊 RabbitMQ 集群的搭建。

    1. 两种模式

    说到集群,小伙伴们可能第一个问题是,如果我有一个 RabbitMQ 集群,那么是不是我的消息集群中的每一个实例都保存一份呢?

    这其实就涉及到 RabbitMQ 集群的两种模式:

    • 普通集群
    • 镜像集群

    1.1 普通集群

    普通集群模式,就是将 RabbitMQ 部署到多台服务器上,每个服务器启动一个 RabbitMQ 实例,多个实例之间进行消息通信。

    此时我们创建的队列 Queue,它的元数据(主要就是 Queue 的一些配置信息)会在所有的 RabbitMQ 实例中进行同步,但是队列中的消息只会存在于一个 RabbitMQ 实例上,而不会同步到其他队列。

    当我们消费消息的时候,如果连接到了另外一个实例,那么那个实例会通过元数据定位到 Queue 所在的位置,然后访问 Queue 所在的实例,拉取数据过来发送给消费者。

    这种集群可以提高 RabbitMQ 的消息吞吐能力,但是无法保证高可用,因为一旦一个 RabbitMQ 实例挂了,消息就没法访问了,如果消息队列做了持久化,那么等 RabbitMQ 实例恢复后,就可以继续访问了;如果消息队列没做持久化,那么消息就丢了。

    大致的流程图如下图:

    1.2 镜像集群

    它和普通集群最大的区别在于 Queue 数据和原数据不再是单独存储在一台机器上,而是同时存储在多台机器上。也就是说每个 RabbitMQ 实例都有一份镜像数据(副本数据)。每次写入消息的时候都会自动把数据同步到多台实例上去,这样一旦其中一台机器发生故障,其他机器还有一份副本数据可以继续提供服务,也就实现了高可用。

    大致流程图如下图:

    1.3 节点类型

    RabbitMQ 中的节点类型有两种:

    • RAM node:内存节点将所有的队列、交换机、绑定、用户、权限和 vhost 的元数据定义存储在内存中,好处是可以使得交换机和队列声明等操作速度更快。
    • Disk node:将元数据存储在磁盘中,单节点系统只允许磁盘类型的节点,防止重启 RabbitMQ 的时候,丢失系统的配置信息

    RabbitMQ 要求在集群中至少有一个磁盘节点,所有其他节点可以是内存节点,当节点加入或者离开集群时,必须要将该变更通知到至少一个磁盘节点。如果集群中唯一的一个磁盘节点崩溃的话,集群仍然可以保持运行,但是无法进行其他操作(增删改查),直到节点恢复。为了确保集群信息的可靠性,或者在不确定使用磁盘节点还是内存节点的时候,建议直接用磁盘节点。

    2. 搭建普通集群

    2.1 预备知识

    大致的结构了解了,接下来我们就把集群给搭建起来。先从普通集群开始,我们就使用 docker 来搭建。

    搭建之前,有两个预备知识需要大家了解:

    1. 搭建集群时,节点中的 Erlang Cookie 值要一致,默认情况下,文件在 /var/lib/rabbitmq/.erlang.cookie,我们在用 docker 创建 RabbitMQ 容器时,可以为之设置相应的 Cookie 值。
    2. RabbitMQ 是通过主机名来连接服务,必须保证各个主机名之间可以 ping 通。可以通过编辑 /etc/hosts 来手工添加主机名和 IP 对应关系。如果主机名 ping 不通,RabbitMQ 服务启动会失败(如果我们是在不同的服务器上搭建 RabbitMQ 集群,大家需要注意这一点,接下来的 2.2 小结,我们将通过 Docker 的容器连接 link 来实现容器之间的访问,略有不同)。

    2.2 开始搭建

    执行如下命令创建三个 RabbitMQ 容器:

    docker run -d --hostname rabbit01 --name mq01 -p 5671:5672 -p 15671:15672 -e RABBITMQ_ERLANG_COOKIE="javaboy_rabbitmq_cookie" rabbitmq:3-management
    docker run -d --hostname rabbit02 --name mq02 --link mq01:mylink01 -p 5672:5672 -p 15672:15672 -e RABBITMQ_ERLANG_COOKIE="javaboy_rabbitmq_cookie" rabbitmq:3-management
    docker run -d --hostname rabbit03 --name mq03 --link mq01:mylink02 --link mq02:mylink03 -p 5673:5672 -p 15673:15672 -e RABBITMQ_ERLANG_COOKIE="javaboy_rabbitmq_cookie" rabbitmq:3-management
    

    运行结果如下:

    三个节点现在就启动好了,注意在 mq02 和 mq03 中,分别使用了 --link 参数来实现容器连接,关于这个参数,如果大家不懂,可以在公众号江南一点雨后台回复 docker,由松哥写的 docker 入门教程,里边有讲这个。这里我就不啰嗦了。另外还需要注意,mq03 容器中要既能够连接 mq01 也能够连接 mq02。

    接下来进入到 mq02 容器中,首先查看一下 hosts 文件,可以看到我们配置的容器连接已经生效了:

    将来在 mq02 容器中,就可以通过 mylink01 或者 rabbit01 访问到 mq01 容器了。

    接下来我们开始集群的配置。

    分别执行如下命令将 mq02 容器加入集群中:

    rabbitmqctl stop_app
    rabbitmqctl join_cluster rabbit@rabbit01
    rabbitmqctl start_app
    

    接下来输入如下命令我们可以查看集群的状态:

    rabbitmqctl cluster_status
    

    可以看到,集群中已经有两个节点了。

    接下来通过相同的方式将 mq03 也加入到集群中:

    rabbitmqctl stop_app
    rabbitmqctl join_cluster rabbit@rabbit01
    rabbitmqctl start_app
    

    接下来,我们可以查看集群信息:

    可以看到,此时集群中已经有三个节点了。

    其实,这个时候,我们也可以通过网页来查看集群信息,在三个 RabbitMQ 实例的 Web 端首页,都可以看到如下内容:

    2.3 代码测试

    接下来我们来简单测试一下这个集群。

    我们创建一个名为 mq_cluster_demo 的父工程,然后在其中创建两个子工程。

    第一个子工程名为 provider,是一个消息生产者,创建时引入 Web 和 RabbitMQ 依赖,如下:

    然后配置 applicaiton.properties,内容如下(注意集群配置):

    spring.rabbitmq.addresses=localhost:5671,localhost:5672,localhost:5673
    spring.rabbitmq.username=guest
    spring.rabbitmq.password=guest
    

    接下来提供一个简单的队列,如下:

    @Configuration
    public class RabbitConfig {
        public static final String MY_QUEUE_NAME = "my_queue_name";
        public static final String MY_EXCHANGE_NAME = "my_exchange_name";
        public static final String MY_ROUTING_KEY = "my_queue_name";
    
        @Bean
        Queue queue() {
            return new Queue(MY_QUEUE_NAME, true, false, false);
        }
    
        @Bean
        DirectExchange directExchange() {
            return new DirectExchange(MY_EXCHANGE_NAME, true, false);
        }
    
        @Bean
        Binding binding() {
            return BindingBuilder.bind(queue())
                    .to(directExchange())
                    .with(MY_ROUTING_KEY);
        }
    }
    

    这个没啥好说的,都是基本内容,接下来我们在单元测试中进行消息发送测试:

    @SpringBootTest
    class ProviderApplicationTests {
    
        @Autowired
        RabbitTemplate rabbitTemplate;
    
        @Test
        void contextLoads() {
            rabbitTemplate.convertAndSend(null, RabbitConfig.MY_QUEUE_NAME, "hello 江南一点雨");
        }
    
    }
    

    这条消息发送成功之后,在 RabbitMQ 的 Web 管理端,我们会看到三个 RabbitMQ 实例上都会显示有一条消息,但是实际上消息本身只存在于一个 RabbitMQ 实例。

    接下来我们再创建一个消息消费者,消息消费者的依赖以及配置和消息生产者都是一模一样,我就不重复了,消息消费者中增加一个消息接收器:

    @Component
    public class MsgReceiver {
    
        @RabbitListener(queues = RabbitConfig.MY_QUEUE_NAME)
        public void handleMsg(String msg) {
            System.out.println("msg = " + msg);
        }
    }
    

    当消息消费者启动成功后,这个方法中只收到一条消息,进一步验证了我们搭建的 RabbitMQ 集群是没问题的。

    2.4 反向测试

    接下来松哥再举两个反例,以证明消息并没有同步到其他 RabbitMQ 实例。

    确保三个 RabbitMQ 实例都是启动状态,关闭掉 Consumer,然后通过 provider 发送一条消息,发送成功之后,关闭 mq01 实例,然后启动 Consumer 实例,此时 Consumer 实例并不会消费消息,反而会报错说 mq01 实例连接不上,这个例子就可以说明消息在 mq01 上,并没有同步到另外两个 MQ 上。相反,如果 provider 发送消息成功之后,我们没有关闭 mq01 实例而是关闭了 mq02 实例,那么你就会发现消息的消费不受影响。

    3. 搭建镜像集群

    所谓的镜像集群模式并不需要额外搭建,只需要我们将队列配置为镜像队列即可。

    这个配置可以通过网页配置,也可以通过命令行配置,我们分别来看。

    3.1 网页配置镜像队列

    先来看看网页上如何配置镜像队列。

    点击 Admin 选项卡,然后点击右边的 Policies,再点击 Add/update a policy,如下图:

    接下来添加一个策略,如下图:

    各参数含义如下:

    • Name: policy 的名称。
    • Pattern: queue 的匹配模式(正则表达式)。
    • Definition:镜像定义,主要有三个参数:ha-mode, ha-params, ha-sync-mode。
      • ha-mode:指明镜像队列的模式,有效值为 all、exactly、nodes。其中 all 表示在集群中所有的节点上进行镜像(默认即此);exactly 表示在指定个数的节点上进行镜像,节点的个数由 ha-params 指定;nodes 表示在指定的节点上进行镜像,节点名称通过 ha-params 指定。
      • ha-params:ha-mode 模式需要用到的参数。
      • ha-sync-mode:进行队列中消息的同步方式,有效值为 automatic 和 manual。
    • priority 为可选参数,表示 policy 的优先级。

    配置完成后,点击下面的 add/update policy 按钮,完成策略的添加,如下:

    添加完成后,我们可以进行一个简单的测试。

    首先确认三个 RabbitMQ 都启动了,然后用上面的 provider 向消息队列发送一条消息。

    发完之后关闭 mq01 实例。

    接下来启动 consumer,此时发现 consumer 可以完成消息的消费(注意和前面的反向测试区分),这就说明镜像队列已经搭建成功了。

    3.2 命令行配置镜像队列

    命令行的配置格式如下:

    rabbitmqctl set_policy [-p vhost] [--priority priority] [--apply-to apply-to] {name} {pattern} {definition}
    

    举一个简单的配置案例:

    rabbitmqctl set_policy -p / --apply-to queues my_queue_mirror "^" '{"ha-mode":"all","ha-sync-mode":"automatic"}'
    

    4. 小结

    好啦,这就是松哥和大家分享的 RabbitMQ 中的集群搭建,感兴趣的小伙伴赶紧去试试吧~

    展开全文
  • Elasticsearch集群

    千次阅读 2022-03-29 15:59:26
    Elasticsearch集群要达到基本高可用,一般要至少启动3个节点,3个节点互相连接,单个节点包括所有角色,其中任意节点停机集群依然可用。为什么要至少3个节点?因为集群选举算法奇数法则。 Elasticserach管理节点职责...
  • ZooKeeper集群搭建

    千次阅读 2022-04-04 17:46:13
    真实的集群是需要部署在不同的服务器上的,但是在我们测试时同时启动很多个虚拟机内存会吃不消,所以我们通常会搭建伪集群,也就是把所有的服务都搭建在一台虚拟机上,用端口进行区分。 我们这里要求搭建一个三个...
  • WebSocket集群解决方案

    千次阅读 2022-04-28 10:32:17
    最近做项目时遇到了需要多用户之间通信的问题,涉及到了WebSocket握手请求,以及集群中WebSocket Session共享的问题。 期间我经过了几天的研究,总结出了几个实现分布式WebSocket集群的办法,从zuul到spring cloud ...
  • Elasticsearch 主从同步之跨集群复制

    千次阅读 2021-12-01 00:18:23
    1、什么是跨集群复制?跨集群复制(Cross-cluster replication,简称:CCR)指的是:索引数据从一个 Elasticsearch 集群复制到另一个 Elasticse...
  • Docker搭建RabbitMQ集群

    千次阅读 2022-01-15 14:42:08
    Docker搭建RabbitMQ集群
  • Redis集群

    千次阅读 2022-02-10 13:56:40
    Redis 集群实现了对Redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在这N个节点中,每个节点存储总数据的1/N。 Redis 集群通过分区(partition)来提供一定程度的可用性(availability): 即使集群中...
  • mysql学习之mysql集群

    万次阅读 2022-04-12 19:00:08
    本片博文是笔者学习《高性能mysql》一书的第一篇学习笔记,对应书籍章节为第10章。主要介绍了mysql集群部署方案主从集群部署。集群方案、主从同步原理、复制类型、docker安装mysql集群示例。
  • 基于Kubernetes集群搭建Elasticsearch集群

    千次阅读 多人点赞 2022-04-09 08:55:24
    基于Kubernetes集群搭建Elasticsearch集群 文章目录基于Kubernetes集群搭建Elasticsearch集群1.部署分析2.准备镜像并推送至Harbor仓库3.创建StorageClass动态PV资源4.编写es集群configmap资源5.编写es集群statfulset...
  • 【Redis】之 Redis 集群

    千次阅读 2022-04-06 17:44:01
    一、集群方案 1、哨兵模式 哨兵的介绍 sentinel,中文名是哨兵。哨兵是 redis 集群机构中非常重要的一个组件,主要有以下功能: 集群监控:负责监控 redis master 和 slave 进程是否正常工作。 消息通知:如果...
  • 1、引言本系列文章介绍如何修复 Elasticsearch 集群的常见错误和问题。这是系列文章的第六篇,主要探讨:Elasticsearch 集群状态变成黄色或者红色,怎么办?第一篇:El...
  • 文章目录系列文章目录前言一、 集群架构一、 为什么需要Redis集群二、 Redis集群的优势三、 集群原理1、数据的分片2、节点的通信3、集群选举4、访问重定向四、集群中的瓶颈五、集群安装中的注意点总结 前言 前面...
  • clickhouse集群搭建详细步骤

    千次阅读 2022-04-27 18:21:32
    Clickhouse集群依赖Zookeeper集群。因此需要先搭建zk集群。 请先参考 【记录】zookeeper集群搭建详细步骤 完成zookeeper集群搭建。 如果zookeeper集群已成功搭建完成,下面开始搭建Clickhouse集群。 需要环境: ...
  • ZooKeeper 集群

    千次阅读 2022-03-22 21:09:07
    为了保证高可用,最好是以集群形态来部署 ZooKeeper,这样只要集群中大部分机器是可用的(能够容忍一定的机器故障),那么 ZooKeeper 本身仍然是可用的。通常 3 台服务器就可以构成一个 ZooKeeper 集群了。ZooKeeper...
  • 运维人员、测试人员、研发人员、实施人员,是否都有过对集群服务搭建、部署及管理的烦恼,常常捣鼓大半天没结果,每次都的拿小本本记下来一些操作流程。 Amazon ElastiCache ElastiCache是一款全托管、低延迟的内存...
  • Eureka 集群搭建

    千次阅读 2021-11-29 20:03:34
    一、Eureka 高可用集群 在实际的生产环境中,eureka 常常是以集群的方式提供服务的,目的就是要保证高可用性,同时它还保证了分区容错性。这也满足了一个健壮的分布式系统所要求的 CAP 理论原则,即 eureka 保证了高...
  • kubekey快速安装高可用k8s集群

    千次阅读 2022-05-02 22:53:16
    KubeKey KubeKey(由 Go 语言开发)是一种全新的安装工具,替代了以前使用的基于 ansible 的安装程序。KubeKey 为您提供灵活的安装...扩缩集群; 升级集群; 安装 Kubernetes 相关的插件(Chart 或 YAML)。 项目地.
  • Nacos集群

    千次阅读 2022-03-18 03:15:19
    1、生产环境Nacos集群的设计要点 2、Nacos集群的部署过程 3、Nacos集群的工作原理 Nacos生产环境架构 Nacos集群的部署过程 注:至少3台服务器部署节点集群 额外部署一台mysql数据库 ...
  • keycloak集群化的思考

    千次阅读 2021-01-13 17:02:51
    单体服务如果想要突破到高并发服务就需要升级为集群服务。同时集群化也为高可用打下了坚实的基础。纵观现在比较流行的服务或者中间件,不管是RabbitMQ还是redis都提供了集群的功能。 作为硬核工业代表的wildfly也不...
  • CentOS7 集群搭建

    千次阅读 2022-04-05 11:45:20
    参考文章 1、CenOS7 Hadoop集群搭建(二):Hadoop集群搭建_机智的小狐狸的博客-CSDN博客 2、使用两台Centos7系统搭建Hadoop-3.1.4完全分布式集群-51CTO.COM 3、CentOS7虚拟机实现Hadoop3.0分布式集群安装_咖喱姬姬...
  • Redis 集群的三种模式

    千次阅读 2021-11-15 10:26:10
    Redis 集群的三种模式 一、主从同步/复制  通过持久化功能,Redis保证了即使在服务器重启的情况下也不会丢失(或少量丢失)数据,因为持久化会把内存中数据保存到硬盘上,重启会从硬盘上加载数据。 但是由于数据是...
  • RabbitMQ集群搭建,高可用普通集群,高可用镜像集群,springboot集群测试
  • nacos集群部署

    千次阅读 2022-03-30 22:21:55
    文章目录前言一、nacos集群配置1.1、解压nacos1.2、重命名文件夹1.3、编辑配置文件1.1.1、进入配置文件1.1.2、编辑application.properties-端口和数据库1.1.3、编辑cluster.conf.example集群配置1.1.4、编辑startup....
  • 文章目录kubernetes集群搭建1. 集群分类2. 集群搭建硬件要求3. 集群搭建方式4. kubeadm部署方式4.1 安装要求4.2 初始化工作4.3 安装Docker/kubeadm/kubelet4.4 部署Kubernetes Master4.5 加入Kubernetes Node4.6 ...
  • 部署k8s集群(k8s集群搭建详细实践版)

    万次阅读 多人点赞 2022-03-29 15:36:45
    k8s集群的搭建过程,本人实操记录,图文并茂,非常详细!

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,399,029
精华内容 559,611
关键字:

集群

友情链接: myYOLOv3-master.zip