转载: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. Linux简介与厂商版本

    Linux简介与厂商版本   作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 1. Linux简介 Linux可以有狭义和广义两种 ...

  2. python_way ,day23 API

    python_way ,day23 1.api认证  .api加密动态请求 2.自定义session 一.api认证 首先提供api的公司,如支付宝,微信,都会给你一个用户id,然后还会让你下一个SD ...

  3. jQuery:使用$获取对象后检查该对象是否存在

    注意: 1)即使jQ获取到网页中不存在的元素也不会报错 2)使用$("#tt")形式获取到的永远是对象,即使网页上没有此元素 jQuery检查某个元素在网页上是否存在时,不能使用以 ...

  4. 基于jQuery的H5调试条

    <!DOCTYPE html> <html> <head> <meta name="viewport" content="wid ...

  5. 关于seafile启动的问题解决

    过了一个国庆,同事反映说seafile服务挂掉了,无法正常连接. 刚才解决了一下,把相关问题简要记录一下: 1.首先连接阿里云,获得相关IP地址(如果已知IP地址,则不需要该步骤)

  6. iOS - MVC 架构模式

    1.MVC 从字面意思来理解,MVC 即 Modal View Controller(模型 视图 控制器),是 Xerox PARC 在 20 世纪 80 年代为编程语言 Smalltalk-80 发 ...

  7. iOS - KVO 键值观察

    1.KVO KVO 是 Key-Value Observing 的简写,是键值观察的意思,属于 runtime 方法.Key Value Observing 顾名思义就是一种 observer 模式用 ...

  8. Ruby方法

    Ruby 方法 Ruby 方法与其他编程语言中的函数类似.Ruby 方法用于捆绑一个或多个重复的语句到一个单元中. 方法名应以小写字母开头.如果您以大写字母作为方法名的开头,Ruby 可能会把它当作常 ...

  9. 有趣的JavaScript小程序

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. go分页

    简单的beego分页功能代码 一个简单的beego分页小插件(源代码在最下面): 支持条件查询 支持参数保留 支持自定义css样式 支持表/视图 支持参数自定义 默认为pno 支持定义生成链接的个数 ...