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

来源:互联网 发布:阿里云注册会员名 编辑:程序博客网 时间:2024/06/18 15:32

远程模块调用比较流行的协议有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

原创粉丝点击