Registry
本文讲解如何通过扩展 org.apache.dubbo.registry.client.ServiceDiscovery SPI,提供自定义的注册中心实现。
在 服务发现 一章中,我们了解了 Dubbo 内置的几个核心注册中心实现 Nacos、Zookeeper 的使用方式与工作原理。本文讲解如何通过扩展 org.apache.dubbo.registry.client.ServiceDiscovery 和 org.apache.dubbo.registry.nacos.NacosServiceDiscoveryFactory SPI,提供自定义的注册中心实现。
本示例的完整源码请参见 dubbo-registry-etcd。除了本示例之外,Dubbo 核心仓库 apache/dubbo 以及扩展库 apache/dubbo-spi-extensions 中的众多注册中心扩展实现,都可以作为扩展参考实现:
1. # Dubbo对外支持的常用注册中心实现
2. nacos=org.apache.dubbo.registry.nacos.NacosServiceDiscoveryFactory
3. zookeeper=org.apache.dubbo.registry.zookeeper.ZookeeperServiceDiscoveryFactory
任务详情
通过扩展 SPI 实现基于的 etcd 注册中心。
实现方式
代码详情
首先,通过继承 AbstractServiceDiscoveryFactory 实现 ServiceDiscoveryFactory 接口
1. public class EtcdServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
3. @Override
4. protected ServiceDiscovery createDiscovery(URL registryURL) {
5. return new EtcdServiceDiscovery(applicationModel, registryURL);
6. }
8. }
EtcdServiceDiscovery 的一些关键方法与实现如下:
1. public class EtcdServiceDiscovery extends AbstractServiceDiscovery {
3. private final Set<String> services = new ConcurrentHashSet<>();
4. private final Map<String, InstanceChildListener> childListenerMap = new ConcurrentHashMap<>();
6. EtcdClient etcdClient;
8. public EtcdServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
9. super(applicationModel, registryURL);
10. EtcdTransporter etcdTransporter = applicationModel.getExtensionLoader(EtcdTransporter.class).getAdaptiveExtension();
12. etcdClient = etcdTransporter.connect(registryURL);
14. etcdClient.addStateListener(state -> {
15. if (state == StateListener.CONNECTED) {
16. try {
17. recover();
18. } catch (Exception e) {
19. logger.error(e.getMessage(), e);
20. }
21. }
22. });
24. this.registryURL = registryURL;
25. }
27. @Override
28. public void doRegister(ServiceInstance serviceInstance) {
29. try {
30. String path = toPath(serviceInstance);
31. etcdClient.putEphemeral(path, new Gson().toJson(serviceInstance));
32. services.add(serviceInstance.getServiceName());
33. } catch (Throwable e) {
34. throw new RpcException("Failed to register " + serviceInstance + " to etcd " + etcdClient.getUrl()
35. + ", cause: " + (OptionUtil.isProtocolError(e)
36. ? "etcd3 registry may not be supported yet or etcd3 registry is not available."
37. : e.getMessage()), e);
38. }
39. }
41. @Override
42. protected void doUnregister(ServiceInstance serviceInstance) {
43. try {
44. String path = toPath(serviceInstance);
45. etcdClient.delete(path);
46. services.remove(serviceInstance.getServiceName());
47. } catch (Throwable e) {
48. throw new RpcException("Failed to unregister " + serviceInstance + " to etcd " + etcdClient.getUrl() + ", cause: " + e.getMessage(), e);
49. }
50. }
52. @Override
53. public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
54. for (String serviceName : listener.getServiceNames()) {
55. registerServiceWatcher(serviceName, listener);
56. }
57. }
59. @Override
60. public List<ServiceInstance> getInstances(String serviceName) {
61. List<String> children = etcdClient.getChildren(toParentPath(serviceName));
62. if (CollectionUtils.isEmpty(children)) {
63. return Collections.emptyList();
64. }
65. List<ServiceInstance> list = new ArrayList<>(children.size());
66. for (String child : children) {
67. ServiceInstance serviceInstance = new Gson().fromJson(etcdClient.getKVValue(child), DefaultServiceInstance.class);
68. list.add(serviceInstance);
69. }
70. return list;
71. }
72. }
SPI配置
在 resources/META-INF/dubbo/org.apache.dubbo.registry.client.ServiceDiscoveryFactory 文件中添加如下配置:
1. etcd=org.apache.dubbo.registry.etcd.EtcdServiceDiscoveryFactory
使用方式
要开启 etcd 作为注册中心,修改应用中的 resources/application.properties 文件中的 registry 配置如下:
1. dubbo.registry.address=etcd://host:port
最后修改 September 13, 2024: Refactor website structure (#2860) (1a4b998f54b)
