1、场景
作为一个网赚类小游戏开发公司,会快速的开发不同的游戏产品,整体架构采用微服务模式,中台模块包括三个功能模块(账户、用户、订单),所有游戏业务项目都与中台交互,从而只关注游戏业务端的功能开发即可。这是根据业务拆分的微服务模块。既然使用了微服务,就涉及到不同服务间的通信,这必然会使用到各种中间件(比如rpc-dubbo/motan,消息中间件-rocketMQ/kafka,Redis等)。
试想几天就要开发一个新产品,如果没有一个专门的中间件集成框架,这些重复的中间件配置管理起来既混乱,又浪费开发人员时间。
2、集成框架pepper-boot
集成框架最初命名caf,其中集成了apollo,motan,redis,rocketMQ,kafka,mybatis,prometheus(业务性能监控),后面将插件的集成抽出为pepper-boot,监控抽出为pepper-metrics,这里我们只介绍pepper-boot,pepper-metrics可阅读metrics模块。
所有产品要做到独立隔离,自然就会想到namespace的概念,因此项目的配置、包括实例名都是通过namespace进行隔离的,每个项目拥有唯一的namespqce。理解了这一点对于下面涉及到的参数解析和实例名称的设置就清楚了。
motan集成
创建了一个启用注解@EnableMotan
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Repeatable(EnableMotans.class) @Import(MotanRegistrar.class) public @interface EnableMotan { String namespace() default "default"; }
其中通过@Import注入了MotanRegistrar类,这个类就是在注册类元信息的时候注册响应的配置处理类
public class MotanRegistrar extends AbstractRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { final String annotationName = EnableMotan.class.getName(); final String annotationsName = EnableMotans.class.getName(); registerBeanDefinitions(importingClassMetadata, registry, annotationName, annotationsName); } protected void dealOne(BeanDefinitionRegistry registry, AnnotationAttributes oneAttributes) { String namespace = oneAttributes.getString("namespace"); Assert.isTrue(StringUtils.isNotEmpty(namespace), "namespace must be specified!");
//可以看到这里注册了一个后置处理器,作用就是将apollo中配置的参数设置到各个config中 BeanRegistrationUtil.registerBeanDefinitionIfBeanNameNotExists( registry, namespace + MotanBeanPostProcessor.class.getSimpleName(), MotanBeanPostProcessor.class ); //注册中心配置Bean,实例名加前缀namespace BeanRegistrationUtil.registerBeanDefinitionIfBeanNameNotExists( registry, namespace + RegistryConfigBean.class.getSimpleName(), RegistryConfigBean.class );
//协议配置Bean BeanRegistrationUtil.registerBeanDefinitionIfBeanNameNotExists( registry, namespace + ProtocolConfigBean.class.getSimpleName(), ProtocolConfigBean.class );
//服务端配置Bean BeanRegistrationUtil.registerBeanDefinitionIfBeanNameNotExists( registry, namespace + BasicServiceConfigBean.class.getSimpleName(), BasicServiceConfigBean.class );
//客户端配置Bean BeanRegistrationUtil.registerBeanDefinitionIfBeanNameNotExists( registry, namespace + BasicRefererConfigBean.class.getSimpleName(), BasicRefererConfigBean.class ); } }
这里说下ImportBeanDefinitionRegistrar,spring提供这个类主要是用来动态注册bean的。
所有实现了该接口的类的都会被ConfigurationClassPostProcessor处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中动态注册的bean是优先与依赖其的bean初始化的,也能被aop、validator等机制处理。
另外可以看到在里面注册了一个后置处理器
public class MotanBeanPostProcessor extends BaseMotanConfiguration implements BeanPostProcessor, Ordered, EnvironmentAware, BeanFactoryAware { @Autowired protected CustomizedPropertiesBinder binder; private Environment environment; private BeanFactory beanFactory; public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof RegistryConfigBean) { String sourceName = StringUtils.substringBefore(beanName, RegistryConfigBean.class.getSimpleName()); RegistryConfigBean registryConfigBean = (RegistryConfigBean) bean; initRegistryConfig(registryConfigBean); String globalPrefix = PREFIX_APP_MOTAN + ".registry"; String nsPrefix = PREFIX_APP_MOTAN + "." + sourceName + ".registry"; if (StringUtils.isAllEmpty(environment.getProperty(globalPrefix + ".address"), environment.getProperty(nsPrefix + ".address"))) { throw new IllegalArgumentException(String.format("%s or %s can not be null!!", globalPrefix, nsPrefix)); } if (StringUtils.isNotEmpty(environment.getProperty(globalPrefix + ".address"))){ Bindable<?> target = Bindable.of(RegistryConfigBean.class).withExistingValue(registryConfigBean); binder.bind(globalPrefix, target); } Bindable<?> target = Bindable.of(RegistryConfigBean.class).withExistingValue(registryConfigBean); binder.bind(nsPrefix, target); } else if (bean instanceof ProtocolConfigBean) { String sourceName = StringUtils.substringBefore(beanName, ProtocolConfigBean.class.getSimpleName()); ProtocolConfigBean protocolConfigBean = (ProtocolConfigBean) bean; initProtocolConfig(protocolConfigBean); Bindable<?> target = Bindable.of(ProtocolConfigBean.class).withExistingValue(protocolConfigBean); binder.bind(PREFIX_APP_MOTAN + "." + sourceName + ".protocol", target); protocolConfigBean.setBeanName(beanName); } else if (bean instanceof BasicServiceConfigBean) { String sourceName = StringUtils.substringBefore(beanName, BasicServiceConfigBean.class.getSimpleName()); String registryBeanName = sourceName + RegistryConfigBean.class.getSimpleName(); RegistryConfigBean registryConfigBean = beanFactory.getBean(registryBeanName, RegistryConfigBean.class); Assert.notNull(registryConfigBean, String.format("%s does not existed in spring context, pls check!", registryBeanName)); String protocolBeanName = sourceName + ProtocolConfigBean.class.getSimpleName(); ProtocolConfigBean protocolConfigBean = beanFactory.getBean(protocolBeanName, ProtocolConfigBean.class); Assert.notNull(protocolConfigBean, String.format("%s does not existed in spring context, pls check!", protocolBeanName)); String portKey = PREFIX_APP_MOTAN + "." + sourceName + ".port"; String port = environment.getProperty(portKey); if (StringUtils.isEmpty(port)) { port = "10010"; } Assert.isTrue(StringUtils.isNotEmpty(port) && NumberUtils.isCreatable(port), String.format("%s=%s must be not null! and must be a number!", portKey, port)); BasicServiceConfigBean basicServiceConfigBean = (BasicServiceConfigBean) bean; initBasicServiceConfig(registryConfigBean, protocolConfigBean, Integer.parseInt(port), basicServiceConfigBean); Bindable<?> target = Bindable.of(BasicServiceConfigBean.class).withExistingValue(basicServiceConfigBean); binder.bind(PREFIX_APP_MOTAN + "." + sourceName + ".basic-service", target); } else if (bean instanceof BasicRefererConfigBean) { String sourceName = StringUtils.substringBefore(beanName, BasicRefererConfigBean.class.getSimpleName()); String registryBeanName = sourceName + RegistryConfigBean.class.getSimpleName(); RegistryConfigBean registryConfigBean = beanFactory.getBean(registryBeanName, RegistryConfigBean.class); Assert.notNull(registryConfigBean, String.format("%s does not existed in spring context, pls check!", registryBeanName)); String protocolBeanName = sourceName + ProtocolConfigBean.class.getSimpleName(); ProtocolConfigBean protocolConfigBean = beanFactory.getBean(protocolBeanName, ProtocolConfigBean.class); Assert.notNull(protocolConfigBean, String.format("%s does not existed in spring context, pls check!", protocolBeanName)); BasicRefererConfigBean basicRefererConfigBean = (BasicRefererConfigBean) bean; initBasicRefererConfig(registryConfigBean, protocolConfigBean, basicRefererConfigBean); Bindable<?> target = Bindable.of(BasicRefererConfigBean.class).withExistingValue(basicRefererConfigBean); binder.bind(PREFIX_APP_MOTAN + "." + sourceName + ".basic-referer", target); } return bean; } @Override public int getOrder() { return 0; } @Override public void setEnvironment(Environment environment) { this.environment = environment; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } }