Registry

本文讲解如何通过扩展 org.apache.dubbo.registry.client.ServiceDiscovery SPI,提供自定义的注册中心实现。

服务发现 一章中,我们了解了 Dubbo 内置的几个核心注册中心实现 NacosZookeeper 的使用方式与工作原理。本文讲解如何通过扩展 org.apache.dubbo.registry.client.ServiceDiscoveryorg.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 {
  2. @Override
  3. protected ServiceDiscovery createDiscovery(URL registryURL) {
  4. return new EtcdServiceDiscovery(applicationModel, registryURL);
  5. }
  6. }

EtcdServiceDiscovery 的一些关键方法与实现如下:

  1. public class EtcdServiceDiscovery extends AbstractServiceDiscovery {
  2. private final Set<String> services = new ConcurrentHashSet<>();
  3. private final Map<String, InstanceChildListener> childListenerMap = new ConcurrentHashMap<>();
  4. EtcdClient etcdClient;
  5. public EtcdServiceDiscovery(ApplicationModel applicationModel, URL registryURL) {
  6. super(applicationModel, registryURL);
  7. EtcdTransporter etcdTransporter = applicationModel.getExtensionLoader(EtcdTransporter.class).getAdaptiveExtension();
  8. etcdClient = etcdTransporter.connect(registryURL);
  9. etcdClient.addStateListener(state -> {
  10. if (state == StateListener.CONNECTED) {
  11. try {
  12. recover();
  13. } catch (Exception e) {
  14. logger.error(e.getMessage(), e);
  15. }
  16. }
  17. });
  18. this.registryURL = registryURL;
  19. }
  20. @Override
  21. public void doRegister(ServiceInstance serviceInstance) {
  22. try {
  23. String path = toPath(serviceInstance);
  24. etcdClient.putEphemeral(path, new Gson().toJson(serviceInstance));
  25. services.add(serviceInstance.getServiceName());
  26. } catch (Throwable e) {
  27. throw new RpcException("Failed to register " + serviceInstance + " to etcd " + etcdClient.getUrl()
  28. + ", cause: " + (OptionUtil.isProtocolError(e)
  29. ? "etcd3 registry may not be supported yet or etcd3 registry is not available."
  30. : e.getMessage()), e);
  31. }
  32. }
  33. @Override
  34. protected void doUnregister(ServiceInstance serviceInstance) {
  35. try {
  36. String path = toPath(serviceInstance);
  37. etcdClient.delete(path);
  38. services.remove(serviceInstance.getServiceName());
  39. } catch (Throwable e) {
  40. throw new RpcException("Failed to unregister " + serviceInstance + " to etcd " + etcdClient.getUrl() + ", cause: " + e.getMessage(), e);
  41. }
  42. }
  43. @Override
  44. public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
  45. for (String serviceName : listener.getServiceNames()) {
  46. registerServiceWatcher(serviceName, listener);
  47. }
  48. }
  49. @Override
  50. public List<ServiceInstance> getInstances(String serviceName) {
  51. List<String> children = etcdClient.getChildren(toParentPath(serviceName));
  52. if (CollectionUtils.isEmpty(children)) {
  53. return Collections.emptyList();
  54. }
  55. List<ServiceInstance> list = new ArrayList<>(children.size());
  56. for (String child : children) {
  57. ServiceInstance serviceInstance = new Gson().fromJson(etcdClient.getKVValue(child), DefaultServiceInstance.class);
  58. list.add(serviceInstance);
  59. }
  60. return list;
  61. }
  62. }

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)