本文基于Dubbo 3.0.2来进行源码研究。
在基于springboot的开发中,dubbot的spring-boot-starter中,DubboAutoConfiguration
自动配置是一个关键类,这个类向spring容器注入了如下关键类:
- ServiceAnnotationPostProcessor
- DubboBootstrapApplicationListener
其中ServiceAnnotationPostProcessor
主要是用来加载被DubbotService
注解修饰的类,而DubboBootstrapApplicationListener
则会监听spring的相关事件,其中:
public void onApplicationEvent(ApplicationEvent event) {
if (isOriginalEventSource(event)) {
if (event instanceof DubboAnnotationInitedEvent) {
applicationContext.getBean(DubboConfigBeanInitializer.BEAN_NAME, DubboConfigBeanInitializer.class);
DubboBootstrap.getInstance().initialize();
} else if (event instanceof ApplicationContextEvent) {
this.onApplicationContextEvent((ApplicationContextEvent) event);
}
}
}
private void onApplicationContextEvent(ApplicationContextEvent event) {
if (DubboBootstrapStartStopListenerSpringAdapter.applicationContext == null) {
DubboBootstrapStartStopListenerSpringAdapter.applicationContext = event.getApplicationContext();
}
if (event instanceof ContextRefreshedEvent) {
onContextRefreshedEvent((ContextRefreshedEvent) event);
} else if (event instanceof ContextClosedEvent) {
onContextClosedEvent((ContextClosedEvent) event);
}
}
private void onContextRefreshedEvent(ContextRefreshedEvent event) {
if (dubboBootstrap.getTakeoverMode() == BootstrapTakeoverMode.SPRING) {
dubboBootstrap.start();
}
}
private void onContextClosedEvent(ContextClosedEvent event) {
if (dubboBootstrap.getTakeoverMode() == BootstrapTakeoverMode.SPRING) {
// will call dubboBootstrap.stop() through shutdown callback.
DubboShutdownHook.getDubboShutdownHook().run();
}
}
其中,当spring容器就绪后会发布ContextRefreshedEvent
,这时候,会调用DubboBootstrap
来将服务发布出来,这是一个总的比较概括的步骤,接下来我们看细节。
首先看ServiceAnnotationPostProcessor
,这个类的主要作用是将被如下注解修饰的类注入到了Spring容器中,同时生成对应的ServiceBean
注入到Spring容器中,在ServiceBean中持有一个ref
对应实际DubboService
修饰的类的引用,而后面的服务暴露都是通过ServiceBean来实现,具体执行服务的方法,则是通过ServiceBean的ref
对应具体的服务的方法去执行。
private final static List<Class<? extends Annotation>> serviceAnnotationTypes = asList(
DubboService.class,
Service.class,
com.alibaba.dubbo.config.annotation.Service.class
);
而在ServiceBean
则是对服务接口的封装:
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
ApplicationContextAware, BeanNameAware, ApplicationEventPublisherAware {
public void afterPropertiesSet() throws Exception {
if (StringUtils.isEmpty(getPath())) {
if (StringUtils.isNotEmpty(getInterface())) {
setPath(getInterface());
}
}
//register service bean and set bootstrap
DubboBootstrap.getInstance().service(this);
}
}
这里会将当前ServiceBean
注册到DubboBootstrap
,然后上面的在ContextRefreshedEvent
事件时,会进行
dubboBootstrap.start();
进行服务的发布:
public DubboBootstrap start() {
if (started.compareAndSet(false, true)) {
startup.set(false);
shutdown.set(false);
awaited.set(false);
initialize();
if (logger.isInfoEnabled()) {
logger.info(NAME + " is starting...");
}
// 1. export Dubbo Services
exportServices();
// If register consumer instance or has exported services
if (isRegisterConsumerInstance() || hasExportedServices()) {
// 2. export MetadataService
exportMetadataService();
// 3. Register the local ServiceInstance if required
registerServiceInstance();
}
referServices();
// wait async export / refer finish if needed
awaitFinish();
if (isExportBackground() || isReferBackground()) {
new Thread(() -> {
while (!asyncExportFinish || !asyncReferFinish) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
logger.error(NAME + " waiting async export / refer occurred and error.", e);
}
}
startup.set(true);
if (logger.isInfoEnabled()) {
logger.info(NAME + " is ready.");
}
onStart();
}).start();
} else {
startup.set(true);
if (logger.isInfoEnabled()) {
logger.info(NAME + " is ready.");
}
onStart();
}
if (logger.isInfoEnabled()) {
logger.info(NAME + " has started.");
}
}
return this;
}
private void exportServices() {
for (ServiceConfigBase sc : configManager.getServices()) {
// TODO, compatible with ServiceConfig.export()
ServiceConfig<?> serviceConfig = (ServiceConfig<?>) sc;
serviceConfig.setBootstrap(this);
if (!serviceConfig.isRefreshed()) {
serviceConfig.refresh();
}
if (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("export async catch error : " + t.getMessage(), t);
}
}, executor);
asyncExportingFutures.add(future);
} else {
if (!sc.isExported()) {
sc.export();
exportedServices.add(sc);
}
}
}
}
可以看到,这里的exportServices
实际上最终会调用ServiceConfig.export
:
// ServiceConfig.java
public synchronized void export() {
if (this.shouldExport() && !this.exported) {
this.init();
// check bootstrap state
if (!bootstrap.isInitialized()) {
throw new IllegalStateException("DubboBootstrap is not initialized");
}
if (!this.isRefreshed()) {
this.refresh();
}
if (!shouldExport()) {
return;
}
if (shouldDelay()) {
DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
} else {
doExport();
}
if (this.bootstrap.getTakeoverMode() == BootstrapTakeoverMode.AUTO) {
this.bootstrap.start();
}
}
}
可以看到在ServiceConfig
中进行export
的时候主要如下几步:
- init
- refresh
- doExport
- 首先是
init
:
public void init() {
if (this.initialized.compareAndSet(false, true)) {
if (this.bootstrap == null) {
this.bootstrap = DubboBootstrap.getInstance();
this.bootstrap.initialize();
}
this.bootstrap.service(this);
ExtensionLoader<ServiceListener> extensionLoader = ExtensionLoader.getExtensionLoader(ServiceListener.class);
this.serviceListeners.addAll(extensionLoader.getSupportedExtensionInstances());
this.checkAndUpdateSubConfigs();
}
initServiceMetadata(provider);
serviceMetadata.setServiceType(getInterfaceClass());
serviceMetadata.setTarget(getRef());
serviceMetadata.generateServiceKey();
}
在init的时候,首先会加载ServiceListener
,相当于是监听器,init的时候主要是加载相关的配置和转化
接下来是refresh
:
public void refresh() {
refreshed.set(true);
try {
// check and init before do refresh
preProcessRefresh();
Environment environment = ApplicationModel.getEnvironment();
List<Map<String, String>> configurationMaps = environment.getConfigurationMaps();
// Search props starts with PREFIX in order
String preferredPrefix = null;
for (String prefix : getPrefixes()) {
if (ConfigurationUtils.hasSubProperties(configurationMaps, prefix)) {
preferredPrefix = prefix;
break;
}
}
if (preferredPrefix == null) {
preferredPrefix = getPrefixes().get(0);
}
// Extract sub props (which key was starts with preferredPrefix)
Collection<Map<String, String>> instanceConfigMaps = environment.getConfigurationMaps(this, preferredPrefix);
Map<String, String> subProperties = ConfigurationUtils.getSubProperties(instanceConfigMaps, preferredPrefix);
InmemoryConfiguration subPropsConfiguration = new InmemoryConfiguration(subProperties);
if (logger.isDebugEnabled()) {
String idOrName = "";
if (StringUtils.hasText(this.getId())) {
idOrName = "[id=" + this.getId() + "]";
} else {
String name = ReflectUtils.getProperty(this, "getName");
if (StringUtils.hasText(name)) {
idOrName = "[name=" + name + "]";
}
}
}
Method[] methods = getClass().getMethods();
for (Method method : methods) {
if (MethodUtils.isSetter(method)) {
String propertyName = extractPropertyName(method.getName());
String kebabPropertyName = StringUtils.convertToSplitName(propertyName, "-");
try {
String value = StringUtils.trim(subPropsConfiguration.getString(kebabPropertyName));
if (StringUtils.hasText(value) && ClassUtils.isTypeMatch(method.getParameterTypes()[0], value) &&
!isIgnoredAttribute(getClass(), propertyName)) {
value = environment.resolvePlaceholders(value);
method.invoke(this, ClassUtils.convertPrimitive(method.getParameterTypes()[0], value));
}
} catch (Exception e) {
}
} else if (isParametersSetter(method)) {
String propertyName = extractPropertyName(method.getName());
String value = StringUtils.trim(subPropsConfiguration.getString(propertyName));
Map<String, String> parameterMap = null;
if (StringUtils.hasText(value)) {
parameterMap = StringUtils.parseParameters(value);
} else {
parameterMap = ConfigurationUtils.getSubProperties(subProperties, PARAMETERS);
}
invokeSetParameters(convert(parameterMap, ""));
}
}
processExtraRefresh(preferredPrefix, subPropsConfiguration);
} catch (Exception e) {
throw new IllegalStateException("Failed to override field value of config bean: "+this, e);
}
postProcessRefresh();
}
refresh
主要是把配置读取并设置到当前ServiceConfig
的相关属性赋值到对应的配置上。
最终服务暴露发布是在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();
}
private void doExportUrls() {
ServiceRepository repository = ApplicationModel.getServiceRepository();
ServiceDescriptor serviceDescriptor = repository.registerService(getInterfaceClass());
repository.registerProvider(
getUniqueServiceName(),
ref,
serviceDescriptor,
this,
serviceMetadata
);
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);
// In case user specified path, register service one more time to map it to path.
repository.registerService(pathKey, interfaceClass);
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
这里首先会获取到注册中心大地址,这里可以看到,注册中心的地址返回的是一个List,证明Dubbo可以进行多注册中心注册,然后会将服务注册到注册中心去,注册完之后进行实际的服务发布
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
Map<String, String> map = buildAttributes(protocolConfig);
serviceMetadata.getAttachments().putAll(map);
URL url = buildUrl(protocolConfig, registryURLs, map);
exportUrl(url, registryURLs);
}
这里首先拼接生成URL,在Dubbo中URL是一个很重要的概念,相关的参数、服务信息都放在URL中,ProtocolConfig
就是我们在文件中配置protocol的相关信息配置,这里看到,一个服务可以通过多个协议暴露:
private URL buildUrl(ProtocolConfig protocolConfig, List<URL> registryURLs, Map<String, String> params) {
String name = protocolConfig.getName();
if (StringUtils.isEmpty(name)) {
name = DUBBO;
}
// export service
String host = findConfigedHosts(protocolConfig, registryURLs, params);
Integer port = findConfigedPorts(protocolConfig, name, params);
URL url = new ServiceConfigURL(name, null, null, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), params);
// 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);
}
return url;
}
private Map<String, String> buildAttributes(ProtocolConfig protocolConfig) {
Map<String, String> map = new HashMap<String, String>();
map.put(SIDE_KEY, PROVIDER_SIDE);
// append params with basic configs,
ServiceConfig.appendRuntimeParameters(map);
AbstractConfig.appendParameters(map, getMetrics());
AbstractConfig.appendParameters(map, getApplication());
AbstractConfig.appendParameters(map, getModule());
// remove 'default.' prefix for configs from ProviderConfig
// appendParameters(map, provider, Constants.DEFAULT_KEY);
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);
}
// append params with method configs,
if (CollectionUtils.isNotEmpty(getMethods())) {
getMethods().forEach(method -> appendParametersWithMethod(method, 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);
}
String[] methods = methods(interfaceClass);
if (methods.length == 0) {
logger.warn("No method found in service interface " + interfaceClass.getName());
map.put(METHODS_KEY, ANY_VALUE);
} else {
map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
}
}
/**
* Here the token value configured by the provider is used to assign the value to ServiceConfig#token
*/
if (ConfigUtils.isEmpty(token) && provider != null) {
token = provider.getToken();
}
if (!ConfigUtils.isEmpty(token)) {
if (ConfigUtils.isDefault(token)) {
map.put(TOKEN_KEY, UUID.randomUUID().toString());
} else {
map.put(TOKEN_KEY, token);
}
}
return map;
}
这里的生成的url,实际上是ServiceConfigURL
:
public ServiceConfigURL(String protocol,
String username,
String password,
String host,
int port,
String path,
Map<String, String> parameters) {
this(new PathURLAddress(protocol, username, password, path, host, port), URLParam.parse(parameters), null);
}
public ServiceConfigURL(String protocol,
String username,
String password,
String host,
int port,
String path,
Map<String, String> parameters,
Map<String, Object> attributes) {
this(new PathURLAddress(protocol, username, password, path, host, port), URLParam.parse(parameters), attributes);
}
protected <T extends URL> T newURL(URLAddress urlAddress, URLParam urlParam) {
return (T) new ServiceConfigURL(urlAddress, urlParam, attributes);
}
最终拼接是在:
// AbstractConfigurator.java
public URL configure(URL url) {
// If override url is not enabled or is invalid, just return.
if (!configuratorUrl.getParameter(ENABLED_KEY, true) || configuratorUrl.getHost() == null || url == null || url.getHost() == null) {
return url;
}
String apiVersion = configuratorUrl.getParameter(CONFIG_VERSION_KEY);
if (StringUtils.isNotEmpty(apiVersion)) {
String currentSide = url.getSide();
String configuratorSide = configuratorUrl.getSide();
if (currentSide.equals(configuratorSide) && CONSUMER.equals(configuratorSide) && 0 == configuratorUrl.getPort()) {
url = configureIfMatch(NetUtils.getLocalHost(), url);
} else if (currentSide.equals(configuratorSide) && PROVIDER.equals(configuratorSide) &&
url.getPort() == configuratorUrl.getPort()) {
url = configureIfMatch(url.getHost(), url);
}
}
else {
url = configureDeprecated(url);
}
return url;
}
private URL configureIfMatch(String host, URL url) {
if (ANYHOST_VALUE.equals(configuratorUrl.getHost()) || host.equals(configuratorUrl.getHost())) {
// TODO, to support wildcards
String providers = configuratorUrl.getParameter(OVERRIDE_PROVIDERS_KEY);
if (StringUtils.isEmpty(providers) || providers.contains(url.getAddress()) || providers.contains(ANYHOST_VALUE)) {
String configApplication = configuratorUrl.getApplication(configuratorUrl.getUsername());
String currentApplication = url.getApplication(url.getUsername());
if (configApplication == null || ANY_VALUE.equals(configApplication)
|| configApplication.equals(currentApplication)) {
Set<String> conditionKeys = new HashSet<String>();
conditionKeys.add(CATEGORY_KEY);
conditionKeys.add(Constants.CHECK_KEY);
conditionKeys.add(DYNAMIC_KEY);
conditionKeys.add(ENABLED_KEY);
conditionKeys.add(GROUP_KEY);
conditionKeys.add(VERSION_KEY);
conditionKeys.add(APPLICATION_KEY);
conditionKeys.add(SIDE_KEY);
conditionKeys.add(CONFIG_VERSION_KEY);
conditionKeys.add(COMPATIBLE_CONFIG_KEY);
conditionKeys.add(INTERFACES);
for (Map.Entry<String, String> entry : configuratorUrl.getParameters().entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
boolean startWithTilde = startWithTilde(key);
if (startWithTilde || APPLICATION_KEY.equals(key) || SIDE_KEY.equals(key)) {
if (startWithTilde) {
conditionKeys.add(key);
}
if (value != null && !ANY_VALUE.equals(value)
&& !value.equals(url.getParameter(startWithTilde ? key.substring(1) : key))) {
return url;
}
}
}
return doConfigure(url, configuratorUrl.removeParameters(conditionKeys));
}
}
}
return url;
}
可以看到,这里会判断是provider端还是consumer端,分别进行URL的组装,说白了就是根据你的配置来进行封装,有些没有的,就默认了。
然后就是根据拼接封装的URL来进行服务的发布:
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);
MetadataUtils.publishServiceDefinition(url);
}
}
this.urls.add(url);
}
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.getServiceKey() + " to registry " + registryURL.getAddress());
} else {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url.getServiceKey());
}
}
doExportUrl(registryURL.putAttribute(EXPORT_KEY, url), true);
}
} else {
if (MetadataService.class.getName().equals(url.getServiceInterface())) {
MetadataUtils.saveMetadataURL(url);
}
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
doExportUrl(url, true);
}
return url;
}
如果是对外发布,则调用exportRemote
,然后执行服务发布在这:
private void doExportUrl(URL url, boolean withMetaData) {
Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
if (withMetaData) {
invoker = new DelegateProviderMetaDataInvoker(invoker, this);
}
Exporter<?> exporter = PROTOCOL.export(invoker);
exporters.add(exporter);
}
可以看到,这里首先是根据URL获取 Invoker
,然后通过PROTOCOL(dubbo中默认的是DubboProtocol)
进行发布,可以看到这里将实际的服务ref
的引用传递过去了,如果是通过jdk方式,则返回的:
// JdkProxyFactory.java
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
Method method = proxy.getClass().getMethod(methodName, parameterTypes);
return method.invoke(proxy, arguments);
}
};
}
默认的是javassist
:
// JavassistProxyFactory.java
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);
}
};
}
可以看到获取Invoker的时候,会将ServiceBean中持有的实际服务的ref引用传递下去,后续实际执行的时候都是通过调用ref来实现
PROTOCOL
则是通过Dubbo
自定义的SPI
机制加载:
private static final Protocol PROTOCOL = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
默认情况下,Protocol是DubboProtocol
,,但是需要注意的是,这里后面获取的是getAdaptiveExtension
,获取到的是Protocol$Adaptive
这就是相当于是一个Wrapper,实际上进行export的时候是调用具体的Protocol去执行的:
public class Protocol$Adaptive implements org.apache.dubbo.rpc.Protocol {
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);
}
但是在Dubbo的SPI机制中,获取指定的SPI的时候,也会去获取对应的wrapper,如果在定义的路径里面,一个类的构造函数传入的类型和自己的类型一致,那么Dubbo的SPI认为这就是一个Wrapper,会先构造一个Warpper出来,然后在吧实际的类型T实例注入进去。上述首先获取到的是 Protocol$Adaptive,然后实际执行方法的却还是rg.apache.dubbo.rpc.Protocol extension = (org.apache.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension(extName) 这里获取到的就是个被warapper封装的Protocol
而在Dubbo中,目前有Wrapper的有这个几个:
Dubbo中会按照order进行排序,最终相当于是ProtocolListenerWrapper => ProtocolFilterWrapper => Protocol$Adaptive => Protocol
这个方式封装了Protocol。
默认情况下最终还是调用了DubboProtocol
进行服务的发布,我们看下其实现:
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
URL url = invoker.getUrl();
// export service.
String key = serviceKey(url);
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
exporterMap.put(key, exporter);
//export an 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 stubproxy support event ,but no stub methods founded."));
}
}
}
openServer(url);
optimizeSerialization(url);
return exporter;
}
进过一系列判断之后,首先进行的是openServer处理:
private void openServer(URL url) {
// find server.
String key = url.getAddress();
//client can export a service which's 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));
}else {
server.reset(url);
}
}
} else {
// server supports reset, use together with override
server.reset(url);
}
}
}
这里判断一个服务的server是否启动时根据host+ip为key进行判断的,如果这个key代表的server没有创建,那么进行创建:
private ProtocolServer createServer(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();
String str = url.getParameter(SERVER_KEY, DEFAULT_REMOTING_SERVER);
if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
throw new RpcException("Unsupported server type: " + str + ", url: " + url);
}
ExchangeServer server;
try {
server = Exchangers.bind(url, requestHandler);
} catch (RemotingException e) {
throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
}
str = url.getParameter(CLIENT_KEY);
if (str != null && str.length() > 0) {
Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();
if (!supportedTypes.contains(str)) {
throw new RpcException("Unsupported client type: " + str);
}
}
return new DubboProtocolServer(server);
}
这里创建的server是一个ExchangeServer
,在Exchangers
通过SPI机制来加载实际的Exchanger
server = Exchangers.bind(url, requestHandler);
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");
return getExchanger(url).bind(url, handler);
}
public static Exchanger getExchanger(URL url) {
String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
return getExchanger(type);
}
这里要获取Exchanger,默认为HeaderExchanger
,其bind方法为:
// HeaderExchanger.java
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
而这里在通过Transporters
中通过SPI加载对应的Transporter
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().bind(url, handler);
}
public static Transporter getTransporter() {
return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
}
而在dubbo中默认的Transporter
为NettyTransporter
,其bind方法为:
public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException {
return new NettyServer(url, handler);
}
可以看到这里实际上返回的就是一个NettyServer:
// NettyServer.java
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
// you can customize name and type of client thread pool by THREAD_NAME_KEY and THREADPOOL_KEY in CommonConstants.
// the handler will be wrapped: MultiMessageHandler->HeartbeatHandler->handler
super(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME), ChannelHandlers.wrap(handler, url));
}
// AbstractServer.java
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
localAddress = getUrl().toInetSocketAddress();
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 {
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);
}
executor = executorRepository.createExecutorIfAbsent(url);
}
// NettyServer.java
protected void doOpen() throws Throwable {
bootstrap = new ServerBootstrap();
bossGroup = NettyEventLoopFactory.eventLoopGroup(1, "NettyServerBoss");
workerGroup = NettyEventLoopFactory.eventLoopGroup(
getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
"NettyServerWorker");
final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
channels = nettyServerHandler.getChannels();
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",
SslHandlerInitializer.sslServerHandler(getUrl(), nettyServerHandler));
}
ch.pipeline()
.addLast("decoder", adapter.getDecoder())
.addLast("encoder", adapter.getEncoder())
.addLast("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
.addLast("handler", nettyServerHandler);
}
});
// bind
ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
channelFuture.syncUninterruptibly();
channel = channelFuture.channel();
}
这里就是我们常见的Netty启动流程,这里可以看到,我们是可以自己设置Netty的IO线程数量,如果没配置,默认是当前机器可用线程数和32取最小值.
另外我们需要重点关注的就是Dubbo中的NettyHandler,我们看下Dubbo中的Handler是怎么封装的,
Dubbo定义了一个ChannelHandler
接口,类结构层次如下:
首先是在DubboProtocol
中实现了一个内部实现类ExchangeHandlerAdapter
:
private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
@Override
public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {
if (!(message instanceof Invocation)) {
throw new RemotingException(channel, "Unsupported request: "
+ (message == null ? null : (message.getClass().getName() + ": " + message))
+ ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
}
Invocation inv = (Invocation) message;
Invoker<?> invoker = getInvoker(channel, inv);
// need to consider backward-compatibility if it's a callback
if (Boolean.TRUE.toString().equals(inv.getObjectAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) {
String methodsStr = invoker.getUrl().getParameters().get("methods");
boolean hasMethod = false;
if (methodsStr == null || !methodsStr.contains(",")) {
hasMethod = inv.getMethodName().equals(methodsStr);
} else {
String[] methods = methodsStr.split(",");
for (String method : methods) {
if (inv.getMethodName().equals(method)) {
hasMethod = true;
break;
}
}
}
if (!hasMethod) {
logger.warn(new IllegalStateException("The methodName " + inv.getMethodName()
+ " not found in callback service interface ,invoke will be ignored."
+ " please update the api interface. url is:"
+ invoker.getUrl()) + " ,invocation is :" + inv);
return null;
}
}
RpcContext.getServiceContext().setRemoteAddress(channel.getRemoteAddress());
Result result = invoker.invoke(inv);
return result.thenApply(Function.identity());
}
@Override
public void received(Channel channel, Object message) throws RemotingException {
if (message instanceof Invocation) {
reply((ExchangeChannel) channel, message);
} else {
super.received(channel, message);
}
}
@Override
public void connected(Channel channel) throws RemotingException {
invoke(channel, ON_CONNECT_KEY);
}
@Override
public void disconnected(Channel channel) throws RemotingException {
if (logger.isDebugEnabled()) {
logger.debug("disconnected from " + channel.getRemoteAddress() + ",url:" + channel.getUrl());
}
invoke(channel, ON_DISCONNECT_KEY);
}
private void invoke(Channel channel, String methodKey) {
Invocation invocation = createInvocation(channel, channel.getUrl(), methodKey);
if (invocation != null) {
try {
received(channel, invocation);
} catch (Throwable t) {
logger.warn("Failed to invoke event method " + invocation.getMethodName() + "(), cause: " + t.getMessage(), t);
}
}
}
private Invocation createInvocation(Channel channel, URL url, String methodKey) {
String method = url.getParameter(methodKey);
if (method == null || method.length() == 0) {
return null;
}
RpcInvocation invocation = new RpcInvocation(method, url.getParameter(INTERFACE_KEY), "", new Class<?>[0], new Object[0]);
invocation.setAttachment(PATH_KEY, url.getPath());
invocation.setAttachment(GROUP_KEY, url.getGroup());
invocation.setAttachment(INTERFACE_KEY, url.getParameter(INTERFACE_KEY));
invocation.setAttachment(VERSION_KEY, url.getVersion());
if (url.getParameter(STUB_EVENT_KEY, false)) {
invocation.setAttachment(STUB_EVENT_KEY, Boolean.TRUE.toString());
}
return invocation;
}
};
我们在建立NettyServer的时候,会传入这个:
// DubboProtocol.java
ExchangeServer server;
try {
server = Exchangers.bind(url, requestHandler);
} catch (RemotingException e) {
throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
}
// HeaderExchanger
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
// Transporters.java
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().bind(url, handler);
}
// NettyTrasporter.java
public RemotingServer bind(URL url, ChannelHandler handler) throws RemotingException {
return new NettyServer(url, handler);
}
public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
super(ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME), ChannelHandlers.wrap(handler, url));
}
return ChannelHandlers.getInstance().wrapInternal(handler, url);
}
return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class)
.getAdaptiveExtension().dispatch(handler, url)));
}
// AllDispatcher.java
public ChannelHandler dispatch(ChannelHandler handler, URL url) {
return new AllChannelHandler(handler, url);
}
可以看到在DubboProtocol中,我们开始只是生成了一个ExchangeHandlerAdapter
的内部实现类,然后逐步往后传递:
- HeaderExchangeHandler封装ExchangeHandlerAdapter
- DecodeHandler封装HeaderExchangeHandler
- AllChannelHandler封装DecodeHandler
- HeartbeatHandler封装AllChannelHandler
- MultiMessageHandler封装HeartbeatHandler
总结下来的封装顺序 MultiMessageHandler -> HeartbeatHandler -> AllChannelHandler -> DecodeHandler -> DecodeHandler -> HeaderExchangeHandler -> ExchangeHandlerAdapter
我们以RECEIVED
事件来看看整个处理流程,但是在介绍事件处理流程之前,我们首先看下这块,启动NettyServer的相关处理:
NettyServer(基于Netty4
)的类结构如图,可以看到,其也实现ChannelHandler
接口,而实际Handler实现,则是交由上面MultiMessageHandler
去实现的,而在NettyServer启动的时候,会传入对应编解码器,其实就是两个ChannelHandler
,Dubbo中的编解码器的实现是DubboCountCodec
,其内部持有一个DubboCodec
,进行实际的编解码工作,这里采用装饰模式来实现
,这里会将对应的二级制流转换为对应的Request
或者Response
,如果想要看Dubbo的详细编解码实现可以查看这个类就行了。需要注意的是请求发出的Request
在解码阶段Request
data部分实际上只是一个DecodeableRpcInvocation
,并没有进行实际数据的解析,这部分是放在了后面的DecodeHandler
中。
Dubbo中实际处理相关业务的是NettyServerHandler
:
// NettyServer.java
// 这里传入的this就是NettyServer自己
final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
当数据流来到时:
// NettyServerHandler.java
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
NettyChannel channel = NettyChannel.getOrAddChannel(ctx.channel(), url, handler);
handler.received(channel, msg);
}
可以看到这里就是通过传入的Handler去实现,最终实际上是在MultiMessageHandler
:
// MultiMessageHandler.java
public void received(Channel channel, Object message) throws RemotingException {
if (message instanceof MultiMessage) {
MultiMessage list = (MultiMessage) message;
for (Object obj : list) {
try {
handler.received(channel, obj);
} catch (ExecutionException e) {
logger.error("MultiMessageHandler received fail.", e);
handler.caught(channel, e);
}
}
} else {
handler.received(channel, message);
}
}
可以看到MultiMessageHandler
功能就是讲复合消息拆开,而MultiMessageHandler
这里的handler则是HeartbeatHandler
public void received(Channel channel, Object message) throws RemotingException {
setReadTimestamp(channel);
if (isHeartbeatRequest(message)) {
Request req = (Request) message;
if (req.isTwoWay()) {
Response res = new Response(req.getId(), req.getVersion());
res.setEvent(HEARTBEAT_EVENT);
channel.send(res);
if (logger.isInfoEnabled()) {
int heartbeat = channel.getUrl().getParameter(Constants.HEARTBEAT_KEY, 0);
if (logger.isDebugEnabled()) {
logger.debug("Received heartbeat from remote channel " + channel.getRemoteAddress()
+ ", cause: The channel has no data-transmission exceeds a heartbeat period"
+ (heartbeat > 0 ? ": " + heartbeat + "ms" : ""));
}
}
}
return;
}
if (isHeartbeatResponse(message)) {
if (logger.isDebugEnabled()) {
logger.debug("Receive heartbeat response in thread " + Thread.currentThread().getName());
}
return;
}
handler.received(channel, message);
}
可以看到HeartbeatHandler
主要功能就是处理心跳相关信息,不是心跳信息则交给内部handler实现,HeartbeatHandler
这里的handerl是AllChannelHandler
:
public void received(Channel channel, Object message) throws RemotingException {
ExecutorService executor = getPreferredExecutorService(message);
try {
executor.execute(new ChannelEventRunnable(channel, handler, ChannelState.RECEIVED, message));
} catch (Throwable t) {
if(message instanceof Request && t instanceof RejectedExecutionException){
sendFeedback(channel, (Request) message, t);
return;
}
throw new ExecutionException(message, channel, getClass() + " error when process received event .", t);
}
}
可以看到在AllChannelHandler
里面处理接收的时候,是通过线程池俩处理的,如果是处理Request
则默认的是一个Fixed的ThreadPoolExecutor
,其默认大小是200,并且这里,如果是消费者端,则只有一个ThreadPoolExecutor,但是如果是服务端,则绑定每个端口都有一个ThreadPoolExecutor。
而在ChannelEventRunnable
中处理消息则是直接使用AllChannelHandler
的handler:
// ChannelEventRunnable.java
public void run() {
if (state == ChannelState.RECEIVED) {
try {
handler.received(channel, message);
} catch (Exception e) {}
} else {
switch (state) {
case CONNECTED:
try {
handler.connected(channel);
} catch (Exception e) {}
break;
case DISCONNECTED:
try {
handler.disconnected(channel);
} catch (Exception e) {}
break;
case SENT:
try {
handler.sent(channel, message);
} catch (Exception e) {}
break;
case CAUGHT:
try {
handler.caught(channel, exception);
} catch (Exception e) {}
break;
default:
logger.warn("unknown state: " + state + ", message is " + message);
}
}
}
而AllChannelHandler
的handler则是DecodeHandler
:
// DecodeHandler.java
public void received(Channel channel, Object message) throws RemotingException {
if (message instanceof Decodeable) {
decode(message);
}
if (message instanceof Request) {
decode(((Request) message).getData());
}
if (message instanceof Response) {
decode(((Response) message).getResult());
}
handler.received(channel, message);
}
private void decode(Object message) {
if (message instanceof Decodeable) {
try {
((Decodeable) message).decode();
} // ~ end of catch
} // ~ end of if
}
这里的工作就是进行decode,还记得上面说的,在编解码器中返回的是Request或者Response对象,但是databa部分并没有解析,在这里进行实际的解码,我们以请求为例,Request中的data部分实际上是DecodeableRpcInvocation
,我们看其实现:
// DecodeableRpcInvocation.java
public void decode() throws Exception {
if (!hasDecoded && channel != null && inputStream != null) {
try {
decode(channel, inputStream);
} catch (Throwable e) {
if (log.isWarnEnabled()) {
log.warn("Decode rpc invocation failed: " + e.getMessage(), e);
}
request.setBroken(true);
request.setData(e);
} finally {
hasDecoded = true;
}
}
}
public Object decode(Channel channel, InputStream input) throws IOException {
ObjectInput in = CodecSupport.getSerialization(channel.getUrl(), serializationType)
.deserialize(channel.getUrl(), input);
this.put(SERIALIZATION_ID_KEY, serializationType);
String dubboVersion = in.readUTF();
request.setVersion(dubboVersion);
setAttachment(DUBBO_VERSION_KEY, dubboVersion);
String path = in.readUTF();
setAttachment(PATH_KEY, path);
String version = in.readUTF();
setAttachment(VERSION_KEY, version);
setMethodName(in.readUTF());
String desc = in.readUTF();
setParameterTypesDesc(desc);
try {
if (ConfigurationUtils.getSystemConfiguration().getBoolean(SERIALIZATION_SECURITY_CHECK_KEY, true)) {
CodecSupport.checkSerialization(path, version, serializationType);
}
Object[] args = DubboCodec.EMPTY_OBJECT_ARRAY;
Class<?>[] pts = DubboCodec.EMPTY_CLASS_ARRAY;
if (desc.length() > 0) {
// if (RpcUtils.isGenericCall(path, getMethodName()) || RpcUtils.isEcho(path, getMethodName())) {
// pts = ReflectUtils.desc2classArray(desc);
// } else {
ServiceRepository repository = ApplicationModel.getServiceRepository();
ServiceDescriptor serviceDescriptor = repository.lookupService(path);
if (serviceDescriptor != null) {
MethodDescriptor methodDescriptor = serviceDescriptor.getMethod(getMethodName(), desc);
if (methodDescriptor != null) {
pts = methodDescriptor.getParameterClasses();
this.setReturnTypes(methodDescriptor.getReturnTypes());
}
}
if (pts == DubboCodec.EMPTY_CLASS_ARRAY) {
if (!RpcUtils.isGenericCall(desc, getMethodName()) && !RpcUtils.isEcho(desc, getMethodName())) {
throw new IllegalArgumentException("Service not found:" + path + ", " + getMethodName());
}
pts = ReflectUtils.desc2classArray(desc);
}
// }
args = new Object[pts.length];
for (int i = 0; i < args.length; i++) {
try {
args[i] = in.readObject(pts[i]);
} catch (Exception e) {
if (log.isWarnEnabled()) {
log.warn("Decode argument failed: " + e.getMessage(), e);
}
}
}
}
setParameterTypes(pts);
Map<String, Object> map = in.readAttachments();
if (map != null && map.size() > 0) {
Map<String, Object> attachment = getObjectAttachments();
if (attachment == null) {
attachment = new LinkedHashMap<>();
}
attachment.putAll(map);
setObjectAttachments(attachment);
}
//decode argument ,may be callback
for (int i = 0; i < args.length; i++) {
args[i] = decodeInvocationArgument(channel, this, pts, i, args[i]);
}
setArguments(args);
String targetServiceName = buildKey((String) getAttachment(PATH_KEY),
getAttachment(GROUP_KEY),
getAttachment(VERSION_KEY));
setTargetServiceUniqueName(targetServiceName);
} catch (ClassNotFoundException e) {
throw new IOException(StringUtils.toString("Read invocation data failed.", e));
} finally {
if (in instanceof Cleanable) {
((Cleanable) in).cleanup();
}
}
return this;
}
可以看到,在这里会进行实际的解析,获取如下重要信息:-
- 请求的服务路径
- 请求的服务方法名称
- 请求的服务的版本号
- 请求服务方法参数信息
- 请求服务方法参数入参
根据这些信息,找到实际的服务提供者,并设置方法参数。
处理完这些之后,接下来还是给DecodeHandler
的内部handler去处理,这里对应就是HeaderExchangeHandler
:
// HeaderExchangeHandler.java
public void received(Channel channel, Object message) throws RemotingException {
final ExchangeChannel exchangeChannel = HeaderExchangeChannel.getOrAddChannel(channel);
if (message instanceof Request) {
// handle request.
Request request = (Request) message;
if (request.isEvent()) {
handlerEvent(channel, request);
} else {
if (request.isTwoWay()) {
handleRequest(exchangeChannel, request);
} else {
handler.received(exchangeChannel, request.getData());
}
}
} else if (message instanceof Response) {
handleResponse(channel, (Response) message);
} else if (message instanceof String) {
if (isClientSide(channel)) {
....;
} else {
String echo = handler.telnet(channel, (String) message);
if (echo != null && echo.length() > 0) {
channel.send(echo);
}
}
} else {
handler.received(exchangeChannel, message);
}
}
void handleRequest(final ExchangeChannel channel, Request req) throws RemotingException {
Response res = new Response(req.getId(), req.getVersion());
if (req.isBroken()) {
Object data = req.getData();
String msg;
if (data == null) {
msg = null;
} else if (data instanceof Throwable) {
msg = StringUtils.toString((Throwable) data);
} else {
msg = data.toString();
}
res.setErrorMessage("Fail to decode request due to: " + msg);
res.setStatus(Response.BAD_REQUEST);
channel.send(res);
return;
}
Object msg = req.getData();
try {
CompletionStage<Object> future = handler.reply(channel, msg);
future.whenComplete((appResult, t) -> {
try {
if (t == null) {
res.setStatus(Response.OK);
res.setResult(appResult);
} else {
res.setStatus(Response.SERVICE_ERROR);
res.setErrorMessage(StringUtils.toString(t));
}
channel.send(res);
} catch (RemotingException e) {}
});
} catch (Throwable e) {
res.setStatus(Response.SERVICE_ERROR);
res.setErrorMessage(StringUtils.toString(e));
channel.send(res);
}
}
可以看到,这里分方法有返回值和无返回值两种情况,如果无返回值,则调用的还是HeaderExchangeHandler
内部的handler去处理,而如果有返回值,则调用的HeaderExchangeHandler
内部的handler的reply
方法处理。
好,这里的handler就是我们在开头说的DubboProtocol
中的内部实现类ExchangeHandlerAdapter
,我们在这里在贴一下源码:
// DubboProtocol.java
private ExchangeHandler requestHandler = new ExchangeHandlerAdapter() {
@Override
public CompletableFuture<Object> reply(ExchangeChannel channel, Object message) throws RemotingException {
if (!(message instanceof Invocation)) {
throw new RemotingException(channel, "Unsupported request: "
+ (message == null ? null : (message.getClass().getName() + ": " + message))
+ ", channel: consumer: " + channel.getRemoteAddress() + " --> provider: " + channel.getLocalAddress());
}
Invocation inv = (Invocation) message;
Invoker<?> invoker = getInvoker(channel, inv);
// need to consider backward-compatibility if it's a callback
if (Boolean.TRUE.toString().equals(inv.getObjectAttachments().get(IS_CALLBACK_SERVICE_INVOKE))) {
String methodsStr = invoker.getUrl().getParameters().get("methods");
boolean hasMethod = false;
if (methodsStr == null || !methodsStr.contains(",")) {
hasMethod = inv.getMethodName().equals(methodsStr);
} else {
String[] methods = methodsStr.split(",");
for (String method : methods) {
if (inv.getMethodName().equals(method)) {
hasMethod = true;
break;
}
}
}
if (!hasMethod) {
logger.warn(new IllegalStateException("The methodName " + inv.getMethodName()
+ " not found in callback service interface ,invoke will be ignored."
+ " please update the api interface. url is:"
+ invoker.getUrl()) + " ,invocation is :" + inv);
return null;
}
}
RpcContext.getServiceContext().setRemoteAddress(channel.getRemoteAddress());
Result result = invoker.invoke(inv);
return result.thenApply(Function.identity());
}
@Override
public void received(Channel channel, Object message) throws RemotingException {
if (message instanceof Invocation) {
reply((ExchangeChannel) channel, message);
} else {
super.received(channel, message);
}
}
@Override
public void connected(Channel channel) throws RemotingException {
invoke(channel, ON_CONNECT_KEY);
}
@Override
public void disconnected(Channel channel) throws RemotingException {
if (logger.isDebugEnabled()) {
logger.debug("disconnected from " + channel.getRemoteAddress() + ",url:" + channel.getUrl());
}
invoke(channel, ON_DISCONNECT_KEY);
}
private void invoke(Channel channel, String methodKey) {
Invocation invocation = createInvocation(channel, channel.getUrl(), methodKey);
if (invocation != null) {
try {
received(channel, invocation);
} catch (Throwable t) {
logger.warn("Failed to invoke event method " + invocation.getMethodName() + "(), cause: " + t.getMessage(), t);
}
}
}
private Invocation createInvocation(Channel channel, URL url, String methodKey) {
String method = url.getParameter(methodKey);
if (method == null || method.length() == 0) {
return null;
}
RpcInvocation invocation = new RpcInvocation(method, url.getParameter(INTERFACE_KEY), "", new Class<?>[0], new Object[0]);
invocation.setAttachment(PATH_KEY, url.getPath());
invocation.setAttachment(GROUP_KEY, url.getGroup());
invocation.setAttachment(INTERFACE_KEY, url.getParameter(INTERFACE_KEY));
invocation.setAttachment(VERSION_KEY, url.getVersion());
if (url.getParameter(STUB_EVENT_KEY, false)) {
invocation.setAttachment(STUB_EVENT_KEY, Boolean.TRUE.toString());
}
return invocation;
}
};
可以看到,这里的received最终都是调用了reply
方法,在reply方法中,首选是根据url参数获取DubboExporter
在获取其Invoker,通过前面的分析我们可以发现,这里的Invoker实际上是一个DelegateProviderMetaDataInvoker
内部持有一个AbstractProxyInvoker
的内部实现。默认是通过JavassistProxyFactory
生成:
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);
}
};
}
最终在Wrapper
中Dubbo会动态生成一个包装类,持有一个实际的服务,最终通过实际服务区执行,我们这里的Invoker是AbstractProxyInvoker
:
public Result invoke(Invocation invocation) throws RpcException {
try {
Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
CompletableFuture<Object> future = wrapWithFuture(value);
CompletableFuture<AppResponse> appResponseFuture = future.handle((obj, t) -> {
AppResponse result = new AppResponse(invocation);
if (t != null) {
if (t instanceof CompletionException) {
result.setException(t.getCause());
} else {
result.setException(t);
}
} else {
result.setValue(obj);
}
return result;
});
return new AsyncRpcResult(appResponseFuture, invocation);
} catch (InvocationTargetException e) {
if (RpcContext.getServiceContext().isAsyncStarted() && !RpcContext.getServiceContext().stopAsync()) {
logger.error("Provider async started, but got an exception from the original method, cannot write the exception back to consumer because an async result may have returned the new thread.", e);
}
return AsyncRpcResult.newDefaultAsyncResult(null, e.getTargetException(), invocation);
} catch (Throwable e) {
throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
AbstractProxyInvoker
有两个内部实现:基于JDK和基于Javassist,基于JDK是直接通过反射来调用,基于Javassist通过warapper,调用实际的服务来执行。
这是一个总的流程,其中有一些细节这里没有特别说明,比如看贴出来的一些代码,可以发现如果是有返回值的,Dubbo实际上是通过异步的方式去执行,然后每次调用都有一个ID,通过判断ID对应channel是否返回值,来判断调用是否完成,后续有时间还会接着研究下。