Dubbo下的多版本并行开发测试解决方案(服务路由)

return registry.lookup(DevVersionRegisterFactoryWrapper.changeApplication((url)));

}

}

写一个RegistryFactory的包装类

/**

  • @author shirenchuang

  • RegistryFactory 的包装类,在注册的时候 修改一下 Application

  • 如果是 迭代环境则把Appliacation=Application_迭代版本号

  • @date 2019/12/5 8:29 下午

*/

public class DevVersionRegisterFactoryWrapper implements RegistryFactory {

private static final Logger logger = LoggerFactory.getLogger(“devVersion”);

private RegistryFactory registryFactory;

/**

  • 注入RegisterFactory

*/

public DevVersionRegisterFactoryWrapper(RegistryFactory registryFactory) {

this.registryFactory = registryFactory;

}

@Override

public Registry getRegistry(URL url) {

//获取当前环境的迭代版本号

if(!StringUtils.isEmpty(MyThreadLocal.localVersion)){

logger.info(“=启动的服务是迭代版本服务 devVersion:{}=”,MyThreadLocal.localVersion);

System.out.println(“====启动的服务是迭代版本服务 devVersion:”+MyThreadLocal.localVersion);

return new DevVersionRegisterWrapper(registryFactory.getRegistry(changeApplication(url)));

}

logger.info(“=启动的服务是稳定版本”);

System.out.println(“=启动的服务是稳定版本”);

return registryFactory.getRegistry(url);

}

public static URL changeApplication(URL url){

if(!StringUtils.isEmpty(MyThreadLocal.localVersion)){

String applicationKey = url.getParameter(Constants.APPLICATION_KEY)+MyThreadLocal.spiltString+MyThreadLocal.localVersion;

URL url2 = url.addParameter(Constants.APPLICATION_KEY,

applicationKey);

logger.info(“=====迭代版本服务修改 Application key:{} =====”,applicationKey);

return url2;

}

return url;

}

}

服务路由

Invoker 包装类

/**

  • @author shirenchuang

  • 2019/12/10

  • 集群扩展包装器

  • 参照 {@link com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker}

*/

