// 服务接口
public interface HelloService {
String sayHello(String name);
}
import org.apache.dubbo.config.annotation.DubboService;
// 服务实现类
@DubboService
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "Hello, " + name;
}
}
# 应用名称
dubbo.application.name=hello-service-provider
# 注册中心地址,这里使用 Zookeeper
dubbo.registry.address=zookeeper://127.0.0.1:2181
# 协议和端口
dubbo.protocol.name=dubbo
dubbo.protocol.port=20880
通过上述两步,便可以将 HelloService 提供给客户端调用。
Dubbo 的 ServiceAnnotationPostProcessor 实现了 Spring 的 BeanDefinitionRegistryPostProcessor 接口,Spring 会在 Bean 定义加载到 BeanFactory 之后、Bean 实例化之前调用该接口的 postProcessBeanDefinitionRegistry 方法。
postProcessBeanDefinitionRegistry 和 postProcessBeanFactory 的区别?
postProcessBeanDefinitionRegistry:调用时机相对较早。在 Spring 容器启动过程中,当所有的 Bean 定义信息(BeanDefinition)加载完成后,但 Bean 实例还未开始创建时,Spring 会优先调用 BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法。这使得开发者可以在这个阶段对 BeanDefinitionRegistry 进行操作,动态地添加、修改或删除 Bean 定义。
postProcessBeanFactory:在 postProcessBeanDefinitionRegistry 方法调用之后执行。当所有的 BeanDefinitionRegistryPostProcessor 处理完毕,且 BeanDefinition 信息可能已经被修改后,Spring 会调用 BeanFactoryPostProcessor 的 postProcessBeanFactory 方法,对 ConfigurableListableBeanFactory 进行处理。
postProcessBeanDefinitionRegistry 方法调用 scanServiceBeans 方法。该方法扫描出哪些类注解了 @DubboService。
private void scanServiceBeans(Set<String> packagesToScan, BeanDefinitionRegistry registry) {
...
DubboClassPathBeanDefinitionScanner scanner =
new DubboClassPathBeanDefinitionScanner(registry, environment, resourceLoader);
BeanNameGenerator beanNameGenerator = resolveBeanNameGenerator(registry);
scanner.setBeanNameGenerator(beanNameGenerator);
for (Class<? extends Annotation> annotationType : serviceAnnotationTypes) {
// 将 DubboService.class 和 Service.class 添加到 includeFilter 中,该 includeFilter 用于后续筛选添加了这些注解的类
scanner.addIncludeFilter(new AnnotationTypeFilter(annotationType));
}
ScanExcludeFilter scanExcludeFilter = new ScanExcludeFilter();
scanner.addExcludeFilter(scanExcludeFilter);
for (String packageToScan : packagesToScan) {
// avoid duplicated scans
if (servicePackagesHolder.isPackageScanned(packageToScan)) {
if (logger.isInfoEnabled()) {
logger.info("Ignore package who has already bean scanned: " + packageToScan);
}
continue;
}
if (AotWithSpringDetector.useGeneratedArtifacts()) {
scanner.setIncludeAnnotationConfig(false);
}
// 将添加了 @Service 注解的类注册为 Bean
scanner.scan(packageToScan);
// Finds all BeanDefinitionHolders of @Service whether @ComponentScan scans or not.
Set<BeanDefinitionHolder> beanDefinitionHolders =
findServiceBeanDefinitionHolders(scanner, packageToScan, registry, beanNameGenerator);
if (!CollectionUtils.isEmpty(beanDefinitionHolders)) {
if (logger.isInfoEnabled()) {
List<String> serviceClasses = new ArrayList<>(beanDefinitionHolders.size());
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
serviceClasses.add(
beanDefinitionHolder.getBeanDefinition().getBeanClassName());
}
logger.info("Found " + beanDefinitionHolders.size()
+ " classes annotated by Dubbo @Service under package [" + packageToScan + "]: "
+ serviceClasses);
}
for (BeanDefinitionHolder beanDefinitionHolder : beanDefinitionHolders) {
// 构造 ServiceBean 并注册到 Spring 容器中
processScannedBeanDefinition(beanDefinitionHolder);
servicePackagesHolder.addScannedClass(
beanDefinitionHolder.getBeanDefinition().getBeanClassName());
}
} else {
if (logger.isWarnEnabled()) {
logger.warn(
CONFIG_NO_ANNOTATIONS_FOUND,
"No annotations were found on the class",
"",
"No class annotated by Dubbo @DubboService or @Service was found under package ["
+ packageToScan + "], ignore re-scanned classes: "
+ scanExcludeFilter.getExcludedCount());
}
}
servicePackagesHolder.addScannedPackage(packageToScan);
}
}
上述方法中,scanner.scan(packageToScan) 和 processScannedBeanDefinition(beanDefinitionHolder) 都分别注册了 Bean。
上述方法中,findServiceBeanDefinitionHolders 找出刚刚注册的 BeanDefinition,然后再一个循环里处理每个 BeanDefinition,得到 ServiceBean 的 BeanDefinition,并调用 registerServiceBeanDefinition 方法将 BeanDefinition 注册到 Spring 容器中:
private void processScannedBeanDefinition(BeanDefinitionHolder beanDefinitionHolder) {
// 拿到 BeanDefinition 的类型
Class<?> beanClass = resolveClass(beanDefinitionHolder);
// 拿到 BeanDefinition 的 @Service 注解
Annotation service = findServiceAnnotation(beanClass);
// 拿到 BeanDefinition 的 @Service 注解的属性
Map<String, Object> serviceAnnotationAttributes = AnnotationUtils.getAttributes(service, true);
String serviceInterface = resolveInterfaceName(serviceAnnotationAttributes, beanClass);
String annotatedServiceBeanName = beanDefinitionHolder.getBeanName();
String beanName = generateServiceBeanName(serviceAnnotationAttributes, serviceInterface);
// 构造 ServiceBean 的 BeanDefinition
AbstractBeanDefinition serviceBeanDefinition =
buildServiceBeanDefinition(serviceAnnotationAttributes, serviceInterface, annotatedServiceBeanName);
// 注册 BeanDefinition
registerServiceBeanDefinition(beanName, serviceBeanDefinition, serviceInterface);
}
这些 BeanDefinition 都被注册到 Spring 容器后,Spring 将在 Bean 初始化时将它们注册到注册中心。
总结来说,Dubbo 利用扫描器,把含有 @DubboService 注解的类扫描出来,得到一个 BeanDefinition 的集合。循环扫描出来的 BeanDefinition 集合,在循环体中,从已扫描的 Bean 定义中,提取注解属性和服务接口名,然后创建一个新的 ServiceBean 类型的 Bean 定义。
服务导出的操作主要是在 Spring 容器的生命周期中,当 ServiceBean 实例化并初始化完成之后触发。更具体地说,Spring 的 AbstractApplicationContext 在执行 refresh 方法时,会发布 ContextRefreshedEvent, DubboBootstrapApplicationListener 接收到 ContextRefreshedEvent 后,会初始化并导出服务。
/**
* An ApplicationListener to control Dubbo application.
*/
public class DubboDeployApplicationListener
implements ApplicationListener<ApplicationContextEvent>, ApplicationContextAware, Ordered {
@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) {
ModuleDeployer deployer = moduleModel.getDeployer();
Assert.notNull(deployer, "Module deployer is null");
Object singletonMutex = LockUtils.getSingletonMutex(applicationContext);
// start module
Future future = null;
synchronized (singletonMutex) {
future = deployer.start();
}
// if the module does not start in background, await finish
if (!deployer.isBackground()) {
try {
future.get();
} catch (InterruptedException e) {
logger.warn(
CONFIG_FAILED_START_MODEL,
"",
"",
"Interrupted while waiting for dubbo module start: " + e.getMessage());
} catch (Exception e) {
logger.warn(
CONFIG_FAILED_START_MODEL,
"",
"",
"An error occurred while waiting for dubbo module start: " + e.getMessage(),
e);
}
}
}
}
默认的 ModuleDeployer 的 start 方法实现如下:
@Override
public Future start() throws IllegalStateException {
// initialize,maybe deadlock applicationDeployer lock & moduleDeployer lock
applicationDeployer.initialize();
return startSync();
}
private synchronized Future startSync() throws IllegalStateException {
if (isStopping() || isStopped() || isFailed()) {
throw new IllegalStateException(getIdentifier() + " is stopping or stopped, can not start again");
}
try {
if (isStarting() || isStarted() || isCompletion()) {
return startFuture;
}
onModuleStarting();
initialize();
// 导出服务
exportServices();
// prepare application instance
// exclude internal module to avoid wait itself
if (moduleModel != moduleModel.getApplicationModel().getInternalModule()) {
applicationDeployer.prepareInternalModule();
}
// refer services
referServices();
// if no async export/refer services, just set started
if (asyncExportingFutures.isEmpty() && asyncReferringFutures.isEmpty()) {
// publish module started event
onModuleStarted();
// 将服务注册到注册中心
registerServices();
// check reference config
checkReferences();
// publish module completion event
onModuleCompletion();
// complete module start future after application state changed
completeStartFuture(true);
} else {
frameworkExecutorRepository.getSharedExecutor().submit(() -> {
try {
// wait for export finish
waitExportFinish();
// wait for refer finish
waitReferFinish();
// publish module started event
onModuleStarted();
// register services to registry
registerServices();
// check reference config
checkReferences();
// publish module completion event
onModuleCompletion();
} catch (Throwable e) {
logger.warn(
CONFIG_FAILED_WAIT_EXPORT_REFER,
"",
"",
"wait for export/refer services occurred an exception",
e);
onModuleFailed(getIdentifier() + " start failed: " + e, e);
} finally {
// complete module start future after application state changed
completeStartFuture(true);
}
});
}
} catch (Throwable e) {
onModuleFailed(getIdentifier() + " start failed: " + e, e);
throw e;
}
return startFuture;
}
其中,exportServices 完成了服务导出,其调用链如下:
exportServices -> exportServiceInternal -> ServiceConfigBase.export
在 ServiceConfig 中:
export -> doExport -> doExportUrls
其中,doExportUrls 主要做了以下几件事:
我们主要关注第四步,其调用链如下:
doExportUrls -> doExportUrlsFor1Protocol -> exportUrl -> exportRemote -> doExportUrl
doExportUrl 从 proxyFactory 拿到 Invoker 对象,利用 Dubbo 的 SPI 机制对服务进行导出。
private void doExportUrl(URL url, boolean withMetaData, RegisterTypeEnum registerType) {
if (!url.getParameter(REGISTER_KEY, true)) {
registerType = RegisterTypeEnum.MANUAL_REGISTER;
}
if (registerType == RegisterTypeEnum.NEVER_REGISTER
|| registerType == RegisterTypeEnum.MANUAL_REGISTER
|| registerType == RegisterTypeEnum.AUTO_REGISTER_BY_DEPLOYER) {
url = url.addParameter(REGISTER_KEY, false);
}
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
if (withMetaData) {
invoker = new DelegateProviderMetaDataInvoker(invoker, this);
}
// 利用 Dubbo 的 SPI 机制对服务进行导出
Exporter<?> exporter = protocolSPI.export(invoker);
exporters
.computeIfAbsent(registerType, k -> new CopyOnWriteArrayList<>())
.add(exporter);
}
如果服务端使用的是 Dubbo 协议,调用的是 DubboProtocol 的 export 方法:
@Override
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
checkDestroyed();
URL url = invoker.getUrl();
// 生成服务键并创建 DubboExporter 对象
String key = serviceKey(url);
DubboExporter<T> exporter = new DubboExporter<>(invoker, key, exporterMap);
// 处理 stub 服务事件支持
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(
PROTOCOL_UNSUPPORTED,
"",
"",
"consumer [" + url.getParameter(INTERFACE_KEY)
+ "], has set stub proxy support event ,but no stub methods founded.");
}
}
}
// 打开服务器
openServer(url);
optimizeSerialization(url);
return exporter;
}
上述 openServer 方法根据服务的 URL 打开服务器,监听指定的网络地址和端口,以便接收来自消费者的请求。该方法会根据配置的协议(如 dubbo 协议)启动相应的网络服务。
完成了服务导出后,回到 ModuleDeployer,registerServices 完成了服务的注册,其调用链如下:
registerServices -> registerServiceInternal -> ServiceConfigBase.register -> Exporter.register
实际调用的类视注册中心的不同而不同,以 ZooKeeper 为例,通过模版方法实现服务注册:
public abstract class FailbackRegistry extends AbstractRegistry {
@Override
public void register(URL url) {
if (!shouldRegister(url)) {
return;
}
super.register(url);
removeFailedRegistered(url);
removeFailedUnregistered(url);
try {
// Sending a registration request to the server side
doRegister(url);
} catch (Exception e) {
Throwable t = e;
// If the startup detection is opened, the Exception is thrown directly.
boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
&& url.getParameter(Constants.CHECK_KEY, true)
&& (url.getPort() != 0);
boolean skipFailback = t instanceof SkipFailbackWrapperException;
if (check || skipFailback) {
if (skipFailback) {
t = t.getCause();
}
throw new IllegalStateException(
"Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: "
+ t.getMessage(),
t);
} else {
logger.error(
INTERNAL_ERROR,
"unknown error in registry module",
"",
"Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(),
t);
}
// Record a failed registration request to a failed list, retry regularly
addFailedRegistered(url);
}
}
}
doRegister 仅仅是在 Zookeeper 创建一个 ZNode(Zookeeper 文件系统中的节点):
@Override
public void doRegister(URL url) {
try {
checkDestroyed();
zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true), true);
} catch (Throwable e) {
throw new RpcException(
"Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
至此,DubboService 完成了服务在注册中心的注册。