Dubbo实现不同分支不同测试环境

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
简介: Dubbo实现不同分支不同测试环境

在对于dubbo的路由机制和注册机制有所了解之后,我们来分析一下如何实现dubbo服务的不同环境隔离。


这一用法的大致思路为:

正常的测试环境中存在着api-consumer调用user-provider的这么一个调用关系,这里我们暂时称之为default版本。


网络异常,图片无法展示
|


假设某一天,业务方突然有一个a需求需要开发改动这个两个模块,此时就应该有这么一个关系链路出现,这里我们暂且称之为 version-a 版本。


网络异常,图片无法展示
|


但是由于version-a版本还是待完善阶段,可能还有一些影响主流程的bug存在,直接发布到测试环境替代掉default版本,可能会影响其他正在测试环境正常运作服务对于default版本的调用情况,因此version-a版本的影响需要被单独限制起来。


理想的情况下应该是这种情景:


网络异常,图片无法展示
|


那么如何实现这种单独的隔离方案呢?


目前自己所处的企业主要是采用dubbo作为微服务架构的基础,所以在进行不同服务环境的隔离时候需要对provider和consumer进行拆分设计。目前在dubbo这块的思路是基于对url配置总线的改造实现,保证不同版本的provider在写入url的时候,会结合需求分支注入一个git.branch的参数:


例如非稳定版本的测试环境中的provider 对应的配置总线中,可以在url的结尾中注入一个git.branch参数:


dubbo://192.168.43.227:9096/com.qiyu.dubbo.common.DubboService?anyhost=true&application=order-provider&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&git.branch=master&interface=com.qiyu.dubbo.common.DubboService&methods=doTest&pid=63261&register=true&release=2.7.3&side=provider&threadpool=fixed&threads=200&timestamp=1609484179452
复制代码


然后在进行路由选择的时候,凡是非稳定版本的调用方只要在启动参数中携带有对应的git.branch参数方可实现对该provider的调用,否则都会调用到默认的稳定版本测试环境中。


对应的实现代码模块:


重写对应的zk注册工厂


package com.qiyu.dubbo.router.starter.zone.zk;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.SPI;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.support.AbstractRegistryFactory;
import org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter;
/**
 * @Author idea
 * @Date created in 3:20 下午 2020/11/26
 */
public class ZoneAwareZookeeperRegisterFactory extends AbstractRegistryFactory {
    private ZookeeperTransporter zookeeperTransporter;
    /**
     *  dubbo的spi自动具有依赖注入的功能
     *
     * @param zookeeperTransporter
     */
    public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
        this.zookeeperTransporter = zookeeperTransporter;
    }
    @Override
    public Registry createRegistry(URL url) {
        return new ZoneAwareZookeeperRegister(url,zookeeperTransporter);
    }
}
复制代码


配合对dubbo.properties的改造实现相关模块的注入:


dubbo.registry.protocol=zoneAwareZookeeperRegisterFactory
复制代码


ps:注意,如果希望能够匹配到自定义的protocol,并且让dubbo服务在启动的时候注入到ZoneAwareZookeeperRegisterFactory的话,需要在配置注册地址的时候,将协议名称置空,例如这么写:


dubbo.registry.address=127.0.0.1:2181
复制代码


而不是:


dubbo.registry.address=zookeeper://127.0.0.1:2181
复制代码


这一部分可以在源代码的这里查询得到:


org.apache.dubbo.registry.RegistryService#register
复制代码


自定义注册服务的策略:


package com.qiyu.dubbo.router.starter.zone.zk;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.registry.zookeeper.ZookeeperRegistry;
import org.apache.dubbo.remoting.zookeeper.ZookeeperTransporter;
/**
 * @Author idea
 * @Date created in 3:18 下午 2020/11/26
 */
@Slf4j
public class ZoneAwareZookeeperRegister extends ZookeeperRegistry {
    public ZoneAwareZookeeperRegister(URL url, ZookeeperTransporter zookeeperTransporter) {
        super(url, zookeeperTransporter);
    }
    @Override
    public void doRegister(URL url) {
        String zone = System.getProperty("git.branch");
        url = url.addParameter("git.branch", zone);
        log.info("当前注册的url为:{}" , url);
        super.doRegister(url);
    }
}
复制代码


现在能够实现不同的provider在注册中心中配置总线url的不同,这样就能在路由router模块起到筛选的效果了。


主要思路:

自定义的一个cluster组件,该cluster组件加入一个叫做ZoneVersionSelectRouter的路由器。


package com.qiyu.dubbo.router.starter.zone.router;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.cluster.Cluster;
import org.apache.dubbo.rpc.cluster.Directory;
import org.apache.dubbo.rpc.cluster.Router;
import org.apache.dubbo.rpc.cluster.RouterChain;
import org.apache.dubbo.rpc.cluster.directory.AbstractDirectory;
import org.apache.dubbo.rpc.cluster.support.FailoverClusterInvoker;
import java.lang.reflect.Field;
import java.util.List;
/**
 * dubbo调用接口之前需要先通过cluster这个组件,在这里做重载,给他注入相关的router配置
 *
 * @author idea
 * @date created in 10:22 下午 2020/11/21
 */
