dubbo系列七dubbo tag路由扩展
Posted 不晓得侬 Blog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dubbo系列七dubbo tag路由扩展相关的知识,希望对你有一定的参考价值。
dubbo tag路由扩展
1.前言
dubbo tag路由用着简单清晰,工作中我们常使用tag路由进行流量隔离,比如多套测试环境,使用dubbo治理平台通过路由规则又麻烦,但是tag路由有两个问题:
1.写着有点麻烦,每次调用要显示的RpcContext.getContext().setAttachment("dubbo.tag", "xxx");
,才行,那么有没有办法可以只是设置下配置就可以实现呢?
2.在consumer一个方法内多处请求provider,第一次请求consumer 端的 dubbo.tag 通过 dubbo 的 attachment 携带给 provider 端,但是请求结束就被ConsumerContextFilter清空了attachment ,因此第二次开始就没有了dubbo.tag携带,这个问题有没方便办法解决?
2.tag路由扩展
针对上述问题:可以写个consumer端filter,每次从环境获取dubbo.tag设置到attachment ,但是,每个项目组都这么做,也麻烦,不够方便和统一,下面介绍一种简单方法,通过配置项
2.1.consumer端设置
consumer请求provider的流程是:
MockClusterInvoker->FailoverClusterInvoker->RegistryDirectory获取引用的Invoker->TagInvoker路由过滤Invoker集合->负载均衡选取Invoker->filter chain->DubboInvoker
那么设置在哪里呢?当然在filter chain可以实现,但是前面也说了,不够统一和方便,那么就只能是在FailoverClusterInvoker前面了,那么设置个TagInvoker,如何生成呢?就使用TagClusterWrapper了,wrapper是个装饰对象,在加载默认的FailoverCluster的时候dubbo spi会自动加载。代码如下
@Component
@ConfigurationProperties(prefix = "zzz.dubbo")
public class DubboProperties {
/** 统一服务端和消费端Tag */
private String tag;
/** 是否强制根据tag选择服务 */
private Boolean tagforce;
//标签路由
public static String consumerTag;
public static Boolean consumerTagforce;
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
this.consumerTag = tag;
}
public Boolean getTagforce() {
return tagforce;
}
public void setTagforce(Boolean tagforce) {
this.tagforce = tagforce;
this.consumerTagforce = tagforce;
}
}
public class TagClusterWrapper implements Cluster {
//需要在META-INF/dubbo/com.alibaba.dubbo.rpc.cluster.Cluster文件内配置tagClusterWrapper=org.pangu.client.dubbo.TagClusterWrapper
private Cluster cluster;
public TagClusterWrapper(Cluster cluster) {
this.cluster = cluster;
}
@Override
public <T> Invoker<T> join(Directory<T> directory) throws RpcException {//RegistryProtocol.doRefer(Cluster, Registry, Class<T>, URL)操作内cluster.join(directory)调用
Invoker<T> targetInvoker = cluster.join(directory);
return new TagInvoker<>(targetInvoker);
}
}
public class TagInvoker<T> implements Invoker<T> {
private static final Logger logger = LoggerFactory.getLogger(TagInvoker.class);
private Invoker<T> invoker;
public TagInvoker(Invoker<T> invoker) {
this.invoker = invoker;
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
if (StringUtils.hasText(DubboProperties.consumerTag)) {
RpcContext context = RpcContext.getContext();
if (!context.getAttachments().containsKey(Constants.TAG_KEY)) {
context.setAttachment(Constants.TAG_KEY, DubboProperties.consumerTag);
}
}
if (DubboProperties.consumerTagforce != null && DubboProperties.consumerTagforce) {
RpcContext context = RpcContext.getContext();
if (!context.getAttachments().containsKey(Constants.FORCE_USE_TAG)) {
context.setAttachment(Constants.FORCE_USE_TAG, "true");
}
}
Result result;
try {
result = invoker.invoke(invocation);
} catch (RpcException e) {
String message = e.getMessage();
if (message != null && message.contains("No provider available for")) {
RpcContext context = RpcContext.getContext();
String consumerTag = context.getAttachments().get(Constants.TAG_KEY);
if (consumerTag != null) {
logger.error("No provider available with Tag : {}", consumerTag);
} else if ("true".equals(context.getAttachments().get(Constants.FORCE_USE_TAG))) {
logger.error("\'tag-force\' equals true, you must specify a tag name");
}
}
throw e;
}
return result;
}
//其它@Override忽略
}
使用方法,在属性文件配置zzz.dubbo.tag=xxx
即可。
这样consumer端每次请求就变为了MockClusterInvoker->TagInvoker->FailoverClusterInvoker,在TagInvoker内给增加tag,使用就很方便了。
2.2.provider端设置
上述设置,需要在代码进行硬编码@Service(tag="xxx")
,硬编码不好,也可以通过属性来设置tag,比如dubbo.provider.tag=xxx
,这样就可以。但是,有没有更傻瓜式的操作,consumer和provider端都设置比如zzz.dubbo.tag=xxx
,就会给dubbo provider service增加tag,这样设置也都相同。实际provider端这样设置就等同生成一个ProviderConfig。代码如下
@Component
public class TagDubboConfigBeanCustomizer implements DubboConfigBeanCustomizer{
@Autowired
private DubboProperties dubboProperties;//和consumer端的DubboProperties相同
@Override
public int getOrder() {
return 0;
}
@Override
public void customize(String beanName, AbstractConfig dubboConfig) {
if (dubboConfig instanceof ProviderConfig) {
providerConfig((ProviderConfig) dubboConfig);
}
}
private void providerConfig(ProviderConfig providerConfig) {
if (StringUtils.isEmpty(providerConfig.getTag())
&& StringUtils.hasText(dubboProperties.getTag())) {
providerConfig.setTag(dubboProperties.getTag());//给provider设置tag
}
}
}
这样在provider端设置属性zzz.dubbo.tag=xxx
即可给provider增加tag为xxx,和consumer端保持一致。
2.3.总结
consumer和provider端代码可以提取作为被依赖的基础jar,当然作为基础依赖,不能直接在TagDubboConfigBeanCustomizer加上@Component,而是要使用自动装配进行注册为bean。
2.4.dubbo属性自动装配说明
在进行provider更改属性进行配置,涉及到了DubboConfigBeanCustomizer,这个用得少,记录下。
dubbo springboot自动装配开启DubboAutoConfiguration,DubboAutoConfiguration内又@EnableDubboConfig,因此注册bean DubboConfigConfiguration.Single,其上注释为
@EnableDubboConfigBindings({
@EnableDubboConfigBinding(prefix = "dubbo.application", type = ApplicationConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.module", type = ModuleConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.registry", type = RegistryConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.protocol", type = ProtocolConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.monitor", type = MonitorConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.provider", type = ProviderConfig.class),
@EnableDubboConfigBinding(prefix = "dubbo.consumer", type = ConsumerConfig.class)
})
public static class Single {
}
//处理了@EnableDubboConfigBindings,那么@EnableDubboConfigBinding的import就不会执行。因为开启的是@EnableDubboConfigBindings,因此EnableDubboConfigBinding的import就不会执行
@EnableDubboConfigBindings 引入 DubboConfigBindingsRegistrar,注册dubbo ConfigBean和DubboConfigBindingBeanPostProcessor,其中dubbo
Configbean就是ApplicationConfig、ProviderConfig等,DubboConfigBindingBeanPostProcessor则是用于属性绑定,比如默认dubbp.provider开头属性绑定到ProviderConfig上。当然也支持自定义,因此自定义DubboConfigBeanCustomizer就可以实现把自定义的属性进行绑定到ProviderConfig。
2.5.springboot Binder使用说明
在dubbo自动装配启动过程中,使用到了配置的绑定,就是使用的Binder,因此记录下Binder。
Binder的使用其实比较简单 有点类似注解ConfigurationProperties的作用,都是将属性绑定到某个具体的对象上。 但是有一点区别 ConfigurationProperties是在容器启动时绑定的,而Binder是我们手动编码动态的绑定上去的。
DubboProperties dubboProperties = Binder.get(environment).bind("zzz.dubbo", DubboProperties.class).orElse(new DubboProperties());
比如要对dubbo进行封装,使用我们自定义的属性配置,那么就使用Binder进行参数绑定。
以上是关于dubbo系列七dubbo tag路由扩展的主要内容,如果未能解决你的问题,请参考以下文章