public class DevVersionClusterInvoker implements Invoker {

private static final Logger logger = LoggerFactory.getLogger(“devVersion”);

private final Directory directory;

private final Invoker invoker;

public DevVersionClusterInvoker(Directory directory, Invoker invoker) {

this.directory = directory;

this.invoker = invoker;

}

@Override

public URL getUrl() {

return directory.getUrl();

}

@Override

public boolean isAvailable() {

return directory.isAvailable();

}

@Override

public void destroy() {

this.invoker.destroy();

}

@Override

public Class getInterface() {

return directory.getInterface();

}

@Override

public Result invoke(Invocation invocation) throws RpcException {

// 找到迭代版本号

return doDevVersionInvoke(invocation, null);

}

@SuppressWarnings({“unchecked”, “rawtypes”})

private Result doDevVersionInvoke(Invocation invocation, RpcException e) {

Result result ;

Invoker minvoker;

List devVersionInvokers = selectDevVersionInvoker(invocation);

if (devVersionInvokers==null||devVersionInvokers.size()==0) {

logger.error("没有找到服务啊~~~~ ");

throw new RpcException(“没有找到服务啊~~~~”);

} else {

minvoker = devVersionInvokers.get(0);

}

try {

result = minvoker.invoke(invocation);

} catch (RpcException me) {

if (me.isBiz()) {

result = new RpcResult(me.getCause());

} else {

throw new RpcException(me.getCode(), getDevVersionExceptionMessage(e, me), me.getCause());

}

} catch (Throwable me) {

throw new RpcException(getDevVersionExceptionMessage(e, me), me.getCause());

}

return result;

}

private String getDevVersionExceptionMessage(Throwable t, Throwable mt) {

String msg = "devVersion error : " + mt.getMessage();

if (t != null) {

msg = msg + “, invoke error is :” + StringUtils.toString(t);

}

return msg;

}

/**

  • 获取对应迭代版本服务

  • @param invocation

  • @return

*/

private List selectDevVersionInvoker(Invocation invocation) {

List invokers = null;

if (invocation instanceof RpcInvocation) {

try {

/其实我们也可以给directory生生一个代理类,来做帅选操作/

invokers = directory.list(invocation);

//经过了dubbo的栓选之后,我们来找自己需要的Invokes

String devVersion = MyThreadLocal.getDevVersion();

List newInvokers = new ArrayList<>();

List stableInvokers = new ArrayList<>();

for (Invoker invoker : invokers){

URL providerUrl ;

//获取应用名称

Method getProviderUrl = invoker.getClass().getDeclaredMethod(“getProviderUrl”);

getProviderUrl.setAccessible(true);

providerUrl = (URL)getProviderUrl.invoke(invoker);

String application = providerUrl.getParameter(Constants.APPLICATION_KEY);

if(StringUtils.isEmpty(devVersion)){

if(application.indexOf(MyThreadLocal.spiltString)==-1){

//不是迭代过来或者本身不是迭代的请求 只能访问非迭代版本

newInvokers.add(invoker);

}

}else {

//是迭代的请求 就需要找对应的迭代服务

if(application.indexOf(MyThreadLocal.spiltString)!=-1){

String version = application.substring(application.indexOf(MyThreadLocal.spiltString)+5);

if(version.equals(devVersion)){

newInvokers.add(invoker);

}

}

}

//找到稳定环境

if(application.indexOf(MyThreadLocal.spiltString)==-1){

stableInvokers.add(invoker);

}

}

if(newInvokers==null||newInvokers.size()==0){

String serviceName = directory.getInterface().getName();

if(StringUtils.isEmpty(devVersion)){

String error = “=====当前消费者自身版本和迭代传递版本均为稳定版本~ ,但是没有找到将要消费的服务=>”+serviceName+" 的稳定版本!!";

logger.error(error);

throw new RuntimeException(error);

}else {

// 请求的是迭代服务, 但是迭代服务没有找到,退而求其次调用稳定环境 )

if(stableInvokers!=null&&stableInvokers.size()>0){

StringBuffer sb = new StringBuffer();

sb.append(“=======当前cap请求的版本为:”).append(devVersion)

.append(“;往后传递版本”).append(devVersion).append(“; 将要消费的服务:”).append(serviceName)

.append(“没有找到与之对应的迭代版本;将会调用稳定版本”);

logger.info(sb.toString());

return stableInvokers;

}else {

//可能有其他的迭代服务,但是不调用

logger.error(“当前请求迭代版本:{},但是不存在迭代服务,也没有找到稳定服务;{},{},{}”,devVersion,serviceName);

throw new RuntimeException(“当前请求迭代版本:”+devVersion+“,但是不存在迭代服务,也没有找到稳定服务;”+serviceName);

}

}

}else {

return newInvokers;

}

} catch (RpcException e) {

logger.error(“获取 迭代版本 的服务时 发生错误~~:”+ directory.getUrl().getServiceInterface() + “, method:” + invocation.getMethodName()

, e);

} catch (NoSuchMethodException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

} catch (InvocationTargetException e) {

e.printStackTrace();

}

}

return invokers;

}

@Override

public String toString() {

return “invoker :” + this.invoker + ",directory: " + this.directory;

}

public static void main(String[] args) {

String application = “application”+MyThreadLocal.spiltString+“1.0.1”;

boolean b = application.indexOf(MyThreadLocal.spiltString)==-1;

application = application.substring(application.indexOf(MyThreadLocal.spiltString)+5);

System.out.print(application);

}

}

/**

  • @author shirenchuang

  • 2019/12/10

  • 集群扩展包装器

  • 参照 {@link com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterInvoker}

*/

public class DevVersionClusterWrapper implements Cluster {

private Cluster cluster;

public DevVersionClusterWrapper(Cluster cluster) {

this.cluster = cluster;

}

@Override

public Invoker join(Directory directory) throws RpcException {

//如果自己是迭代环境,则使用包装

return new DevVersionClusterInvoker(directory,

this.cluster.join(directory));

}

}

