zendiscovery 的Ping机制
ping是集群发现的基本手段,通过在网络上广播或者指定ping某些节点获取集群信息,从而可以找到集群的master加入集群。zenDiscovery实现了两种凭机制:广播与单播。本篇将详细分析一些这MulticastZenPing机制的实现为后面的集群发现和master选举做好铺垫。
首先看一下广播(MulticastZenPing),广播的原理很简单,节点启动后向网络发送广播信息,任何收到的节点只要集群名字相同都应该对此广播信息作出回应。这样该节点就获取了集群的相关信息。它定义了一个action:"internal:discovery/zen/multicast"和广播的信息头:INTERNAL_HEADER 。之前说过NettyTransport是cluster通信的基础,但是广播却没有使它。它使用了java的MulticastSocket。这里简单的介绍一下MulticastSocket的使用。它是一个UDP 机制的socket,用来进行多个数据包的广播。它可以帮到一个ip形成一个group,任何MulticastSocket都可以join进来,组内的socket发送的信息会被订阅了改组的所有机器接收到。elasticsearch对其进行了封装形成了MulticastChannel,有兴趣可以参考相关源码。
首先看一下MulticastZenPing的几个辅助内部类:

它总共定义了4个内部类,这些内部类和它一起完成广播功能。FinalizingPingCollection是一pingresponse的容器,所有的响应都用它来存储。MulticastPingResponseRequestHandler它是response处理类,类似于之前所说的nettytransportHandler,它虽然使用的不是netty,但是它也定义了一个messageReceived的方法,当收到请求时直接返回一个response。MulticastPingResponse就不用细说了,它就是一个响应类。最后要着重说一下Receiver类,因为广播并不是使用NettyTransport,因此对于消息处理逻辑都在Receiver中。在初始化MulticastZenPing时会将receiver注册进去。
protected void doStart() throws ElasticsearchException {
try {
....
multicastChannel = MulticastChannel.getChannel(nodeName(), shared,
new MulticastChannel.Config(port, group, bufferSize, ttl, networkService.resolvePublishHostAddress(address)),
new Receiver());//将receiver注册到channel中
} catch (Throwable t) {
....
}
}
Receiver类基础了Listener,实现了3个方法,消息经过onMessage方法区分,如果是内部ping则使用handleNodePingRequest方法处理,否则使用handleExternalPingRequest处理,区分方法很简单,就是读取信息都看它是否符合所定义的INTERNAL_HEADER 信息头。下面是nodeping处理的代码:
private void handleNodePingRequest(int id, DiscoveryNode requestingNodeX, ClusterName requestClusterName) {
....
final DiscoveryNodes discoveryNodes = contextProvider.nodes();
final DiscoveryNode requestingNode = requestingNodeX;
if (requestingNode.id().equals(discoveryNodes.localNodeId())) {
// 自身发出的ping,忽略
return;
}
//只接受本集群ping
if (!requestClusterName.equals(clusterName)) {
...return;
}
// 两个client间不需要ping
if (!discoveryNodes.localNode().shouldConnectTo(requestingNode)) {return;
}
//新建一个response
final MulticastPingResponse multicastPingResponse = new MulticastPingResponse();
multicastPingResponse.id = id;
multicastPingResponse.pingResponse = new PingResponse(discoveryNodes.localNode(), discoveryNodes.masterNode(), clusterName, contextProvider.nodeHasJoinedClusterOnce());
//无法连接的情况
if (!transportService.nodeConnected(requestingNode)) {
// do the connect and send on a thread pool
threadPool.generic().execute(new Runnable() {
@Override
public void run() {
// connect to the node if possible
try {
transportService.connectToNode(requestingNode);
transportService.sendRequest(requestingNode, ACTION_NAME, multicastPingResponse, new EmptyTransportResponseHandler(ThreadPool.Names.SAME) {
@Override
public void handleException(TransportException exp) {
logger.warn("failed to receive confirmation on sent ping response to [{}]", exp, requestingNode);
}
});
} catch (Exception e) {
if (lifecycle.started()) {
logger.warn("failed to connect to requesting node {}", e, requestingNode);
}
}
}
});
} else {
transportService.sendRequest(requestingNode, ACTION_NAME, multicastPingResponse, new EmptyTransportResponseHandler(ThreadPool.Names.SAME) {
@Override
public void handleException(TransportException exp) {
if (lifecycle.started()) {
logger.warn("failed to receive confirmation on sent ping response to [{}]", exp, requestingNode);
}
}
});
}
}
}
另外的一个方法是处理外部ping信息,处理过程是返回cluster的信息(这种外部ping的具体作用没有研究不是太清楚)。以上是响应MulticastZenPing的过程,收到其它节点的响应信息后它会把本节点及集群的master节点相关信息返回给广播节点。这样广播节点就获知了集群的相关信息。在MulticastZenPing类中还有一个类 MulticastPingResponseRequestHandler,它的作用是广播节点对其它节点对广播信息响应的回应,广播节点的第二次发送信息的过程。它跟其它TransportRequestHandler一样它有messageReceived方法,在启动时注册到transportserver中,只处理一类action:"internal:discovery/zen/multicast"。下面再看一下ping请求的发送策略,代码如下:
public void ping(final PingListener listener, final TimeValue timeout) {
....
//产生一个id
final int id = pingIdGenerator.incrementAndGet();
try {
receivedResponses.put(id, new PingCollection());
sendPingRequest(id);//第一次发送ping请求
// 等待时间的1/2后再次发送一个请求
threadPool.schedule(TimeValue.timeValueMillis(timeout.millis() / 2), ThreadPool.Names.GENERIC, new AbstractRunnable() {
@Override
public void onFailure(Throwable t) {
logger.warn("[{}] failed to send second ping request", t, id);
finalizePingCycle(id, listener);
}
@Override
public void doRun() {
sendPingRequest(id);
//再过1/2时间再次发送一个请求
threadPool.schedule(TimeValue.timeValueMillis(timeout.millis() / 2), ThreadPool.Names.GENERIC, new AbstractRunnable() {
@Override
public void onFailure(Throwable t) {
logger.warn("[{}] failed to send third ping request", t, id);
finalizePingCycle(id, listener);
}
@Override
public void doRun() {
// make one last ping, but finalize as soon as all nodes have responded or a timeout has past
PingCollection collection = receivedResponses.get(id);
FinalizingPingCollection finalizingPingCollection = new FinalizingPingCollection(id, collection, collection.size(), listener);
receivedResponses.put(id, finalizingPingCollection);
logger.trace("[{}] sending last pings", id);
sendPingRequest(id);
//最后一次发送请求,超时的1/4后
threadPool.schedule(TimeValue.timeValueMillis(timeout.millis() / 4), ThreadPool.Names.GENERIC, new AbstractRunnable() {
@Override
public void onFailure(Throwable t) {
logger.warn("[{}] failed to finalize ping", t, id);
}
@Override
protected void doRun() throws Exception {
finalizePingCycle(id, listener);
}
});
}
});
}
});
} catch (Exception e) {
logger.warn("failed to ping", e);
finalizePingCycle(id, listener);
}
}
发送过程主要是调用sendPingRequest(id)方法,在该方法中会将id,信息头,版本,本地节点信息一起写入到BytesStreamOutput中然后将其进行广播,这个广播信息会被其它机器上的Receiver接收并处理,并且响应该ping请求。另外一个需要关注的是以上加说明的部分,它通过链时的定期发送请求,在等待时间内可能会发出4次请求,这种发送方式会造成大量的ping请求重复,幸好ping的资源消耗小,但是好处是可以尽可能保证在timeout这个时间段内集群的新增节点都能收到这个ping信息。在单播中也采用了该策略。
总结一下广播的过程:广播使用的是jdk的MulticastSocket,在timeout时间内4次发生ping请求,ping请求包括一个id,信息头,本地节点的一些信息;这些信息在其它节点中被接收到交给Receiver处理,Receiver会将集群的master和本机的相关信息通过transport返回给广播节点。广播节点收到这些信息后会理解使用transport返回一个空的response。至此一个广播过程完成。
在节点分布在多个网段时,广播就失效了,因为广播信息不可达。这个时间就需要使用单播去ping指定的节点获取cluster的相关信息。这就是单播的用处。单播使用的是NettyTransport,它会使用跟广播一样的链式请求向指定的节点发送请求。信息的处理方式是之前所介绍的NettyTransport标准的信息处理过程。这里就不再做详细说明,有兴趣可以参考相关源码。
zendiscovery 的Ping机制的更多相关文章
- Ribbon Ping机制
在负载均衡器中,提供了 Ping 机制,每隔一段时间,会去 Ping 服务器,判断服务器是否存活,该工作由 com.netflix.loadbalancer.IPing 接口的实现类负责,如果单独使用 ...
- 阶段总结-Java基础-超进阶
Gitee项目地址:https://gitee.com/zc10010/java_interview_guide/tree/master/知识点话术 项目叫话术,但是我觉得作为知识点学习是挺不错的. ...
- MHA参数 转
http://blog.csdn.net/wulantian/article/details/12503473 http://blog.csdn.net/wulantian/article/categ ...
- 记录最近的几个bug
记录最近出的几个bug connection reset by peer 最近服务器经常性的出现connection reset by peer的错误,开始我们只是以为小概率的网络断开导致的,可是随着 ...
- 使用.NET Remoting开发分布式应用——基于租约的生存期
一.概述 知名类型的SingleCall对象可以在客户程序的方法调用之后被垃圾收集器清理掉,因为它没有保持状态,属于无状态的.而客户激活的类型的对象和知名类型的SingleTon对象都属于生存期长的对 ...
- 关于 Mybatis的原生连接池 和 DBCP 连接池
一 遇到的问题: 项目用的play框架,数据库DB2, 持久化框架是Mybatis, 连接池用的是Mybatis原生的,遇到的问题是:有时候抛出如下异常: play.api.UnexpectedEx ...
- MySQL高可用系列之MHA(二)
一.參数说明 MHA提供了一系列配置參数.深入理解每一个參数的详细含义,对优化配置.合理使用MHA非常重要.非常多高可用性也都是通过合理配置一些參数而实现的. MHA包含例如以下配置參数,分别说明例如 ...
- DG archive gap
什么是archive gap Archive Gap就是standby端日志应用的过程中丢失的一段范围的redo.典型的发生在standby端不能接收primary的redo信息或者接收后不能应用这些 ...
- USB 3.0规范中译本 附录
本文为CoryXie原创译文,转载及有任何问题请联系cory.xie#gmail.com. 附录A 符号编码 表A-1显示了对于数据字符字节到符号的编码. 表 A-2显示了对于特殊符号的编码. R ...
随机推荐
- Java基础学习总结(19)——Java环境变量配置
前言 学习java的第一步就要搭建java的学习环境,首先是要安装JDK,JDK安装好之后,还需要在电脑上配置"JAVA_HOME"."path"." ...
- ECNUOJ 2144 抗震机械制造
抗震机械制造 Time Limit:1000MS Memory Limit:65536KBTotal Submit:312 Accepted:78 Description 为了应付可能到来的地震,E ...
- 做一个萌萌哒的button之box-shadow
接上篇:http://blog.csdn.net/u010037043/article/details/47035077 一.box-shadow box-shadow是给元素块加入周边阴影效果. b ...
- BitSet的使用
有些程序须要处理二进制有序集,标准库提供了bitset 类型,其实,bitset 是一个二进制容器.容器中每个元素都是一位二进制码,或为 0,或为 1. bitset除了能够訪问指定下标的bit位以外 ...
- 智课雅思短语---一、be no exception
智课雅思短语---一.be no exception 一.总结 一句话总结:…也不例外? …be no exception 1.经济的快速发展? the rapid development of ec ...
- centos中mysql 安装以及配置,建库
1.检测系统内部有没有安装其他的mysql数据库 rpm -qa | grep mysql 然后如果有的话删除这些mysql yum remove 查出来的所有名字 2.彻底删除系统中mysql的目录 ...
- 在hive执行创建表的命令,遇到异常com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: Specified key was too long; max key length is 767 bytes
今天在练习hive的操作时,在创建数据表时,遇到了异常 FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.ex ...
- java web应用调用python深度学习训练的模型
之前参见了中国软件杯大赛,在大赛中用到了深度学习的相关算法,也训练了一些简单的模型.项目线上平台是用java编写的web应用程序,而深度学习使用的是python语言,这就涉及到了在java代码中调用p ...
- 【Codeforces Round #459 (Div. 2) A】Eleven
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 这个数列增长很快的. 直接暴力模拟看看是不是它的一项就好了 [代码] #include <bits/stdc++.h> ...
- Mysql学习总结(10)——MySql触发器使用讲解
触发器(TRIGGER)是由事件来触发某个操作.这些事件包括INSERT语句.UPDATE语句和DELETE语句.当数据库系统执行这些事件时,就会激活触发器执行相应的操作.MySQL从5.0.2版本开 ...