Dubbo-服务提供者启动流程
参考:https://blog.csdn.net/prestigeding/article/details/80536385
设计图

ServiceBean类结构图

InitializingBean:其声明的接口为afterPropertiesSet方法,顾名思义,就是在bean初始化所有属性之后调用。 DisposableBean:其声明的接口为destroy()方法,在Spring BeanFactory销毁一个单例实例之前调用。 ApplicationContextAware:其声明的接口为void setApplicationContext(ApplicationContext applicationContext),实现了该接口,Spring容器在初始化Bean时会调用该方法,注入ApplicationContext:已方便该实例可以直接调用applicationContext获取其他Bean。 ApplicationListener< ContextRefreshedEvent>:容器重新刷新时执行事件函数。 BeanNameAware:其声明的接口为:void setBeanName(String name),实现该接口的Bean,其实例可以获取该实例在BeanFactory的id或name。 FactoryBean:Spring初始化Bean的另外一种方式,例如dubbo:reference,需要返回的对象并不是ReferenceBean,而是要返回ref指定的代理类来执行业务操作,故这里使用FactoryBean非常合适,FactoryBean定义了如下三个方法: T getObject() throws Exception:获取需要返回的结果对象。 Class<?> getObjectType():获取返回对象的类型。 boolean isSingleton():返回是否是单例。
Spring事件监听
DubboBootstrapApplicationListener接收Spring容器刷新完成事件

