在业务系统中实现对已有的各个业务校验规则Rule的增强,因为太多的Rule实现依赖了外部系统而变得不可控,并且系统对规则基本定位成强校验,这样我们系统的可用性以及稳定性会被外部系统所左右,于是提出了对规则可以动态降级,实现运行时绕过一些规则的校验(当然,需要在业务容忍一致性和系统可用性之间权衡)。同事的想法:提供一个基类来负责执行是否降级的功能,然后每个具体的实现类继承这个基类,在执行真正的规则校验逻辑之前,调用父类的方法判断是否走校验。我觉得这样做的问题有两点,首先,规则本身不应该去关注降级的问题,这样规则的职责更加纯粹。其次,之前已经有很多规则实现,这样做要去改变之前的规则实现,而且是通过继承的方式来修改,但是JAVA受限于单继承,同时对每个实现的改动都是类似的,比较机械化。考虑了下,决定使用动态代理对规则实现降级管理。代码实现很简单:
public class DegraderableRuleWrapper implements FactoryBean, BeanNameAware {
private final static Logger LOG = LoggerFactory.getLogger(DegraderableRuleWrapper.class);
private final static String ITEM_RULE_NAME_SKIP_NAME = "com.***.ruleSkipNameList";
/**
* 需要跳过的规则列表
*/
private static Set<String> skipRuleNameList = Sets.newHashSet();
/**
* 用于分隔字符串的工具类
*/
private static final Splitter SPLITTER = Splitter.on(',').omitEmptyStrings().trimResults();
private Rule target;
private String ruleName;
private Object delegate;
public void setTarget(Rule target) {
this.target = target;
//ruleName = target.getClass().getSimpleName();
}
@Override
public void setBeanName(String name) {
ruleName = name;
}
@Override
public Object getObject() throws Exception {
if (delegate == null) {
delegate = createProxy();
}
return delegate;
}
private Object createProxy() {
@SuppressWarnings("unchecked")
Class<?>[] allInterfaces = (Class<?>[]) ClassUtils.getAllInterfaces(target.getClass()).toArray(new Class<?>[0]);
Object wrapperedRule = Proxy.newProxyInstance(target.getClass().getClassLoader(), allInterfaces, new SwitchListener());
LOG.warn("create a degraderableRule for rule {}.", ruleName);
return wrapperedRule;
}
@Override
public Class<? extends Rule> getObjectType() {
return Rule.class;
}
@Override
public boolean isSingleton() {
return false;
}
static{
initialSkipRuleListAndListenChange();
}
private static void initialSkipRuleListAndListenChange() {
/*
* 开始并不主动拉取,而是注册监听,等待异步回调,减少应用启动时间。
*/
Diamond.addListener(ITEM_RULE_NAME_SKIP_NAME, Constants.DEFAULT_GROUP, new ManagerListenerAdapter() {
@Override
public void receiveConfigInfo(String configInfo) {
LOG.warn("Got new skipRuleNameList, " + configInfo);
Iterable<String> ruleNames = SPLITTER.split(configInfo);
Set<String> newSkipRuleNames = Sets.newHashSet(ruleNames);
skipRuleNameList = newSkipRuleNames;
}
});
}
private class SwitchListener implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (skipRuleNameList.contains(ruleName)) {
LOG.warn("Rule {} is in skipList, skip it.", ruleName);
return RuleResult.successResult();
}
return method.invoke(target, args);
}
}
}
实现中使用了spring的factorybean,beanname,google的guava工具类以及淘宝开源的diamond集中式静态配置管理工具。PS:diamond主要是实现对集群的配置管理,其他的方式也很多,比如使用zookeeper也可以,当然使用存储+线程定时轮询变化(其实diamond的实现方式就很类似,只不过加上了比较多的容灾功能)也OK。
对于实现需要注意的是这行代码:
Class<?>[] allInterfaces = (Class<?>[]) ClassUtils.getAllInterfaces(target.getClass()).toArray(new Class<?>[0]);
开始的时候直接使用
target.getClass().getInterfaces()
发现继承过来的实现,获取不到类实现的接口,也算是细节的一个点吧。
使用这个规则的地方只需要配置下这个bean即可,配置方式如下:
<bean id="testPlatformRule" class="com.***.rule.DegraderableRuleWrapper">
<property name="target">
<bean class="com.***.rule.impl.TestPlatformRule" />
</property>
</bean>
这样其他地方在引用这个bean的时候就已经是进行了降级管理的Rule了,这样实现了降级功能的统一管理和规则本身的职责单一性。