架构师日记——手写利用HTTP协议远程模块调用

远程模块调用比较流行的协议有RPC协议、RMI协议和HTTP协议
本篇博客教大家如何使用HTTP协议试远程模块的调用

1.定架构

总体架构如下
这里写图片描述
接下来我一个一个讲解他们的作用

2.ModuleModel

这个类的作用是记录不同uuid对应的module的信息,包括远端ip和url等,代码如下

/**
 * 调用远端方法的协议类
 */
public class InteractiveModel {
    
    
    /**
     * 操作类型
     */
    private String opeType;
    /**
     * 操作参数
     */
    private Map<String,Object>map=new HashMap<String ,Object>();

    public InteractiveModel(String opeType, Map<String, Object> map) {
        this.opeType = opeType;
        this.map = map;
    }

    public InteractiveModel() {
    }

    public String getOpeType() {
        return opeType;
    }

    public void setOpeType(String opeType) {
        this.opeType = opeType;
    }

    public Map<String, Object> getMap() {
        return map;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }
}

3.InteractiveModel

这个类可以说是调用方法的协议类,它包含操作类型和操作参数,代码如下

/**
 * 调用远端方法的协议类
 */
public class InteractiveModel {
    
    
    /**
     * 操作类型
     */
    private String opeType;
    /**
     * 操作参数
     */
    private Map<String,Object>map=new HashMap<String ,Object>();

    public InteractiveModel(String opeType, Map<String, Object> map) {
        this.opeType = opeType;
        this.map = map;
    }

    public InteractiveModel() {
    }

    public String getOpeType() {
        return opeType;
    }

    public void setOpeType(String opeType) {
        this.opeType = opeType;
    }

    public Map<String, Object> getMap() {
        return map;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }
}

4.InteractiveCallHelper

InteractiveCallHelper是调用远端方法的发起者,这个类可以独立于这个项目,为了方便我就我不把它单独提取出来了,既可以返回string也可以返还一个指定类型的对象。这个类的作用主要有以下几个
1.根据不同的uuid去获取到远端方法的url
2.实现参数中非法字符的转换
3.组合url和参数
代码如下

/**
 * 调用远端方法的发起者
 */
public class InteractiveCallHelper {
    
    
    /**
     *
     * @param moduleId 模块Id
     * @param opeType 需要调用的具体的业务操作类型
     * @param mapParams 调用所需要传递的参数
     * @return 服务端返回的Json数据
     */
    public String call(String moduleId, String opeType, Map<String,Object>mapParams){
        //1.根据moduleId去获取到该模块部署的信息,理论上要根据moduleId去设置,这里写死
        ModuleModel moduleModel=new ModuleModel();
        moduleModel.setDeployIP("localhost");
        moduleModel.setDeployPort("8080");
        moduleModel.setIntereractiveUrl("/goods/call");

        //2.准备要传递的数据,把Map转换为JSON
        String paramStr= JSON.toJSONString(mapParams);
        //因为#在url里有特殊含义,所以将#替换为*
        paramStr=paramStr.replace("#","*");

        //3.拼接一个远程调用的URL
        String urlStr="http://"+ moduleModel.getDeployIP()+":"+moduleModel.getDeployPort()+moduleModel.getIntereractiveUrl()
                +"?jsonParam={opeType:"+opeType+",map:"+paramStr+"}";

        //4.使用URL进行远程调用,流式操作
        BufferedReader in =null;
        String retJson="";
        try {
            URL url=new URL(urlStr);
            URLConnection urlConnection=url.openConnection();
            // 定义 BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(
                    urlConnection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                retJson += line;
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return retJson;
    }

    /**
     *
     * @param moduleId 模块Id
     * @param opeType 需要调用的具体的业务操作类型
     * @param mapParams 调用所需要传递的参数
     * @param cls 需要转的类型
     * @return  服务器返回的对象
     */
    public <T>T call(String moduleId, String opeType, Map<String,Object>mapParams,Class<T> cls){
        String json=call(moduleId,opeType,mapParams);
        return JSON.parseObject(json,cls);
    }

    /**
     * 测试方法
     * @param args
     */
    public static void main(String[]args){
        InteractiveCallHelper interactiveCallHelper=new InteractiveCallHelper();
        Map<String,Object>map=new HashMap<String,Object>();
        String str=interactiveCallHelper.call("goods","1",map);
        System.out.println(str);
    }
}

5.InteractiveBaseController

这个类是远端被调用方法的基类,这个类是个抽象类,任何远端可被调用的controller都要继承它,继承call方法和实现它的doCall方法,代码如下

/**
 * 远端被调用方法的基类
 */
public abstract class InteractiveBaseController {
    
    
    /**
     * 解析参数
     * @param jsonParam
     * @return
     */
    @RequestMapping("/call")
    @ResponseBody
    public Object call(String jsonParam){
        if (jsonParam!=null&&jsonParam.contains("*")){
            //将*变回#
            jsonParam=jsonParam.replace("*","#");
        }
        InteractiveModel interactiveModel= JSON.parseObject(jsonParam,InteractiveModel.class);
        Object ret=doCall(interactiveModel.getOpeType(),interactiveModel.getMap());
        return ret;
    }
    protected  abstract Object doCall(String opetype, Map<String,Object>map);
}

6.GoodsModel

这个类是JavaBean,和数据库对应的实体类,可自由替换,代码如下

/**
 * 要操作的实体类
 */
public class GoodsModel {
    
    
    private String uuid;
    private String name;

    public GoodsModel(String uuid, String name) {
        this.uuid = uuid;
        this.name = name;
    }

    public GoodsModel() {
    }

    public String getUuid() {
        return uuid;
    }

    public void setUuid(String uuid) {
        this.uuid = uuid;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "GoodsModel{" +
                "uuid='" + uuid + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

7.GoodsController

远程方法示例,根据不同opetype执行不同的操作,如opetype为1时往数据库插入一个实体,代码如下

@RestController
@RequestMapping(value = "goods")
public class GoodsController extends InteractiveBaseController{
    
    

    @Override
    protected Object doCall(String opetype, Map<String, Object> map) {
        //执行不同的操作
        if ("1".equals(opetype)){

        }else if ("2".equals(opetype)){

        }
        GoodsModel goodsModel=new GoodsModel();
        goodsModel.setName("abc");
        goodsModel.setUuid("132132");
        System.out.println("call");
        return goodsModel;
    }
}

一个利用HTTP远程调用模块的Demo就写好了,但HTTP协议效率较低,所以在日常开发中,我们常常使用RPC协议实现远程调用模块,比如使用著名的Dubbo框架

完整代码已上传到github:https://github.com/jkgeekJack/UseRemoteInvoker

猜你喜欢

转载自blog.csdn.net/qq_32198277/article/details/77523210