public class DubboBootstrapApplicationListener extends OneTimeExecutionApplicationContextEventListener
implements Ordered {
/**
* The bean name of {@link DubboBootstrapApplicationListener}
*
* @since 2.7.6
*/
public static final String BEAN_NAME = "dubboBootstrapApplicationListener";
private final DubboBootstrap dubboBootstrap;
public DubboBootstrapApplicationListener() {
this.dubboBootstrap = DubboBootstrap.getInstance();
}
@Override
public void onApplicationContextEvent(ApplicationContextEvent event) {
if (event instanceof ContextRefreshedEvent) {
onContextRefreshedEvent((ContextRefreshedEvent) event);
} else if (event instanceof ContextClosedEvent) {
onContextClosedEvent((ContextClosedEvent) event);
}
}
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
dubboBootstrap.start();
}
private void onContextClosedEvent(ContextClosedEvent event) {
dubboBootstrap.stop();
}
@Override
public int getOrder() {
return LOWEST_PRECEDENCE;
}
}
DubboBootstrap#start
/**
* Start the bootstrap
*/
public DubboBootstrap start() {
if (started.compareAndSet(false, true)) {
ready.set(false);
// 读取配置信息
initialize();
if (logger.isInfoEnabled()) {
logger.info(NAME + " is starting...");
}
// 1. export Dubbo Services
// 发布Dubbo服务
exportServices();
// Not only provider register
if (!isOnlyRegisterProvider() || hasExportedServices()) {
// 2. export MetadataService
exportMetadataService();
//3. Register the local ServiceInstance if required
registerServiceInstance();
}
referServices();
if (asyncExportingFutures.size() > 0) {
new Thread(() -> {
try {
this.awaitFinish();
} catch (Exception e) {
logger.warn(NAME + " exportAsync occurred an exception.");
}
ready.set(true);
if (logger.isInfoEnabled()) {
logger.info(NAME + " is ready.");
}
}).start();
} else {
ready.set(true);
if (logger.isInfoEnabled()) {
logger.info(NAME + " is ready.");
}
}
if (logger.isInfoEnabled()) {
logger.info(NAME + " has started.");
}
}
return this;
}
private void exportServices() {
configManager.getServices().forEach(sc -> {
// TODO, compatible with ServiceConfig.export()
ServiceConfig serviceConfig = (ServiceConfig) sc;
serviceConfig.setBootstrap(this);
// 异步发布
if (exportAsync) {
ExecutorService executor = executorRepository.getServiceExporterExecutor();
Future<?> future = executor.submit(() -> {
sc.export();
exportedServices.add(sc);
});
asyncExportingFutures.add(future);
}
// 同步发布
else {
sc.export();
exportedServices.add(sc);
}
});
}
ServiceConfig#export
public synchronized void export() {
// 是否需要发布,由<dubbo:service export="true|false" />来指定
// 未指定由<dubbo:provider export="true|false" />来指定
if (!shouldExport()) {
return;
}
if (bootstrap == null) {
bootstrap = DubboBootstrap.getInstance();
bootstrap.initialize();
}
// 检查和更新子配置
checkAndUpdateSubConfigs();
//init serviceMetadata
serviceMetadata.setVersion(getVersion());
serviceMetadata.setGroup(getGroup());
serviceMetadata.setDefaultGroup(getGroup());
serviceMetadata.setServiceType(getInterfaceClass());
serviceMetadata.setServiceInterfaceName(getInterface());
serviceMetadata.setTarget(getRef());
// 延迟发布
if (shouldDelay()) {
// 创建并执行在给定延迟后启用的一次性操作
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
}
// 及时发布
else {
doExport();
}
exported();
}
ServiceConfig#checkAndUpdateSubConfigs
private void checkAndUpdateSubConfigs() {
// 如果为<dubbo:service />|<dubbo:reference />设置了全局配置,
// 如<dubbo:provider />|<dubbo:consumer />,则缺省为全局配置
completeCompoundConfigs();
// 没有配置<dubbo:provider />,设置默认ProviderConfig对象
checkDefault();
// 没有配置<dubbo:protocol />,设置默认protocol,或者将protocolIds转化为ProtocolConfig对象
checkProtocol();
// SPI方式初始化一些配置(ReferenceConfig|ServiceConfig)
List<ConfigInitializer> configInitializers = ExtensionLoader.getExtensionLoader(ConfigInitializer.class)
.getActivateExtension(URL.valueOf("configInitializer://"), (String[]) null);
configInitializers.forEach(e -> e.initServiceConfig(this));
// if protocol is not injvm checkRegistry
// 如果protocol不仅仅是本地协议-injvm则设置默认注册配置或者将registryIds转化为RegistryConfig对象
// 校验注册信息的合法性,如address是否为空
if (!isOnlyInJvm()) {
checkRegistry();
}
// 刷新配置,一些未配置的信息使用默认配置
this.refresh();
if (StringUtils.isEmpty(interfaceName)) {
throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
}
// 泛型调用服务
if (ref instanceof GenericService) {
interfaceClass = GenericService.class;
if (StringUtils.isEmpty(generic)) {
generic = Boolean.TRUE.toString();
}
}
// 普通调用服务
else {
try {
interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
.getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
// 校验接口和方法合法性
checkInterfaceAndMethods(interfaceClass, getMethods());
// 校验ref合法性
checkRef();
generic = Boolean.FALSE.toString();
}
if (local != null) {
if ("true".equals(local)) {
local = interfaceName + "Local";
}
Class<?> localClass;
try {
localClass = ClassUtils.forNameWithThreadContextClassLoader(local);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(localClass)) {
throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName);
}
}
// 本地存根,调用远程服务之前进行一些处理,根据处理结果判断是否要进行远程调用
if (stub != null) {
if ("true".equals(stub)) {
stub = interfaceName + "Stub";
}
Class<?> stubClass;
try {
stubClass = ClassUtils.forNameWithThreadContextClassLoader(stub);
} catch (ClassNotFoundException e) {
throw new IllegalStateException(e.getMessage(), e);
}
if (!interfaceClass.isAssignableFrom(stubClass)) {
throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName);
}
}
// 本地存根合法性校验,如是否有有参构造器
checkStubAndLocal(interfaceClass);
// 校验mock合法性,如mock类是否实现了interfaceClass
// false,不调用mock服务。
// true,当服务调用失败时,调用mock服务。
// default,当服务调用失败时,调用mock服务。
// force,强制调用mock服务(不管服务能否调用成功)。
ConfigValidationUtils.checkMock(interfaceClass, this);
// 校验配置参数值合法性
ConfigValidationUtils.validateServiceConfig(this);
// SPI方式配置信息校验成功后的后置处理
postProcessConfig();
}
ServiceConfig#doExportUrls
private void doExportUrls() {
ServiceRepository repository = ApplicationModel.getServiceRepository();
// 注册服务描述映射接口全类名
ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
// ProviderModel 表示服务提供者模型,此对象中存储了与服务提供者相关的信息。
// 比如服务的配置信息,服务实例等。每个被导出的服务对应一个 ProviderModel。
// ApplicationModel 持有所有的 ProviderModel。
repository.registerProvider(
getUniqueServiceName(),
ref,
serviceDescriptor,
this,
serviceMetadata
);
/**
* 解析获得注册中心URL集合
*
* 1.同类型注册中心
* <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181;192.168.88.128:2181" />
* 解析获得两条数据:
* registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=3060&qos.port=22226®istry=zookeeper×tamp=1610074712005
* registry://192.168.88.128:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=3060&qos.port=22226®istry=zookeeper×tamp=1610074712005
* 2.同类型注册中心
* <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181,192.168.88.128:2181" />
* 解析获得一条数据:
* registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&backup=192.168.88.128:2181&dubbo=2.0.2&pid=10212&qos.port=22226®istry=zookeeper×tamp=1610074806198
* 3.不同类型注册中心
* <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181" />
* <dubbo:registry protocol="redis" address="127.0.0.1:8848" />
* 解析获得两条数据:
* registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=10196&qos.port=22226®istry=zookeeper×tamp=1610074944556
* registry://127.0.0.1:8848/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=10196&qos.port=22226®istry=redis×tamp=1610074944567
*/
List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
for (ProtocolConfig protocolConfig : protocols) {
/**
* 如果设置了contextpath参数,设置方式有两种:
* <dubbo:protocol contextpath="demoProvider" name="dubbo" port="20881" />
* <dubbo:provider contextpath="demoProvider" />
* 未设置contextpath参数,pathKey=demoProviderGroup/org.apache.dubbo.demo.DemoService/v1.0
* 设置了contextpath参数,pathKey=demoProviderGroup/demoProvider/org.apache.dubbo.demo.DemoService/v1.0
*/
String pathKey = URL.buildKey(getContextPath(protocolConfig)
.map(p -> p + "/" + path)
.orElse(path), group, version);
// 如果指定了路径,则再次注册服务描述映射路径,<pathKey, ServiceDescriptor>
repository.registerService(pathKey, interfaceClass);
serviceMetadata.setServiceKey(pathKey);
// 指定协议注册发布服务
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
ConfigValidationUtils#loadRegistries
public static List<URL> loadRegistries(AbstractInterfaceConfig interfaceConfig, boolean provider) {
// check && override if necessary
List<URL> registryList = new ArrayList<URL>();
ApplicationConfig application = interfaceConfig.getApplication();
List<RegistryConfig> registries = interfaceConfig.getRegistries();
if (CollectionUtils.isNotEmpty(registries)) {
for (RegistryConfig config : registries) {
String address = config.getAddress();
if (StringUtils.isEmpty(address)) {
// 若 address 为空,则将其设为 0.0.0.0
address = ANYHOST_VALUE;
}
// 如果address不是N/A,说明服务需要远程注册到注册中心
if (!RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
Map<String, String> map = new HashMap<String, String>();
// 将ApplicationConfig、RegistryConfig中的属性值添加到map中
AbstractConfig.appendParameters(map, application);
AbstractConfig.appendParameters(map, config);
// 添加 path、pid,protocol 等信息到 map 中
map.put(PATH_KEY, RegistryService.class.getName());
AbstractInterfaceConfig.appendRuntimeParameters(map);
if (!map.containsKey(PROTOCOL_KEY)) {
map.put(PROTOCOL_KEY, DUBBO_PROTOCOL);
}
/**
* 1. <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181;192.168.88.128:2181" />
* 解析获得两条数据:
* zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=7220&qos.port=22226×tamp=1610073722877
* zookeeper://192.168.88.128:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=7220&qos.port=22226×tamp=1610073722877
* 2. <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181,192.168.88.128:2181" />
* 解析获得一条数据:
* zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&backup=192.168.88.128:2181&dubbo=2.0.2&pid=3164&qos.port=22226×tamp=1610073611665
*/
List<URL> urls = UrlUtils.parseURLs(address, map);
for (URL url : urls) {
url = URLBuilder.from(url)
// 添加®istry=zookeeper参数信息
.addParameter(REGISTRY_KEY, url.getProtocol())
// 将 URL协议头设置为 registry
// zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=3128&qos.port=22226®istry=zookeeper×tamp=1610073611665
// registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=3128&qos.port=22226®istry=zookeeper×tamp=1610073611665
.setProtocol(extractRegistryType(url))
.build();
// 通过判断条件,决定是否添加 url 到 registryList:
// 如果是服务提供者且配置了register=false,则忽略该地址
// 如果是服务消费者且配置了subscribe=false则忽略该地址
if ((provider && url.getParameter(REGISTER_KEY, true))
|| (!provider && url.getParameter(SUBSCRIBE_KEY, true))) {
registryList.add(url);
}
}
}
}
}
return registryList;
}
ServiceConfig#doExportUrlsFor1Protocol
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
String name = protocolConfig.getName();
// 如果协议名为空,或空串,则将协议名变量设置为 dubbo
if (StringUtils.isEmpty(name)) {
name = DUBBO;
}
// 添加 side、版本、时间戳以及进程号等信息到 map 中
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, PROVIDER_SIDE);
// 添加dubbo版本号、timestamp、pid等信息到 map 中
ServiceConfig.appendRuntimeParameters(map);
// 通过反射将MetricsConfig、ApplicationConfig、ModuleConfig、ProviderConfig、
// ProtocolConfig、ServiceConfig中的属性值添加到map中
AbstractConfig.appendParameters(map, getMetrics());
AbstractConfig.appendParameters(map, getApplication());
AbstractConfig.appendParameters(map, getModule());
AbstractConfig.appendParameters(map, provider);
AbstractConfig.appendParameters(map, protocolConfig);
AbstractConfig.appendParameters(map, this);
MetadataReportConfig metadataReportConfig = getMetadataReportConfig();
if (metadataReportConfig != null && metadataReportConfig.isValid()) {
map.putIfAbsent(METADATA_KEY, REMOTE_METADATA_STORAGE_TYPE);
}
// methods 为 MethodConfig 集合,MethodConfig 中存储了 <dubbo:method> 标签的配置信息
if (CollectionUtils.isNotEmpty(getMethods())) {
for (MethodConfig method : getMethods()) {
// 添加 MethodConfig 对象的字段信息到 map 中,键 = 方法名.属性名。
// 比如存储 <dubbo:method name="sayHello" retries="2"> 对应的 MethodConfig,
// 键 = sayHello.retries,map = {"sayHello.retries": 2, "xxx": "yyy"}
AbstractConfig.appendParameters(map, method, method.getName());
String retryKey = method.getName() + ".retry";
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey);
// 检测 MethodConfig retry 是否为 false,若是,则设置重试次数为0
if ("false".equals(retryValue)) {
map.put(method.getName() + ".retries", "0");
}
}
// 获取 ArgumentConfig 列表
List<ArgumentConfig> arguments = method.getArguments();
if (CollectionUtils.isNotEmpty(arguments)) {
for (ArgumentConfig argument : arguments) {
// convert argument type 检测 type 属性是否为空,或者空串(分支1 ⭐️)
if (argument.getType() != null && argument.getType().length() > 0) {
Method[] methods = interfaceClass.getMethods();
// visit all methods
if (methods.length > 0) {
for (int i = 0; i < methods.length; i++) {
String methodName = methods[i].getName();
// target the method, and get its signature
// 比对方法名,查找目标方法
if (methodName.equals(method.getName())) {
Class<?>[] argtypes = methods[i].getParameterTypes();
// one callback in the method
if (argument.getIndex() != -1) {
// 检测 ArgumentConfig 中的 type 属性与方法参数列表
// 中的参数名称是否一致,不一致则抛出异常(分支2 ⭐️)
if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {
// 添加 ArgumentConfig 字段信息到 map 中,
// 键前缀 = 方法名.index,比如:
// map = {"sayHello.3": true}
AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex());
} else {
throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
}
} else { // 分支3 ⭐️
// multiple callbacks in the method
for (int j = 0; j < argtypes.length; j++) {
Class<?> argclazz = argtypes[j];
// 从参数类型列表中查找类型名称为 argument.type 的参数
if (argclazz.getName().equals(argument.getType())) {
AbstractConfig.appendParameters(map, argument, method.getName() + "." + j);
if (argument.getIndex() != -1 && argument.getIndex() != j) {
throw new IllegalArgumentException("Argument config error : the index attribute and type attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
}
}
}
}
}
}
}
// 用户未配置 type 属性,但配置了 index 属性,且 index != -1
} else if (argument.getIndex() != -1) { // 分支4 ⭐️
// 添加 ArgumentConfig 字段信息到 map 中 <dubbo:argument index='0' callback="true" />
AbstractConfig.appendParameters(map, argument, method.getName() + "." + argument.getIndex());
} else {
throw new IllegalArgumentException("Argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
}
}
}
} // end of methods for
}
// 检测 generic 是否为 "true",并根据检测结果向 map 中添加不同的信息
if (ProtocolUtils.isGeneric(generic)) {
map.put(GENERIC_KEY, generic);
map.put(METHODS_KEY, ANY_VALUE);
} else {
String revision = Version.getVersion(interfaceClass, version);
if (revision != null && revision.length() > 0) {
map.put(REVISION_KEY, revision);
}
// 为接口生成包裹类 Wrapper,Wrapper 中包含了接口的详细信息,比如接口方法名数组,字段信息等
String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
// 添加方法名到 map 中,如果包含多个方法名,则用逗号隔开,比如 method = init,destroy
if (methods.length == 0) {
logger.warn("No method found in service interface " + interfaceClass.getName());
map.put(METHODS_KEY, ANY_VALUE);
} else {
// 将逗号作为分隔符连接方法名,并将连接后的字符串放入 map 中
map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
}
// token配置方式有两种:
// <dubbo:service token="true" />
// <dubbo:provider token="true" />
if(ConfigUtils.isEmpty(token) && provider != null) {
token = provider.getToken();
}
// 添加 token 到 map 中
// 消费者提供了与提供者一致的token,才能访问提供者提供的服务
if (!ConfigUtils.isEmpty(token)) {
// 如果是true或者是default则使用UUID
if (ConfigUtils.isDefault(token)) {
map.put(TOKEN_KEY, UUID.randomUUID().toString());
} else {
map.put(TOKEN_KEY, token);
}
}
//init serviceMetadata attachments
serviceMetadata.getAttachments().putAll(map);
// 解析服务提供者的IP地址与端口
String host = findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = findConfigedPorts(protocolConfig, name, map);
// 组装 URL,获取上下文路径
// dubbo://192.168.88.2:20881/demo-provider/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.88.2&bind.port=20881&deprecated=false&dispatcher=iDispatcher&dubbo=2.0.2&dynamic=true&executes=2&generic=false&interface=org.apache.dubbo.demo.DemoService&loadbalance=leastactive&methods=sayHello,sayHelloAsync&module=demo-module&pid=1368&qos.port=22226&release=&sayHello.retries=2&side=provider&threadpool=iThreadPool×tamp=1610093664272
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
// SPI方式自定义ConfiguratorFactory、Configurator对象添加额外的参数
if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.hasExtension(url.getProtocol())) {
// 加载 ConfiguratorFactory,并生成 Configurator 实例,然后通过实例配置 url
url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.getExtension(url.getProtocol()).getConfigurator(url).configure(url);
}
/**
* 一个服务可能既是Provider又是Consumer,因此就存在他自己调用自己服务的情况
* 如果再通过网络去访问,那自然是舍近求远,因此他是有本地暴露服务的这个设计
*
* 本地暴露是暴露在JVM中,不需要网络通信
* 远程暴露是将ip,端口等信息暴露给远程客户端,调用时需要网络通信
*/
String scope = url.getParameter(SCOPE_KEY);
// 如果配置为 none 不暴露服务,不向注册中心注册服务信息
// 不配置scope时,既暴露服务也考虑向注册中心注册服务信息
if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
// 当配置不是 remote 时则进行本地服务暴露 (当配置为 remote 时只进行远程服务暴露)
if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
// 本地(injvm)服务暴露,在本地JVM内存中创建存储Exporter对象
exportLocal(url);
}
// 当配置不是 local 时进行远程服务暴露 (当配置为 local 时只进行本地服务暴露)
if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
// 注册中心地址存在时,向注册中心注册服务信息
if (CollectionUtils.isNotEmpty(registryURLs)) {
for (URL registryURL : registryURLs) {
// 如果配置<dubbo:registry protocol="injvm" address="127.0.0.1:0" />时
// 只暴露本地服务,不向注册中心注册服务信息
if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
continue;
}
// 如果dubbo:service的dynamic属性未配置, 尝试取dubbo:registry的dynamic属性,
// 该属性的作用是否启用动态注册,如果设置为false,服务注册后,其状态显示为disable,
// 需要人工启用,当服务不可用时,也不会自动移除,同样需要人工处理,此属性不要在生产环境上配置。
url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
// 加载监视器链接
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
if (monitorUrl != null) {
// 将监视器链接作为参数添加到 url 中
url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
}
if (logger.isInfoEnabled()) {
if (url.getParameter(REGISTER_KEY, true)) {
logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
} else {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
}
// For providers, this is used to enable custom proxy to generate invoker
String proxy = url.getParameter(PROXY_KEY);
if (StringUtils.isNotEmpty(proxy)) {
registryURL = registryURL.addParameter(PROXY_KEY, proxy);
}
// 为服务提供类(ref)生成 Invoker(dubbo的远程调用实现类)
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
// DelegateProviderMetaDataInvoker 用于持有 Invoker 和 ServiceConfig
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 导出服务,并生成 Exporter
// NettyServer监听IP为bind.ip(192.168.88.2),端口为bind.port(20881)
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
exporters.add(exporter);
}
}
// 当配置<dubbo:registry protocol="zookeeper" address="N/A" />时
// 不存在注册中心地址,仅暴露服务
else {
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
exporters.add(exporter);
}
/**
* @since 2.7.0
* ServiceData Store
*/
WritableMetadataService metadataService = WritableMetadataService.getExtension(url.getParameter(METADATA_KEY, DEFAULT_METADATA_STORAGE_TYPE));
if (metadataService != null) {
metadataService.publishServiceDefinition(url);
}
}
}
this.urls.add(url);
}
RegistryProtocol#export
/**
* 1.调用 doLocalExport 暴露服务
* 2.向注册中心注册服务,为了消费端获取可用服务
* 3.向注册中心进行订阅,为了服务提供者URL发送变化后重新暴露服务
* 4.创建并返回 DestroyableExporter
*/
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
// 获取注册中心 URL,以 zookeeper 注册中心为例:registry:// -> zookeeper://
// zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.88.2%3A20881%2Fdemo-provider%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bind.ip%3D192.168.88.2%26bind.port%3D20881%26deprecated%3Dfalse%26dispatcher%3DiDispatcher%26dubbo%3D2.0.2%26dynamic%3Dtrue%26executes%3D2%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26loadbalance%3Dleastactive%26methods%3DsayHello%2CsayHelloAsync%26module%3Ddemo-module%26monitor%3Ddubbo%253A%252F%252F127.0.0.1%253A2181%252Forg.apache.dubbo.registry.RegistryService%253Fapplication%253Ddemo-provider%2526dubbo%253D2.0.2%2526pid%253D1444%2526protocol%253Dregistry%2526qos.port%253D22226%2526refer%253Dapplication%25253Ddemo-provider%252526dubbo%25253D2.0.2%252526interface%25253Dorg.apache.dubbo.monitor.MonitorService%252526interval%25253D100%252526pid%25253D1444%252526qos.port%25253D22226%252526register.ip%25253D192.168.88.2%252526timestamp%25253D1610333468481%2526registry%253Dzookeeper%2526timestamp%253D1610333468168%26pid%3D1444%26qos.port%3D22226%26release%3D%26sayHello.retries%3D2%26side%3Dprovider%26threadpool%3DiThreadPool%26timestamp%3D1610333468176&pid=1444&qos.port=22226×tamp=1610333468168
URL registryUrl = getRegistryUrl(originInvoker);
// 获取URL的export属性值即服务提供者的URL
// dubbo://192.168.88.2:20881/demo-provider/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.88.2&bind.port=20881&deprecated=false&dispatcher=iDispatcher&dubbo=2.0.2&dynamic=true&executes=2&generic=false&interface=org.apache.dubbo.demo.DemoService&loadbalance=leastactive&methods=sayHello,sayHelloAsync&module=demo-module&monitor=dubbo%3A%2F%2F127.0.0.1%3A2181%2Forg.apache.dubbo.registry.RegistryService%3Fapplication%3Ddemo-provider%26dubbo%3D2.0.2%26pid%3D1444%26protocol%3Dregistry%26qos.port%3D22226%26refer%3Dapplication%253Ddemo-provider%2526dubbo%253D2.0.2%2526interface%253Dorg.apache.dubbo.monitor.MonitorService%2526interval%253D100%2526pid%253D1444%2526qos.port%253D22226%2526register.ip%253D192.168.88.2%2526timestamp%253D1610333468481%26registry%3Dzookeeper%26timestamp%3D1610333468168&pid=1444&qos.port=22226&release=&sayHello.retries=2&side=provider&threadpool=iThreadPool×tamp=1610333468176
URL providerUrl = getProviderUrl(originInvoker);
// 获取订阅 URL,服务提供者向注册中心订阅自己,主要是为了服务提供者URL发送变化后重新暴露服务,当然会将dubbo:reference的check属性设置为false
// provider://192.168.88.2:20881/demo-provider/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.88.2&bind.port=20881&category=configurators&check=false&deprecated=false&dispatcher=iDispatcher&dubbo=2.0.2&dynamic=true&executes=2&generic=false&interface=org.apache.dubbo.demo.DemoService&loadbalance=leastactive&methods=sayHello,sayHelloAsync&module=demo-module&monitor=dubbo%3A%2F%2F127.0.0.1%3A2181%2Forg.apache.dubbo.registry.RegistryService%3Fapplication%3Ddemo-provider%26dubbo%3D2.0.2%26pid%3D1444%26protocol%3Dregistry%26qos.port%3D22226%26refer%3Dapplication%253Ddemo-provider%2526dubbo%253D2.0.2%2526interface%253Dorg.apache.dubbo.monitor.MonitorService%2526interval%253D100%2526pid%253D1444%2526qos.port%253D22226%2526register.ip%253D192.168.88.2%2526timestamp%253D1610333468481%26registry%3Dzookeeper%26timestamp%3D1610333468168&pid=1444&qos.port=22226&release=&sayHello.retries=2&side=provider&threadpool=iThreadPool×tamp=1610333468176
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
// 创建监听器,当配置中心数据发生变化时进行监听处理
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
// 根据外部配置刷新本地配置(如果外部配置和本地配置信息不一致的话)
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
// 暴露远程服务并加缓存,根据各自协议,服务提供者建立网络服务器在特定端口建立监听,监听来自消息消费端服务的请求
// 以dubbo协议为例,创建NettyServer监听指定IP和端口
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
// 根据注册中心URL,从注册中心工厂中获取指定的注册中心实现类:zookeeper注册中心的实现类为:ZookeeperRegistry
// ZookeeperRegistry的构造器中连接注册中心
final Registry registry = getRegistry(originInvoker);
// 获取服务提供者URL中的register属性
// 如果为true则调用注册中心的ZookeeperRegistry#register方法向注册中心注册服务(实际由其父类FailbackRegistry实现)
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
// 根据 register 属性的值决定是否注册服务,默认为true
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register) {
// 向注册中心注册服务
// 以zookeeper为例将服务信息存储到相应目录中
register(registryUrl, registeredProviderUrl);
}
// register stated url on provider model
registerStatedUrl(registryUrl, registeredProviderUrl, register);
exporter.setRegisterUrl(registeredProviderUrl);
exporter.setSubscribeUrl(overrideSubscribeUrl);
// 服务提供端向注册中心进行订阅,即将弃用,这种方式使用于2.6.x版本或更早版本
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
// 服务注册暴露成功后进行通知
notifyExport(exporter);
// 创建并返回 DestroyableExporter
return new DestroyableExporter<>(exporter);
}
RegistryProtocol#doLocalExport
@SuppressWarnings("unchecked")
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
String key = getCacheKey(originInvoker);
// 访问缓存,不存在则写缓存
return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
// 创建 Invoker 委托类对象
// providerUrl=dubbo://192.168.88.2:20881/demo-provider/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.88.2&bind.port=20881&deprecated=false&dispatcher=iDispatcher&dubbo=2.0.2&dynamic=true&executes=2&generic=false&interface=org.apache.dubbo.demo.DemoService&loadbalance=leastactive&methods=sayHello,sayHelloAsync&module=demo-module&monitor=dubbo%3A%2F%2F127.0.0.1%3A2181%2Forg.apache.dubbo.registry.RegistryService%3Fapplication%3Ddemo-provider%26dubbo%3D2.0.2%26pid%3D1444%26protocol%3Dregistry%26qos.port%3D22226%26refer%3Dapplication%253Ddemo-provider%2526dubbo%253D2.0.2%2526interface%253Dorg.apache.dubbo.monitor.MonitorService%2526interval%253D100%2526pid%253D1444%2526qos.port%253D22226%2526register.ip%253D192.168.88.2%2526timestamp%253D1610333468481%26registry%3Dzookeeper%26timestamp%3D1610333468168&pid=1444&qos.port=22226&release=&sayHello.retries=2&side=provider&threadpool=iThreadPool×tamp=1610333468176
Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
// 根据Dubbo内置的SPI机制,将调用DubboProtocol#export方法
return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
});
}
DubboProtocol#export
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
// dubbo://192.168.88.2:20881/demo-provider/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.88.2&bind.port=20881&deprecated=false&dispatcher=iDispatcher&dubbo=2.0.2&dynamic=true&executes=2&generic=false&interface=org.apache.dubbo.demo.DemoService&loadbalance=leastactive&methods=sayHello,sayHelloAsync&module=demo-module&monitor=dubbo%3A%2F%2F127.0.0.1%3A2181%2Forg.apache.dubbo.registry.RegistryService%3Fapplication%3Ddemo-provider%26dubbo%3D2.0.2%26pid%3D11080%26protocol%3Dregistry%26qos.port%3D22226%26refer%3Dapplication%253Ddemo-provider%2526dubbo%253D2.0.2%2526interface%253Dorg.apache.dubbo.monitor.MonitorService%2526interval%253D100%2526pid%253D11080%2526qos.port%253D22226%2526register.ip%253D192.168.88.2%2526timestamp%253D1610336987787%26registry%3Dzookeeper%26timestamp%3D1610336987412&pid=11080&qos.port=22226&release=&sayHello.retries=2&side=provider&threadpool=iThreadPool×tamp=1610336987422
URL url = invoker.getUrl();
// 获取服务标识,理解成服务坐标也行。由服务组名,服务名,服务版本号以及端口组成。比如:
// demoGroup/org.apache.dubbo.demo.DemoService:1.0.1:20880
String key = serviceKey(url);
// 创建 DubboExporter
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
// 将 <key, exporter> 键值对放入缓存中
exporterMap.put(key, exporter);
// 本地存根相关校验
Boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);
Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);
if (isStubSupportEvent && !isCallbackservice) {
String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);
if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
if (logger.isWarnEnabled()) {
logger.warn(new IllegalStateException("consumer [" + url.getParameter(INTERFACE_KEY) +
"], has set stubproxy support event ,but no stub methods founded."));
}
}
}
// 启动服务器
openServer(url);
// 优化序列化
optimizeSerialization(url);
return exporter;
}
DubboProtocol#openServer
private void openServer(URL url) {
// 获取 host:port,并将其作为服务器实例的 key,用于标识当前的服务器实例
String key = url.getAddress();
// 服务消费端可以暴露仅供服务提供端调用的服务
boolean isServer = url.getParameter(IS_SERVER_KEY, true);
if (isServer) {
ProtocolServer server = serverMap.get(key);
if (server == null) {
synchronized (this) {
server = serverMap.get(key);
if (server == null) {
// 创建服务器实例
serverMap.put(key, createServer(url));
}
}
} else {
// 如果服务器已经存在,用当前URL重置服务器
// 因为一个dubbo应用服务中会有多个<dubbo:service />标签
// 这些标签都会在服务提供者的同一个IP地址、端口号上暴露服务
server.reset(url);
}
}
}
DubboProtocol#createServer
/**
* 1.检测是否存在 server 参数所代表的 Transporter 拓展
* 2.创建服务器实例
* 3.检测是否支持 client 参数所表示的 Transporter 拓展,不存在也是抛出异常
*/
private ProtocolServer createServer(URL url) {
url = URLBuilder.from(url)
// 为服务提供者url增加channel.readonly.sent属性默认为true,表示在发送请求时是否等待将字节写入socket后再返回
.addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
// 为服务提供者url增加heartbeat属性,表示心跳间隔时间,默认为60*1000,表示60s
.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
// 为服务提供者url增加codec属性,默认值为dubbo,协议编码方式
.addParameter(CODEC_KEY, DubboCodec.NAME)
.build();
// 获取 server 属性值默认为 netty
String str = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER);
// 通过 SPI 检测是否存在 server 参数所代表的 Transporter 实现,不存在则抛出异常
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
throw new RpcException("Unsupported server type: " + str + ", url: " + url);
}
ExchangeServer server;
try {
// 根据服务提供者URL,ExchangeHandler构建ExchangeServer实例
server = Exchangers.bind(url, requestHandler);
} catch (RemotingException e) {
throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
}
// 获取 client 参数,可指定 netty,mina
str = url.getParameter(CLIENT_KEY);
if (str != null && str.length() > 0) {
// 获取所有的 Transporter 实现类名称集合,比如 supportedTypes = [netty, mina]
Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
// 检测当前 Dubbo 所支持的 Transporter 实现类名称列表中,
// 是否包含 client 所表示的 Transporter,若不包含,则抛出异常
if (!supportedTypes.contains(str)) {
throw new RpcException("Unsupported client type: " + str);
}
}
return new DubboProtocolServer(server);
}
Exchangers#bind
public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handler == null) {
throw new IllegalArgumentException("handler == null");
}
url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
// 获取 Exchanger,默认为 HeaderExchanger
// 紧接着调用 HeaderExchanger 的 bind 方法创建 ExchangeServer 实例
return getExchanger(url).bind(url, handler);
}
HeaderExchanger#bind
@Override
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
// 创建 HeaderExchangeServer 实例,该方法包含了多个逻辑,分别如下:
// 1. new HeaderExchangeHandler(handler)
// 2. new DecodeHandler(new HeaderExchangeHandler(handler))
// 3. Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler)))
// 4. new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))))
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
Transporters#bind
public static RemotingServer bind(URL url, ChannelHandler... handlers) throws RemotingException {
if (url == null) {
throw new IllegalArgumentException("url == null");
}
if (handlers == null || handlers.length == 0) {
throw new IllegalArgumentException("handlers == null");
}
ChannelHandler handler;
if (handlers.length == 1) {
handler = handlers[0];
} else {
// 如果 handlers 元素数量大于1,则创建 ChannelHandler 分发器
handler = new ChannelHandlerDispatcher(handlers);
}
// 获取 Transporter 实例,并调用bind方法
return getTransporter().bind(url, handler);
}
Transporter继承结构图

