摘要:最近一个使用的项目中需要进行性能调优。是不允许进入进行操作的。如上的写法表示和是启用的。从开始提供的注解。最后启用这两个在项目启动入口小结通过上述几步,在项目启动时通过注解的条件判断,实现不同的装配,从而启用不同的。
最近一个使用Spring的项目中需要进行性能调优。方式基本上是编写新的代码实现原来一样的业务逻辑,只是实现方式有一些调整,例如增加cache,优化算法等等。
一开始大家希望直接在原有代码基础上修改,但是这样一来,就要跟上每周一次的发布节奏,一周搞定难度太大。于是决定拷贝出的package来重构。在没启用之前这个package下都是dead code。这样做的好处有几点:
在调优后的code启用前,业务至少不会受影响。
利用docker的特性,可以实现灰度发布,比如启动两个docker,一个是老的code,一个启用新的code,利用nginx实现分流。
灰度发布后发现有紧急bug,只需要devOps修改一点配置,重启docker可以再切回老的code。
出发点既然要实现上述第三点,也就是利用配置来实现切换,那么这个Enable的flag就不应该写到代码里,甚至是配置文件里,因为项目启动都是在docker中通过spring-boot的cmd直接启动的。DevOps是不允许进入docker进行操作的。
实现想到我们的整个部署架构是基于Kubernetes的,可以通过修改工程的deployment.yaml文件来实现。原理就是deployment里面设置一个docker的Env,Key是FeatureToggle,Value可以是这样FeatureA,FeatureB ,当docker启动时,JVM(Java代码)可以通过System.getenv()来获得环境变量,从来知道这个Feature是需要启用还是不启用。如上的写法表示FeatureA和FeatureB是启用的。
我们可以写一个简单的接口实现来判断:
public boolean isFeatureEnable(String featureName) { // 用System.getenv("FeatureToggle")读取环境变量判断是否包含参数的featureName // ... }进一步使用
虽然我们有了判断方法,但是因为项目组的人都有洁癖,我们不希望代码中到处都是
if(isFeatureEnable(featureA)) { // new code } else { // old code }
这样实在是太ugly了。
我们需要利用spring的IoC特性来切换implementations。Spring从4.0开始提供Conditional的注解。结合@Configuration就可以实现app启动时的不同Bean的注入。
写一个FeatureA的Condition Class
public class FeatureACondition implements Condition{ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { return isFeatureEnable("featureA") } }
再写一个Spring的Configuration来使用这个Condition
@Configuration @Conditional(FeatureACondition.class) public class FeatureAConfiguration { @Bean(name="bizService") public BizService bizService(){ return new EnhancedBizService(); } }
当然如果要实现互斥的切换,即启用FeatureA另一个Bean就不能加载的话,那么再写一个NotFeatureA的Configuration就可以了。
@Configuration @Conditional(NotFeatureACondition.class) public class NotFeatureAConfiguration { @Bean(name="bizService") public BizService bizService(){ return new OldBizService(); } }
这样一来,当FeatureA启用时BizService这个interface的实现就是EnhancedBizService,反之它的实现就是OldBizService。
当然你在configuration上用@ComponentScan,@Import等等都是没问题的,在启动时都会最先判断Conditional,如果不满足spring根本不会继续下面的扫描或者加载操作。
最后启用这两个Config
在项目启动入口
@SpringBootApplication @Import({NotFeatureAConfiguration.class, FeatureAConfiguration.class}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }小结
通过上述几步,在spring项目启动时通过conditional注解的条件判断,实现不同Bean的装配,从而启用不同的Feature。
对于Devops而言,只需要在deployment里面修改Env的内容,再重启deploy这个app就可以实现Feature Toggle了。即使你不使用Kubernetes,docker-compose也是一样的道理。
通过修改docker-compose.yml实现:
environment: - FeatureToggle=FeatureA,FeatureB - SESSION_SECRET
总而言之就是充分利用OO语言的优势,实现可拔插的FeatureToggle。接下来我们还会继续研究如何Runtime的启用Feature,我也发现了一个已有的轮子togglz。如果有朋友用过,欢迎反馈使用感受。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/65246.html
摘要:顾名思义,其主要作用是解析标签。本例中没有用到上述的注解,所以均为。继续追踪这行代码的内部实现获取的名称调用的方法注册过程见处理的别名,本例中没有别名,不进入循环的具体内容有待研究,不展开。到此为止,已经被注册到中。 接上篇 3 reader 注册配置类 该 part 的起点: public AnnotationConfigApplicationContext(Class... ann...
摘要:用法先创建个组件,,,分别在类上加上注解。发现有一个属性源码注释这样说的自动检测使用组件。在的方法中,表示不匹配,代表匹配。这就说明使用注册组件有种方式。 Spring注解应用篇--IOC容器Bean组件注册 这是Spring注解专题系类文章,本系类文章适合Spring入门者或者原理入门者,小编会在本系类文章下进行企业级应用实战讲解以及spring源码跟进。 环境准备 编译器IDEA...
摘要:这里有一个参数,主要是用来指定该配置项在配置文件中的前缀。创建一个配置类,里面没有显式声明任何的,然后将刚才创建的导入。创建实现类,返回的全类名。创建实现类,实现方法直接手动注册一个名叫的到容器中。前言 小伙伴们是否想起曾经被 SSM 整合支配的恐惧?相信很多小伙伴都是有过这样的经历的,一大堆配置问题,各种排除扫描,导入一个新的依赖又得添加新的配置。自从有了 SpringBoot 之后,咋...
摘要:通过操作系统进行条件判断,从而进行配置。分别对布尔,字符串和数字三种类型进行判断。通过指定的资源文件是否存在进行条件判断,比如判断来决定是否自动装配组件。判断当前环境是否是应用。 Spring Boot中的那些Conditional spring boot中为我们提供了丰富的Conditional来让我们得以非常方便的在项目中向容器中添加Bean。本文主要是对各个注解进行解释并辅以代码...
阅读 3448·2021-09-02 09:53
阅读 1767·2021-08-26 14:13
阅读 2705·2019-08-30 15:44
阅读 1288·2019-08-30 14:03
阅读 1934·2019-08-26 13:42
阅读 2992·2019-08-26 12:21
阅读 1288·2019-08-26 11:54
阅读 1888·2019-08-26 10:46