转载:http://www.codelast.com/

在一套分布式的online services系统中,各service通常不会放在一台服务器上,而是通过Zookeeper这样的东西,将自己的service信息注册到上面,service的使用者通过Zookeeper来发现各service的信息,从而可以将request发送到不同的service上去处理。

如上图所示,两个Service Provider 1和2分别在192.168.1.5和192.168.1.6这两台服务器的2688端口上提供服务,服务的地址和端口注册到了Zookeeper中。Service User通过查询Zookeeper,可得知这些服务的信息。通常,Service User与Service Provider之间的通信,是通过connection pool实现的,因为Service User不可能假定在第一次查询到所有Service Provider的信息之后,它们就是一直存活的,假如某个Service Provider因为程序问题死掉了,向它发送request只会造成大量的失败结果,因此通常会实现一个connection pool来保证实时更新节点的信息,当有一个Service Provider从Zookeeper上消失之后,从connection pool中取出的connection总是可用的(即:总能通过它把request发送到一个有效的Service Provider那里)。
文章来源:http://www.codelast.com/
这里,我们保证了当一个Service Provider从Zookeeper上退出后,我们一定不会再用它,但是,我们如何保证一个Service Provider还存活的情况下,都能处于可服务的状态呢?例如,Service Provider 1的程序工作一直很稳定,但是某天由于ISP的原因,它和Zookeeper之间的网络中断了5分钟,于是Zookeeper会无法向它发送心跳包,最终Zookeeper会认为session expired了,从而会把它注册的临时节点给移除掉(必须是注册临时节点啊,要不然当Service Provider挂了之后节点还在,岂不出乱子了)。移除掉之后,问题就来了:5分钟后中断的网络恢复正常了,Service Provider 1的程序也一直没有死掉,它又可以serving了,但是由于它在Zookeeper中注册的节点没了,所以Service User通过connection pool是永远也无法向Service Provider 1发送request的,于是所有的request还是都发到了Service Provider 2那里,造成它的负载一直很大,系统的处理能力减弱。
因此,为了避免这种问题,我们需要在Service Provider中提供Zookeeper掉线自动重新注册的功能。
【1】用Java怎么注册Zookeeper
Curator库是一个绝好的选择。它是Netflix开发的一套开源软件。
什么?没听说过Netflix?全美三分之一的带宽都是被这家公司占用的你不知道?
好吧,那么火爆得一塌糊涂的美剧《纸牌屋》你总听说过吧?就是这家公司花钱制作的。如果这也没听说过的话,那么你只能去Google啦。
题外话:科技和生活娱乐息息相关啊!
如果要问为什么不直接用Zookeeper官方的API,而是使用包装过的Curator,那只能用一句话来解释:Curator很好很强大很方便。
文章来源:http://www.codelast.com/
【2】代码
『A』使用Curator注册Zookeeper的代码

1
2
3
4
5
6
7
8
CuratorFramework curator = CuratorFrameworkFactory.newClient("zookeeper.codelast.com:2181",
5000, 3000, new RetryNTimes(5, 1000));
curator.start();
String regContent = "192.168.1.5:2688";
String zkRegPathPrefix = "/codelast/service-provider-";
//TODO:
curator.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.forPath(zkRegPathPrefix, regContent.getBytes("UTF-8"));

解释一下:
zookeeper.codelast.com:2181 是你的Zookeeper server的host:port。
192.168.1.5:2688 是注册的Zookeeper节点的内容,表示Service Provider 1在1912.168.1.5的2688端口提供服务。当你用Zookeeper客户端zkCli.sh的get命令时,获取的返回值的第一行就是这个内容。
CreateMode.EPHEMERAL_SEQUENTIAL 表示注册的节点是临时的,并且其命名是顺序增加的。
/codelast/service-provider- 是把service注册到Zookeeper中的哪个路径下。上面代码的效果就是,注册的节点的完整路径会类似于 /codelast/service-provider-0000000001
文章来源:http://www.codelast.com/
『B』上面的代码在session expired的情况下,是无法自动重新在Zookeeper中注册的。要实现这种功能,需要添加一个实现了ConnectionStateListener接口的类,并应用到CuratorFramework对象上。我们需要在上面代码中的“TODO”处添加如下代码:

1
2
MyConnectionStateListener stateListener = new MyConnectionStateListener(zkRegPathPrefix, regContent);
curator.getConnectionStateListenable().addListener(stateListener);

