dubbo中的Mock实现机制

2023-10-29

Mock是SOA之中的一个很有用的功能,不仅可以用来进行服务降级,也可以用来在测试中模拟服务调用的各种异常情况。dubbo框架里面的mock是在服务使用者这一端实现的,下面对实现机制进行分析:

1. Mock的植入

很显然,既然提供了mock机制,那么mock应该作为一个环节插入到服务使用者的整个处理流程之中,而dubbo的设计基本采用了装饰器模式,一层一层的进行包装,这个具体的植入点就在RegistryProctocol通过Cluster来创建ClusterInvoker的时候:

RegistryProctocol的doRefer方法:

   return cluster.createClusterInvoker(registryDirectory);

 cluster的类型为Cluster$Adaptive,这实际上是一个通用的代理类,它会根据regsitryDirectory的getConsumerUrl方法返回的Url中的cluster参数的值来定位到实际的Cluster的实现类上,如果Url之中没有指定cluster,那么会采用Cluster的SPI注解上配置的默认值FailoverCluster.NAME,也就是默认情况下会调用ExtensionLoader<Clsuter>内部的key为failover的实例:

   

  @SPI(FailoverCluster.NAME)
  public interface Cluster
  {


在dubbo的配置文件  classpath:/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.cluster.Cluster中,failover对应的是FailoverCluster类: 

mock=com.alibaba.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper
failover=com.alibaba.dubbo.rpc.cluster.support.FailoverCluster
failfast=com.alibaba.dubbo.rpc.cluster.support.FailfastCluster
failsafe=com.alibaba.dubbo.rpc.cluster.support.FailsafeCluster
failback=com.alibaba.dubbo.rpc.cluster.support.FailbackCluster
forking=com.alibaba.dubbo.rpc.cluster.support.ForkingCluster
available=com.alibaba.dubbo.rpc.cluster.support.AvailableCluster
switch=com.alibaba.dubbo.rpc.cluster.support.SwitchCluster
mergeable=com.alibaba.dubbo.rpc.cluster.support.MergeableCluster
broadcast=com.alibaba.dubbo.rpc.cluster.support.BroadcastCluster


但是ExtensionLoader在实例化对象时,有个比较特殊的地方,那就是在实例化完成之后,会自动套上当前的ExtensionLoader中的Wrapper类,上面的mock所对应的MockClusterWrapper就是这样的一个Wrapper:

 

private T wrapInstance(T instance) throws IllegalArgumentException, SecurityException, ...
{
    Set<Class<?>> wrapperClassesToUse = wrapperClasses;
    T wrapper=instance;
    if (CollectionUtils.isNotEmpty(wrapperClassesToUse))
    {
        for (Class<?> wrapperClassToUse : wrapperClassesToUse) 
        {
        wrapper=(T) wrapperClassToUse.getConstructor(type).newInstance(wrapper);
        wrapper = injectDependentExtension(wrapper);
        }
    }
    return wrapper;
}


 

也就是实例化出来的FailoverCluster会被套上一层MockClusterWrapper,总结一下就是:

Cluster$Adaptive -> 定位到内部key为failover的对象 ->FailoverCluster->外部套上MockClusterWrapper ,

这样RegistryProctocol的doRefer方法中的:

 return cluster.createClusterInvoker(registryDirectory);


实际上调用的是MockClusterWrapper 的 createClusterInvoker方法,MockClusterWrapper 的 createClusterInvoker方法如下:

public class MockClusterWrapper implements Cluster 
{
    private Cluster cluster;
    public MockClusterWrapper(Cluster cluster)
    {
         this.cluster = cluster;
    }
     @Override
     public <T> Invoker<T> createClusterInvoker(Directory<T> directory) throws RpcException
     {
      return new MockClusterInvoker<T>(directory,
        this.cluster.createClusterInvoker(directory));
     }
}


 

也就是实际创建的ClusterInvoker是封装了FailoverClusterInvoker的MockClusterInvoker,这样就成功地在Invoker之中植入了Mock机制。

  那么,最终就是服务使用者的接口代理-> MockClusterInvoker -> FailoverClusterInvoker。

 

2. Mock的执行

   mock的执行主要由MockClusterInvoker完成,MockClusterInvoker的invoke方法的执行逻辑如下:

   (1)如果在没有配置之中没有设置mock,那么直接把方法调用转发给实际的Invoker(也就是FailoverClusterInvoker)

 

    @Override
    public Result invoke(Invocation invocation) throws RpcException
    {
	Result result = null;
        
        String mockValue = directory.getUrl().getMethodParameter(
            invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim(); 
        if (mockValue.length() == 0 || mockValue.equalsIgnoreCase("false"))
        {
        	//no mock
        	result = this.invoker.invoke(invocation);
        }


 (2)如果配置了强制执行Mock,比如发生服务降级,那么直接按照配置执行mock之后返回:

 else if (mockValue.startsWith("force"))
 {
       if (logger.isWarnEnabled())
       {
          logger.info("force-mock: " + invocation.getMethodName() + " force-mock enabled , url: " +  directory.getUrl());
        }
       //force:direct mock
        result = doMockInvoke(invocation, null);
 }


(3) 如果是其它的情况,比如只是配置的是mock=fail:return null,那么就是在正常的调用出现异常的时候按照配置执行mock:

//fail-mock
 try 
 {
    result = this.invoker.invoke(invocation);
 }
 catch (RpcException rpcException) 
 {
     if (rpcException.isBiz())
     {
	throw rpcException;
     } 
     else
     {
	if (logger.isWarnEnabled())
	{
	logger.info("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " 
	 +  directory.getUrl(), rpcException);
        }
	result = doMockInvoke(invocation, rpcException);
     }
 }


(2)和(3)最终都会通过调用doMockInvoke来完成mock调用,doMockInvoke方法会首先尝试调用selectMockInvoker方法来看看用户有没有配置过MockInvoker:

private Result doMockInvoke(Invocation invocation,RpcException actualRpcException)
{
    	List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);


 selectMockInvoker的代码如下,依靠在Invocation的attachment里面做个标记来告诉directory的list方法应该返回MockInvoker,代码的注释里面说这么做是临时之举,未来会修改:

 

private List<Invoker<T>> selectMockInvoker(Invocation invocation)
{
    //TODO generic invoker?
   if (invocation instanceof RpcInvocation)
   {
         //存在隐含契约(虽然在接口声明中增加描述,但扩展性会存在问题.同时放在attachement中的做法需要改进
      ((RpcInvocation)invocation).setAttachment(Constants.INVOCATION_NEED_MOCK, Boolean.TRUE.         toString());
      //directory根据invocation中attachment是否有Constants.INVOCATION_NEED_MOCK,来判断获取的是nor       //mal invokers or mock invokers
       List<Invoker<T>> invokers = directory.list(invocation);
       return invokers;
   }
   else 
   {
       return null ;
   }
}

一般情况下,directory是RegistryDirectory,RegistryDirectory的list方法里面与mock有关的部分主要是router,RegistryDirectory在初始化内部的routers的时候,会人为的加上一个MockInvokerRouter,

AbstractDirectory的setRouters方法:


protected void setRouters(List<Router> routers)
{
     // copy list
     routers = routers == null ? new  ArrayList<Router>() : new ArrayList<Router>(routers);
     // append url router
     String routerValue = url.getParameter(Constants.ROUTER_KEY);
     if (routerValue != null && routerValue.length() > 0)
      {
          RouterFactory routerFactory = 
            ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerValue);
          routers.add(routerFactory.getRouter(url));
      }
     // append mock invoker selector
      routers.add(new MockInvokersRouter());
      Collections.sort(routers);
      this.routers = routers;
 }


RegistryDirectory的list方法最后由router来对invokers进行处理:

AbstractDirectory的list方法:

public List<Invoker<T>> list(Invocation invocation) throws RpcException
{
     if (destroyed)
     {
          throw new RpcException("Directory already destroyed .url: "+ getUrl());
     }
     List<Invoker<T>> invokers = doList(invocation);
     List<Router> routersToUse = this.routers; // local reference
     if (routersToUse != null && routersToUse.size() > 0) 
     {
         for (Router router: routersToUse)
         {
            try
            {
                 if (router.getUrl() == null || router.getUrl().getParameter(
                     Constants.RUNTIME_KEY, true))
                 {
                       invokers = router.route(invokers, getConsumerUrl(), invocation);
                   }
             } 
             catch (Throwable t)
             {
                  logger.error("Failed to execute router: " + getUrl() + ", cause: " +
                     t.getMessage(), t);
              }
          }
      }
      return invokers;
    }

MockInvokersRouter的route方法如下,根据Invocation的的attachment里面是否有mock标记(这个标记在前述的MockClusterInvoker的selectMockInvoker方法里面设置)来决定是返回正常的Invoker还是MockInvoker:

 

public <T> List<Invoker<T>> route(final List<Invoker<T>> invokers,URL url, final Invocation invocation) throws RpcException
{
    if (invocation.getAttachments() == null)
    {
	return getNormalInvokers(invokers);
    } 
    else
    {
	String value = invocation.getAttachments().get(Constants.INVOCATION_NEED_MOCK);
	if (value == null) 
	{
	    return getNormalInvokers(invokers);
	}
	else if (Boolean.TRUE.toString().equalsIgnoreCase(value))
	{
	    return getMockInvokers(invokers);
	} 
    }
    return invokers;
}

负责返回MockInvoker的是下面的getMockInvokers方法,在一般的情况下,是不会配置mock协议的,所以这个方法返回null:

 

private <T> List<Invoker<T>> getMockInvokers(final List<Invoker<T>> invokers)
{
     if (! hasMockProviders(invokers))
     {
	return null;
     }
     List<Invoker<T>> resultInvokers = new ArrayList<Invoker<T>>(1);
     for (Invoker<T> invoker : invokers)
     {
	if (invoker.getUrl().getProtocol().equals(Constants.MOCK_PROTOCOL))
	{
	    resultInvokers.add(invoker);
	}
     }
     return resultInvokers;
}


这样一直返回到MockClusterInvoker的doMockInvoke方法之中,selectMockInvoker返回空,那么MockClusterInvoker的doMockInvoke方法会根据url来

构造一个MockInvoker:

private Result doMockInvoke(Invocation invocation,RpcException actualRpcException)
{
    List<Invoker<T>> mockInvokers = selectMockInvoker(invocation);
    Invoker<T> mockInvokerToUse ;
    if (mockInvokers == null || mockInvokers.size() == 0)
    {
	mockInvokerToUse = (Invoker<T>) new MockInvoker(directory.getUrl());
    } 
    else 
    {
	mockInvokerToUse = mockInvokers.get(0);
    }
		
    Result result = null;
    try 
    {
	result = mockInvokerToUse.invoke(invocation);
    } 
    catch (RpcException mockRpcException)
    {

最后在构造出来的MockInvoker上调用invoke方法来执行mock调用,invoke方法的流程比较简单,对mockValue进行处理之后就看是返回mock值还是抛出异常,或者是加载并调用Mock类:

 

public Result invoke(Invocation invocation) throws RpcException 
{
    	String mockValue = 
    	    getUrl().getParameter(invocation.getMethodName()+"."+Constants.MOCK_KEY);
    	if (invocation instanceof RpcInvocation) 
    	{
    	    ((RpcInvocation) invocation).setInvoker(this);
    	}
    	if (StringUtils.isBlank(mockValue))
    	{
    	    mockValue = getUrl().getParameter(Constants.MOCK_KEY);
    	}
    	
    	if (StringUtils.isBlank(mockValue))
    	{
    	    throw new RpcException(
    	       new IllegalAccessException("mock can not be null. url :" + url));
    	}
        mockValue = normalizeMock(URL.decode(mockValue));
        if (Constants.RETURN_PREFIX.trim().equalsIgnoreCase(mockValue.trim()))
        {
            RpcResult result = new RpcResult();
            result.setValue(null);
            return result;
        }
        
        if (mockValue.startsWith(Constants.RETURN_PREFIX)) 
        {
           ....
        } 
        
        if (mockValue.startsWith(Constants.THROW_PREFIX)) 
        {
           ....
        }
         //impl mock
        try
        {
           Invoker<T> invoker = getInvoker(mockValue);
           return invoker.invoke(invocation);
        }
        catch (Throwable t) 
        {
          throw new RpcException("Failed to create mock implemention class " + mockValue , t);
        }

    }



 

 



 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

dubbo中的Mock实现机制 的相关文章

  • linux查看磁盘io性能

    1 查看磁盘 IO 性能 1 1 top 命令 top 命令通过查看 CPU 的 wa 值来判断当前磁盘 IO 性能 如果这个数值过大 很可能是磁盘 IO 太高了 当然也可能是其他原因 例如网络 IO 过高等 1 2 sar 命令 sar

随机推荐

  • Jupyter Notebook使用-如何设置代码单元启用自动换行

    最近开始使用 Jupyter Notebook 至于它的强大功能不需要我做过多介绍 接下来直接进入正题 在使用的过程中我发现了一个这样的问题 当编辑的代码过长时将会出现滚动条 滑动条 像下面这样 但是当我将滚动条拉到底时 问题出现了 侧边栏
  • 代码审计-Java项目&JDBC&Mybatis&Hibernate&注入&预编译&写法

    文章目录 Javaweb 数据库操作 模式 写法 预编译等 环境搭建 JDBC 注入分析 关于预编译 Mybatis 注入分析 Hibernate 注入分析 总结 Javaweb 代码审计SQL注入 INXEDU在线网校 Javaweb 数
  • python构建IP代理池(Proxy Pool)

    基本原理 代理实际上指的就是代理服务器 它的功能是代理网络用户去取得网络信息 也可以说它是网络信息的中转站 在我们正常请求一个网站时 是将请求发送给 Web 服务器 Web 服务器把响应传回给我们 如果设置了代理服务器 实际上就是在本机和服
  • 多数据源配置(application.properties或application.yml配置详情)

    1 导入Maven依赖
  • ssh key问题解决

    u r the butter of my bread the breath to my life Julie Julia 某些情况下 原来的ssh连接会失效 比如误删了 ssh下面的东西 这时 需要重新生成key并加入gitlab或gith
  • 列表的基本操作

    描述 在两行中分别输入一个字符串 分别将其转换为列表 a 和 b 按要求完成以下功能 1 输出两个列表的拼接结果 2 输出列表 a 重复3次的结果 3 输出列表 b 中第3个元素和最后一个元素 4 输出列表 a 中序号1至4之间的元素 5
  • git工具下载

    文章目录 下载客户端 git下载教程 git下载地址 Tortoise下载地址 Tortoise下载教程 Git以及Github详细解析教程 码云学习安装视频 下载客户端 git下载教程 https www cnblogs com xuew
  • Android 使用updatefun 来自动更新

    这几天研究了一下APP的自动更新 并且是那种最方便使用的 找了一下 找到一个框架 updatefun 使用方法比较简单 记录一下使用方法和遇到的问题 使用步骤 1 使用Android studio 的依赖方式 dependencies co
  • 快速定位当前页面的Activity

    方法1 通过AndroidStudio的Terminal 利用一个指令可以快速定位当前页面的类名 1 把手机用数据线连到电脑 手机打开到需要的定位的页面 2 打开AndroidStudio 在AndroidStudio底部选择Termina
  • 实现一个简单的python小脚本的一些必要步骤

    1 编写python代码时在开头添上 python27 2 设置环境变量路径 在系统变量path中新建一个你要运行python脚本的文件夹的绝对路径 D python 3 运行方式 a 直接双击xx py文件 b 添加环境变量后 在cmd中
  • 工程有限元(1)

    有限法的基本思想 有限元概述 结构分析问题 有限元法的思路 有限元法的一般步骤 本文内容是整理的 工程有限元 课程内容 便于日后复习以及读者学习 有限元概述 有限元法 Finite Element Anaslysis FEM 是通过数学描述
  • c++继承下

    继承的方式主要分为单继承 多继承 菱形继承 普通单继承 指向派生类的基类指针或者引用 其类型仍然属于基类类型 而不是派生类类型 include
  • for(auto i : v)遍历容器元素

    for auto i v 遍历容器元素 1 auto 2 auto 3 const auto 4 const auto C 11 新增了一种循环 基于范围 range based 的 for 循环 这简化了一种常见的循环任务 对数组 或容器
  • 计算机重新如何连接网络打印机,电脑怎样连接打印机,小编教你电脑如何连接网络打印机...

    打印机是办公室里经常会用到的一种办公设备 由于工作性质的不同 以及其他原因 网络打印机可以实现多台电脑连接 实现资源共享 网络打印机自带ip 只需指定ip就可以快速连接 那电脑如何连接网络打印机 下面 小编给大家讲解电脑连接网络打印机的技巧
  • 基于类帕累托贯序抽样算法求解单目标优化问题附matlab代码

    作者简介 热爱科研的Matlab仿真开发者 修心和技术同步精进 matlab项目合作可私信 个人主页 Matlab科研工作室 个人信条 格物致知 更多Matlab完整代码及仿真定制内容点击 智能优化算法 神经网络预测 雷达通信 无线传感器
  • 智能合约平台开发指南

    随着区块链技术的普及 智能合约平台已经成为了这个领域的一个重要趋势 智能合约可以自动化执行合同条款 大大减少了执行和监督合同条款所需的成本和时间 那么 如何开发一个智能合约平台呢 以下是一些关键步骤 一 选择合适的区块链平台 智能合约通常运
  • pgsql数据库实现导入导出

    pgsql数据库实现导入导出 1 导出表 pg dump h 数据库ip U 用户名 数据库名 t 表名 gt 路径 例 pg dump h 127 0 0 1 U sysdba data center t book gt data boo
  • Prompt入门

    Prompt的范式大抵是两种 续写Prefix 用在GPT2 3那种单向LM预训练模型上 输入 好好学习 翻译成英文 输出 good good study 完形填空 用在BERT那种MLM式预训练模型上 比如情感分类任务可以输入 这个饼不错
  • Idea 中 Git 不提交当前分支修改代码并切换分支

    1 当前分支修改代码切换分支 日常开发中 我们可能会碰到我们正在修改当前 01 分支的代码 突然要去修改另外一个 02 分支的代码情况 而我们 01 分支写的代码还未经过测试 并不能马上提交 这个时候我们切换到 02 分支就会有问题 比如弹
  • dubbo中的Mock实现机制

    Mock是SOA之中的一个很有用的功能 不仅可以用来进行服务降级 也可以用来在测试中模拟服务调用的各种异常情况 dubbo框架里面的mock是在服务使用者这一端实现的 下面对实现机制进行分析 1 Mock的植入 很显然 既然提供了mock机