motan网关设计
- 实现思路
- 1.调用方法约定
- 2.调用格式约定
- 代码实现
- 方法 MotanRedirectUtil
- 具体使用
- 服务端使用
- 网关端使用 (源码中已经有了可直接使用)
最近加入的一个it从零开始搭建的公司,rpc框架选型为motan。之所以用motan主要是由于公司分两个团队java和php,motan可以实现跨语言调用且比较轻量,所以选择了motan。
由于后端项目之间使用rpc进行通讯,没有mvc层。对接前端需要提供http接口给到前端,因此需要有个motan网关来完成对接前端的任务。
实现思路
翻阅了下资料
https://github.com/weibocom/motan/issues/581 实现motan网关有两种方式
1.使用motan-go开发网关。
2.修改motan源码,进行自定义转发。
由于对go语言不熟,修改motan源码的工作量比较大。因此这里选择了一个比较折中的方式。使用springboot开发motan网关,在原有的motan的基础上包装一层转发逻辑,实现motan接口的动态转发。
基本思路如下
网关和服务间使用一个公共的motan接口的方法进行调用,然后由公共run方法去根据约定好的规矩进行转发,去调用实际的方法。
这个样做的好处就是我不用关心服务提供方的接口变动情况,只需要按照约定的给定参数就是可以直接调用。无需像普通的motan rpc服务调用需要频繁重启服务。
1.调用方法约定
规定服务方听的公共方法为
String run(String param) throws Exception;
参数为String 返回值为String 方法名为run,
2.调用格式约定
调用参数为json字符串,必须包含两个参数 redirectMethod 需要调用的方法,redirectClazz 调用方法的类
这两个主要用来动态调用的时候反射调用到具体方法
代码实现
有了上面的思路,下面我们来实现下具体的代码。
方法 MotanRedirectUtil
这个类是核心,里面规定了参数调用,请求方调用方法,和相应方调用方法
package com.happotech.motan.core.util;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* motan 网关转发util
* @author songxulin
* @date 2021/4/14
*/
@Component
public class MotanRedirectUtil implements ApplicationContextAware {
//spring上下文
private static ApplicationContext applicationContext;
public static Object getMotanBean(Class clazz){
return applicationContext.getBean(clazz);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
//发送请求
public static JSONObject requestRun(Object iActMotanService,String param,String ApiUrlPrefix,String redirectMethod) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
param = getParam(param,ApiUrlPrefix,redirectMethod);
Method run = iActMotanService.getClass().getMethod("run", String.class);
return JSONObject.parseObject(run.invoke(iActMotanService,param).toString());
}
//相应请求
public static String responseRun(String param,Class clazz) throws Exception {
JSONObject parseObject = JSONObject.parseObject(param);
String redirectMethod = parseObject.getString("redirectMethod");
String redirectClazz = parseObject.getString("redirectClazz");
if(clazz.getName().equals(redirectClazz)&&"run".equals(redirectMethod)){
throw new Exception("run 方法不允许重定向");
}
Class<?> aClass = Class.forName(redirectClazz);
Object motanBean = MotanRedirectUtil.getMotanBean(aClass);
Method method = aClass.getMethod(redirectMethod, String.class);
Object invoke = method.invoke(motanBean, param);
if(invoke instanceof String){
return (String) invoke;
}
return JSONObject.toJSONString(invoke);
}
//获取参数
public static String getParam(String body,String ApiUrlPrefix,String redirectMethod){
JSONObject param = JSONObject.parseObject(body);
param.put("redirectMethod",redirectMethod);
param.put("redirectClazz",ApiUrlPrefix.replaceAll("/",".").substring(1,ApiUrlPrefix.length()-1));
return param.toJSONString();
}
}
上面就是核心代码
将核心代码打包,提交到私服和本地仓库中,就可以方便的在网关端和服务端使用了。
<dependency>
<groupId>com.happotech</groupId>
<artifactId>happo-motan-client-core</artifactId>
<version>1.0.1</version>
</dependency>
具体使用
使用的时,可以将核心代码打包成jar,引入到服务端和网关端使用。
服务端使用
必须包含 String run(String param) 方法。
/**
* @author songxulin
* @date 2021/4/14
*/
public interface IActMotanService {
String run(String param) throws Exception;
}
实现
@MotanService
public class ActMotanServiceImpl implements IActMotanService {
@Override
public String run(String param) throws Exception {
return MotanRedirectUtil.responseRun(param,IActMotanService.class);
}
}
网关端使用 (源码中已经有了可直接使用)
@RestController
public class MotanController {
@MotanReferer
private IMotanService iMotanService;
//ApiUrlPrefix 为接口类的全路径
private static final String ApiUrlPrefix = "/com/happotech/actmgr/motan/service/api/IMgrService/";
@RequestMapping(ApiUrlPrefix + "{method}")
@ResponseBody
public JSONObject urcService(@RequestBody String body, @PathVariable("method") String method, HttpServletRequest request)
throws Exception {
return MotanRedirectUtil.requestRun(iActMotanService, body, ApiUrlPrefix, method);
}
}
这样motan的网关就实现了,后面如果有新的项目加入进来,只需要加入新的的controller就可以了。
motan网关源码入口