跟着小程学微服务-Mock自动化系统的原理及实现

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

一、前言

今天我将和大家一起探讨我们的“自动化Mock系统1.0版本”。

二、测试人员面临的测试问题

我公司目前用的是基于Dubbo的微服务改造,服务之间的调用链路冗长,每个服务又是单独的团队在维护,每个团队又在不断的演进和维护各个服务,那么对测试人员将是非常大的挑战。

测试人员每次进行功能测试的时候,测试用例每次都需要重新写一遍,无法将测试用例的数据沉淀,尤其是做自动化测试的时候,测试人员准备测试数据就需要很长时间,效率非常低。

目前接口自动化测试框架也多种多样,testng,junit,Fitnesse等,但都需要测试人员具备测试代码编写能力,如果要做好和手工接口测试一样效果的自动化测试更是需要大量的代码堆积,后期维护代码成本非常大。因此做成简单配置用例流,无需编写测试代码的系统是更贴合实际工作要求。

举个例子:拿互联网支付系统来说,某个团队新增了支付交易的需求,这时候要进行测试,测试人员除了要测试支付交易需求本身是否正确,同时也要结合上下游的服务整体进行回归测试,这时候开发人员往往在支付交易系统中采用“硬编码”的方式对上下游的系统进行“挡板”,如果测试人员对测试数据有所调整那么“挡板”也要跟着调整,同时在项目正式上线的时候,如果开发人员没有将“挡板”程序去除干净,将面临严重的线上问题。

三、Dubbo的Mock功能

1、Dubbo的Mock使用

Dubbo自带的Mock功能首先是为了做服务降级,比如某验权服务,当服务提供方全部挂掉后,客户端不抛出异常,而是通过Mock数据返回授权失败。

我们从官网上举一个例子来说明:

<dubbo:reference interface="com.foo.BarService" mock="force" />

我们可以在期望的reference标签上加一个mock="force",就可以将当前服务设置为mock。但是设置完mock属性后还没有结束,需要有一个Mock类对应我们的服务接口类。

规则如下:
接口名 + Mock后缀,服务接口调用失败Mock实现类,该Mock类必须有一个无参构造函数。

对应到com.foo.BarService的话,则创建BarServiceMock类。

public class BarServiceMock implements BarService {    
public String sayHello(String name) {        
  // 你可以伪造容错数据,此方法只在出现RpcException时被执行   return "容错数据";    } }

经过以上设置后,当调用BarService进行远程调用的话,直接请求到BarServiceMock类上面进行模拟测试。

2、Dubbo Mock的原理解析

在dubbo的配置文件中
classpath:/META-INF/dubbo/internal/com.alibaba.dubbo.rpc.cluster.Cluster
可以看到如下配置列表:

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

我们可以看到配置文件中实际上有五大路由策略:

  • AvailableCluster: 获取可用的调用。遍历所有Invokers判断Invoker.isAvalible,只要一个有为true直接调用返回,不管成不成功。

  • BroadcastCluster: 广播调用。遍历所有Invokers, 逐个调用每个调用catch住异常不影响其他invoker调用。

  • FailbackCluster: 失败自动恢复, 对于invoker调用失败, 后台记录失败请求,任务定时重发, 通常用于通知。

  • FailfastCluster: 快速失败,只发起一次调用,失败立即保错,通常用于非幂等性操作。

  • FailoverCluster: 失败转移,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟。

Dubbo中默认使用的是FailoverCluster策略,而在实际执行的过程中是FailoverCluster会被先被注入到MockClusterWrapper中,过程就是:

Cluster$Adaptive -> 定位到内部key为failover的对象 ->FailoverCluster->注入到MockClusterWrapper

MockClusterWrapper内部会创建一个MockClusterInvoker对象。实际创建是封装了FailoverClusterInvoker的MockClusterInvoker,这样就成功地在Invoker之中植入了Mock机制。

我们来看MockClusterInvoker的内部实现:

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

    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);  
      }
  • 如果配置了强制执行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);  
    }
  • 如果是其它的情况,比如只是配置的是mock=fail:return null,那么就是在正常的调用出现异常的时候按照配置执行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);  
       }  
    }
