Dubbo中的服务都会通过ServiceConfig
(Spring容器为ServiceBean
)对象的export()
方法进行服务注册。
1. ServiceConfig.export()
export
为服务注册的入口,内部调用了doExport()
方法和exported()
方法。
public synchronized void export() {
if (shouldDelay()) {
// 延迟注册
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
// 注册
doExport();
}
//发布服务注册事件 ServiceConfigExportedEvent
exported();
}
2. ServiceConfig.doExport()
doExport
方法很简单,标记注册状态exported
为true
,并设置默认path
为接口名称。
protected synchronized void doExport() {
if (unexported) {
throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
}
if (exported) {
return;
}
exported = true;
if (StringUtils.isEmpty(path)) {
// 如果没有设置path,默认为 interfaceName
// interfaceName: org.finalframework.dubbo.service.HelloService
path = interfaceName;
}
doExportUrls();
}
3. ServiceConfig.doExportUrls()
private void doExportUrls() {
ServiceRepository repository = ApplicationModel.getServiceRepository();
ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
repository.registerProvider(
getUniqueServiceName(),
ref,
serviceDescriptor,
this,
serviceMetadata
);
// 解析注册地址 <dubbo:registry address="zookeeper://127.0.0.1:2181" protocol="zookeeper" port="2181" />
// 格式: registry://{host}:{port}/org.apache.dubbo.registry.RegistryService?
// registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=final-dubbo-provider&dubbo=2.0.2&pid=16114®istry=zookeeper&release=2.7.8×tamp=1598503117736
List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
//遍历协议进行服务注册
// <dubbo:protocol name="dubbo" port="20880" />
for (ProtocolConfig protocolConfig : protocols) {
// pathKey = [{group}/]{interfaceServiceName}[:{version}]
// org.finalframework.dubbo.service.HelloService
String pathKey = URL.buildKey(getContextPath(protocolConfig)
.map(p -> p + "/" + path)
.orElse(path), group, version);
// In case user specified path, register service one more time to map it to path.
repository.registerService(pathKey, interfaceClass);
// TODO, uncomment this line once service key is unified
serviceMetadata.setServiceKey(pathKey);
//进行服务注册
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
4. ConfigValidationUtils.loadRegistries(AbstractInterfaceConfig, boolean)
public static List<URL> loadRegistries(AbstractInterfaceConfig interfaceConfig, boolean provider) {
// check && override if necessary
List<URL> registryList = new ArrayList<URL>();
// <dubbo:application hostname="MAC.local" name="final-dubbo-provider" />
ApplicationConfig application = interfaceConfig.getApplication();
// <dubbo:registry address="zookeeper://127.0.0.1:2181" protocol="zookeeper" port="2181" />
List<RegistryConfig> registries = interfaceConfig.getRegistries();
if (CollectionUtils.isNotEmpty(registries)) {
for (RegistryConfig config : registries) {
String address = config.getAddress();
if (StringUtils.isEmpty(address)) {
address = ANYHOST_VALUE;
}
if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
Map<String, String> map = new HashMap<String, String>();
AbstractConfig.appendParameters(map, application);
AbstractConfig.appendParameters(map, config);
map.put(PATH_KEY, RegistryService.class.getName());
AbstractInterfaceConfig.appendRuntimeParameters(map);
if (!map.containsKey(PROTOCOL_KEY)) {
map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);
}
List<URL> urls = UrlUtils.parseURLs(address, map);
for (URL url : urls) {
url = URLBuilder.from(url)
.addParameter(REGISTRY_KEY, url.getProtocol())
.setProtocol(extractRegistryType(url))
.build();
if ((provider && url.getParameter(REGISTER_KEY, true))
|| (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
registryList.add(url);
}
}
}
}
}
return registryList;
}
5. ServiceConfig.doExportUrlsFor1Protocol(ProtocolConfig , List )
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
...
// export service
String host = findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = findConfigedPorts(protocolConfig, name, map);
// dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=16114&release=2.7.8&side=provider×tamp=1598504045682
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
...
//获取Scope,一般为 null
String scope = url.getParameter(SCOPE_KEY);
// don't export when none is configured
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
// export to local if the config is not remote (export to remote only when config is remote)
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
//本地注册
exportLocal(url);
}
// export to remote if the config is not local (export to local only when config is local)
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
// 远程注册
if (CollectionUtils.isNotEmpty(registryURLs)) {
for (URL registryURL : registryURLs) {
...
// JavassistProxyFactory.getInvoker(proxy,Class,URL)
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// RegistryProtocol.export(Invoker)
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
exporters.add(exporter);
}
} else {
...
// JavassistProxyFactory.getInvoker(proxy,Class,URL)
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// RegistryProtocol.export(Invoker)
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
exporters.add(exporter);
}
}
}
...
}
6. ServiceConfig.exportLocal(URL)
exportLocal(URL)
方法
private void exportLocal(URL url) {
// url: dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=25489&release=2.7.8&side=provider×tamp=1598529167646
// local: injvm://127.0.0.1/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=17480&release=2.7.8&side=provider×tamp=1598504670606
URL local = URLBuilder.from(url)
.setProtocol(LOCAL_PROTOCOL)
.setHost(LOCALHOST_VALUE)
.setPort(0)
.build();
// PROTOCOL: org.apache.dubbo.rpc.Protocol$Adaptive
// jad org.apache.dubbo.rpc.Protocol$Adaptive
// exporter: ListenerExporterWrapper
// exporter.exporter: InJvmExporter
Exporter<?> exporter = PROTOCOL.export(
PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, local));
exporters.add(exporter);
logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry url : " + local);
}
7. JavassistProxyFactory.getInvoker(proxy,Class,URL)
@Override
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
// 创建动态代理
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
}
};
}
8.RegistryProtocol.export(Invoker)
export(Invoker)
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
//originInvoker.url: registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=final-dubbo-provider&dubbo=2.0.2&export=dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider×tamp=1598530524889&pid=26061®istry=zookeeper&release=2.7.8×tamp=1598530524880
// registryUrl: zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=final-dubbo-provider&dubbo=2.0.2&export=dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider×tamp=1598530524889&pid=26061&release=2.7.8×tamp=1598530524880
URL registryUrl = getRegistryUrl(originInvoker);
// url to export locally
// providerUrl: dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider×tamp=1598530524889
URL providerUrl = getProviderUrl(originInvoker);
// Subscribe the override data
// FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call
// the same service. Because the subscribed is cached key with the name of the service, it causes the
// subscription information to cover.
// overrideSubscribeUrl: provider://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&category=configurators&check=false&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider×tamp=1598530524889
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
// providerUrl: dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider×tamp=1598530524889
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
//export invoker
// 本地注册
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
// url to registry
//
final Registry registry = getRegistry(originInvoker);
// registeredProviderUrl: dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider×tamp=1598530524889
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
// decide if we need to delay publish
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register) {
// 注册
register(registryUrl, registeredProviderUrl);
}
// register stated url on provider model
registerStatedUrl(registryUrl, registeredProviderUrl, register);
exporter.setRegisterUrl(registeredProviderUrl);
exporter.setSubscribeUrl(overrideSubscribeUrl);
// Deprecated! Subscribe to override rules in 2.6.x or before.
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
notifyExport(exporter);
//Ensure that a new exporter instance is returned every time export
return new DestroyableExporter<>(exporter);
}
getRegistry(Invoker)
protected Registry getRegistry(final Invoker<?> originInvoker) {
// registryUrl: zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=final-dubbo-provider&dubbo=2.0.2&export=dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&bind.ip=10.12.201.2&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=26061&release=2.7.8&side=provider×tamp=1598530524889&pid=26061&release=2.7.8×tamp=1598530524880
URL registryUrl = getRegistryUrl(originInvoker);
// registryFactory: RegistryFactory$Adaptive
return registryFactory.getRegistry(registryUrl);
}
register(URL,URL)
private void register(URL registryUrl, URL registeredProviderUrl) {
// registryFactory: RegistryFactory$Adaptive
// jad org.apache.dubbo.registry.RegistryFactory$Adaptive
// registry: ListenerRegistryWrapper->ZookeeperRegistry
Registry registry = registryFactory.getRegistry(registryUrl);
registry.register(registeredProviderUrl);
}
9. ZookeeperRegistry.register(URL)
FailbackRegistry#register(URL)
@Override
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 {
// Sending a registration request to the server side
// ZookeeperRegistry.doRegister(URL)
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);
}
// Record a failed registration request to a failed list, retry regularly
addFailedRegistered(url);
}
}
doRegister(URL)
@Override
public void doRegister(URL url) {
// url: dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=25489&release=2.7.8&side=provider×tamp=1598529167646
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);
}
}
10. CuratorZookeeperClient.create(String path, boolean ephemeral)
public void create(String path, boolean ephemeral) {
// path: /dubbo/org.finalframework.dubbo.service.HelloService/providers/dubbo://10.12.201.2:20880/org.finalframework.dubbo.service.HelloService?anyhost=true&application=final-dubbo-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.finalframework.dubbo.service.HelloService&metadata-type=remote&methods=hello&pid=25489&release=2.7.8&side=provider×tamp=1598529167646
// ephemeral: true
if (!ephemeral) {
if(persistentExistNodePath.contains(path)){
return;
}
if (checkExists(path)) {
persistentExistNodePath.add(path);
return;
}
}
int i = path.lastIndexOf('/');
if (i > 0) {
create(path.substring(0, i), false);
}
if (ephemeral) {
// 创建临时节点
createEphemeral(path);
} else {
// 创建永久节点
createPersistent(path);
persistentExistNodePath.add(path);
}
}
11. ServiceConfig.exported()
exported()
方法在服务注册完成后调用,用于分发ServiceConfigExportedEvent
事件。
public void exported() {
// dispatch a ServiceConfigExportedEvent since 2.7.4
dispatch(new ServiceConfigExportedEvent(this));
}
在Spring
容器中时,ServiceBean
重写了exported()
方法,并调用了publishExportEvent()
方法进行``事件的发布。
@Override
public void exported() {
super.exported();
// Publish ServiceBeanExportedEvent
publishExportEvent();
}
/**
* @since 2.6.5
*/
private void publishExportEvent() {
ServiceBeanExportedEvent exportEvent = new ServiceBeanExportedEvent(this);
applicationEventPublisher.publishEvent(exportEvent);
}