public class ZoneAwareCluster implements Cluster {
    @Override
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        AbstractDirectory ad = (AbstractDirectory) directory;
        RouterChain routerChain = ad.getRouterChain();
        try {
            Field routerField = routerChain.getClass().getDeclaredField("routers");
            routerField.setAccessible(true);
            List<Router> routers = (List<Router>) routerField.get(routerChain);
            routers.add(new ZoneVersionSelectRouter());
            return new FailoverClusterInvoker<>(directory);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
复制代码


该路由组件可以根据consumer端的分支参数,选择性地调用不同的provider。


package com.qiyu.dubbo.router.starter.zone.router;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.rpc.Invocation;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.RpcException;
import org.apache.dubbo.rpc.cluster.Router;
import org.springframework.util.StringUtils;
import java.util.Arrays;
import java.util.List;
/**
 * 自定义的一个router组件: 基于zone参数进行不同版本选择的一个router组件
 *
 * @author idea
 * @date created in 5:11 下午 2020/11/21
 */
@Slf4j
public class ZoneVersionSelectRouter implements Router {
    @Override
    public URL getUrl() {
        return null;
    }
    @Override
    public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
        //每次请求都会被切入到这个route模块
        String consumerBranch = System.getProperty("git.branch");
        log.info(this.getRouterName() + " consumerBranch is {} ", consumerBranch);
        Invoker providerInvoker = null;
        for (Invoker<T> invoker : invokers) {
            String providerBranch = invoker.getUrl().getParameter("git.branch");
            if (StringUtils.isEmpty(providerBranch)) {
                providerInvoker = invoker;
            } else if (providerBranch.equals(consumerBranch)) {
                log.info(this.getRouterName() + " providerVersion matched was {} ", providerBranch);
                return invokers;
            }
        }
        log.info(this.getRouterName() + " providerVersion matched  default version ");
        return Arrays.asList(providerInvoker);
    }
    @Override
    public boolean isRuntime() {
        return false;
    }
    @Override
    public boolean isForce() {
        return false;
    }
    @Override
    public int getPriority() {
        return -100;
    }
    private String getRouterName() {
        return " ====== ZoneVersionSelectRouter ====== ";
    }
}
复制代码


记得要配置dubbo.properties文件:


dubbo.consumer.cluster=zoneAware
复制代码

最后便是spi配置文件的设置了:

网络异常,图片无法展示
|


RegisterFactory的spi拓展配置


zoneAwareZookeeperRegisterFactory=com.qiyu.dubbo.router.starter.zone.zk.ZoneAwareZookeeperRegisterFactory
复制代码


Cluster的spi拓展配置


zoneAware=com.qiyu.dubbo.router.starter.zone.router.ZoneAwareCluster
复制代码


启动的时候,加入-Dgit.branch参数,实现不同分支的流量隔离:


网络异常,图片无法展示
|


一个基本的dubbo服务隔离就基本完成了。


那么我们来进行一下总结,这一方案主要结合了哪些技术?


服务暴露过程中,配置总线的重写规则制定。


服务调用过程中,对于路由层的改造。


dubbo的spi机制理解与实践。

相关实践学习
基于MSE实现微服务的全链路灰度
通过本场景的实验操作,您将了解并实现在线业务的微服务全链路灰度能力。
目录
相关文章
|
Dubbo JavaScript 前端开发
可视化Dubbo测试工具iubbox 2.0版本发布啦
可视化Dubbo测试工具iubbox 2.0版本发布啦
666 0
|
4月前
|
测试技术
单元测试策略问题之行覆盖率和分支覆盖率之间的问题如何解决
单元测试策略问题之行覆盖率和分支覆盖率之间的问题如何解决
155 7
|
3月前
|
Java 测试技术 API
SpringBoot单元测试快速写法问题之计算测试用例的分支覆盖率如何解决
SpringBoot单元测试快速写法问题之计算测试用例的分支覆盖率如何解决
|
存储 缓存 运维
【运维知识高级篇】一篇文章带你搞懂Git!(Git安装+全局配置+Git初始化代码仓库+Git四大区域+Git四种状态+Git常用命令+Git分支+Git测试代码回滚)
【运维知识高级篇】一篇文章带你搞懂Git!(Git安装+全局配置+Git初始化代码仓库+Git四大区域+Git四种状态+Git常用命令+Git分支+Git测试代码回滚)
251 0
|
Shell Linux Go
《Linux操作系统编程》第八章 Shell程序设计: shell 语言结构,包括测试、分支、循环、跳转、函数、语句组
《Linux操作系统编程》第八章 Shell程序设计: shell 语言结构,包括测试、分支、循环、跳转、函数、语句组
119 0
|
分布式计算 Dubbo 前端开发
软件测试|Dubbo 接口测试原理及多种方法
软件测试|Dubbo 接口测试原理及多种方法
软件测试|Dubbo 接口测试原理及多种方法
|
关系型数据库 MySQL Java
树莓派开发笔记(十六):树莓派4B+安装mariadb数据库(mysql开源分支)并测试基本操作
树莓派使用数据库时,优先选择sqlite数据库,但是sqlite是文件数据库同时仅针对于单用户的情况,考虑到多用户的情况,在树莓派上部署安装mariadb数据库服务(mysql的开源分支),通过读写锁事务等使用,可以实现多进程可以操作同一个数据库的同一个表的读写并行操作。
树莓派开发笔记(十六):树莓派4B+安装mariadb数据库(mysql开源分支)并测试基本操作
|
分布式计算 资源调度 Dubbo
干货 | Dubbo 接口测试原理及多种方法实践总结
干货 | Dubbo 接口测试原理及多种方法实践总结
|
Dubbo Java 应用服务中间件
基于 JMeter 完成 Dubbo 接口的测试
基于 JMeter 完成 Dubbo 接口的测试
|
Dubbo Java 应用服务中间件
使用dubbo-go搭建dubbo接口测试平台
作为接口测试平台,没办法引入所有提供方定义的接口jar包,可以有以下方案来解决: dubbo支持telnet协议调用dubbo接口 dubbo的泛化调用可以在不引入提供方接口定义jar包的情况下对接口进行调用
295 0
使用dubbo-go搭建dubbo接口测试平台