拦截器 带入 标签Version

消费者拦截器

/**

  • @Description 消费别人服务的时候会走到这里

  •          要把 迭代版本号 放到参数里面传到 服务提供者
    
  • @Author shirenchuang

  • @Date 2019/12/1 10:20 PM

**/

@Activate(group = {Constants.CONSUMER})

public class DevVersionConsumerFilter implements Filter {

private static final Logger logger = LoggerFactory.getLogger(“devVersion”);

@Override

public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {

String traceId = TraceUtil.getTraceId();

RpcContext.getContext().setAttachment(“myTraceId”,traceId);

String toDevVersion = MyThreadLocal.getDevVersion();

RpcContext.getContext().setAttachment(“devVersion”,toDevVersion);

doLog(invoker,invocation,traceId);

return invoker.invoke(invocation);

}

private void doLog(Invoker invoker, Invocation invocation,String traceId){

String interfaceName = invoker.getInterface().getCanonicalName();

String method = invocation.getMethodName();

String methodFullName = interfaceName + “.” + method;

StringBuffer sb = new StringBuffer();

sb.append(“==TraceId:”).append(traceId)

.append(“=== ConsumerFilter:当前自身版本:”).append(MyThreadLocal.localVersion)

.append(“; 接收传递版本:”).append(MyThreadLocal.getFromVersion())

.append(“; 往后传递版本:”).append(MyThreadLocal.getDevVersion())

.append(" ;调用服务=> ").append(methodFullName);

logger.info(sb.toString());

}

}

提供者拦截器

/**

  • @Description 当前服务提供者在被真正调用之前获取 消费者过来的迭代版本号

  •          然后保存在本地线程变量中,在调用其他dubbo服务的时候 要带上版本号
    
  • @Author shirenchuang

  • @Date 2019/12/1 10:20 PM

**/

@Activate(group = {Constants.PROVIDER})

public class DevVersionProviderFilter implements Filter {

private static final Logger logger = LoggerFactory.getLogger(“devVersion”);

@Override

public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {

String fromTraceId = RpcContext.getContext().getAttachment(“myTraceId”);

TraceUtil.traceLocal.set(fromTraceId);

String myTraceId = TraceUtil.getTraceId();

String fromDevVersion = RpcContext.getContext().getAttachment(“devVersion”);

//放入到本地线程存放

MyThreadLocal.devVersion.set(fromDevVersion);

doLog(invoker,invocation,myTraceId);

return invoker.invoke(invocation);

}

private void doLog(Invoker invoker, Invocation invocation,String traceId){

String interfaceName = invoker.getInterface().getCanonicalName();

String method = invocation.getMethodName();

String methodFullName = interfaceName + “.” + method;

StringBuffer sb = new StringBuffer();

sb.append(“==TraceId:”).append(traceId)

.append(" ProviderFilter:当前自身版本:").append(MyThreadLocal.localVersion)

.append(“; 接收传递版本:”).append(RpcContext.getContext().getAttachment(“devVersion”))

.append(“; 往后传递版本:”).append(RpcContext.getContext().getAttachment(“devVersion”))

.append(" ;将被调用服务=> ").append(methodFullName);

logger.info(sb.toString());

}

}

标签的存取

在dubbo服务启动的时候 通过Jvm参数传入; 透传的标签Version通过ThreadLocal保存;

public class MyThreadLocal {

private static final Logger logger = LoggerFactory.getLogger(“devVersion”);

public static ThreadLocal devVersion = new TransmittableThreadLocal();

//public static ThreadLocal devVersion = new ThreadLocal();

/用户Application评价的固定字符;/

public static String spiltString = “dev”;

public static String localVersion ;

static {

localVersion = System.getProperty(“localVersion”);

logger.info(“====devVersion:{} ========”,devVersion);

System.out.println(“s====devVersion: ========”+devVersion);

}

public static String getFromVersion(){

return devVersion.get();

}

/**

  • 如果本地变量没有 则可能是第一个发起方;

  • 则去当前服务的版本号,然后一直传递下去;

  • @return

*/

你可能感兴趣的:(dubbo)