上一節中講到了dubbo中consumer的啓動過程,鏈接爲 Dubbo源碼解析 二、Consumer的啓動過程 ,這一節我們將一些provider的啓動過程,看一下dubbo是如何將provider提供的服務暴露出來。
同樣的,下面以provider中 DemoServiceImpl 爲例:
public class DemoServiceImpl implements DemoService {
@Override
public String sayHello(String name) {
System.out.println("[" + new SimpleDateFormat("HH:mm:ss").format(new Date()) + "] Hello " + name + ", request from consumer: " + RpcContext.getContext().getRemoteAddress());
return "Hello " + name + ", response from provider: " + RpcContext.getContext().getLocalAddress();
}
}
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
<!-- provider's application name, used for tracing dependency relationship -->
<dubbo:application name="demo-provider"/>
<!-- use multicast registry center to export service -->
<!--<dubbo:registry address="multicast://224.5.6.7:1234"/>-->
<dubbo:registry group="providertest" protocol="zookeeper" address="127.0.0.1:2181" />
<!-- use dubbo protocol to export service on port 20880 -->
<dubbo:protocol name="dubbo" port="20880"/>
<!-- service implementation, as same as regular local bean -->
<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>
<!-- declare the service interface to be exported -->
<dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>
</beans>
首先就是要找到其<dubbo:service>的beanDefinitionParse,找到其最終的實現類:
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
static {
Version.checkDuplicate(DubboNamespaceHandler.class);
}
@Override
public void init() {
. . .
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
. . .
}
}
dubbo中的解析器DubboBeanDefinitionParser在上一節中已經講到到,這一篇就不再贅述了。我們可以看到最終根據這個節點生成的是ServiceBean,我們看一下ServierBean繼承類與實現的接口:
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware {
. . .
}
可以看到,ServiceBean實現了Spring中的DisposableBean與ApplicationListener<ContextRefreshedEvent>接口,說明ServiceBean實現了自定義的初始化方法,且監聽了ContextRefreshedEvent事件,也就是說在Spring容器啓動後的事件,我們分別看一下這些方法中做了哪些處理:
@Override
@SuppressWarnings({"unchecked", "deprecation"})
public void afterPropertiesSet() throws Exception {
// 獲取ProtocolConfig、ApplicationConfig ModuleConfig.class、RegistryConfig等信息,這點與ReferenceBean的afterPropertiesSet()方法很像,就不再贅述
if (getProvider() == null) {
. . .
}
if (getApplication() == null
&& (getProvider() == null || getProvider().getApplication() == null)) {
. . .
}
if (getModule() == null
&& (getProvider() == null || getProvider().getModule() == null)) {
. . .
}
if ((getRegistries() == null || getRegistries().isEmpty())
&& (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().isEmpty())
&& (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().isEmpty())) {
. . .
}
. . .
// 是否延遲導出,如果不是,在初始化bean的時候即導出服務
if (!isDelay()) {
export();
}
}
而在ContextRefreshedEvent事件中的處理爲:
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (isDelay() && !isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
因爲Service一定是要導出才能對外提供服務,如果如果要延遲加導出的話,那麼導出動作是在Spring容器啓動完成之後進行的,我們看一下導出做了哪些事情:
protected synchronized void doExport() {
. . .
// 檢查相應組件
checkApplication();
checkRegistry();
checkProtocol();
appendProperties(this);
checkStubAndMock(interfaceClass);
if (path == null || path.length() == 0) {
path = interfaceName;
}
// 將服務進行導出
doExportUrls();
ProviderModel providerModel = new ProviderModel(getUniqueServiceName(), this, ref);
ApplicationModel.initProviderModel(getUniqueServiceName(), providerModel);
}
private void doExportUrls() {
List<URL> registryURLs = loadRegistries(true);
// 每個協議配置都需要單獨導出一次,
for (ProtocolConfig protocolConfig : protocols) {
// 每個協議配置都需要在不同的註冊中心進行註冊
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}
}
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
. . .
String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = this.findConfigedPorts(protocolConfig, name, map);
URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);
String scope = url.getParameter(Constants.SCOPE_KEY);
// don't export when none is configured
if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {
// export to local if the config is not remote (export to remote only when config is remote)
if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
exportLocal(url);
}
// export to remote if the config is not local (export to local only when config is local)
if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
if (logger.isInfoEnabled()) {
logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
}
if (registryURLs != null && !registryURLs.isEmpty()) {
for (URL registryURL : registryURLs) {
url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));
URL monitorUrl = loadMonitor(registryURL);
if (monitorUrl != null) {
url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
}
if (logger.isInfoEnabled()) {
logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
}
// 注意,此invoker不是通過protocol來獲取到的,而是通過proxyFactory,直接得到一個本地ref的代理對象invoker,調用此invoker的方法即最終會調用ref的方法
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
// 將服務進行導出,假如我們的註冊中心是zooKeeper,那麼會將服務註冊早zooKeeper上面
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
} else {
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
Exporter<?> exporter = protocol.export(wrapperInvoker);
exporters.add(exporter);
}
}
}
this.urls.add(url);
}
至此,服務導出完畢