在微服务中,服务可能是部署和运行在不同的区域的。 不同区域的服务,认证方式也不同,接口调用方式不同。
|接入区| ----网关-----|业务区|
另外,随着产品架构的迭代升级,相同的微服务在不同代际版本的上述服务调用方式也会有差异。 这就给了测试开发人员提出了一个需求,希望能在功能测试时尽可能地抹平这些差异,让测试人员能专注于测试层面的测试。 从测试角度看,会有以下几个典型的场景
A: 外部服务接口,提供给外部机构或个人使用,一般位于接入区,涉及业务的应用,认证较为严格,可能需要证书等方式进行认证。
B: 管理类服务接口,提供给业务部门的业务终端或者是前台使用。
C: 内部服务接口,被上述服务调用的接口。
在接口调用过程中,可能涉及到以下的内容 1)Head : user-agent: 服务间调用时,会对user-agent 进行判断,看客户端是 来自普通浏览器还是内置浏览器,如httpclient X-CSRF-TOKEN, 对于外部服务接口来说,需要在登录后将登录接口返回的CSRF-TOKEN从cookies中取出,存放到head中,以用于后续服务接口的顺利调用。
首先定义一个接口,包括了登录和发请求两个方法
package com.github.http;
public interface Operator {
public String logon(String logonUrl,String userName, String password);
public String doPost(String requestUrl,String body);
}
然后写写一个类类实现这个接口,假设这是一个最常见的外部服务接口A的调用类,它的参数通过body进行传输,并且需要登录。
package com.github.http;
public class OperatorA implements Operator {
@Override
public String logon(String logonUrl, String userName, String password) {
return null;//TODO
}
@Override
public String doPost(String requestUrl, String body) {
return null;//TODO
}
}
这个时候笔者发现,如果按照一般策略模式进行实现的话,那么如果还需要对服务间接口C编写调用策略时,发现Operator 接口类需要额外增加一个方法
public String doPost(String requestUrl, Map params)
因为这类接口通过params进行数据传输。如果修改Operator的接口,那么已经写好的OperatorA接口也要做响应的修改来新增这个方法。另外根据之前所述,内部服务调用时不需要登录,因此OperatorB中其实并不需要logon方法,但是采用这种方式,还是需要在OperatorB中实现这个方法。这就不是很友好了。
新增一个抽象类AbstractOperator 来实现这个接口,然后上述各个Operator来继承并选择性实现各自所需的方法,就可以避免上述麻烦了。最后通过HttpOperator 来提供统一的调用入口。
package com.github.http;
import java.util.Map;
public abstract class AbstractOperator implements Operator {
public String logon(String logonUrl, String userName, String password){
throw new UnsupportedOperationException();
}
public String doPost(String requestUrl, String body) {
throw new UnsupportedOperationException();
}
public String doPost(String requestUrl, Map params){
throw new UnsupportedOperationException();
}
}
package com.github.http;
import java.util.Map;
public interface Operator {
public String logon(String logonUrl,String userName, String password);
public String doPost(String requestUrl,String body);
public String doPost(String requestUrl, Map params);
}
例如在OperatorB中只要实现以下方法即可:
package com.github.http;
public class OperatorB extends AbstractOperator {
@Override
public String doPost(String requestUrl, String body) {
System.out.println("post body on B");
return null;
}
}
最终通过一个HttpOperator 来提供统一的调用入口,各个应用的服务调用可以写成类似这样,
HttpOperator operator= new HttpOperator(OperatorType.A);
operator.doPost("www.baidu.com","{}");
具体实现如下,
package com.github.http;
import java.util.Map;
public class HttpOperator {
OperatorType type;
AbstractOperator operator;
HttpOperator(OperatorType type){
this.type=type;
setOperator(type);
}
public String logon(String logonUrl, String userName, String password){
return operator.logon(logonUrl,userName,password);
}
public String doPost(String requestUrl, String body) {
return operator.doPost(requestUrl,body);
}
public String doPost(String requestUrl, Map params){
return operator.doPost(requestUrl,params);
}
private void setOperator(OperatorType type) {
switch (type) {
case A:
operator= new OperatorA();
case B:
operator= new OperatorB();
case C:
operator= new OperatorC();
}
}
public static void main(String argv[]){
HttpOperator operator= new HttpOperator(OperatorType.A);
operator.doPost("www.baidu.com","");
}
}