然后再实现MyConnectionStateListener类:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* A class to monitor connection state & re-register to Zookeeper when connection lost.
*
* @author Darran Zhang @ codelast.com
*/
public class MyConnectionStateListener implements ConnectionStateListener {
private String zkRegPathPrefix;
private String regContent;
public MyConnectionStateListener(String zkRegPathPrefix, String regContent) {
this.zkRegPathPrefix = zkRegPathPrefix;
this.regContent = regContent;
}
@Override
public void stateChanged(CuratorFramework curatorFramework, ConnectionState connectionState) {
if (connectionState == ConnectionState.LOST) {
while (true) {
try {
if (curatorFramework.getZookeeperClient().blockUntilConnectedOrTimedOut()) {
curatorFramework.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL)
.forPath(zkRegPathPrefix, regContent.getBytes("UTF-8"));
break;
}
} catch (InterruptedException e) {
//TODO: log something
break;
} catch (Exception e) {
//TODO: log something
}
}
}
}
}

文章来源:http://www.codelast.com/
此类负责监听connection的状态,并在检测到LOST状态时(此时client已经从Zookeeper中掉线)重新注册。
注意,在检测到LOST状态后,上面的代码用了一个while (true) 死循环来不断尝试重新连接Zookeeper server,连不上不罢休。

【3】测试
如何测试?当然要创造出一种session expired的情况,让Zookeeper server把它认为已经掉线的节点的注册信息给移除掉。怎么让session expire呢?官方网站上有这么一段话

Is there an easy way to expire a session for testing?
 
Yes, a ZooKeeper handle can take a session id and password. This constructor is used to recover a session after total application failure. For example, an application can connect to ZooKeeper, save the session id and password to a file, terminate, restart, read the session id and password, and reconnect to ZooKeeper without loosing the session and the corresponding ephemeral nodes. It is up to the programmer to ensure that the session id and password isn't passed around to multiple instances of an application, otherwise problems can result.
 
In the case of testing we want to cause a problem, so to explicitly expire a session an application connects to ZooKeeper, saves the session id and password, creates another ZooKeeper handle with that id and password, and then closes the new handle. Since both handles reference the same session, the close on second handle will invalidate the session causing a SESSION_EXPIRED on the first handle.

文章来源:http://www.codelast.com/
但是需要这么麻烦吗?直接通过OS的防火墙就可以做到。例如,在RedHat上,通过如下命令:

1
iptables -A INPUT -d codelast.com -p tcp --sport 2181 -j DROP

你将可以阻止来自codelast.com这个域名的所有输入(INPUT)的流量。这会导致Zookeeper server和client之间的心跳失效,互相认为对方已经掉线了。对Zookeeper server来说,当它认为client掉线时,就会把client节点从Zookeeper中移除。这个时候,如果client程序没有重新注册的能力,那么当网络恢复后,client程序虽然是能正常运行的,但是也失去了提供service的能力——因为service的使用者已经无法通过Zookeeper发现它了。
我们先启动Service Provider,然后利用执行上面的命令(如果不是RedHat,请自行Google)阻断网络连接一段时间,你最终将会观察到Zookeeper中注册的节点已经没了,也就是说我们让session expired了。
然后,执行如下命令:

1
iptables -F

文章来源:http://www.codelast.com/
该命令可以恢复防火墙的设置。此时,网络恢复连接,你会看到Curator打印出来很多信息,提示已经重新连接上了Zookeeper server。但是注意,如果你没有像前面的代码一样提供自动重新注册的能力,那么,先前在Zookeeper中注册的节点并不会出现!也就是说,连上了也没用,它已经不能被发现了。
在添加了自动重新注册的功能后,Zookeeper中注册的节点就会自动重新被创建出来了。这就达到了我们要的效果。

