多环境测试遇到的问题及解决方案

最近新到的公司由于在服务化,领导叫我调研并使用 apache dubbo 做微服务改造。因为之前的公司基本上都是使用的 dubbo,所以对于服务改造过程并不复杂。而且我还把自己的改造指南分享到公司内网 wiki。这次主要是和大家分享一下在服务改造之后在本地测试遇到的问题。

在这之前分享一个在使用 dubbo 服务化时遇到的一个问题。最开始的时候本来使用的是 dubbo 2.7.5,但是在 dubbo-admin 不能显示元数据一直找这个问题的解决方案没有找到。因为无法显示元数据就不能进行利用它的接口测试功能,所以最后还是决定把版本回退到 2.7.3 这样在 dubbo-admin 页面就可以展示接口服务测试功能了。

因为我们使用的环境是 aws ,并且使用了 aws 提供的 redis 服务。主要是用来做分布锁及缓存作用,但是在本地调度的时候遇到一个问题。那就是无法连接 Redis 服务,之前遇到这个问题就是注释掉使用 Redis 作分布式锁的代码。这种方式对于研发人员及其不友好。

所以我的思路就是在本地环境配置文件中定义一个标注当前运行环境是本地的值。然后抽象出一个锁服务,之前是在业务代码里面直接操作 Redis。

public interface LockService {

	boolean lock(String key, long timeout, TimeUnit timeUnit);

	void unlock(String key);

}

判断当前环境的这个值是 local 就不使用 Redis 锁,直接返回 true,同样对应的 unlock 方法也是一个的逻辑,判断当前环境是本地环境就直接返回。

public class LockServiceImpl implements LockService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Value("${environment}")
    private String environment;

    @Override
    public boolean lock(String key, long timeout, TimeUnit timeUnit) {
        if(isLocal()){
            return true;
        }
        boolean result = false;
        try {
            // redis 锁操作
        } catch (Exception e) {
            log.error("LockService#lock lock false the key is  {}, exception info {}", key, e);
            unlock(key);
        }
        return result;
    }

    @Override
    public void unlock(String key) {
        if(isLocal()) {
            return;
        }
        try {
            // redis 释放锁操作
        } catch (Exception e) {
            log.error("LockService#unlock unlock false the key is  {}, exception info {}", key, e);
        }
    }

    private boolean isLocal(){
        return "local".equals(enviroment);
    }

}

在项目集成 Dubbo 之后,代码需要进行本地测试。之前服务是通过 restful 把服务暴露出去的。在进行服务测试的时候我也写了相就的 TestController。但是这个暴露的测试类服务我又不想在其它环境暴露出去。所以又需要之前提到的环境代码配置。重复即是不好的,基于这个思考我就把这个运行环境配置利用 Spring boot 中的ConfigurationProperties抽取到了 RuntimeEnvironmentConfig 当中。

@Data
@ConfigurationProperties(prefix = "project.runtime")
public class RuntimeEnvironmentConfig {

	private String environment;

	private String supportDubbo;

	public boolean isLocal(){
		return CommonConstants.LOCAL_ENVIRONMENT.equals(environment);
	}
	
	public boolean supportDubbo(){
		return YesNoEnum.YES.getCode().equals(supportDubbo);
	}

}

当然了里面还包括了是否激活 dubbo 进行远程调用的配置。判断逻辑我写在了 RuntimeEnvironmentConfig 当中,这也是一种充血模型的编程思想。需要使用到运行环境配置的使用者只需要注入这个配置类,并且调用它提供的方法进行相应的业务处理即可。并不是很需要知道内部的实现逻辑,而且也不需要 equals 逻辑到处写,配置类已经判断好了。而激活这个配置类只需要在 Spring boot 当中的启动类添加如下代码即可:

@EnableConfigurationProperties(RuntimeEnvironmentConfig.class)
@SpringBootApplication
public class Bootstrap {

    public static void main(String[] args) {
        SpringApplication.run(Bootstrap.class, args);
    }

}

关于测试类的环境隔离我使用的是 Spring mvc 中的 HandlerInterceptor 机制,对于每一个测试类我的 uri 统一使用 /test/xxx 类型。比如:

@RestController
@RequestMapping("test")
public class PayEngineClientController {

	@Resource
	private PayEngineClient payEngineClientAdapter;

	/**
	 * 根据请求号查询收单支付订单
	 * @param requestNo
	 * @return
	 */
	@RequestMapping("collect/query")
	public BaseResponse<CollectQryRspVO> collectQuery(String requestNo){
		BaseResponse<CollectQryRspVO> response = payEngineClientAdapter.collectQuery(requestNo);
		return response;
	}
}

具体的拦截器类如下:

public class UnauthorizedHandlerInterceptor extends HandlerInterceptorAdapter {

	private RuntimeEnvironmentConfig runtimeEnvironmentConfig;

	public UnauthorizedHandlerInterceptor(RuntimeEnvironmentConfig runtimeEnvironmentConfig) {
		this.runtimeEnvironmentConfig = runtimeEnvironmentConfig;
	}

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		if(runtimeEnvironmentConfig.isLocal()) {
			return true;
		}
		BaseResponse baseResponse = BaseResponse.error("401", "Unauthorized");
		ServletOutputStream outputStream = response.getOutputStream();
		outputStream.print(JSON.toJSONString(baseResponse));
		return false;
	}

}

然后通过 @EnableWebMvc 注解把拦截器添加到 Spring MVC 框架当中。

@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {

	@Autowired
	private RuntimeEnvironmentConfig environmentConfig;

	@Bean
	public HandlerInterceptor unauthorizedHandlerInterceptor(){
		return new UnauthorizedHandlerInterceptor(environmentConfig);
	}
	
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		// 测试接口非 Local 环境不允许访问
		registry.addInterceptor(unauthorizedHandlerInterceptor()).addPathPatterns("/test/**");
	}
}

在通过 postman 进行接口测试时,配置了project.runtime.environment=local 进行接口访问的时候可以正常调用。如果去除配置就会是以下响应:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值