Redis Gossip广播通信 2.redis gossip的应用

Redis Cluster节点通信

Redis Cluster采用P2P的Gossip协议,每个节点与其他节点间不断通信交换信息,一段时间后节点信息一致,每个节点都知道集群的完整信息。

Redis Cluster通信过程:

(1)集群中的每个节点都会单独开辟一个TCP通道,用于节点之间彼此通信,通信端口号在基础端口上加10000;

(2)每个节点在固定周期内通过特定规则选择几个节点发送ping消息;

(3)接收到ping消息的节点用pong消息作为响应。

集群中每个节点通过一定规则挑选要通信的节点,每个节点可能知道全部节点,也可能仅知道部分节点,只要这些节点彼此可以正常通信,最终它们会达到一致的状态

当节点出故障、新节点加入、主从角色变化、槽信息变更等事件发生时,通过不断的ping/pong消息通信,经过一段时间后所有的节点都会知道整个集群全部节点的最新状态,从而达到集群状态同步的目的。

Gossip消息Gossip协议的主要职责就是信息交换,信息交换的载体就是节点彼此发送的Gossip消息,常用的Gossip消息可分为:

• meet消息:用于通知新节点加入。消息发送者通知接收者加入到当前集群,meet消息通信正常完成后,接收节点会加入到集群中并进行周期性的ping、pong消息交换;

• ping消息:集群内交换最频繁的消息,集群内每个节点每秒向多个其他节点发送ping消息,用于检测节点是否在线和交换彼此状态信息。ping消息发送封装了自身节点和部分其他节点的状态数据。

• pong消息:当接收到ping、meet消息时,作为响应消息回复给发送方确认消息正常通信。pong消息内部封装了自身状态数据。节点也可以向集群内广播自身的pong消息来通知整个集群对自身状态进行更新。

• fail消息:当节点判定集群内另一个节点下线时,会向集群内广播一个fail消息,其他节点接收到fail消息之后把对应节点更新为下线状态。

消息格式:

所有的消息格式划分为:消息头和消息体。

消息头:

包含自身的状态数据,发送节点关键信息,如节点id、槽映等节点标识(主从角色,是否下线)等。

Redis Gossip广播通信 2.redis gossip的应用_第1张图片

消息体:

定义发送消息的数据。

消息体在Redis内部采用clusterMsgData结构声明:

Redis Gossip广播通信 2.redis gossip的应用_第2张图片

通信消息处理流程

当接收到ping、meet消息时,接收节点会解析消息内容并根据自身的识别情况做出相应处理:

接收节点收到ping/meet消息时,执行解析消息头和消息体流程:

• 解析消息头过程:

消息头包含了发送节点的信息

如果发送节点是新节点且消息是meet类型,则加入到本地节点列表;

如果是已知节点,则尝试更新发送节点的状态,如槽映射关系、主从角色等状态。

• 解析消息体过程:

如果消息体的clusterMsgDataGossip数组包含的节点是新节点,则尝试发起与新节点的meet握手流程;

如果是已知节点,则根据clusterMsgDataGossip中的flags字段判断该节点是否下线,用于故障转移。

消息处理完后回复pong消息,内容同样包含消息头和消息体,发送节点接收到回复的pong消息后,采用类似的流程解析处理消息并更新与接收节点最后信息时间,完成一次消息通信。

Redis Gossip广播通信 2.redis gossip的应用_第3张图片

通信规则

Redis集群内节点通信采用固定频率(定时任务每秒执行10次)。

由于内部需要频繁地进行节点信息交换,而ping/pong消息会携带当前节点和部分其他节点的状态数据,势必会加重带宽和计算的负担。

• 通信节点选择过多可以让信息及时交换,但是成本过高;

• 通信节点选择过少会降低集群内所有节点彼此信息交换频率,从而影响故障判定、新节点发现等需求的速度。

Redis Gossip广播通信 2.redis gossip的应用_第4张图片

节点选择

消息交换的成本主要体现在单位时间选择发送消息的节点数量和每个消息携带的数据量。

(1)选择发送消息的节点数量集群内每个节点维护定时任务默认每秒执行10次,每秒会随机选择5个节点找出最久没有通信的节点发送ping消息,用于Gossip信息交换的随机性。

每100毫秒都会扫描本地节点列表

如果发现节点最后一次接受pong消息的时间大于cluster_node_timeout/2,则立刻发送ping消息,防止该节点信息太长时间未更新。

根据以上规则得出每个节点每秒需要发送ping消息的数量=1+10*num(node.pong_received> cluster_node_timeout/2),因此cluster_node_timeout参数对消息发送的节点数量影响非常大。

当我们的带宽资源紧张时,可以适当调大此参数。

但是如果cluster_node_timeout过大会影响消息交换的频率从而影响故障转移、槽信息更新、新节点发现的速度。

因此需要根据业务容忍度和资源消耗进行平衡。同时整个集群消息总交换量也跟节点数成正比。

(2)消息数据量每个ping消息的数据量体现在消息头和消息体中,其中消息头主要占用空间的字段是myslots[CLUSTER_SLOTS/8],占用2KB,这块空间占用相对固定。

消息体会携带一定数量的其他节点信息用于信息交换。而消息体携带数据量跟集群的节点数量相关,集群越大每次消息通信的成本也就更高。

ping和pong

 

Redis Gossip广播通信 2.redis gossip的应用_第5张图片

通信开销

Redis Cluster内节点通信自身开销:

(1)节点自身信息,主要是自己负责的slots信息:slots[CLUSTER_SLOTS/8],占用2KB;

(2)携带总节点1/10的其他节点的状态信息(1个节点的状态数据约为104byte)

注:并不是所有的都是携带十分之一的节点信息的。如果total_nodes/10小于3,那就至少携带3个节点信息;

如果total_nodes/10大于total_nodes-2,最多携带total_nodes-2个节点信息;

Else就total_nodes/10个节点信息。

Redis Gossip广播通信 2.redis gossip的应用_第6张图片

节点状态信息:clusterMsgDataGossip,ping、meet、pong采用clusterMsgDataGossip数组作为消息体。

 

所以每个Gossip消息大小为2KB+total_nodes/10*104bRedis Cluster带宽消耗主要为:Redis Gossip广播通信 2.redis gossip的应用_第7张图片业务操作(读写)消耗+Gossip消息消耗。

假设:节点数为64*2=128,floor(122)=12

每个Gossip消息的大小约为:2KB+12*104b ≈ 3KB。

根据之前的每个节点每秒需要发送ping消息的数量=1+10*num(node.pong_received> cluster_node_timeout/2)

假设:cluster_node_timeout为15秒时,num=20

即开销=3KB*(1+10*20)*2*20=25MB/s;

cluster_node_timeout为30秒时,num=5

即开销=3KB*(1+10*5)*2*20=6MB/s。

可以看出影响Gossip开销的主要两点:

Cluster Redis的节点数和cluster_node_timeout设置的阈值:

那如果节点越多,Gossip消息就越大,最近接收pong消息时间间隔大于cluster_node_timeout/2秒的节点也会越多,那么带宽的开销越大。

所以得出如下结论:

(1)尽量避免大集群,针对大集群就拆分出去;

(2)如果某些场景必须使用大集群,那就可以通过增大cluster_node_timeout来降低带宽的消耗,但是会影响failover的时效,这个可以根据业务场景和集群具体状态评估;

(3)docker的分配问题,将大集群打散到小集群的物理机上,可以平衡和更高效的利用资源。

 

参考资料:

http://tech-happen.site/d0ff83d1.html

你可能感兴趣的:(Redis)