3、Dubbo Mock的适用场景

Dubbo的Mock功能主要是为了做服务降级而使用的,服务提供方在客户端执行容错逻辑,在出现RpcException(比如网络失败,超时等)时进行容错,然后执行降级Mock逻辑。自身并不适合做Mock测试系统。

四、自动化Mock系统的实现

1、Mock系统的简单用例图

640?wx_fmt=png&wxfrom=5&wx_lazy=1


2、Mock系统的架构图

640?wx_fmt=png&wxfrom=5&wx_lazy=1


为了基于Dubbo实现Mock功能,需要对Dubbo源码进行一些必要的修改,通过上面的架构图我们可以看到,实际上我们正是利用了Dubbo的Filter chain过滤器链这一机制实现的,为了方便大家更好的理解,下面将简单介绍一下Dubbo的Filter机制。

2.1、Dubbo的Filter原理分析

Filter:是一种递归的链式调用,用来在远程调用真正执行的前后加入一些逻辑,跟aop的拦截器servlet中filter概念一样的。

Filter接口定义:

@SPIpublic interface Filter {   
 Result invoke(Invoker<?> invoker,Invocation invocation)
 throws RpcException
; }

Filter的实现类需要打上@Activate注解, @Activate的group属性是个string数组,我们可以通过这个属性来指定这个filter是在consumer, provider还是两者情况下激活,所谓激活就是能够被获取,组成filter链。

List<Filter> filters =ExtensionLoader.getExtensionLoader(Filter.class).getAct ivateExtension(invoker.getUrl(),key, group);
Key就是SERVICE_FILTER_KEY还是REFERENCE_FILTER_KEYGroup就是consumer或者provider

关于SPI的详细介绍请大家参考我之前写的另一篇文章http://www.jianshu.com/p/46aa69643c97

ProtocolFilterWrapper:在服务的暴露与引用的过程中根据KEY是PROVIDER还是CONSUMER来构建服务提供者与消费者的调用过滤器链,Filter最终都要被封装到Wrapper中的。

public <T> Exporter<T> export(Invoker<T>invoker)throws RpcException {
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER)); }
  public <T> Invoker<T> refer(Class<T> type,URL url)
  throws RpcException {    
    return buildInvokerChain(protocol.refer(type, url),Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER); }

构建filter链,当我们获取激活的filter集合后就通过ProtocolFilterWrapper类中的buildInvokerChain方法来构建。

