dubbo源码学习(1)- 服务注册
- SimpleApplicationEventMulticaster
spring 事件广播器,触发监听器的事件
listener.onApplicationEvent(event); - DubboBootstrapApplicationListener extends OnceApplicationContextEventListener
// 监听器进行启动 dubboBootstrap
public class DubboBootstrapApplicationListener extends OnceApplicationContextEventListener implements Ordered {
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
dubboBootstrap.start();
}
}
- DubboBootstrap启动过程进行暴露服务
public class DubboBootstrap{
public DubboBootstrap start() {
if (started.compareAndSet(false, true)) {
ready.set(false);
initialize();
if (logger.isInfoEnabled()) {
logger.info(NAME + " is starting...");
}
// 1. export Dubbo Services
// 开始暴露服务
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.");
}
ExtensionLoader<DubboBootstrapStartStopListener> exts = getExtensionLoader(DubboBootstrapStartStopListener.class);
exts.getSupportedExtensionInstances().forEach(ext -> ext.onStart(this));
}).start();
} else {
ready.set(true);
if (logger.isInfoEnabled()) {
logger.info(NAME + " is ready.");
}
ExtensionLoader<DubboBootstrapStartStopListener> exts = getExtensionLoader(DubboBootstrapStartStopListener.class);
exts.getSupportedExtensionInstances().forEach(ext -> ext.onStart(this));
}
if (logger.isInfoEnabled()) {
logger.info(NAME + " has started.");
}
}
return this;
}
private void exportServices() {
// configManger.getServices() 里面就是所有服务,遍历
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(() -> {
try {
exportService(serviceConfig);
} catch (Throwable t) {
logger.error("export async catch error : " + t.getMessage(), t);
}
});
// 记录暴露服务
asyncExportingFutures.add(future);
} else {
exportService(serviceConfig);
}
});
}
private void exportService(ServiceConfig sc) {
if (exportedServices.containsKey(sc.getServiceName())) {
throw new IllegalStateException("There are multiple ServiceBean instances with the same service name: [" +
sc.getServiceName() + "], instances: [" +
exportedServices.get(sc.getServiceName()).toString() + ", " +
sc.toString() + "]. Only one service can be exported for the same triple (group, interface, version), " +
"please modify the group or version if you really need to export multiple services of the same interface.");
}
// 调用服务
sc.export();
exportedServices.put(sc.getServiceName(), sc);
}
}
configManager 存放的信息
- 暴露服务
4.1 服务暴露过程 由serviceConfig 交由 protocol执行后续流程
服务仓库类,存放services、consumers、providers
public class ServiceRepository extends LifecycleAdapter implements FrameworkExt {
public static final String NAME = "repository";
// services
private ConcurrentMap<String, ServiceDescriptor> services = new ConcurrentHashMap<>();
// consumers
private ConcurrentMap<String, ConsumerModel> consumers = new ConcurrentHashMap<>();
// providers
private ConcurrentMap<String, ProviderModel> providers = new ConcurrentHashMap<>();
// useful to find a provider model quickly with serviceInterfaceName:version
private ConcurrentMap<String, ProviderModel> providersWithoutGroup = new ConcurrentHashMap<>();
}
类ServiceBean 继承了 ServiceConfig
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
ApplicationContextAware, BeanNameAware, ApplicationEventPublisherAware {
private static final long serialVersionUID = 213195494150089726L;
private final transient Service service;
private transient ApplicationContext applicationContext;
private transient String beanName;
// 用于触发事件
private ApplicationEventPublisher applicationEventPublisher;
/**
* 暴露服务
* @since 2.6.5
*/
@Override
public void exported() {
super.exported();
// Publish ServiceBeanExportedEvent
publishExportEvent();
}
/**
* @since 2.6.5
*/
private void publishExportEvent() {
ServiceBeanExportedEvent exportEvent = new ServiceBeanExportedEvent(this);
applicationEventPublisher.publishEvent(exportEvent);
}
}
很多注册流程在ServiceConfig中
public class ServiceConfig<T> extends ServiceConfigBase<T> {
@Override
public synchronized void export() {
if (bootstrap == null) {
bootstrap = DubboBootstrap.getInstance();
// compatible with api call.
if (null != this.getRegistry()) {
bootstrap.registries(this.getRegistries());
}
bootstrap.initialize();
}
checkAndUpdateSubConfigs();
initServiceMetadata(provider);
serviceMetadata.setServiceType(getInterfaceClass());
serviceMetadata.setTarget(getRef());
serviceMetadata.generateServiceKey();
if (!shouldExport()) {
return;
}
// 延迟暴露
if (shouldDelay()) {
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
// 去暴露服务
doExport();
}
//暴露之后的事情
exported();
}
// 暴露服务
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;
}
//
doExportUrls();
//
bootstrap.setReady(true);
}
private void doExportUrls() {
// 获得 服务仓库
ServiceRepository repository = ApplicationModel.getServiceRepository();
// 反射获得服务的接口类信息,并注册到服务仓库服务仓库的services中
ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
// 组合服务所有信息,添加到到服务仓库中provider中
repository.registerProvider(
getUniqueServiceName(),
ref,
serviceDescriptor,
this,
serviceMetadata
);
// 通过注册中心配置,得到注册中心的注册请求地址,支持多注册中心,转换为registry协议
// nacos注册地址
//registry://192.168.31.113:8848/org.apache.dubbo.registry.RegistryService?application=yifei-system-service&dubbo=2.0.2&id=org.apache.dubbo.config.RegistryConfig#0&namespace=lzzdubbo&pid=11036&qos.enable=false®istry=nacos&release=2.7.12×tamp=1643380219846
List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
int protocolConfigNum = protocols.size();
for (ProtocolConfig protocolConfig : protocols) {
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.
// 带上 group version 等信息注册进去
repository.registerService(pathKey, interfaceClass);
// 发布服务
doExportUrlsFor1Protocol(protocolConfig, registryURLs, protocolConfigNum);
}
}
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs, int protocolConfigNum) {
String name = protocolConfig.getName();
if (StringUtils.isEmpty(name)) {
name = DUBBO;
}
//组织一些服务接口的元数据信息
Map<String, String> map = new HashMap<String, String>();
// ....中间很多对map进行属性填充
//init serviceMetadata attachments
serviceMetadata.getAttachments().putAll(map);
// export service 服务地址、端口,组装调用的url
String host = findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = findConfigedPorts(protocolConfig, name, map, protocolConfigNum);
// dubbo://192.168.31.15:21881/com.example.dubbo.UserDubboService?
// anyhost=true&application=yifei-system-service&bind.ip=192.168.31.15&bind.port=21881&deprecated=false
// &dubbo=2.0.2&dubbo.tag=tag1&dynamic=true&generic=false&
// interface=com.example.dubbo.UserDubboService&metadata-type=remote&
// methods=getUserName&pid=11036&qos.enable=false&release=2.7.12
// &revision=1.0.0&service.name=ServiceBean:/com.example.dubbo.UserDubboService:1.0.0
// &side=provider×tamp=1643380626633&version=1.0.0
URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);
// You can customize Configurator to append extra parameters
// 是否有扩展点
if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.hasExtension(url.getProtocol())) {
url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
.getExtension(url.getProtocol()).getConfigurator(url).configure(url);
}
// 设置为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)
// 只限本地 调用的
//injvm://127.0.0.1/com.example.dubbo.UserDubboService?anyhost=true&application=yifei-system-service&bind.ip=192.168.31.15&bind.port=21881&deprecated=false&dubbo=2.0.2&dubbo.tag=tag1&dynamic=true&generic=false&interface=com.example.dubbo.UserDubboService&metadata-type=remote&methods=getUserName&pid=11036&qos.enable=false&release=2.7.12&revision=1.0.0&service.name=ServiceBean:/com.example.dubbo.UserDubboService:1.0.0&side=provider×tamp=1643380626633&version=1.0.0
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) {
if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) {
url = url.addParameterIfAbsent(REGISTRY_TYPE_KEY, SERVICE_REGISTRY_TYPE);
}
//if protocol is only injvm ,not register
if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
continue;
}
url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));
// 生成监控地址
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, registryURL);
if (monitorUrl != null) {
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);
}
// private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
// javasisitProxyFactory
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass,
registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
// 构建注册代理
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 调用注册方法进行 服务注册 使用了责任链QosProtocolWrapper->ProtocolFilterWrapper ->ProtocolListenerWrapper->
//InterfaceCompatibleRegistryProtocol->
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
exporters.add(exporter);
}
} 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);
}
MetadataUtils.publishServiceDefinition(url);
}
}
this.urls.add(url);
}
}
4.2 核心注册代码
4.2.1 生成 服务实现类的代理类,用于被调用时的执行
// 生成interfaceClass代理类,proxy 的是对应的实现类
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass,
registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
// 再包装一层 属性,包含代理类和元数据信息
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 使用protocol 进行暴露
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
exporters.add(exporter);
- PROXY_FACTORY 代理工厂
将生成导出服务代理的实现,
JavassistProxy工厂是它的默认实现,
支持自适应扩展点
/** 将生成导出服务代理的实现,
* JavassistProxy工厂是它的默认实现
* A {@link ProxyFactory} implementation that will generate a exported service proxy,the JavassistProxyFactory is its
* default implementation
*/
private static final ProxyFactory PROXY_FACTORY = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
- JavassistProxyFactory 进行生成代理类
public class JavassistProxyFactory extends AbstractProxyFactory {
@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);
}
};
}
}
代理类的属性包含
4.2.2 protocol进行执行注册流程
Exporter<?> exporter = PROTOCOL.export(wrapperInvoker);
PROTOCOL 如下, 也是自适应扩展点,默认是dubboProtocol
private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
注册责任链
最后的InterfaceCompatibleRegistryProtocol 是继承于 RegistryProtocol,接着就调用 RegistryProtocol.export(final Invoker originInvoker)
注册协议类,进行向注册中心进行注册服务
public class RegistryProtocol implements Protocol {
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
URL registryUrl = getRegistryUrl(originInvoker);
// url to export locally
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.
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
// export invoker
// 根据 provider 生成 dubbo调用器,
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
// url to registry
final Registry registry = getRegistry(originInvoker);
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
// decide if we need to delay publish
boolean register = providerUrl.getParameter(REGISTER_KEY, true);
if (register) {
// todo 进行 注册,本例子 使用nacosRegistry
// registry 是一个 ListenerRegistryWrapper, 里面包了一个nacosRegistry ,调用register ,调用doRegister(是一个模板方法,
// nacosRegistry 进行了实现,内部又使用nacos-client端进行了 注册过程)
registry.register(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);
}
@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<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
// 返回一个更新 用的代理
return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
});
}
}
调用nacos-client中的注册服务namingService,进行服务注册
public class NacosRegistry extends FailbackRegistry {
@Override
public void doRegister(URL url) {
final String serviceName = getServiceName(url);
final Instance instance = createInstance(url);
/**
* namingService.registerInstance with {@link org.apache.dubbo.registry.support.AbstractRegistry#registryUrl}
* default {@link DEFAULT_GROUP}
*
* in https://github.com/apache/dubbo/issues/5978
*/
// 使用nacos-client 向nacos-server注册服务
execute(namingService -> namingService.registerInstance(serviceName,
getUrl().getParameter(GROUP_KEY, Constants.DEFAULT_GROUP), instance));
}
}
其他注册器
扩展问题
- 如何设置异步暴露
我这边是通过启动的时候提前获取DubboBootstrap实例,并设置好属性 ,这样监听器触发 时这个实例的exportAsync就是true
@SpringBootApplication
@EnableDubbo
public class Dubbo2ProviderApplication {
public static void main(String[] args) {
DubboBootstrap dubboBootstrap = DubboBootstrap.getInstance();
dubboBootstrap.exportAsync();
SpringApplication.run(Dubbo2ProviderApplication.class, args);
}
}