背景
很多应用在进行多租户改造的时候,需要将原来的单体应用拆分成一个manager和多个agent,manager部署在公司,agent部署在租户的环境,manager通常用于调度转发、业务管理,agent主要是在客户环境中承担具体业务处理,诸如对接大数据组件,承担计算任务等。
而多数情况下manager和agent之间是跨公网调用,不是处于同一内网环境通过服务调用的。而真正可以处理具体业务是在agent,所以在跨公网的条件下基于tcp协议如RMI是行不通的,所以要进行restful的改造。
为了尽量不影响现有应用代码,做了如下改造:agent抽象出api接口,manager只需要依赖相应的agent暴露的api接口即可完成调度转发。
我们要做的就是基于http的远程调用。
使用场景
跨公网的远程调用,不支持tcp协议。
方案
参考mybaties的实现方法考虑使用代理模式,在调用端只有api接口,接口的实现是被代理掉的,在项目启动时会为每一个配置的接口生成一个java动态代理,这个动态代理做的事情就是将方法需要的参数都序列化,通过http request传给agent,然后agent执行并返回结果。本质是利用jdk动态代理将方法的调用转化成http请求调用。
实现
1:首先获取所有需要代理的类(实现动态代理时构造方法获取每一个类的method),为每一个需要代理的类注册实例
2: 在实现动态代理的invoke方法内,通过抽象方法HttpRequestService,设置http请求转发到agent通用的api接口中,将agent所需要的,诸如method,class名称,请求参数等,序列化传过去。
HttpRpcInvocator(Class<?> clazz, HttpRpcService httpRpcService) {
this.clazz = clazz;
this.httpRpcService = httpRpcService;
for(Method method: clazz.getMethods()) {
String methodName = method.getName();
if (!methodName.equals("equals") &&! methodName.equals("hashCode") && !methodName.equals("clone") && !methodName.equals("toString")) {
methodSet.add(method);
}
}
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
if (method == null || !methodSet.contains(method)) {
StringBuilder methods = new StringBuilder();
methods.append("[");
Iterator it = methodSet.iterator();
while (it.hasNext()) {
methods.append(((Method)it.next()).getName()).append(",");
}
methods.deleteCharAt(methods.lastIndexOf(","));
methods.append("]");
}
return httpRpcService.httpRequest(method, args);
}
public void httpRequest(String domain, HttpRpcParam param) {
// manager调用agent通用http接口,封装请求param,将agent所需要的,诸如method,class名称,请求参数等,序列化传过去
}
2:在agent实现一个通用的api接口,用以接收所有来自master的请求,然后通过反射获取结果,最后将结果返回给manager,manager序列化返回结果。
@RequestMapping(value = "/api", method = RequestMethod.POST, produces = (MediaType.APPLICATION_OCTET_STREAM_VALUE))
private ResponseEntity<Object> entrance(HttpServletRequest request) {
// agent通用api接口,接收所有来自manager的请求,然后通过反射获取结果,最后将结果返回给manager
}