博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SSM(十三) 将dubbo暴露出HTTP服务
阅读量:6410 次
发布时间:2019-06-23

本文共 8279 字,大约阅读时间需要 27 分钟。

dubbo暴露为http服务.jpg

前言

通常来说一个dubbo服务都是对内给内部调用的,但也有可能一个服务就是需要提供给外部使用,并且还不能有使用语言的局限性。

比较标准的做法是对外的服务我们统一提供一个openAPI,这样的调用方需要按照标准提供相应的appID以及密钥来进行验签才能使用。这样固然是比较规范和安全,但复杂度也不亚于开发一个单独的系统了。

这里所讲到的没有那么复杂,就只是把一个不需要各种权限检验的dubbo服务对外提供为HTTP服务。

调用示例:

dubbo-http封面.jpg

准备工作

以下是本文所涉及到的一些知识点:

  • Spring相关知识。
  • Java反射相关知识。
  • SpringMVC相关知识。

其实思路很简单,就是利用SpringMVC提供一个HTTP接口。

在该接口中通过入参进行反射找到具体的dubbo服务实现进行调用。

HttpProviderConf配置类

首先需要定义一个HttpProviderConf类用于保存声明需要对外提供服务的包名,毕竟我们反射时需要用到一个类的全限定名:

public class HttpProviderConf {    /**     * 提供http访问的包     */    private List
usePackage ; //省略getter setter方法}复制代码

就只有一个usePackage成员变量,用于存放需要包名。

至于用List的原因是允许有多个。

请求响应入参、出参

HttpRequest入参

public class HttpRequest {    private String param ;//入参    private String service ;//请求service    private String method ;//请求方法    //省略getter setter方法}复制代码

其中param是用于存放真正调用dubbo服务时的入参,传入json在调用的时候解析成具体的参数对象。

service存放dubbo服务声明的interface API的包名。

method则是真正调用的方法名称。

HttpResponse 响应

public class HttpResponse implements Serializable{    private static final long serialVersionUID = -552828440320737814L;    private boolean success;//成功标志    private String code;//信息码    private String description;//描述    //省略getter setter方法}复制代码

这里只是封装了常用的HTTP服务的响应数据。

暴露服务controller

最重要的则是controller里的实现代码了。

先贴代码:

@Controller@RequestMapping("/dubboAPI")public class DubboController implements ApplicationContextAware{    private final static Logger logger = LoggerFactory.getLogger(DubboController.class);    @Autowired    private HttpProviderConf httpProviderConf;    //缓存作用的map    private final Map
> cacheMap = new HashMap
>(); protected ApplicationContext applicationContext; @ResponseBody @RequestMapping(value = "/{service}/{method}",method = RequestMethod.POST) public String api(HttpRequest httpRequest, HttpServletRequest request, @PathVariable String service, @PathVariable String method) { logger.debug("ip:{}-httpRequest:{}",getIP(request), JSON.toJSONString(httpRequest)); String invoke = invoke(httpRequest, service, method); logger.debug("callback :"+invoke) ; return invoke ; } private String invoke(HttpRequest httpRequest,String service,String method){ httpRequest.setService(service); httpRequest.setMethod(method); HttpResponse response = new HttpResponse() ; logger.debug("input param:"+JSON.toJSONString(httpRequest)); if (!CollectionUtils.isEmpty(httpProviderConf.getUsePackage())){ boolean isPac = false ; for (String pac : httpProviderConf.getUsePackage()) { if (service.startsWith(pac)){ isPac = true ; break ; } } if (!isPac){ //调用的是未经配置的包 logger.error("service is not correct,service="+service); response.setCode("2"); response.setSuccess(false); response.setDescription("service is not correct,service="+service); } } try { Class
serviceCla = cacheMap.get(service); if (serviceCla == null){ serviceCla = Class.forName(service) ; logger.debug("serviceCla:"+JSON.toJSONString(serviceCla)); //设置缓存 cacheMap.put(service,serviceCla) ; } Method[] methods = serviceCla.getMethods(); Method targetMethod = null ; for (Method m : methods) { if (m.getName().equals(method)){ targetMethod = m ; break ; } } if (method == null){ logger.error("method is not correct,method="+method); response.setCode("2"); response.setSuccess(false); response.setDescription("method is not correct,method="+method); } Object bean = this.applicationContext.getBean(serviceCla); Object result = null ; Class
[] parameterTypes = targetMethod.getParameterTypes(); if (parameterTypes.length == 0){ //没有参数 result = targetMethod.invoke(bean); }else if (parameterTypes.length == 1){ Object json = JSON.parseObject(httpRequest.getParam(), parameterTypes[0]); result = targetMethod.invoke(bean,json) ; }else { logger.error("Can only have one parameter"); response.setSuccess(false); response.setCode("2"); response.setDescription("Can only have one parameter"); } return JSON.toJSONString(result) ; }catch (ClassNotFoundException e){ logger.error("class not found",e); response.setSuccess(false); response.setCode("2"); response.setDescription("class not found"); } catch (InvocationTargetException e) { logger.error("InvocationTargetException",e); response.setSuccess(false); response.setCode("2"); response.setDescription("InvocationTargetException"); } catch (IllegalAccessException e) { logger.error("IllegalAccessException",e); response.setSuccess(false); response.setCode("2"); response.setDescription("IllegalAccessException"); } return JSON.toJSONString(response) ; } /** * 获取IP * @param request * @return */ private String getIP(HttpServletRequest request) { if (request == null) return null; String s = request.getHeader("X-Forwarded-For"); if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s)) { s = request.getHeader("Proxy-Client-IP"); } if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s)) { s = request.getHeader("WL-Proxy-Client-IP"); } if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s)) { s = request.getHeader("HTTP_CLIENT_IP"); } if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s)) { s = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (s == null || s.length() == 0 || "unknown".equalsIgnoreCase(s)) { s = request.getRemoteAddr(); } if ("127.0.0.1".equals(s) || "0:0:0:0:0:0:0:1".equals(s)) try { s = InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException unknownhostexception) { return ""; } return s; } public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; }复制代码

先一步一步的看:

  • 首先是定义了一个DubboController,并使用了SpringMVC的注解对外暴露HTTP服务。

  • 实现了org.springframework.context.ApplicationContextAware类,

    实现了setApplicationContext()方法用于初始化Spring上下文对象,在之后可以获取到容器里的相应对象。

  • 核心的invoke()方法。

  • 调用时:http://127.0.0.1:8080/SSM-SERVICE/dubboAPI/com.crossoverJie.api.UserInfoApi/getUserInfo
  • 具体如上文的调用实例。先将com.crossoverJie.api.UserInfoApigetUserInfo赋值到httpRequest入参中。
  • 判断传入的包是否是对外提供的。如下配置:

    com.crossoverJie.api
    复制代码

    其中的com.crossoverJie.api就是自己需要暴露的包名,可以多个。

  • 接着在缓存map中取出反射获取到的接口类类型,如果获取不到则通过反射获取,并将值设置到缓存map中,这样不用每次都反射获取,可以节省系统开销(反射很耗系统资源)。

  • 接着也是判断该接口中是否有传入的getUserInfo方法。
  • 取出该方法的参数列表,如果没有参数则直接调用。
  • 如果有参数,判断个数。这里最多只运行一个参数。也就是说在真正的dubbo调用的时候只能传递一个BO类型,具体的参数列表可以写到BO中。因为如果有多个在进行json解析的时候是无法赋值到两个参数对象中去的。
  • 之后进行调用,将调用返回的数据进行返回即可。

总结

通常来说这样提供的HTTP接口再实际中用的不多,但是很方便调试。

比如写了一个dubbo的查询接口,在测试环境或者是预发布环境中就可以直接通过HTTP请求的方式进行简单的测试,或者就是查询数据。比在Java中写单测来测试或查询快的很多。

安装

git clone https://github.com/crossoverJie/SSM-DUBBO-HTTP.git复制代码
cd SSM-DUBBO-HTTP复制代码
mvn clean复制代码
mvn install复制代码

使用

com.crossoverJie
SSM-HTTP-PROVIDER
1.0.0
复制代码

spring配置

com.crossoverJie.api
复制代码

插件地址:

项目地址:

个人博客地址:。

GitHub地址:。

weixin

转载地址:http://izkra.baihongyu.com/

你可能感兴趣的文章
Hadoop和MapReduce初识
查看>>
istio sidecar自动注入过程分析
查看>>
caffe 画loss曲线
查看>>
innodb系统表空间维护
查看>>
从头开始写框架(二):孕育框架的种子_下
查看>>
4-数据类型(2)
查看>>
百度图片爬虫
查看>>
cocos2dx 3.2之Lua打飞机项目
查看>>
JAVA- JDBC之DBHelper
查看>>
AQS同步组件及ReentrantLock和synchronized的区别
查看>>
bzoj1901: Zju2112 Dynamic Rankings 主席树+树状数组
查看>>
jeewx的使用_01 接入和验证
查看>>
java.net.BindException: Address already in use:80 linux
查看>>
用javascript向一个网页连接接口发送请求,并接收该接口返回的json串
查看>>
PHP语言 -- Smarty缓存
查看>>
谈谈JavaScript中function多重理解
查看>>
窗口置顶 - 仿TopWind
查看>>
(转)通过注册表修改VC6.0的字体
查看>>
oracle 查询 函数练习2
查看>>
浅析如何学习基于ARM平台的嵌入式系统(转载)
查看>>