痛点分析
场景分析
日常工作中我们的开发环境会氛围d(研发),t(测试),q(准生产,或者预发环境)和生产环境,同样我们的dubbo注册中心也随着我们的环境的不同而不同.例如会在yml文件中进行如下配置
如果该zk只有你一个人注册,那么在你的应用端掉服务端时肯定只有你一个服务,可是日常开发中都是多个人用同一个zk,其他人的服务也注册到了该zk上,要是你们代码一致,那万事大吉,该怎么测怎么测,可是如果你的代码和别人代码有差异,蛋疼的事情来了,你会发现自己在掉服务的时候时好时坏.于是有了以下的思考
- 让自己的队友暂时不要连zk,自己进行测试
- 自己在本地debug自己的服务,自测验证完成后在提到d环境
貌似第二个方案比较合理,那么怎么才能在本地debug自己的服务呢?
解决方案
- 首先当然是上官网查询看怎么配置咯,官网配置如下dubbo服务直连与分组
再稍微唠叨一下官网的配置吧,具体配置如下
<!--服务直连-->
<dubbo:reference id="xxxService" interface="com.alibaba.xxx.XxxService" url="dubbo://localhost:20890" />
<!--服务分组-->
<dubbo:service group="feedback" interface="com.xxx.IndexService" />
<dubbo:service group="member" interface="com.xxx.IndexService" />
<dubbo:reference id="feedbackIndexService" group="feedback" interface="com.xxx.IndexService" />
<dubbo:reference id="memberIndexService" group="member" interface="com.xxx.IndexService" />
分组还要配置那么多,相信大多数人都选服务直连,这时候又有人问了,小帅我们dubbo用的是注解啊,该怎么配呢? 先来看一下Reference注解里的内容
@Deprecated
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
public @interface Reference {
//...省略若干代码,手动滑稽
String group() default "";
String url() default "";
//...省略若干代码,手动滑稽
}
可以看到Reference注解和xml配置基本一致,只需要配置group或者url属性即可,如下
@Reference(url="dubbo://localhost:20880")
好了,有了这两种法宝,兴致冲冲的本地自测完,提交代码,关机下班😁
回到家中后冥冥之中觉得有点不对劲,猛然一机灵发现自己的reference配置url="dubbo://localhost:20890"竟然也被提交了上去,完了完了,愚蠢的小帅教我这么写,也没让我在提交代码的时候去掉这些配置,等我过去喷一下他,这不是教我每天一个离职小技巧吗?还好发现的比较早,要是代码上线了那可不得了.
所以官网推荐的方法虽然简单,但是还是有风险的,如果你觉得自己比较谨慎,在提交代码代码前一定会检查并去掉这些无用配置的,那么恭喜你,下面的废话可以不用看了,可是你有没有思考过有没有其他方法来规避这种情况呢,不去修改配置文件或者@Reference属性,而且能直连本地,是不是想想都有点小兴奋
- 利用spring框架中的扩展点来丰富bean,并且实现上述功能
刚好小帅研究过一阵spring源码,看完之后总想利用spring的扩展点来搞点事情,可是一直无门,总觉得spring源码白看了,那么我们的dubbo无侵入式本地自测能不能利用spring的扩展点来实现呢,talk is cheap, show me the code
@Component
@Slf4j
public class DubboConfigBeanProcessor implements BeanFactoryPostProcessor, EnvironmentAware {
private Environment environment;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
//如果env值为dev,则给referenceBean添加属性dubbo://localhost:20880
if("dev".equalsIgnoreCase(environment.getProperty("env"))){
Map<String, ReferenceBean> beansOfType = configurableListableBeanFactory.getBeansOfType(ReferenceBean.class);
beansOfType.forEach( (key, value) ->{
value.setUrl("dubbo://localhost:20880");
});
}
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
好了,核心代码就这么点,那么有人会问了你为什么会知道要这么写,这个就要说说spring源码,小帅后续会出点spring源码的文章,在这先简单说点bean加载过程
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
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.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
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) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
关键就在invokeBeanFactoryPostProcessors(beanFactory);这一步中,BeanFactoryPostProcessors是spring提供的一个扩展点,如果有兴趣大家可以在小帅后续的文章中与小帅讨论相关东西.
我是有丶小帅,一个混迹在互联网公司的复制粘贴工程师,如果大家喜欢小帅的原创文章就多多支持啦,我们下一篇见!