一. JGroups协议栈简介
JGroups最强大的功能在于它灵活的协议栈配置,允许开发者调整协议栈中的协议从而适应自己实践的环境和需求。
JGroups启动时会根据配置的协议栈,构成一个协议栈Java对象,发送的消息会自顶向下的依次经过各层协议进行处理,直至发送到网络链路层。同样,收到的消息,JGroups也会将消息自底向上的传递,直至回调ReceiverAdapter交由应用程序。
消息在协议栈的流程如图:
二. JGroups协议栈详解
1. State Transfer(状态传输协议)
STATE_TRANSFER 是一个已有状态的传输协议,向周围节点传输二进制流。在state的提供端,JGroups创建一个outputStream并放到一个byte[]中,然后将这个output stream传递给getState(outputStream)回调,在state状态的请求端,创建一个inputstream并传递到回调方法setState(InputStream)中。为了将程序的state传送给刚加入的集群新节点。
STATE_TRANSFER必须要加载整个节点状态到内存中,然后传输给加入的节点。主要的限制是要传输的State过大,可能会导致内存好进。如果是很大的State 传输用STATE或者STATE_SOCK协议。如果state很小,就可以用STATE_TRANSFER。
2. RSVP(资源预留协议)
不是一个可靠交付的协议。但是在可靠消息交付的协议中(如NAKACK, UNICAST2, UNICAST)中作为参数。
3. FRAG and FRAG2(消息分片协议)
FRAG和FRAG2协议会将大消息分片成小消息,然后发送这些小的消息,在接收端,这些分片的小消息会再重新组装成一个大消息,然后交付给应用程序。FRAG和FRAG2可以同时为单播和多播工作。FRAG和FRAG2的区别是FRAG2比FRAG少了一份复制,因此推荐使用FRAG2作为分片协议。FRAG序列号一个消息需要知道确切的大小(包括消息头),而FRAG2不包含消息头,故更快。
参数:frag_size,消息的最大字节数,如果超过就要分片。
4. Flow Control(流量控制协议)
流量控制关注适应发送端的发送数度和很慢接收端的接受速度。如果发送端在持续一段时间内发送的速度高于接受的速度,接受者要么将消息排队,要么丢弃消息,要么触发重传。另外,有些节点会有虚假的流浪,将会引起更大的消息重传。流量控制限制发送者,这样接受者将不会出现消息超载的情况。
注意:可以通过将消息的flag属性设为Message.NO_FC来直接越过流量控制。
FC(流量控制协议)用一个基于计数的系统,每一个发送者都有一个最大的计数并且每当发送一个消息的时候,递减这个数。当这个数小于0时,发送者阻塞住,并且只有在收到一个接受者发送的补充消息之后再重新开始发送消息。
接受者维护一个所有发送者的计数表,并且在收到一个消息后,递减表中相应发送者的计数。当发送者的计数低于一个阀值时,接受者会发送一个补充消息给发送者,这个阀值用min_bytes或者min_threhold来定义。
max_credits:最大的发送字节数,当发送达到这个字节数时,则必须收到一个ack确认,默认值为500000字节。
min_credits = max_credits * min_threshold
将组播流量控制(MFC)和单播流量控制(UFC)分开有助于实现不同的需求。
5. STABLE(消息稳定协议)
为了服务潜在的重传请求,节点必须存储它所接受到的消息知道集群中所有的节点都已经收到了这些消息后。一个消息的消息稳定说明该消息被集群中所有的节点都收到了。
稳定协议会每隔一段时间(或者收到一定数量的字节后)发起一个共识协议。来组播该节点中稳定消息中最大的消息好。这个动作被称为消化。当每个节点收到其他人的稳定消息时,将会触发计算当前为止最小的稳定消息号,这个是稳定矢量,并说明了当前只有这么多的消息被所有的成员节点收到。
这个稳定矢量将会广播给组里的所有节点,然后节点就可以将他的重传表中比稳定矢量小的消息全部删除,这些消息就能够被垃圾回收了。
STABLE 垃圾回收消息是被所有的节点都收到了的消息,每个节点必须存储所有的消息是因为它可能被要求重传。只要当我们确定了某个消息被所有节点收到后才能把该消息从重传Buffer中删除。稳定的周期性告知他收到消息的最大值和最小值,这样就可以通过计算集群中收到消息的最小值来丢弃那些小于最小值消息。
注意:STABLE也能够通过配置在运行期所收到的消息字节数,建议在高速发送消息的情况下使用,因为基于时间的发送稳定消息也许要快于基于稳定垃圾收集的消息。
6. UNICAST,UNICAST2,UNICAST3 (点对点的可靠消息交付)
UNICAST为发送者与接受者之间点对点消息传输提供可靠的消息交付和先进先出(FIFO)保证。
可靠交付意味着发送端发送的消息不会丢失,所有的消息都会在发送端被标记一个序号。如果接收端检查序号发现没有收到某一个序号,则会要求发送端重新发送该序号的消息。
FIFO意味着接收端接收到消息的顺序是与发送端发送消息的顺序是一致的。
7. NAKACK,NAKACK2(组播的可靠消息交付)
NAKACK 与UNICAST的功能类似,区别在于NAKACK是保证了组播的消息可靠交付和先进先出(FIFO)。
8. GMS(Group Membership:组成员消息协议)
Group membership关注于集群中新成员节点的加入,处理现有节点离开集群以及发现疑似故障节点的处理。判断新加入一个新的节
点的算法如下:
- loop
- find initial members (discovery)
- if no responses:
- become singleton group and break out of the loop
- else:
- determine the coordinator (oldest member) from the responses
- send JOIN request to coordinator
- wait for JOIN response
- if JOIN response received:
- install view and break out of the loop
- else
- sleep for 5 seconds and continue the loop
9. BARRIER
BARRIER被一些状态传输协议所使用,如果配置了状态传输协议,一般要配置该协议。
10. VERIFY_SUSPECT(检查疑似故障节点协议)
检查一个怀疑出现问题的节点是否真的死亡,在排查它之前进行最后一次检查,如果该节点有回应,则去掉怀疑信息。
工作流程如下:该协议捕获一个传输到上层协议的SUSPECT事件,然后检查被怀疑的节点是否是真的死亡。如果是真的,它将SUSPECT事件继续向协议栈的上层协议传送。否则,就丢弃SUSPECT事件。在协议栈中的位置,VERIFY_SUSPECT必须放在失败检查协议上面,放在GMS协议下面,因为GMS协议是SUSPECT事件的接受者。
11.FD_ALL(节点故障检查,组播心跳包方式)
通过一个简单的心跳协议来进行故障检测,每个成员周期性的组播一个心跳。每个成员也维持一个所有成员的信息表,如果收到成员P的数据包或者心跳包,则该节点重置P的最近活动时间戳。如果一个节点的时间戳超过了timeout时间,则怀疑该节点是否出现故障。然后Coordinator用VERIFY_SUSPECT来检查该检点是否出现故障。
12. FD_SOCK(节点故障检查,tcp连接环)
FD_SOCK是一个基于Tcp sockets两两相近节点建立tcp链接的一个环,类是与FD,但是不用心跳包。
每个节点连接到他的邻近节点,这样如果A检测到B的socket异常关闭,则B可能出现了问题,可组播一个怀疑B的消息。
13.MERGE2,MERGE3(子集群合并协议)
如果由于网络原因(临时中断)集群中出现多个Coordinator怎么办?PING之上的MERGE2协议就可以解决这个问题。MERGE2做到在网络恢复集群探测到有多个Coordinator组成多个子集群的时候发送MERGE消息给所有节点,将某些Coordinator降级使得集群重新整合成一个。
14. PING(初始节点发现协议)
发现初始成员,通过组播一个ping命令到指定的组播地址用于决定coordinator(最早的集群节点),每个节点会一个包(C,A),C代表coordinator的地址,A代表该节点自己的地址。N毫秒或者M个回应之后,加入者从响应包中决定coordinator,然后发送一个加入请求到给该节点(通过GMS处理)。如果没有收到任何响应,则表示自己是该集群中第一个节点,即自己就是coordinator。
和TCPPING不同,PING能够动态发现,以为着该节点不需要知道其他以前节点的位置(地址),PING用IP多播来发送一个发现请求给其他节点,因为它需要UDP作为传输手段。
15. TCPPING(初始节点发现协议)
TCPPING也是用来发现初始成员节点的,但是用的是TCP传输协议,而且它还需要知道其他成员的静态地址列表。
16. UDP(数据传输层协议)
17. TCP(数据传输层协议)