前言
Dubbo的服务发布与注册原理是Dubbo框架的核心功能之一,它实现了微服务架构下的服务注册与发现机制。以下是对Dubbo服务发布与注册原理的详细解析
本文是基于 dubbo-3.1.0 版本进行分析
一、服务监听ContextRefreshedEvent
在springboot 中,refresh 是 AbstractApplicationContext 中的一个方法,负责初始化 ApplicationContext 容器,容器必须调用 refresh 才能正常工作是进行,refresh的最后处理了 finishRefresh 方法,改方法会广播一个ContextRefreshedEvent容器刷新完成事件,所有监听了该事件的bean都会去执行相关逻辑处理。
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
// 省略无关代码***
// 初始化生命周期处理器,调用生命周期处理器onRefresh方法,发布ContextRefreshedEvent事件,JMX相关处理
this.finishRefresh();
// 省略无关代码***
}
}
protected void finishRefresh() {
// 清除上下文资源缓存(如扫描中的ASM元数据) scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// 广播刷新完成事件
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
if (!NativeDetector.inNativeImage()) {
LiveBeansView.registerApplicationContext(this);
}
}
dubbo很好的结合了spring的这一个拓展点,在这个拓展点开始实现服务的发布。可以看到,DubboDeployApplicationListener实现了ContextRefreshedEvent的消息监听
public class DubboDeployApplicationListener implements ApplicationListener<ApplicationContextEvent>, ApplicationContextAware, Ordered {
private static final Logger logger = LoggerFactory.getLogger(DubboDeployApplicationListener.class);
private ApplicationContext applicationContext;
private ApplicationModel applicationModel;
private ModuleModel moduleModel;
@Override
public void onApplicationEvent(ApplicationContextEvent event) {
if (nullSafeEquals(applicationContext, event.getSource())) {
if (event instanceof ContextRefreshedEvent) {
onContextRefreshedEvent((ContextRefreshedEvent) event);
} else if (event instanceof ContextClosedEvent) {
onContextClosedEvent((ContextClosedEvent) event);
}
}
}
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
// 获取配置的deployer 进行发布,默认是 DefaultModuleDeployer
ModuleDeployer deployer = moduleModel.getDeployer();
Assert.notNull(deployer, "Module deployer is null");
// start module
Future future = deployer.start();
// if the module does not start in background, await finish
if (!deployer.isBackground()) {
try {
// 等待发布结束
future.get();
} catch (InterruptedException e) {
logger.warn("Interrupted while waiting for dubbo module start: " + e.getMessage());
} catch (Exception e) {
logger.warn("An error occurred while waiting for dubbo module start: " + e.getMessage(), e);
}
}
}
}
DefaultModuleDeployer 中,真正核心的是ServiceConfig,ServiceConfig才是去实际发布的动作
public class DefaultModuleDeployer extends AbstractDeployer<ModuleModel> implements ModuleDeployer {
// DefaultApplicationDeployer
private ApplicationDeployer applicationDeployer;
@Override
public synchronized Future start() throws IllegalStateException {
...
// 不管是 DefaultApplicationDeployer 还是DefaultModuleDeployer的initialize方法,都是处理相关配置文件
// 其功能等价于监听器 DubboConfigApplicationListener
applicationDeployer.initialize();
initialize();
// 真正触发 服务注册功能
exportServices();
...
return startFuture;
}
private void exportServices() {
// 从ModuleConfigManager遍历每一个ServiceBean,即dubbo.service
for (ServiceConfigBase sc : configManager.getServices()) {
exportServiceInternal(sc);
}
}
private void exportServiceInternal(ServiceConfigBase sc) {
ServiceConfig<?> serviceConfig = (ServiceConfig<?>) sc;
if (!serviceConfig.isRefreshed()) {
serviceConfig.refresh();
}
if (sc.isExported()) {
return;
}
// 异步发布 默认同步
if (exportAsync || sc.shouldExportAsync()) {
ExecutorService executor = executorRepository.getServiceExportExecutor();
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
if (!sc.isExported()) {
sc.export();
exportedServices.add(sc);
}
} catch (Throwable t) {
logger.error(getIdentifier() + " export async catch error : " + t.getMessage(), t);
}
}, executor);
asyncExportingFutures.add(future);
} else {
if (!sc.isExported()) {
// 开始发布
sc.export();
exportedServices.add(sc);
}
}
}
}
二、服务发布ServiceConfig
1、时序图
ServiceConfig,结合暴露服务时序,我们发现ServiceConfig开始,进行服务的暴露。
2、serviceConfig.export()
synchronized 双重检查锁,保证服务只发布一次
public class ServiceConfig<T> extends ServiceConfigBase<T> {
@Override
public void export() {
// 如果已经发布过
if (this.exported) {
return;
}
// ensure start module, compatible with old api usage
getScopeModel().getDeployer().start();
// 防止并发
synchronized (this) {
if (this.exported) {
return;
}
if (!this.isRefreshed()) {
this.refresh();
}
if (this.shouldExport()) {
this.init();
if (shouldDelay()) {
// 延迟发布
doDelayExport();
} else {
// 开始进行发布
doExport();
}
}
}
}
protected synchronized void doExport() {
if (unexported) {
throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
}
if (exported) {
return;
}
if (StringUtils.isEmpty(path)) {
path = interfaceName;
}
// 执行服务发布逻辑
doExportUrls();
// 服务标志位,保证只发布一次
exported();
}
}
3、serviceConfig.doExportUrls()
在 serviceConfig.doExportUrls() 中主要做了两件事:
- 获取所有注册中心URL
- 遍历所有 protocols进行注册发布
public class ServiceConfig<T> extends ServiceConfigBase<T> {
protected List<ProtocolConfig> protocols;
private void doExportUrls() {
ModuleServiceRepository repository = getScopeModel().getServiceRepository();
ServiceDescriptor serviceDescriptor;
final boolean serverService = ref instanceof ServerService;
if (serverService) {
serviceDescriptor = ((ServerService) ref).getServiceDescriptor();
repository.registerService(serviceDescriptor);
} else {
serviceDescriptor = repository.registerService(getInterfaceClass());
}
// ProviderModel 表示服务提供者模型,此对象中存储了与服务提供者相关的信息
// 比如服务的配置信息,服务实例等。每个被导出的服务对应一个 ProviderModel
providerModel = new ProviderModel(getUniqueServiceName(),
ref,
serviceDescriptor,
getScopeModel(),
serviceMetadata, interfaceClassLoader);
// Compatible with dependencies on ServiceModel#getServiceConfig(), and will be removed in a future version
providerModel.setConfig(this);
providerModel.setDestroyCaller(getDestroyRunner());
repository.registerProvider(providerModel);
// 获取注册中心地址
List<URL> registryURLs = ConfigValidationUtils.loadRegistries(this, true);
// 遍历所有的协议
for (ProtocolConfig protocolConfig : protocols) {
String pathKey = URL.buildKey(getContextPath(protocolConfig)
.map(p -> p + "/" + path)
.orElse(path), group, version);
// stub service will use generated service name
if (!serverService) {
// In case user specified path, register service one more time to map it to path.
repository.registerService(pathKey, interfaceClass);
}
// 真正的暴露,通过协义和注册中心去完成暴露
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
providerModel.setServiceUrls(urls);
}
}
registryURLs地址如下所示,注册模式为zookeeper
registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=dubbo-springboot-start-provider
&dubbo=2.0.2&pid=5536&qos.enable=false®istry=zookeeper&release=3.1.0×tamp=1724516467041
4、serviceConfig.doExportUrlsFor1Protocol()
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
// 构建配置参数
Map<String, String> map = buildAttributes(protocolConfig);
// remove null key and null value
map.keySet().removeIf(key -> key == null || map.get(key) == null);
// init serviceMetadata attachments
serviceMetadata.getAttachments().putAll(map);
// 构建url 默认使用dubbo协议 host port
URL url = buildUrl(protocolConfig, map);
// 暴露相应的url
exportUrl(url, registryURLs);
}
构建好的url如下所示
dubbo://192.168.0.101:20880/org.sjl.dubbo.AsyncProvider?
anyhost=true
&application=dubbo-springboot-start-provider
&background=false
&bind.ip=192.168.0.101
&bind.port=20880
&deprecated=false
&dubbo=2.0.2
&dynamic=true
&generic=false
&interface=org.sjl.dubbo.AsyncProvider
&methods=sayHiAsync,sayHello,sayHelloAsync
&pid=22524
&qos.enable=false
&release=3.1.0
&side=provider
&timeout=30000
×tamp=1724516860685
4、serviceConfig.exportUrl()
private void exportUrl(URL url, List<URL> registryURLs) {
// 获取暴露范围
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)) {
url = exportRemote(url, registryURLs);
if (!isGeneric(generic) && !getScopeModel().isInternal()) {
MetadataUtils.publishServiceDefinition(url, providerModel.getServiceModel(), getApplicationModel());
}
}
}
this.urls.add(url);
}
获取export的范围,这个是通过dubbo.provider.scope来配置的,可选项有remote和local,表示是否需要发布远程服务。默认情况下既会暴露本地服务也会暴露远程服务。
5、serviceConfig.exportRemote()
private URL exportRemote(URL url, List<URL> registryURLs) {
if (CollectionUtils.isNotEmpty(registryURLs)) {
for (URL registryURL : registryURLs) {
if (SERVICE_REGISTRY_PROTOCOL.equals(registryURL.getProtocol())) {
url = url.addParameterIfAbsent(SERVICE_NAME_MAPPING_KEY, "true");
}
//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.putAttribute(MONITOR_KEY, monitorUrl);
}
// 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);
}
if (logger.isInfoEnabled()) {
if (url.getParameter(REGISTER_KEY, true)) {
logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL.getAddress());
} else {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
}
// 将url封装到registryURL 中进行传输
doExportUrl(registryURL.putAttribute(EXPORT_KEY, url), true);
}
} else {
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
// 没有registryURL ,则直接将url进行传输
doExportUrl(url, true);
}
return url;
}
6、serviceConfig.doExportUrl()
protocolSPI = this.getExtensionLoader(Protocol.class).getAdaptiveExtension();
@SuppressWarnings({"unchecked", "rawtypes"})
private void doExportUrl(URL url, boolean withMetaData) {
// 包装成invoker,所有执行的本质都是invoker
// 自适应扩展点,默认使用javaasisst 代理
// ref:接口实现 interfaceClass:接口 url:registerUrl
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
if (withMetaData) {
invoker = new DelegateProviderMetaDataInvoker(invoker, this);
}
// spi 机制,自适应扩展点 默认走dubbo协议
Exporter<?> exporter = protocolSPI.export(invoker);
exporters.add(exporter);
}
通过javaasisst生成一个代理类,将要发布的接口、实现类、注册url封装起来,统一代理
三、协议 Protocol
1、protocol spi扩展点
filter=org.apache.dubbo.rpc.cluster.filter.ProtocolFilterWrapper
listener=org.apache.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=org.apache.dubbo.rpc.support.MockProtocol
serializationwrapper=org.apache.dubbo.rpc.protocol.ProtocolSerializationWrapper
dubbo=org.apache.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=org.apache.dubbo.rpc.protocol.injvm.InjvmProtocol
rest=org.apache.dubbo.rpc.protocol.rest.RestProtocol
grpc=org.apache.dubbo.rpc.protocol.grpc.GrpcProtocol
tri=org.apache.dubbo.rpc.protocol.tri.TripleProtocol
registry=org.apache.dubbo.registry.integration.InterfaceCompatibleRegistryProtocol
service-discovery-registry=org.apache.dubbo.registry.integration.RegistryProtocol
qos=org.apache.dubbo.qos.protocol.QosProtocolWrapper
2、Protocol$Adaptive
是一个动态生成的适配器类,它会根据传递进来的参数来选择合适的协议来发布服务。
@SPI("dubbo")
ublic class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
public void destroy() {
throw new UnsupportedOperationException("The method public abstract void org.apache.dubbo.rpc.Protocol.destroy() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public int getDefaultPort() {
throw new UnsupportedOperationException("The method public abstract int org.apache.dubbo.rpc.Protocol.getDefaultPort() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
public org.apache.dubbo.rpc.Exporter export(org.apache.dubbo.rpc.Invoker arg0) throws org.apache.dubbo.rpc.RpcException {
if (arg0 == null) throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");
if (arg0.getUrl() == null)
throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");
org.apache.dubbo.common.URL url = arg0.getUrl();
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.export(arg0);
}
public org.apache.dubbo.rpc.Invoker refer(java.lang.Class arg0, org.apache.dubbo.common.URL arg1) throws org.apache.dubbo.rpc.RpcException {
if (arg1 == null) throw new IllegalArgumentException("url == null");
org.apache.dubbo.common.URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if (extName == null)
throw new IllegalStateException("Failed to get extension (org.apache.dubbo.rpc.Protocol) name from url (" + url.toString() + ") use keys([protocol])");
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
public java.util.List getServers() {
throw new UnsupportedOperationException("The method public default java.util.List org.apache.dubbo.rpc.Protocol.getServers() of interface org.apache.dubbo.rpc.Protocol is not adaptive method!");
}
}
根据当前传递过来的Invoker中携带的url为registerUrl,协议头是service-discovery-registry,获得的扩展点就是RegistryProtocol。
在调用RegistryProtocol之前,dubbo通过动态加载Wrapper进行功能的增强
ExtensionLoader.loadClass中
private void loadClass(Map<String, Class<?>> extensionClasses, java.net.URL resourceURL, Class<?> clazz, String name,
boolean overridden) {
// 省略其它代码 ***
if (clazz.isAnnotationPresent(Adaptive.class)) {
cacheAdaptiveClass(clazz, overridden);
// 如果是包装类
} else if (isWrapperClass(clazz)) {
cacheWrapperClass(clazz);
} // 省略其它代码 ***
}
protected boolean isWrapperClass(Class<?> clazz) {
Constructor<?>[] constructors = clazz.getConstructors();
// 判断构造函数
for (Constructor<?> constructor : constructors) {
if (constructor.getParameterTypes().length == 1 && constructor.getParameterTypes()[0] == type) {
return true;
}
}
return false;
}
// 例如ProtocolListenerWrapper就符合
public ProtocolListenerWrapper(Protocol protocol) {
if (protocol == null) {
throw new IllegalArgumentException("protocol == null");
}
this.protocol = protocol;
}
3、ProtocolWrapper
org.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName);
public T getExtension(String name) {
// 省略其它代码 ***
Object instance = holder.get();
if (instance == null) {
synchronized (holder) {
instance = holder.get();
if (instance == null) {
// 构建
instance = createExtension(name);
holder.set(instance);
}
}
}
return (T) instance;
}
private T createExtension(String name) {
// 省略其它代码 ***
// 对cacheWrapperClass集合进行判断,如果集合不为空,则进行包装
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (CollectionUtils.isNotEmpty(wrapperClasses)) {
for (Class<?> wrapperClass : wrapperClasses) {
instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));
}
}
return instance;
// 省略其它代码 ***
}
至此形成的调用链为
ProtocolFilterWrapper -> ProtocolListenerWrapper -> QosProtocolWrapper -> RegistryProtocol
- ProtocolFilterWrapper,对invoker进行filter的包装,实现请求的过滤
- ProtocolListenerWrapper, 用于服务export时候插入监听机制
- QosprotocolWrapper, 如果当前配置了注册中心,则会启动一个Qos server.qos是dubbo的在线运维命令,dubbo2.5.8新版本重构了telnet模块,提供了新的telnet命令支持,新版本的telnet端口与dubbo协议的端口是不同的端口,默认为22222
4、RegistryProtocol.export()
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
// step1:获取注册中心地址 service-discovery-registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?***
URL registryUrl = getRegistryUrl(originInvoker);
// 这里是获得服务提供者的url dubbo://192.168.0.101:20880/org.sjl.dubbo.AsyncProvider?anyhost=true&***
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.
// 订阅override数据。在admin控制台可以针对服务进行治理,比如修改权重,修改路由机制等
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
Map<URL, NotifyListener> overrideListeners = getProviderConfigurationListener(providerUrl).getOverrideListeners();
overrideListeners.put(registryUrl, overrideSubscribeListener);
providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
//export invoker
// 服务发布
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);
// url to registry 获取注册中心
final Registry registry = getRegistry(registryUrl);
final URL registeredProviderUrl = getUrlToRegistry(providerUrl, registryUrl);
// decide if we need to delay publish (provider itself and registry should both need to register)
// 是否延迟发布 提供者本身和注册表都需要注册
boolean register = providerUrl.getParameter(REGISTER_KEY, true) && registryUrl.getParameter(REGISTER_KEY, true);
if (register) {
// 存在注册中心,需要注册
register(registry, registeredProviderUrl);
}
// register stated url on provider model
registerStatedUrl(registryUrl, registeredProviderUrl, register);
exporter.setRegisterUrl(registeredProviderUrl);
exporter.setSubscribeUrl(overrideSubscribeUrl);
if (!registry.isServiceDiscovery()) {
// 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);
}
在RegistryProtocol.export中,有两个核心流程
- 调用 doLocalExport启动本地服务,也就是netty server
- 调用 register方法进行服务地址的注册
5、RegistryProtocol.doLocalExport()
public class RegistryProtocol implements Protocol {
// 启动本地服务
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
// key 为 getProviderUrl
String key = getCacheKey(originInvoker);
// map.computeIfAbsent(key, Function):解决RMI重复暴露端口冲突的问题,已经暴露的服务不再暴露。
return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
// protocol.export(invokerDelegate) 进行进一步的协议相关的服务发布
return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
});
}
}
protocol是通过依赖注入来初始化的一个协议扩展点,并且我们可以看到这个protocol.export()方法上增加了@Adaptive注解,表示它是一个动态适配的扩展点,当前没有指定protocol,走默认的dubbo,形成的调用链为
ProtocolListenerWrapper ->QosProtocolWrapper ->ProtocolFilterWrapper-DubboProtocol
6、DubboProtocol.export
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
checkDestroyed();
URL url = invoker.getUrl();
// export service.
String key = serviceKey(url);
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
//export a stub service for dispatching event
// 是否是存根事件
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 stub proxy support event ,but no stub methods founded."));
}
}
}
// 开启一个服务
openServer(url);
optimizeSerialization(url);
return exporter;
}
6、DubboProtocol.openServer
双重检查锁,一个服务地址 host:port 只允许创建一个服务
private void openServer(URL url) {
checkDestroyed();
// find server.
// 获取 host:port,并将其作为服务器实例的 key,用于标识当前的服务器实例
String key = url.getAddress();
// client can export a service which only for server to invoke
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));
return;
}
}
}
// server supports reset, use together with override
// 重置url相关配置
server.reset(url);
}
}
7、DubboProtocol.createServer
private ProtocolServer createServer(URL url) {
// 组装url,添加心跳时间、编解码参数
url = URLBuilder.from(url)
// send readonly event when server closes, it's enabled by default
.addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
// enable heartbeat by default
.addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
.addParameter(CODEC_KEY, DubboCodec.NAME)
.build();
// 默认走DEFAULT_REMOTING_SERVER : netty
// 通过 SPI 检测是否存在 server 参数所代表的 Transporter 拓展,不存在则抛出异常
String transporter = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER);
if (StringUtils.isNotEmpty(transporter) && !url.getOrDefaultFrameworkModel().getExtensionLoader(Transporter.class).hasExtension(transporter)) {
throw new RpcException("Unsupported server type: " + transporter + ", url: " + url);
}
// 创建ExchangeServer.
ExchangeServer server;
try {
server = Exchangers.bind(url, requestHandler);
} catch (RemotingException e) {
throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
}
transporter = url.getParameter(CLIENT_KEY);
if (StringUtils.isNotEmpty(transporter) && !url.getOrDefaultFrameworkModel().getExtensionLoader(Transporter.class).hasExtension(transporter)) {
throw new RpcException("Unsupported client type: " + transporter);
}
DubboProtocolServer protocolServer = new DubboProtocolServer(server);
loadServerProperties(protocolServer);
return protocolServer;
}
8、ExchangeServer.bin
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");
// 调用 HeaderExchanger 的 bind 方法创建 ExchangeServer 实例
return getExchanger(url).bind(url, handler);
}
public static Exchanger getExchanger(URL url) {
// 默认header
String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
return url.getOrDefaultFrameworkModel().getExtensionLoader(Exchanger.class).getExtension(type);
}
headerExchanger.bind中,我们需要关注transporters.bind
@Override
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
transporters.bind
public class Transporters {
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 {
handler = new ChannelHandlerDispatcher(handlers);
}
return getTransporter(url).bind(url, handler);
}
public static Transporter getTransporter(URL url) {
return url.getOrDefaultFrameworkModel().getExtensionLoader(Transporter.class).getAdaptiveExtension();
}
}
对最开始的protocol分析,可以看出url.getOrDefaultFrameworkModel().getExtensionLoader(Transporter.class).getAdaptiveExtension()会基于Transporter$Adaptive方法进行适配,因为没有在外层指定通讯,这里走的是默认netty通讯,执行的是NettyTransporter
三、Netty
1、NettyTransporter.bind
public class NettyTransporter implements Transporter {
public static final String NAME = "netty";
@Override
public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException {
// 创建一个nettyserver
return new NettyServer(url, handler);
}
}
2、NettyServer
public class NettyServer extends AbstractServer {
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
// you can customize name and type of client thread pool by THREAD_NAME_KEY and THREAD_POOL_KEY in CommonConstants.
// the handler will be wrapped: MultiMessageHandler->HeartbeatHandler->handler
super(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME), ChannelHandlers.wrap(handler, url));
// read config before destroy
serverShutdownTimeoutMills = ConfigurationUtils.getServerShutdownTimeout(getUrl().getOrDefaultModuleModel());
}
/**
* Init and start netty server
*
* @throws Throwable
*/
// 初始化和启动netty服务
@Override
protected void doOpen() throws Throwable {
bootstrap = new ServerBootstrap();
bossGroup = createBossGroup();
workerGroup = createWorkerGroup();
final NettyServerHandler nettyServerHandler = createNettyServerHandler();
channels = nettyServerHandler.getChannels();
initServerBootstrap(nettyServerHandler);
// bind
ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
channelFuture.syncUninterruptibly();
channel = channelFuture.channel();
}
protected void initServerBootstrap(NettyServerHandler nettyServerHandler) {
boolean keepalive = getUrl().getParameter(KEEP_ALIVE_KEY, Boolean.FALSE);
bootstrap.group(bossGroup, workerGroup)
.channel(NettyEventLoopFactory.serverSocketChannelClass())
.option(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
.childOption(ChannelOption.SO_KEEPALIVE, keepalive)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// FIXME: should we use getTimeout()?
int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
if (getUrl().getParameter(SSL_ENABLED_KEY, false)) {
ch.pipeline().addLast("negotiation", new SslServerTlsHandler(getUrl()));
}
ch.pipeline()
.addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
.addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
.addLast("handler", nettyServerHandler);
}
});
}
}
public abstract class AbstractServer extends AbstractEndpoint implements RemotingServer {
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
executorRepository = url.getOrDefaultApplicationModel().getExtensionLoader(ExecutorRepository.class).getDefaultExtension();
localAddress = getUrl().toInetSocketAddress();
// 获取 host :port
String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
if (url.getParameter(ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
bindIp = ANYHOST_VALUE;
}
bindAddress = new InetSocketAddress(bindIp, bindPort);
this.accepts = url.getParameter(ACCEPTS_KEY, DEFAULT_ACCEPTS);
try {
// NettyServer.doOpen 启动服务器
doOpen();
if (logger.isInfoEnabled()) {
logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
}
} catch (Throwable t) {
throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
+ " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
}
executors.add(executorRepository.createExecutorIfAbsent(url));
}
}
这里用到了一个handler来处理客户端传递过来的请求:nettyServerHandler
从DubboProtocol类的requestHandler属性到NettyServer的nettyServerHandler属性。这一路会经历很多Handler,经过层层封装,最后才封装成NettyServerHandler
它会经历如下Handler,前五个是netty调用链,后面就是dubbo的调用链了
NettyServerHandler-> NettyServer-> MultiMessageHandler-> HeartbeatHandler-> AllChannelHandler
-> DecodeHandler-> HeaderExchangeHandler-> ExchangeHandler
当客户端连接服务端,或者发送数据到服务端的时候,
首先会由NettyServerHandler处理请求,然后依次将请求传递下去,最后到ExchangeHandler.
总结
Dubbo的服务发布主要做了以下几件事情:
- 读取配置
- 初始化配置对象
- 组装服务URL
- 创建代理类并缓存
- 注册服务到注册中心
- 创建网络通信服务器(netty)
- 监控服务状态(Heartbeat)