Zookeeper注册节点的掉线自动重新注册及测试方法的更多相关文章

  1. 关于.net服务启动注册到zookeeper,但是注册节点20分钟自动消失解决办法

        ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,作用简单描述就是相当于一个中介,服务提供者将服务注册到zk,服务调用者直接从zk获取,zk的作用就是协调     最近碰到公 ...

  2. WPF 设置程序开机自动运行(+注册表项)

    #region 设置程序开机自动运行(+注册表项) RegistryKey rgkRun = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Micr ...

  3. 修改注册表实现Windows自动登陆

    昨天再修一条case时无意间发现这个case竟然要重启机器,并且要用指定的账户自动登陆Windows.然后就发现了,简单的修改下注册表就可以完成自动登陆了. 首先,在“run”里输入“regedit” ...

  4. vs install 安装时自动添加注册表

    思路:使用自定义 解决方案添加类库项目 添加安装程序类 随后右键查看代码 在构造函数添加事件 同时完成这个事件,在此事件中根据需要添加我们需要的内容,此处为添加注册表,并根据安装目录添加url pro ...

  5. dubbo源码解析-zookeeper创建节点

    前言 在之前dubbo源码解析-本地暴露中的前言部分提到了两道高频的面试题,其中一道dubbo中zookeeper做注册中心,如果注册中心集群都挂掉,那发布者和订阅者还能通信吗?在上周的dubbo源码 ...

  6. Ignite集群管理——基于Zookeeper的节点发现

    Ignite支持基于组播,静态IP,Zookeeper,JDBC等方式发现节点,本文主要介绍基于Zookeeper的节点发现. 环境准备,两台笔记本电脑A,B.A笔记本上使用VMware虚拟机安装了U ...

  7. 3.16 使用Zookeeper对HDFS HA配置自动故障转移及测试

    一.说明 从上一节可看出,虽然搭建好了HA架构,但是只能手动进行active与standby的切换: 接下来看一下用zookeeper进行自动故障转移: # 在启动HA之后,两个NameNode都是s ...

  8. 从新注册 .DLL CMD 运行regsvr32 *.dll注册该DLL 或 regsvr32 /s *.DLL 求证

    从新注册 .DLL  CMD 运行regsvr32  *.dll注册该DLL  或 regsvr32 /s  *.DLL 求证

  9. zookeeper watch 节点

    zjtest7-redis:/root/zk# cat a1.pl use ZooKeeper; use AnyEvent; use AE; use Data::Dumper; use IO::Soc ...

随机推荐

  1. Mac : 强大的截图

    来源:http://irising.me/2011/11/12135/ Mac的截图功能扩展功能很强大的,不要用QQ那个COM+Ctrl+A弱爆了的截图了~ 首先说一下两种截图1.Command+sh ...

  2. mysql 选择性高

    选择性高是指能够过滤掉更多不需要的记录.举例来说,在一个公司里,使用性别只能过滤掉一半的人,而使用姓名一般可以过滤掉99%以上的人,因为会有重名情况,而使用员工号,选出一个,其他的全部过滤掉.也就是说 ...

  3. 颜色缩减(带Trackbar)【从毛星云Opencv3编程入门P75 P111例程改编】

    最近学了点opencv,买了毛星云的书,大力推荐哦. 颜色缩减,自己加了个Trackbar看起来更直观一些. 我一般自己先看一遍程序,脑子里有个大概印象了,再自己写一遍,这样出了错误会印象更深刻. 1 ...

  4. sizeof和strlen()的区别

    二者有本质上的区别 从定义可以知道sizeof只是一个operator,而strlen()则是定义一个定义在<string.h>中的函数;所以sizeof(string)是在计算strin ...

  5. SyncServer obj

  6. TWaver HTML5 (2D)----数据元素

    概述 数据元素是数据模型的基本要素,用于描述图形网元,业务网元,或者纯数据.TWaver HTML5中所有数据元素都继承自twaver.Data.为不同功能的需求,预定义了三类数据类型:twaver. ...

  7. ASP.NET页面与IIS底层交互和工作原理详解

    转载自:http://www.cnblogs.com/lidabo/archive/2012/03/13/2393200.html 第一回: 引言 我查阅过不少Asp.Net的书籍,发现大多数作者都是 ...

  8. jQuery图片延迟加载插件jQuery.lazyload使用方法(转)

    使用方法 1.引用jquery和jquery.lazyload.js到你的页面 <script src="jquery-1.11.0.min.js"></scri ...

  9. VBA中find的一些使用方法

    用excel处理数据的时候,无论是使用VBA还是函数,查找和引用都是两大主要的工作,VBA中的find系列的方法(find.findnext.Range.FindPrevious)返回range对象, ...

  10. 通配符+countif()解决大于15位数的计数问题

    excel的最大精度是15位,如果一个单元格中存储的数字超过15位,那么函数在计算的时候将会出现问题,它们会将15位之后的数字变成0. 在这种情况下,需要在函数中加入通配符,例如,统计A列中,A1出现 ...