文章目录
- spring + dubbo provider启动过程
- 前言
- 说明
- 准备阶段
- 环境准备
- 样例代码
- 步骤
- 加载配置文件
- 解析xml标签
- spring容器加载所有单列对象
- 加载dubbo服务
- 服务的创建
- 服务的初始化
- 服务暴露
- doExportUrlsFor1Protocol分析
- 总结
- 参考
spring + dubbo provider启动过程
前言
今天是卸载lol的第一天,在此定一个目标,不拿offer,不重装游戏。本着轻松写技术文章的原则,以后前言部分宏小白我都说一些有的没得,如果有幸看到我的文章,希望大家都是有共同爱好,可以畅所欲言,而不是为了生活孤独着煎熬的前行。
如果你玩lol, 在lol你最喜欢玩的英雄是啥,宏小白最喜欢玩的是”文森特“,怀恋有杀人刀的日子,一刀一个小朋友。
欢迎来到dubbo provider启动分析(de lai lian meng)
说明
本次分析的是dubbo2.6.1中dubbo-demo-provider的启动过程,配置文件采用xml的方式,过程比较枯燥,但可以让我们加深理解下spring容器初始化过程,以及结合dubbo,是如何完成服务的注册的,还有如何使用netty完成服务提供者的启动。
准备阶段
环境准备
本地一份dubbo的代码 2.6.1
自行安装zookeeper
开发工具 idea或者eclipse
样例代码
启动类入口Provider.java
public class Provider {
public static void main(String[] args) throws Exception {
//Prevent to get IPV6 address,this way only work in debug mode
//But you can pass use -Djava.net.preferIPv4Stack=true,then it work well whether in debug mode or not
System.setProperty("java.net.preferIPv4Stack", "true");
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-provider.xml"});
context.start();
//
ProtocolConfig.destroyAll();
System.in.read(); // press any key to exit
}
}
配置文件 dubbo-demo-provider.xml
详细的xml配置参考dubbo官方网站
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/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-2.5.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- provider's application name, used for tracing dependency relationship -->
<dubbo:application name="demo-provider" logger="jcl"/>
<!--提供者配置 delay设为-1时,表示延迟到Spring容器初始化完成时暴露服务 -->
<dubbo:provider delay="-1" retries="0"
>
</dubbo:provider>
<!--注册中心 -->
<dubbo:registry address="zookeeper://127.0.0.1:2181"
/>
<bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>
<!--对外暴露的一个服务 -->
<dubbo:service
interface="com.alibaba.dubbo.demo.DemoService" group="g1" ref="demoService" filter="demo" deprecated="false" callbacks="1000" timeout="200000" accesslog="true">
<!--方法配置 -->
<dubbo:method name="say01" deprecated="true" />
<!--协议配置 -->
</dubbo:service>
<dubbo:protocol accesslog="true" name="dubbo" port="-1" server="netty4" />
</beans>
服务调用过程不是本文分析的内容,这边不贴对应的code。
调整日志级别为debug
步骤
加载配置文件
进入容器构造方法
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
//加载xml文件 可以是多个
setConfigLocations(configLocations);
if (refresh) {
//容器启动 重点方法
refresh();
}
}
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//构造beanFactory 完成xml文件标签的解析
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
// 子类方法实现
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//先执行beanFactoryPostProcessors接口 再执行BeanPostProcessors接口
//dobbo2.6版本中未定义
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//dobbo2.6版本中未定义
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
// 初始国际化配置 未定义使用默认的DelegatingMessageSource
initMessageSource();
// Initialize event multicaster for this context.
// 未定义使用默认的SimpleApplicationEventMulticaster
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
// 子类方法实现
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
// 实列化未配置懒加载的单列类 重点类
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.完成事件发送
finishRefresh();
}
catch (BeansException ex) {
........
finally {
........
}
}
}
解析xml标签
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
spring容器加载所有单列对象
针对xml文件依次加载对应的配置类
调用流程
单列加载过程 详细步骤不展开
AbstractApplicationContext.finishBeanFactoryInitialization();
beanFactory.preInstantiateSingletons();
this.getBean(beanName);
this.doGetBean(name, (Class)null, (Object[])null, false);
this.getSingleton(beanName, new ObjectFactory<Object>()
AbstractBeanFactory.this.createBean(beanName, mbd, args);
// 这一步可能生成代理类
this.resolveBeforeInstantiation(beanName, mbdToUse);
this.doCreateBean(beanName, mbdToUse, args);
最开始加载ApplicationConfig
加载dubbo服务
服务的创建
当加载beanName为com.alibaba.dubbo.demo.DemoService时,可以看出其对应是一个ServiceBean类
其中包括方法配置类,接口配置类,服务配置类,以及实现spring相关的aware接口,spring的相关接口会在spring的生命周期触发对应的方法
接着跟踪代码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {
instanceWrapper = (BeanWrapper)this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
// 创建单列的实列 返回的是一个BeanWrapper 装饰者模式
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
reslolved为true autowiredNecessary为false 所以进入this.instantiateBean方法
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
try {
Object beanInstance;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return AbstractAutowireCapableBeanFactory.this.getInstantiationStrategy().instantiate(mbd, beanName, AbstractAutowireCapableBeanFactory.this);
}
}, this.getAccessControlContext());
} else {
// 选择一个类初始化策略 进行初始化
beanInstance = this.getInstantiationStrategy().instantiate(mbd, beanName, this);
}
//初始化的类包装进一个beanWrapper
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
this.initBeanWrapper(bw);
return bw;
} catch (Throwable var6) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", var6);
}
}
调用BeanUtils采用默认的构造函数初始化ServiceBean 这时我们可以进入到SeviceBean类中了。
其中父类静态包含以下两个方法
/**
* 自适应 Protocol 实现对象
* 使用javaassit字节码工具生成Protocol$Adaptive类 生成有Adaptive注解的方法
*/
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
/**
* 自适应 ProxyFactory 实现对象 ProxyFactory$Adaptive
*/
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
执行完构造函数以后
满足三个条件后singletonfactories加入该类用来解决循环依赖问题
//单列+允许循环依赖+当前类正在创建
mbd.isSingleton() && this.allowCircularReferences && this.isSingletonCurrentlyInCreation(beanName);
服务的初始化
由于ServiceBean实现了InitializingBean接口,接着初始化过程进入afterPropertiesSet方法
类属性转载中可能包含多个bean的加载 包含其中配置的方法
<dubbo:method name=“say01” deprecated=“true” />
该方法我们单独拿出来看
public void afterPropertiesSet() throws Exception {
//如果provider为空,从spring容器中拿到provider配置并进行设置
........
//如果Application配置为空 从容器拿到并配置
........
//设置module模块信息配置 可以为空
........
//设置registry中心
//设置监视器
..........
//设置协议
.........
//非延迟,直接暴露服务
if (!isDelay()) {
//服务暴露 重点方法
export();
}
}
服务暴露
由于我们初始设置了delay="-1",服务的暴露阶段会在spring所有非懒加载的单列bean厚的阶段执行
由于实现了ApplicationListener的接口,监听了对应的ContextRefreshedEvent事件,我们又进入了ServiceBean方法体中
public void onApplicationEvent(ContextRefreshedEvent event) {
if (isDelay() && !isExported() && !isUnexported()) {
if (logger.isInfoEnabled()) {
logger.info("The service ready on spring started. service: " + getInterface());
}
export();
}
}
/**
* 暴露服务 synchronized修饰,保证线程安全
*/
public synchronized void export() {
........
// 延迟暴露
if (delay != null && delay > 0) {
delayExportExecutor.schedule(new Runnable() {
public void run() {
doExport();
}
}, delay, TimeUnit.MILLISECONDS);
// 立即暴露
} else {
//方法逻辑与spring类似 核心逻辑在doXXX中
doExport();
}
}
doExport()方法核心逻辑
/**
* 执行暴露服务
*/
protected synchronized void doExport() {
...........
// 校验 ApplicationConfig 配置。
checkApplication();
// 校验 RegistryConfig 配置。
checkRegistry();
// 校验 ProtocolConfig 配置数组。
checkProtocol();
// 读取环境变量和 properties 配置到 ServiceConfig 对象。
appendProperties(this);
// 校验 Stub 和 Mock 相关的配置
checkStubAndMock(interfaceClass);
// 服务路径,缺省为接口名
if (path == null || path.length() == 0) {
path = interfaceName;
}
// 暴露服务
doExportUrls();
// 等待 qos
ProviderModel providerModel = new ProviderModel(getUniqueServiceName(), this, ref);
ApplicationModel.initProviderModel(getUniqueServiceName(), providerModel);
}
private void doExportUrls() {
//获取所有注册中心
List<URL> registryURLs = loadRegistries(true);
// @1
for (ProtocolConfig protocolConfig : protocols) {
//依次暴露
doExportUrlsFor1Protocol(protocolConfig, registryURLs);
// @2
}
}
生成的registryUrl格式为:
registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&logger=jcl&pid=19364&qos.port=22222®istry=zookeeper×tamp=1585492774389
doExportUrlsFor1Protocol分析
调用链
ServiceBean#onApplicationEvent
ServiceConfig#export
ServiceConfig#doExport
doExportUrlsFor1Protocol
-
用Map存储该协议的所有配置参数,包括协议名称、dubbo版本、当前系统时间戳、进程ID、application配置、module配置、默认服务提供者参数(ProviderConfig)、协议配置、服务提供Dubbo:service的属性。
-
如果dubbo:service有dubbo:method子标签,则dubbo:method以及其子标签的配置属性,都存入到Map中,属性名称加上对应的方法名作为前缀。dubbo:method的子标签dubbo:argument,其键为方法名.参数序号。
-
添加methods键值对,存放dubbo:service的所有方法名,多个方法名用,隔开,如果是泛化实现,填充genric=true,methods为"*";
-
根据是否开启令牌机制,如果开启,设置token键,值为静态值或uuid。
-
如果协议为本地协议(injvm),则设置protocolConfig#register属性为false,表示不向注册中心注册服务,在map中存储键为notify,值为false,表示当注册中心监听到服务提供者发送变化(服务提供者增加、服务提供者减少等事件时不通知。
-
解析服务提供者的IP地址与端口。
-
根据协议名称、协议host、协议端口、contextPath、相关配置属性(application、module、provider、protocolConfig、service及其子标签)构建服务提供者URI。
-
获取dubbo:service标签的scope属性,其可选值为none(不暴露)、local(本地)、remote(远程),如果配置为none,则不暴露。默认为local。
-
根据scope来暴露服务,如果scope不配置,则默认本地与远程都会暴露,如果配置成local或remote,那就只能是二选一。
具体服务暴露使用了自适应的类还有动态代理类JavassistProxyFactory 笔者再多准备一些知识后文进行讲解补充
Protocol A d a p t i v e 和 P r o x y F a c t o r y Adaptive和 ProxyFactory Adaptive和ProxyFactoryAdaptive
本地服务暴露
/**
* Invoker. (API/SPI, Prototype, ThreadSafe)
*
* Invoker 是实体域,它是 Dubbo 的核心模型,其它模型都向它靠扰,或转换成它。
* 它代表一个可执行体,可向它发起 invoke 调用。
* 它有可能是一个本地的实现,也可能是一个远程的实现,也可能一个集群实现。
*
* @see com.alibaba.dubbo.rpc.Protocol#refer(Class, com.alibaba.dubbo.common.URL)
* @see com.alibaba.dubbo.rpc.InvokerListener
* @see com.alibaba.dubbo.rpc.protocol.AbstractInvoker
*/
public interface Invoker<T> extends Node {
/**
* get service interface.
*
* @return service interface.
*/
Class<T> getInterface();
/**
* invoke.
*
* @param invocation
* @return result
* @throws RpcException
*/
Result invoke(Invocation invocation) throws RpcException;
}
待续。。。
总结
这里主要笔者也想熟悉下spring容器的生命周期,所以没有完全按照第二个参考文章的流程跑一变。另外笔者在debug觉得绕来绕去很是头疼,这边也总结一下个人方法,再调试断点的时候,应该先抓住主干流程,不要一直往里面走,尤其是这种开源框架,调用层次很深,有时候就不知道把自己带到那里的,所以要有效的结合debug的堆栈信息,当不小心进入到方法体,想直接出来的时候,告诉大家一个小技巧 drop frame,可以退出当前方法栈帧,回到上一个方法。
参考
dubbo官方网站
java知音 ——知音不火,天理难容。
最后
以上就是糟糕鸭子最近收集整理的关于spring + dubbo provider启动过程spring + dubbo provider启动过程的全部内容,更多相关spring内容请搜索靠谱客的其他文章。
发表评论 取消回复