Dubbo内部使用zkclient操作zookeeper
com.github.sgroschupf
zkclient
0.1
ZKClient示例
public class CreateSession {
public static ZkClient connectZK(){
//zk集群的地址
String ZKServers = "192.168.99.100:2181";
ZkClient zkClient = new ZkClient(ZKServers,10000,10000,new SerializableSerializer());
zkClient.subscribeStateChanges(new IZkStateListener() {
public void handleNewSession() throws Exception {
System.out.println("handleNewSession()");
}
public void handleStateChanged(KeeperState stat) throws Exception {
//链接关闭重连等
System.out.println( "handleStateChanged,stat:" + stat);
}
});
System.out.println("conneted ok!");
return zkClient;
}
}
- 节点创建、更新、删除的操作
package com.soa.other.zk;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;
import org.apache.zookeeper.CreateMode;
public class NodeManager {
public void createNode() {
ZkClient zkClient = CreateSession.connectZK();
User user = new User();
user.setId(1);
user.setName("agan1");
/**
* "/testUserNode" :节点的地址 user:数据的对象 CreateMode.PERSISTENT:创建的节点类型
*/
// zkClient.createPersistent("/testUserNode", user);
String path = zkClient.create("/aganNode", user,CreateMode.PERSISTENT);
// 输出创建节点的路径
System.out.println("创建节点:" + path);
}
public void updateNode() {
ZkClient zkClient = CreateSession.connectZK();
User user = new User();
user.setId(2);
user.setName("agan2");
/**
* testUserNode 节点的路径 user 传入的数据对象
*/
zkClient.writeData("/aganNode", user);
System.out.println("修改aganNode节点成功" );
}
public void deleteNode(){
ZkClient zkClient = CreateSession.connectZK();
//删除单独一个节点,返回true表示成功
boolean e1 = zkClient.delete("/aganNode");
//返回 true表示节点成功 ,false表示删除失败
System.out.println("删除aganNode节点是否成功:"+e1);
}
public static void main(String[] args) {
NodeManager nm=new NodeManager();
nm.createNode();
//断点
nm.updateNode();
//断点
nm.deleteNode();
}
}
- 订阅子节点变化
package com.soa.other.zk;
import java.util.List;
import org.I0Itec.zkclient.IZkChildListener;
import org.I0Itec.zkclient.ZkClient;
/**
*订阅节点的信息改变(创建节点,删除节点,添加子节点)
*/
public class SubscribeChildChanges {
private static class ZKChildListener implements IZkChildListener{
/**
* handleChildChange: 用来处理服务器端发送过来的通知
* parentPath:对应的父节点的路径
* currentChilds:子节点的相对路径
*/
public void handleChildChange(String parentPath, List currentChilds) throws Exception {
System.out.println("订阅节点的信息改变(创建节点,删除节点,添加子节点)"+parentPath+" "+currentChilds.toString());
}
}
public static void main(String[] args) throws InterruptedException {
ZkClient zkClient = CreateSession.connectZK();
/**
* "/testUserNode" 监听的节点,可以是现在存在的也可以是不存在的
*/
zkClient.subscribeChildChanges("/aganNode", new ZKChildListener());
Thread.sleep(Integer.MAX_VALUE);
}
}
- 订阅节点数据变化
package com.soa.other.zk;
import org.I0Itec.zkclient.IZkDataListener;
import org.I0Itec.zkclient.ZkClient;
import org.I0Itec.zkclient.serialize.SerializableSerializer;
/**
* 订阅节点的数据内容的变化
*/
public class SubscribeDataChanges {
private static class ZKDataListener implements IZkDataListener {
public void handleDataChange(String dataPath, Object data)
throws Exception {
System.out.println("订阅节点的数据内容的变化"+dataPath + ":" + data.toString());
}
public void handleDataDeleted(String dataPath) throws Exception {
System.out.println("订阅节点的数据内容被删除"+dataPath);
}
}
public static void main(String[] args) throws InterruptedException {
ZkClient zkClient = CreateSession.connectZK();
zkClient.subscribeDataChanges("/aganNode", new ZKDataListener());
Thread.sleep(Integer.MAX_VALUE);
}
}
Dubbo与ZK交互
Dubbo与zk交互主要是通过Registry这个接口的实现类ZookeeperRegistry来完成的,ZookeeperRegistry封装了和注册中心交互的细节,底层使用ZookeeperClient接口通过zkclient或者Curator操纵zk
#com.alibaba.dubbo.registry.integration.RegistryProtocol#export
public Exporter export(final Invoker originInvoker) throws RpcException {
//export invoker
final ExporterChangeableWrapper exporter = doLocalExport(originInvoker);
//registry provider
//获取注册中心实例
final Registry registry = getRegistry(originInvoker);
final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
registry.register(registedProviderUrl);
// 订阅override数据
// FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
//保证每次export都返回一个新的exporter实例
......
};
}
获取Registry
1. 获取RegistryFactory
获取RegistryFactory 扩展点,扩展点名称为zookeeper,用于创建Registry
/**
* 根据invoker的地址获取registry实例
* @param originInvoker
* @return
*/
private Registry getRegistry(final Invoker> originInvoker){
URL registryUrl = originInvoker.getUrl();
if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY);
registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY);
}
return registryFactory.getRegistry(registryUrl);
}
public class RegistryFactory$Adpative implements com.alibaba.dubbo.registry.RegistryFactory {
public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.registry.RegistryFactory) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.registry.RegistryFactory extension = (com.alibaba.dubbo.registry.RegistryFactory)
//extName =zookeeper
ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.registry.RegistryFactory.class)
.getExtension(extName);
return extension.getRegistry(arg0);
}
}
2.创建Registry
使用AbstractRegistryFactory#getRegistry
获取Registry,调用子类ZookeeperRegistryFactory.createRegistry
创建Registry
#com.alibaba.dubbo.registry.support.AbstractRegistryFactory#getRegistry
public Registry getRegistry(URL url) {
url = url.setPath(RegistryService.class.getName())
.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
.removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
String key = url.toServiceString();
// 锁定注册中心获取过程,保证注册中心单一实例
LOCK.lock();
try {
Registry registry = REGISTRIES.get(key);
if (registry != null) {
return registry;
}
//创建注册中心并加入缓存
registry = createRegistry(url);
if (registry == null) {
throw new IllegalStateException("Can not create registry " + url);
}
REGISTRIES.put(key, registry);
return registry;
} finally {
// 释放锁
LOCK.unlock();
}
}
#com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistryFactory#createRegistry
public Registry createRegistry(URL url) {
return new ZookeeperRegistry(url, zookeeperTransporter);
}
Registry的继承关系如下
父类
AbstractRegistry
中统一完成缓存配置文件的加载
public AbstractRegistry(URL url) {
setUrl(url);
// 启动文件保存定时器
syncSaveFile = url.getParameter(Constants.REGISTRY_FILESAVE_SYNC_KEY, false);
String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getHost() + ".cache");
File file = null;
if (ConfigUtils.isNotEmpty(filename)) {
file = new File(filename);
if(! file.exists() && file.getParentFile() != null && ! file.getParentFile().exists()){
if(! file.getParentFile().mkdirs()){
throw new IllegalArgumentException("Invalid registry store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
}
}
}
//
this.file = file;
loadProperties();
notify(url.getBackupUrls());
}
把文件C:\Users\bobo.dubbo\dubbo-registry-192.168.48.117.cache中的内容加载为properties,内容每个接口对应注册中心的地址的缓存
最终得到的Registry实例为ZookeeperRegistry类型
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
super(url);
if (url.isAnyHost()) {
throw new IllegalStateException("registry address == null");
}
String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
if (! group.startsWith(Constants.PATH_SEPARATOR)) {
group = Constants.PATH_SEPARATOR + group;
}
this.root = group;
//创建zkclient
zkClient = zookeeperTransporter.connect(url);
//接收下层同意封装后发送上来的消息
zkClient.addStateListener(new StateListener() {
public void stateChanged(int state) {
if (state == RECONNECTED) {
try {
recover();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
});
}
3.初始化ZKClient
zookeeperTransporter.connect(url);
初始化ZK
public class ZkclientZookeeperTransporter implements ZookeeperTransporter {
public ZookeeperClient connect(URL url) {
return new ZkclientZookeeperClient(url);
}
}
#com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry#ZookeeperRegistry
public ZkclientZookeeperClient(URL url) {
super(url);
client = new ZkClient(url.getBackupAddress());
client.subscribeStateChanges(new IZkStateListener() {
public void handleStateChanged(KeeperState state) throws Exception {
ZkclientZookeeperClient.this.state = state;
if (state == KeeperState.Disconnected) {
stateChanged(StateListener.DISCONNECTED);
} else if (state == KeeperState.SyncConnected) {
stateChanged(StateListener.CONNECTED);
}
}
public void handleNewSession() throws Exception {
stateChanged(StateListener.RECONNECTED);
}
});
}
使用中间转换层解耦底层不同客户端的事件机制,将底层不同的事件转换后向上层传递
小结
RegistryProtocol.export
-->>registryFactory.getRegistry
-->>ZookeeperRegistryFactory.createRegistry
-->>ZookeeperRegistry
-->>zookeeperTransporter.connect
-->>ZookeeperClient
-->>ZkclientZookeeperClient
注册provider到zk
创建并获取到连接了zk的registry之后就向zk注册provider的节点,执行流程如下:
-->registry.register(registedProviderUrl)//创建节点
-->AbstractRegistry.register
-->FailbackRegistry.register
-->doRegister(url)//向zk服务器端发送注册请求
-->ZookeeperRegistry.doRegister
-->zkClient.create
-->AbstractZookeeperClient.create//dubbo/com.alibaba.dubbo.demo.DemoService/providers/
dubbo%3A%2F%2F192.168.100.52%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26
application%3Ddemo-provider%26dubbo%3D2.0.0%26generic%3Dfalse%26interface%3D
com.alibaba.dubbo.demo.DemoService%26loadbalance%3Droundrobin%26methods%3DsayHello%26owner%3
Dwilliam%26pid%3D2416%26side%3Dprovider%26timestamp%3D1474276306353
-->createEphemeral(path);//临时节点 dubbo%3A%2F%2F192.168.100.52%3A20880%2F.............
-->createPersistent(path);//持久化节点 dubbo/com.alibaba.dubbo.demo.DemoService/providers
#com.alibaba.dubbo.registry.integration.RegistryProtocol#export
public Exporter export(final Invoker originInvoker) throws RpcException {
//export invoker
//1.启动netty远程暴露
final ExporterChangeableWrapper exporter = doLocalExport(originInvoker);
//registry provider
//2.连接zk获取registry(ZookeeperRegistry):registry封装了和注册中心交互和心跳的功能
final Registry registry = getRegistry(originInvoker);
final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
//3.向zk注册服务,创建节点
registry.register(registedProviderUrl);
// 订阅override数据
// FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
#com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry#doRegister
protected void doRegister(URL url) {
try {
zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
} catch (Throwable e) {
throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
#com.alibaba.dubbo.remoting.zookeeper.support.AbstractZookeeperClient#create
//中间层,下层是zkclient和curator
public void create(String path, boolean ephemeral) {
//将path按分割符切分依次创建
int i = path.lastIndexOf('/');
if (i > 0) {
create(path.substring(0, i), false);
}
if (ephemeral) {
//服务提供者信息是临时节点,会话失效自动删除
createEphemeral(path);
} else {
//创建持久化节点
createPersistent(path);
}
}
#com.alibaba.dubbo.remoting.zookeeper.zkclient.ZkclientZookeeperClient#createEphemeral
public void createEphemeral(String path) {
try {
client.createEphemeral(path);
} catch (ZkNodeExistsException e) {
}
}
创建并订阅configurators
/dubbo/com.alibaba.dubbo.demo.DemoService/configurators
包含服务的配置信息,在注册完成provider之后需要订阅该节点,当这个节点数据变更的时候zk会通知订阅了监听器
- 入口
RegistryProtocol#export
#com.alibaba.dubbo.registry.integration.RegistryProtocol#export
public Exporter export(final Invoker originInvoker) throws RpcException {
...
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
...
}
- 代码执行流程
-->registry.subscribe//订阅ZK
-->AbstractRegistry.subscribe
-->FailbackRegistry.subscribe
-->doSubscribe(url, listener)// 向服务器端发送订阅请求
-->ZookeeperRegistry.doSubscribe
-->new ChildListener()
-->实现了 childChanged
-->实现并执行 ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
//A
-->zkClient.create(path, false);//第一步:先创建持久化节点/dubbo/com.alibaba.dubbo.demo.DemoService/configurators
-->zkClient.addChildListener(path, zkListener)
-->AbstractZookeeperClient.addChildListener
//C
-->createTargetChildListener(path, listener)//第三步:收到订阅后的处理,交给FailbackRegistry.notify处理
-->ZkclientZookeeperClient.createTargetChildListener
-->new IZkChildListener()
-->实现了 handleChildChange //收到订阅后的处理
-->listener.childChanged(parentPath, currentChilds);
-->实现并执行ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
-->收到订阅后处理 FailbackRegistry.notify
//B
-->addTargetChildListener(path, targetListener)////第二步
-->ZkclientZookeeperClient.addTargetChildListener
-->client.subscribeChildChanges(path, listener)//第二步:启动加入订阅/dubbo/com.alibaba.dubbo.demo.DemoService/configurators
-->notify(url, listener, urls)
-->FailbackRegistry.notify
-->doNotify(url, listener, urls);
-->AbstractRegistry.notify
-->saveProperties(url);//把服务端的注册url信息更新到C:\Users\bobo\.dubbo\dubbo-registry-192.168.48.117.cache
-->registryCacheExecutor.execute(new SaveProperties(version));//采用线程池来处理
-->listener.notify(categoryList)
-->RegistryProtocol.notify
-->RegistryProtocol.this.getProviderUrl(originInvoker)//通过invoker的url 获取 providerUrl的地址
创建持久化节点/dubbo/com.alibaba.dubbo.demo.DemoService/configurators
,然后创建上层统一的监听器ChildListener通过抽象类AbstractZookeeperClient
完成事件的监听,并在方法回调的时候触发ZookeeperRegistry.this.notify
#com.alibaba.dubbo.registry.zookeeper.ZookeeperRegistry#doSubscribe
protected void doSubscribe(final URL url, final NotifyListener listener) {
...
List urls = new ArrayList();
for (String path : toCategoriesPath(url)) {
ConcurrentMap listeners = zkListeners.get(url);
if (listeners == null) {
zkListeners.putIfAbsent(url, new ConcurrentHashMap());
listeners = zkListeners.get(url);
}
ChildListener zkListener = listeners.get(listener);
if (zkListener == null) {
//创建监听器
listeners.putIfAbsent(listener, new ChildListener() {
public void childChanged(String parentPath, List currentChilds) {
//节点数据变更后的处理
ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
}
});
zkListener = listeners.get(listener);
}
//创建持久化节点/dubbo/com.alibaba.dubbo.demo.DemoService/configurators
zkClient.create(path, false);
//向zk注册监听
List children = zkClient.addChildListener(path, zkListener);
if (children != null) {
urls.addAll(toUrlsWithEmpty(url, path, children));
}
}
notify(url, listener, urls);
...
}
将上层统一的监听器转换成为zk(IZkChildListener )或者curatro需要的监听器,并注册到各自的实现中
#com.alibaba.dubbo.remoting.zookeeper.support.AbstractZookeeperClient#addChildListener
public List addChildListener(String path, final ChildListener listener) {
ConcurrentMap listeners = childListeners.get(path);
if (listeners == null) {
childListeners.putIfAbsent(path, new ConcurrentHashMap());
listeners = childListeners.get(path);
}
TargetChildListener targetListener = listeners.get(listener);
if (targetListener == null) {
//将上层监听器的hook,子类实现完成上层统一实现,子类收到事件完成回调
listeners.putIfAbsent(listener, createTargetChildListener(path, listener));
targetListener = listeners.get(listener);
}
//注册监听器,因为路径和注册时机都无法确定所以不能像状态监听那样提前注册
return addTargetChildListener(path, targetListener);
}
#com.alibaba.dubbo.remoting.zookeeper.zkclient.ZkclientZookeeperClient#addTargetChildListener
public IZkChildListener createTargetChildListener(String path, final ChildListener listener) {
return new IZkChildListener() {
public void handleChildChange(String parentPath, List currentChilds)
throws Exception {
listener.childChanged(parentPath, currentChilds);
}
};
}
public List addTargetChildListener(String path, final IZkChildListener listener) {
//实际订阅节点
return client.subscribeChildChanges(path, listener);
}