扩展Dubbo,改用Curator2连接Zookeeper

最近,使用Zookeeper实现一个简单的配置中心。 其间,使用Curator(毕竟都是Apache旗下)作为Zookeeper的客户端实现。

但是,当整合进Dubbo时,发现出现Curator版本不兼容。 Dubbo支持curator,但dubbo时间久远,当时的版本是curator: 1.1.10。而我使用的curator最新的2.11.1版本

不过查看Curator版本说明时,发现:

This is the first release of Apache Curator (it's numbered 2.0.0 because Apache Curator is a continuation of Netflix Curator which was in version 1.x).

curator 2.** 版本只是,当时Apache接手后用以区分Netflix的,基本api都保持1.*的,只是包名改为org.apache.*的命名规则

Dubbo因众所周知的原因,基本维护更新都处于停滞状态。 所以,想要用新版本的类库,都要靠自己动手实现了。

基本思路有两个:

  1. 从git中clone出dubbo源码,进行修改后,重新打入maven私库;
  2. 使用dubbo的扩展机制进行外部扩展,以外挂的方式进行增强扩展。

为了,后续维护方便,这里选择第2种方式。

那需要先来了解一下Dubbo的扩展方式。Dubbo设计之初,目标还是挺宏大的,从功能成熟度上可见一斑。 但是,阿里一贯作风就是开源等于丢弃,管杀不管埋(哈哈,吐槽ing)。

Dubbo本身提供了扩展点加载机制 。 主要基于JAVA的SPI机制。

而Dubbo针对Zookeeper的客户端实现也提供了SPI扩展点:

package com.alibaba.dubbo.remoting.zookeeper;

@SPI("zkclient")
public interface ZookeeperTransporter {

	@Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
	ZookeeperClient connect(URL url);

}

也即是,只需要通过SPI扩展实现自己的ZookeeperTransporter即可直接扩展Dubbo的Zookeeper客户端。

第一步:

新建一个Maven工程:

pom.xml

<artifactId>roc-dubbo</artifactId>
<version>0.2.0</version>
<name>roc-dubbo</name>

<dependencies>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>dubbo</artifactId>
        <version>2.5.3</version>
    </dependency>
    <dependency>
        <groupId>org.apache.curator</groupId>
        <artifactId>curator-framework</artifactId>
        <version>2.11.1</version>
    </dependency>
</dependencies>

第二步,实现AbstractZookeeperClient

Dubbo对于Zookeeper的使用,基本都已封装为com.alibaba.dubbo.remoting.zookeeper.ZookeeperClient接口。另外还提供一个的抽象类com.alibaba.dubbo.remoting.zookeeper.support.AbstractZookeeperClient

所以,只需要继承AbstractZookeeperClient就可以很方便的进行扩展。

Curator2ZookeeperClient.java

package com.roc.dubbo.remoting.zookeeper.curator2;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.remoting.zookeeper.ChildListener;
import com.alibaba.dubbo.remoting.zookeeper.StateListener;
import com.alibaba.dubbo.remoting.zookeeper.support.AbstractZookeeperClient;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.CuratorWatcher;
import org.apache.curator.framework.state.ConnectionState;
import org.apache.curator.framework.state.ConnectionStateListener;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;

import java.util.List;

/**
 * Created by roc on 2017/2/24.
 */
public class Curator2ZookeeperClient extends AbstractZookeeperClient<CuratorWatcher> {

    private final CuratorFramework client;

    public Curator2ZookeeperClient(URL url) {
        super(url);
        try {
            CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder()
                    .connectString(url.getBackupAddress())
                    .retryPolicy(new RetryNTimes(Integer.MAX_VALUE, 1000))
                    .connectionTimeoutMs(5000);
            String authority = url.getAuthority();
            if (authority != null && authority.length() > 0) {
                builder = builder.authorization("digest", authority.getBytes());
            }
            client = builder.build();
            client.getConnectionStateListenable().addListener(new ConnectionStateListener() {
                @Override
                public void stateChanged(CuratorFramework client, ConnectionState state) {
                    if (state == ConnectionState.LOST) {
                        Curator2ZookeeperClient.this.stateChanged(StateListener.DISCONNECTED);
                    } else if (state == ConnectionState.CONNECTED) {
                        Curator2ZookeeperClient.this.stateChanged(StateListener.CONNECTED);
                    } else if (state == ConnectionState.RECONNECTED) {
                        Curator2ZookeeperClient.this.stateChanged(StateListener.RECONNECTED);
                    }
                }
            });
            client.start();
        } catch (Exception e) {
            logger.error("Zookeeper connection failed : " + e.getMessage());
            throw new IllegalStateException(e.getMessage(), e);
        }


    }

    public void delete(String path) {
        try {
            client.delete().forPath(path);
        } catch (KeeperException.NoNodeException e) {
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    public List<String> getChildren(String path) {
        try {
            return client.getChildren().forPath(path);
        } catch (KeeperException.NoNodeException e) {
            return null;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    public boolean isConnected() {
        return client.getZookeeperClient().isConnected();
    }

    protected void doClose() {
        client.close();
    }

    @Override
    protected void createPersistent(String path) {
        try {
            client.create().forPath(path);
        } catch (KeeperException.NodeExistsException e) {
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    @Override
    protected void createEphemeral(String path) {
        try {
            client.create().withMode(CreateMode.EPHEMERAL).forPath(path);
        } catch (KeeperException.NodeExistsException e) {
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    private class CuratorWatcherImpl implements CuratorWatcher {

        private volatile ChildListener listener;

        public CuratorWatcherImpl(ChildListener listener) {
            this.listener = listener;
        }

        public void unwatch() {
            this.listener = null;
        }

        @Override
        public void process(WatchedEvent event) throws Exception {
            if (listener != null) {
                listener.childChanged(event.getPath(), client.getChildren().usingWatcher(this).forPath(event.getPath()));
            }
        }
    }

    protected CuratorWatcher createTargetChildListener(String path, ChildListener listener) {
        return new CuratorWatcherImpl(listener);
    }

    protected List<String> addTargetChildListener(String path, CuratorWatcher listener) {
        try {
            return client.getChildren().usingWatcher(listener).forPath(path);
        } catch (KeeperException.NoNodeException e) {
            return null;
        } catch (Exception e) {
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

    protected void removeTargetChildListener(String path, CuratorWatcher listener) {
        ((CuratorWatcherImpl) listener).unwatch();
    }
}

第三步,实现ZookeeperTransporter

如上面所说,Dubbo实际上是通过ZookeeperTransporter来获取com.alibaba.dubbo.remoting.zookeeper.ZookeeperClient,从而进行ZooKeeper操作的。 所以,接下来需要实现自己的ZookeeperTransporter

Curator2ZookeeperTransporter.java

package com.roc.dubbo.remoting.zookeeper.curator2;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.remoting.zookeeper.ZookeeperClient;
import com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter;

/**
 * Created by roc on 2017/2/27.
 */
public class Curator2ZookeeperTransporter implements ZookeeperTransporter {

    @Override
    public ZookeeperClient connect(URL url) {
        return new Curator2ZookeeperClient(url);
    }
}

第四步,编写SPI注册文件

上文已经介绍过了,Dubbo利用SPI方式进行扩展。 按Dubbo的方式,需要提供META-INF/dubbo/{扩展接口的全类名}

这里,我们需要的是META-INF/dubbo/com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter文件:

META-INF/dubbo/com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter:

curator2=com.roc.dubbo.remoting.zookeeper.curator2.Curator2ZookeeperTransporter

OK,将上面的工程编译打包后,就可以直接在Dubbo的<dubbo:registry ... client="curator2" />中,直接使用扩展的curator2客户端了。

这里我们使用Spring Boot的Java-Base方式进行注册中心配置:

DubboProperties.java

/**
 * 
 */
package com.roc.dubbo.boot.configuration;

import org.springframework.boot.context.properties.ConfigurationProperties;

import lombok.Data;

/**
 * @author "Ren PengFei (ROC)"
 * @date 2016年11月8日 下午6:09:05
 * @description
 */
@ConfigurationProperties(prefix = "dubbo")
@Data
public class DubboProperties {

	private String applicationName;

	private String applicationOrganizatione;

	private String applicationOwner;

	private String applicationLoggerr;

	private int applicationShutdownTimeout;

	private String registryProtocol;

	private String registryClient="zkclient";

	private String registryAddress;

	private String registryFile;

	private String protocolName;

	private int protocolPort;

	private String providerLayer;

	private String providerGroup;

	private int timeout;

	private int retries = 1;

	private int delay = -1;

	private String monitorProtocol = "registry";

	private String consumerGroup;


}
@Bean
public RegistryConfig registryConfig() {
	RegistryConfig registryConfig = new RegistryConfig();
	registryConfig.setProtocol(dubboProperties.getRegistryProtocol());
	registryConfig.setAddress(dubboProperties.getRegistryAddress());
	registryConfig.setFile(dubboProperties.getRegistryFile());
	registryConfig.setClient(dubboProperties.getRegistryClient());
	return registryConfig;
}

到此为止,就完成了Dubbo的Curator2为Zookeeper客户端的扩展。 只需要在使用curator2的地方,引入上面的工程jar依赖就可以。不需要改动Dubbo代码。

Dubbo通过SPI的方式,可以对传输协议,io层实现,各种Filter,序列化等等进行方便的扩展。具体如下:

  • 协议扩展
  • 调用拦截扩展
  • 引用监听扩展
  • 暴露监听扩展
  • 集群扩展
  • 路由扩展
  • 负载均衡扩展
  • 合并结果扩展
  • 注册中心扩展
  • 监控中心扩展
  • 扩展点加载扩展
  • 动态代理扩展
  • 编译器扩展
  • 消息派发扩展
  • 线程池扩展
  • 序列化扩展
  • 网络传输扩展
  • 信息交换扩展
  • 组网扩展
  • Telnet命令扩展
  • 状态检查扩展
  • 容器扩展
  • 页面扩展
  • 缓存扩展
  • 验证扩展
  • 日志适配扩展

转载于:https://my.oschina.net/roccn/blog/847220

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值