for (int i = filters.size() - 1; i >= 0; i --) {      
  final Filter filter = filters.get(i);      
  final Invoker<T> next = last;   last = new Invoker<T>() {            
    public Result invoke(Invocation invocation)throws RpcException {
      return filter.invoke(next, invocation);     }           //。。。。。。。
//其他方法       }; }
2.2、Mock流程介绍

640?wx_fmt=png&wxfrom=5&wx_lazy=1


注:我们在<dubbo:application name>中新加了自定义的“env=test”这样的属性配置用来标明当前环境是测试的还是正式的,用户每次通过Dubbo请求的远程服务的时候,都会首先经过我们自定义的Filter,我们自定义的Filter会首先判断当前的环境是test还是正式,如果是test的环境则直接访问Mock配置中心获取提前配置好的Mock数据并封装成用户定义的Response对象返回。

3、Mock系统的配置中心

Mock配置中心就是用户将mock数据与应用环境建立关系的系统,整个系统就像一个工作流引擎:

环境设置->应用名称设置->挡板规则设置->Facade服务接口设置->方法规则设置

  • 环境设置

640?wx_fmt=png&wxfrom=5&wx_lazy=1


注:如果尚未映射来源IP地址到环境,则点击环境列表导航链接,进入环境列表页面,点击添加,输入源IP及环境名,点击确定按钮,实现源IP到所设环境的映射。每个用户都可以建立属于自己的测试环境。

  • 应用名称设置

640?wx_fmt=png&wxfrom=5&wx_lazy=1


注:创建所使用系统的应用名称,Mock配置中心默认使用<dubbo:application name>中的名称作为应用名称。

  • 挡板规则

640?wx_fmt=png&wxfrom=5&wx_lazy=1


注:每一个挡板规则都是由一个环境名称和应用名称组成的唯一挡板,在挡板设置中选择环境名称和应用名称,并且设置挡板的有效状态。

  • Facade规则

640?wx_fmt=png&wxfrom=5&wx_lazy=1


注:每一个Facade就是一个Dubbo的服务接口类,在这里将自己的Facade名称与全路径与挡板名称对应,以标识哪些Facade服务接口类是属于哪个挡板的。

  • 方法规则

640?wx_fmt=png&wxfrom=5&wx_lazy=1


注:方法规则是用来设置每个Facade中的需要mock的方法的,可以对不同的方法设置方法执行时间、方法抛出的异常等等。


4、Mock系统的其他功能

由于不少应用项目开发完后想对其进行单独压测,而很多时候应用系统和其他业务系统形成了依赖关系,如果不布署其他应用系统则无法完成压测,为了更好的支持性能测试组进行挡板压测,Mock系统支持压测功能,而Mock系统自身也可以达到单台服务器1000TPS以上(8C8G)。(全文完)


来源:中生代技术

原文链接

相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
相关文章
|
20天前
|
Kubernetes 负载均衡 微服务
Kubernetes 生态系统中的微服务治理
【8月更文第29天】随着微服务架构的普及,管理分布式系统的复杂性也随之增加。Kubernetes 作为容器编排的事实标准,为微服务架构提供了强大的支持。结合像 Istio 这样的服务网格工具,Kubernetes 能够有效地解决微服务治理中的诸多挑战,如服务发现、负载均衡、流量管理和安全策略等。
27 1
|
5天前
|
缓存 Java 开发者
开发故事:一个 @Async 如何搞瘫整个微服务系统
大家好,我是小米,一个热爱分享技术的29岁开发者。本文讲述了一个困扰我们团队的开发环境问题,最终发现罪魁祸首竟是 `@Async` 注解。我们通过详细分析错误日志和 Spring 的 Bean 代理机制,逐步排查并解决了这一难题。文章介绍了三种解决方案:调整依赖结构、使用 `@Lazy` 延迟加载以及禁用 `@Async` 的代理功能。希望对你有所帮助!欢迎关注我的微信公众号“软件求生”,获取更多技术干货!
15 5
开发故事:一个 @Async 如何搞瘫整个微服务系统
|
9天前
|
存储 弹性计算 运维
自动化监控和响应ECS系统事件
阿里云提供的ECS系统事件用于记录云资源信息,如实例启停、到期通知等。为实现自动化运维,如故障处理与动态调度,可使用云助手插件`ecs-tool-event`。该插件定时获取并转化ECS事件为日志存储,便于监控与响应,无需额外开发,适用于大规模集群管理。详情及示例可见链接文档。
|
5天前
|
运维 Cloud Native Devops
云原生架构的崛起与实践云原生架构是一种通过容器化、微服务和DevOps等技术手段,帮助应用系统实现敏捷部署、弹性扩展和高效运维的技术理念。本文将探讨云原生的概念、核心技术以及其在企业中的应用实践,揭示云原生如何成为现代软件开发和运营的主流方式。##
云原生架构是现代IT领域的一场革命,它依托于容器化、微服务和DevOps等核心技术,旨在解决传统架构在应对复杂业务需求时的不足。通过采用云原生方法,企业可以实现敏捷部署、弹性扩展和高效运维,从而大幅提升开发效率和系统可靠性。本文详细阐述了云原生的核心概念、主要技术和实际应用案例,并探讨了企业在实施云原生过程中的挑战与解决方案。无论是正在转型的传统企业,还是寻求创新的互联网企业,云原生都提供了一条实现高效能、高灵活性和高可靠性的技术路径。 ##
13 3
|
19天前
|
运维 监控 应用服务中间件
自动化运维:打造高效、稳定的系统环境
【8月更文挑战第30天】本文将探讨如何通过自动化运维技术,提升系统的稳定性和效率。我们将从基础概念出发,逐步深入到实践应用,分享一些实用的工具和技术,以及如何将这些工具和技术融入到日常的运维工作中。无论你是运维新手,还是有一定经验的老手,都能在这篇文章中找到有价值的信息。让我们一起探索自动化运维的世界,提升我们的工作效率,让系统运行得更加平稳。
|
4天前
|
运维 监控 数据可视化
高效运维的秘密武器:自动化工具链的构建与实践在当今数字化时代,IT系统的复杂性和规模不断增加,使得传统的手动运维方式难以应对日益增长的业务需求。因此,构建一套高效的自动化工具链成为现代运维的重要任务。本文将深入探讨如何通过自动化工具链提升IT运维效率,确保系统稳定运行,并实现快速响应和故障恢复。
随着企业IT架构的不断扩展和复杂化,传统的手动运维已无法满足业务需求。自动化工具链的构建成为解决这一问题的关键。本文介绍了自动化工具链的核心概念、常用工具及其选择依据,并通过实际案例展示了自动化工具链在提升运维效率、减少人为错误、优化资源配置等方面的显著效果。从监控系统到自动化运维平台,再到持续集成/持续部署(CI/CD)的流程,我们将一步步揭示如何成功实施自动化工具链,助力企业实现高效、稳定、可靠的IT运维管理。
|
18天前
|
微服务 API Java
微服务架构大揭秘!Play Framework如何助力构建松耦合系统?一场技术革命即将上演!
【8月更文挑战第31天】互联网技术飞速发展,微服务架构成为企业级应用主流。微服务将单一应用拆分成多个小服务,通过轻量级通信机制交互。高性能Java Web框架Play Framework具备轻量级、易扩展特性,适合构建微服务。本文探讨使用Play Framework构建松耦合微服务系统的方法。Play采用响应式编程模型,支持模块化开发,提供丰富生态系统,便于快速构建功能完善的微服务。
26 0
|
18天前
|
数据采集 运维 监控
自动化运维:用Python打造简易监控系统
【8月更文挑战第31天】在追求高效的IT世界里,自动化运维不再是奢侈品而是必需品。本文将通过一个Python示例,展示如何构建一个简单的系统监控工具。从数据采集到警报触发,我们将一步步解锁自动化的秘密,让你的服务器管理变得轻松而高效。
|
20天前
|
Kubernetes Cloud Native Docker
云原生之旅:从容器到微服务的架构演变
【8月更文挑战第29天】在数字化时代的浪潮下,云原生技术以其灵活性、可扩展性和弹性管理成为企业数字化转型的关键。本文将通过浅显易懂的语言和生动的比喻,带领读者了解云原生的基本概念,探索容器化技术的奥秘,并深入微服务架构的世界。我们将一起见证代码如何转化为现实中的服务,实现快速迭代和高效部署。无论你是初学者还是有经验的开发者,这篇文章都会为你打开一扇通往云原生世界的大门。
|
21天前
|
负载均衡 应用服务中间件 持续交付
微服务架构下的Web服务器部署
【8月更文第28天】随着互联网应用的不断发展,传统的单体应用架构逐渐显露出其局限性,特别是在可扩展性和维护性方面。为了解决这些问题,微服务架构应运而生。微服务架构通过将应用程序分解成一系列小型、独立的服务来提高系统的灵活性和可维护性。本文将探讨如何在微服务架构中有效部署和管理Web服务器实例,并提供一些实际的代码示例。
51 0