自动化测试若何接见差别的环境对应服务实例

首先先容做一下场景先容:

  1、我们公司的测试环境比较复杂,预发环境(UAT)一套,SIT环境4套,DEV环境7套。我是卖力中台模块的测试,功效类似一个订单中央,然则功效相对比较复杂。网关进来的95%以上的请求都要我卖力的模块来处置(岂论线上营业照样线下营业,因此所有的环境都要经由我卖力模块。

     2、我们公司使用的grpc微服务框架,而我卖力的中台模块,都是通过grpc的微服务接口(不提供http接口),对于测试来讲,这是个不幸的新闻。

那么我们中台的接口自动化测试是若何来实现的呢?

自动化测试若何接见差别的环境对应服务实例

这个是完整 RPC 架构图

自动化测试若何接见差别的环境对应服务实例

一个 RPC 的焦点功效主要有 5 个部门组成,分别是:客户端、客户端 Stub、网络传输模块、服务端 Stub、服务端等

自动化测试若何接见差别的环境对应服务实例

  • 客户端(Client):服务挪用方。
  • 客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络新闻,再通过网络传输发送给服务端。
  • 服务端存根(Server Stub):吸收客户端发送过来的请求新闻并举行解包,然后再挪用内陆服务举行处置。
  • 服务端(Server):服务的真正提供者。
  • Network Service:底层传输,可以是 TCP 或 HTTP。

领会上面的基本知识。现在来先容我是若何实现的。

1、建立毗邻到远程服务器的 channel
2、构建使用该channel的客户端stub
3、挪用服务方式,执行RPC挪用
4、封装成Controller

 构建客户端stub

public class Client { //样例 stub
    private DemoServiceGrpc.DemoServiceBlockingStub demoServiceBlockingStub; //原生的stub 点对点测试
    public Client() { ManagedChannel channel = null; try { String ip =PropertiesUtils.getValue("****.grpc.ip"); String port = PropertiesUtils.getValue("****.grpc.port"); channel = ManagedChannelBuilder.forTarget("static://" + ip + ":" + port).usePlaintext().build(); } catch (Exception e) { e.printStackTrace(); } demoServiceBlockingStub = DemoServiceGrpc.newBlockingStub(channel); } public DemoServiceGrpc.DemoServiceBlockingStub getdemoServiceBlockingStub() { return demoServiceBlockingStub; } }

 封装Controller:

自动化测试若何接见差别的环境对应服务实例

 

那么简朴的http 接口服务就实现了。接下来重点来了,若何实现部署一台服务接见差别环境呢????

详细实现:

基于spring提供原生的 AbstractRoutingDataSource ,参考一些文档自己实现切换

1、 为了区分差别环境的设置,接纳了application-{}.yaml文件来隔离, 然后通过application.yaml文件来控制加载所有的设置文件

自动化测试若何接见差别的环境对应服务实例

2、application.yaml设置

浅析CAS与AtomicInteger原子类

自动化测试若何接见差别的环境对应服务实例

3、在Springboot的启动类上,排除掉datasource自动设置

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class GrpcApplication { public static void main(String[] args) { SpringApplication.run(GrpcApplication.class, args); } }

4、新建一个EnvContext类,接纳ThreadLocal的方式,对每个请求线程的环境变量举行隔离,这里容易遇到坑,springboot都是内嵌的tomcat启动模式,若是tomcat设置了链接的重用规则,那么若是env的信息没有被消灭,可能会导致错误加载设置

/** * 用来存放环境的变量,用于动态的去切换 */
public class EnvContext { public static ThreadLocal<String> envThreadLocal = new InheritableThreadLocal<>(); public static String getEnv(){ return envThreadLocal.get(); } public static void setEnv(String env){ envThreadLocal.set(env); } public static void clear(){ envThreadLocal.remove(); } }

5、建立一个DynamicDataSource, 这里继续了AbstractRoutingDataSource,动态数据源类集成了Spring提供的AbstractRoutingDataSource类,AbstractRoutingDataSource 中获取数据源的方式就是 determineTargetDataSource,而此方式又通过 determineCurrentLookupKey 方式获取查询数据源的key。

public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return EnvContext.getEnv(); } }

