服務註冊流程
RegistryProtocol
在dubbo服務發佈流程已經分析了發佈服務流程還有服務註冊流程
export
在RegistryProtocol#export
繼續服務註冊流程
// 根據invoker中的url獲取Registry實例: zookeeperRegistry
// ListenerRegistryWrapper(ZookeeperRegistry) 後面註冊服務闡述
final Registry registry = getRegistry(originInvoker);
// 獲取要註冊的到註冊中心的URL: dubbo://ip:port
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
// decide if we need to delay publish
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
// 是否配置了註冊中心,如果是,則需要註冊
if (register) {
// 將ProviderURL註冊到註冊中心
register(registryUrl, registeredProviderUrl);
}
getRegistry
protected Registry getRegistry(final Invoker<?> originInvoker) {
// 把 url 轉化爲對應配置的註冊中心的具體協議 如:zookeeper://ip:port
URL registryUrl = getRegistryUrl(originInvoker);
// 根據具體協議從registryFactory獲取指定的註冊中心實現 ZookeeperRegistry
return registryFactory.getRegistry(registryUrl);
}
RegistryProtocol#register
public void register(URL registryUrl, URL registeredProviderUrl) {
// registryFactory是一個擴展點RegistryFactory$Adaptive
// 並且這個擴展點有一個包裝類 RegistryFactoryWrapper
// 最終獲取的的registryFactory是RegistryFactoryWrapper(ZookeeperRegistryFactory)
// 這裏獲得的registry的是 ListenerRegistryWrapper(ZookeeperRegistry)
Registry registry = registryFactory.getRegistry(registryUrl);
registry.register(registeredProviderUrl);
ProviderModel model = ApplicationModel.getProviderModel(registeredProviderUrl.getServiceKey());
model.addStatedUrl(new ProviderModel.RegisterStatedURL(
registeredProviderUrl,
registryUrl,
true
));
}
RegistryFactory
方法級別自適應擴展點在RegistryProtocol
通過set依賴注入
@SPI("dubbo")
public interface RegistryFactory {
@Adaptive({"protocol"})
Registry getRegistry(URL url);
}
RegistryFactory
擴展點的配置文件META-INF\dubbo\internal\org.apache.dubbo.registry.RegistryFactory
service-discovery-registry=org.apache.dubbo.registry.client.ServiceDiscoveryRegistryFactory
wrapper=org.apache.dubbo.registry.RegistryFactoryWrapper
dubbo=org.apache.dubbo.registry.dubbo.DubboRegistryFactory
multicast=org.apache.dubbo.registry.multicast.MulticastRegistryFactory
zookeeper=org.apache.dubbo.registry.zookeeper.ZookeeperRegistryFactory
redis=org.apache.dubbo.registry.redis.RedisRegistryFactory
consul=org.apache.dubbo.registry.consul.ConsulRegistryFactory
etcd3=org.apache.dubbo.registry.etcd.EtcdRegistryFactory
nacos=org.apache.dubbo.registry.nacos.NacosRegistryFactory
sofa=org.apache.dubbo.registry.sofa.SofaRegistryFactory
multiple=org.apache.dubbo.registry.multiple.MultipleRegistryFactory
RegistryFactory$Adaptive
public class RegistryFactory$Adaptive implements org.apache.dubbo.registry.RegistryFactory {
public org.apache.dubbo.registry.Registry getRegistry(org.apache.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg0;
// extName -> zookeeper
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Failed to get extension (org.apache.dubbo.registry.RegistryFactory) name from url (" + url.toString() + ") use keys([protocol])");
// 返回的RegistryFactory是RegistryFactoryWrapper(ZookeeperRegistryFactory)
org.apache.dubbo.registry.RegistryFactory extension = (org.apache.dubbo.registry.RegistryFactory)ExtensionLoader.getExtensionLoader(org.apache.dubbo.registry.RegistryFactory.class).getExtension(extName);
return extension.getRegistry(arg0);
}
}
RegistryFactoryWrapper
可以看到RegistryFactory的擴展點配置文件中有wrapper=org.apache.dubbo.registry.RegistryFactoryWrapper
所有RegistryFactory$Adaptive#getRegistry
方法中獲取的RegistryFactory
是用RegistryFactoryWrapper
增強的ZookeeperRegistryFactory
public class RegistryFactoryWrapper implements RegistryFactory {
private RegistryFactory registryFactory;
public RegistryFactoryWrapper(RegistryFactory registryFactory) {
this.registryFactory = registryFactory;
}
@Override
public Registry getRegistry(URL url) {
return new ListenerRegistryWrapper(registryFactory.getRegistry(url),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(RegistryServiceListener.class)
.getActivateExtension(url, "registry.listeners")));
}
}
ZookeeperRegistryFactory
getRegistry
ZookeeperRegistryFactory
中沒有看到getRegistry
所有調用其父類AbstractRegistryFactory
的getRegistry方法
public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
private ZookeeperTransporter zookeeperTransporter;
/**
* Invisible injection of zookeeper client via IOC/SPI
* @param zookeeperTransporter
*/
public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
this.zookeeperTransporter = zookeeperTransporter;
}
@Override
public Registry createRegistry(URL url) {
return new ZookeeperRegistry(url, zookeeperTransporter);
}
}
AbstractRegistryFactory.getRegistry
public Registry getRegistry(URL url) {
if (destroyed.get()) {
LOGGER.warn("All registry instances have been destroyed, failed to fetch any instance. " +
"Usually, this means no need to try to do unnecessary redundant resource clearance, all registries has been taken care of.");
return DEFAULT_NOP_REGISTRY;
}
// 獲取url
url = URLBuilder.from(url)
.setPath(RegistryService.class.getName())
.addParameter(INTERFACE_KEY, RegistryService.class.getName())
.removeParameters(EXPORT_KEY, REFER_KEY)
.build();
String key = url.toServiceStringWithoutResolving();
// Lock the registry access process to ensure a single instance of the registry
LOCK.lock();
try {
// 從緩存中獲取Register
Registry registry = REGISTRIES.get(key);
if (registry != null) {
return registry;
}
//create registry by spi/ioc
// 創建registry
registry = createRegistry(url); // 這是一個模板方法,有子類實現如ZookeeperRegistryFactory.createRegistry
if (registry == null) {
throw new IllegalStateException("Can not create registry " + url);
}
REGISTRIES.put(key, registry);
return registry;
} finally {
// Release the lock
LOCK.unlock();
}
}
createRegistry
ZookeeperRegistryFactory#createRegistry
@Override
public Registry createRegistry(URL url) {
return new ZookeeperRegistry(url, zookeeperTransporter);
}
通過上面的方法最終得到的registry是ListenerRegistryWrapper(ZookeeperRegistry)
ZookeeperRegistry
ZookeeperRegistry構造函數
這個方法中使用了 CuratorZookeeperTransport 來實現 zk 的連接
public class ZookeeperRegistry extends FailbackRegistry {
private final static Logger logger = LoggerFactory.getLogger(ZookeeperRegistry.class);
private final static String DEFAULT_ROOT = "dubbo";
private final String root;
private final Set<String> anyServices = new ConcurrentHashSet<>();
private final ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>> zkListeners = new ConcurrentHashMap<>();
private final ZookeeperClient zkClient;
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
super(url);
if (url.isAnyHost()) {
throw new IllegalStateException("registry address == null");
}
// 獲得group名稱
String group = url.getParameter(GROUP_KEY, DEFAULT_ROOT);
if (!group.startsWith(PATH_SEPARATOR)) {
group = PATH_SEPARATOR + group;
}
this.root = group;
// ZookeeperTransporter是一個擴展點 基於方法適配 默認使用curator實現
// 產生一個zookeeper連接
zkClient = zookeeperTransporter.connect(url);
// 添加zookeeper狀態變化事件
zkClient.addStateListener((state) -> {
if (state == StateListener.RECONNECTED) {
logger.warn("Trying to fetch the latest urls, in case there're provider changes during connection loss.\n" +
" Since ephemeral ZNode will not get deleted for a connection lose, " +
"there's no need to re-register url of this instance.");
ZookeeperRegistry.this.fetchLatestAddresses();
} else if (state == StateListener.NEW_SESSION_CREATED) {
logger.warn("Trying to re-register urls and re-subscribe listeners of this instance to registry...");
try {
ZookeeperRegistry.this.recover();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
} else if (state == StateListener.SESSION_LOST) {
logger.warn("Url of this instance will be deleted from registry soon. " +
"Dubbo client will try to re-register once a new session is created.");
} else if (state == StateListener.SUSPENDED) {
} else if (state == StateListener.CONNECTED) {
}
});
}
registry.register(registedProviderUrl)
繼續往下分析,會調用 registry.register 去將 dubbo://的協議地址註冊到 zookeeper 上
ZookeeperRegistry 這個類中並沒有 register 這個方法,但是他的父類 FailbackRegistry 中存在 register 方法,而這個類又重寫了 AbstractRegistry 類中的 register 方法。所以我們可以直接定位到 FailbackRegistry 這個類中的 register 方法中
FailbackRegistry.register
- FailbackRegistry,從名字上來看,是一個失敗重試機制
- 調用父類的 register 方法,講當前 url 添加到緩存集合中
public void register(URL url) {
if (!acceptable(url)) {
logger.info("URL " + url + " will not be registered to Registry. Registry " + url + " does not accept service of this protocol type.");
return;
}
super.register(url);
removeFailedRegistered(url);
removeFailedUnregistered(url);
try {
// 調用子類實現真正的服務註冊,把 url 註冊到 zk 上
doRegister(url);
} catch (Exception e) { // 失敗重試
Throwable t = e;
// If the startup detection is opened, the Exception is thrown directly.
// 如果開啓了啓動時檢測,則直接拋出異常
boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
&& url.getParameter(Constants.CHECK_KEY, true)
&& !CONSUMER_PROTOCOL.equals(url.getProtocol());
boolean skipFailback = t instanceof SkipFailbackWrapperException;
if (check || skipFailback) {
if (skipFailback) {
t = t.getCause();
}
throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
} else {
logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
}
// 將失敗的註冊請求記錄到失敗列表,定時重試
addFailedRegistered(url);
}
}
ZookeeperRegistry.doRegister
最終調用 curator 的客戶端把服務地址註冊到 zk
public void doRegister(URL url) {
try {
// 這個zkClient 是CuratorZookeeperClient 也是基於擴展點機制實現的
zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
} catch (Throwable e) {
throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}