以NettyTransporter为例,NettyTransporter#bind
@Override
public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException {
// 创建 NettyServer
return new NettyServer(url, handler);
}
NettyServer#doOpen
@Override
protected void doOpen() throws Throwable {
NettyHelper.setNettyLoggerFactory();
// 创建 boss 和 worker 线程池
ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
// 创建 ServerBootstrap
bootstrap = new ServerBootstrap(channelFactory);
final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
channels = nettyHandler.getChannels();
// https://issues.jboss.org/browse/NETTY-365
// https://issues.jboss.org/browse/NETTY-379
// final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true));
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("backlog", getUrl().getPositiveParameter(BACKLOG_KEY, Constants.DEFAULT_BACKLOG));
// 设置 PipelineFactory
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() {
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
ChannelPipeline pipeline = Channels.pipeline();
/*int idleTimeout = getIdleTimeout();
if (idleTimeout > 10000) {
pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));
}*/
pipeline.addLast("decoder", adapter.getDecoder());
pipeline.addLast("encoder", adapter.getEncoder());
pipeline.addLast("handler", nettyHandler);
return pipeline;
}
});
// bind 绑定到指定的 ip 和端口上
channel = bootstrap.bind(getBindAddress());
}
从DubboProtocol#openServer中可以看出,解析第一个<dubbo:service/>标签时会调用DubboProtocol#createServer方法,在解析后续的<dubbo:service/>标签时并不会调用DubboProtocol#createServer方法,而是会调用DubboProtocolServer#reset方法即调用NettyServer#reset方法
AbstractServer#reset
@Override
public void reset(URL url) {
if (url == null) {
return;
}
try {
if (url.hasParameter(ACCEPTS_KEY)) {
int a = url.getParameter(ACCEPTS_KEY, 0);
if (a > 0) {
this.accepts = a;
}
}
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
try {
if (url.hasParameter(IDLE_TIMEOUT_KEY)) {
int t = url.getParameter(IDLE_TIMEOUT_KEY, 0);
if (t > 0) {
this.idleTimeout = t;
}
}
} catch (Throwable t) {
logger.error(t.getMessage(), t);
}
// 调整线程池的相关线程数量?
// 多个<dubbo:service />标签对应一个executor,线程池core数量和max数量怎么确定?
executorRepository.updateThreadpool(url, executor);
// 覆盖原先NettyServer的volatile URL url的属性,NettyHandler上加了注解@Sharable?
super.setUrl(getUrl().addParameters(url.getParameters()));
}

浙公网安备 33010602011771号