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接口的动态转发。
基本思路如下

java 出口网关 接口配置 java实现网关_服务端

网关和服务间使用一个公共的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网关源码入口