6、界说一个枚举类,放入所有的环境信息

@Getter @AllArgsConstructor @NoArgsConstructor public enum EnvEnum { DEV1("dev1","开发环境dev1"), DEV2("dev2","开发环境dev2"), DEV3("dev3","开发环境dev3"), DEV4("dev4","开发环境dev4"), DEV5("dev5","开发环境dev5"), DEV6("dev6","开发环境dev6"), DEV7("dev7","开发环境dev7"), SIT1("sit1","集成环境SIT1"), SIT2("sit2","集成环境SIT2"), SIT3("sit3","集成环境SIT3"), SIT4("sit4","集成环境SIT4"), UAT("uat","集成环境UAT"); public String env; public String desc; }

7、重点来了,我们通过AOP, 去拿到每次http的请求头中的header信息,来动态的切换EnvContext中的env设置

@Aspect @Component @Slf4j public class EnvAop { public ThreadLocal<String> threadLocal = new ThreadLocal<>(); @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping ) && @annotation(io.swagger.annotations.ApiOperation))") public void ex(){} @Around("ex()") public Object envAop(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object result; try { //获取每个请求的header,拿到环境变量的参数,存入ThreadLocal中,供每个线程使用
            RequestAttributes ra = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes sra = (ServletRequestAttributes) ra; HttpServletRequest request = sra.getRequest(); // 获取请求头
            Enumeration<String> enumeration = request.getHeaderNames(); //             String env = request.getHeader("env"); if(StringUtils.isEmpty(env)){ log.info("~~~~ 阻挡到http请求,环境变量信息为空,设置为默认dev1", env); env = EnvEnum.DEV1.env; } log.info("~~~~ 阻挡到http请求,环境变量信息为{}", env); EnvConfig.envThreadLocal.set(env); result = proceedingJoinPoint.proceed(); } finally { //请求竣事后,将环境变量的信息从ThreadLocal中移除
 EnvConfig.clear(); log.info("~~~~ http请求竣事,重置env的信息为{}" , EnvConfig.getEnv()); } return result; } }

 

8、编写一个工具类,动态获取Spring的容器ApplicationContext

@Component public class SpringContextUtil { @Resource private ApplicationContext applicationContext; private static ConfigurableApplicationContext context; private static BeanFactory factoryBean; @PostConstruct public void init() { context = (ConfigurableApplicationContext) applicationContext; factoryBean = context.getBeanFactory(); } public static BeanFactory getFactoryBean() { return factoryBean; } public static ConfigurableApplicationContext getApplicationContext() { return context; } }

9、然后编写一个设置信息动态读取工具类,每次请求进来,env会动态切换,然后工具类会自动拼装env信息去读取

  

public class PropertiesUtils { public static String getValue(String key) throws Exception { Environment environment = (Environment) SpringContextUtil.getApplicationContext().getBean("environment"); String value = environment.getProperty(EnvConfig.getEnv() + "." + key); if(StringUtils.isEmpty(value)){ throw new Exception("设置信息获取失败,请检查application-"+ EnvConfig.getEnv()+".yaml文件!, key = " + key + " , env = " + EnvConfig.getEnv()); } return value; } }

 

到此  实现通过http请求 中header中设置env参数来实现  动态切换服务器(以此类推可以修改同过parame或者url中的参数来实现动态切换服务器)

 重点注重:

   实现client 的毗邻的方式不能通过Springboot  的@service @Autowired来实现  否则无法实现动态切换服务器  也就是Controller内里每次使用client的时刻  都要new

        由于通过Bean实现的话,启动的时刻就已经加载完成了,无法实现动态加载

 

声明:该文章参考公司同事(章帅)的文章

 

 

  

原创文章,作者:admin,如若转载,请注明出处:https://www.2lxm.com/archives/5760.html