前言
通常来说一个dubbo
服务都是对内给内部调用的,但也有可能一个服务就是需要提供给外部使用,并且还不能有使用语言的局限性。
比较标准的做法是对外的服务我们统一提供一个openAPI
,这样的调用方需要按照标准提供相应的appID
以及密钥来进行验签才能使用。这样固然是比较规范和安全,但复杂度也不亚于开发一个单独的系统了。
这里所讲到的没有那么复杂,就只是把一个不需要各种权限检验的dubbo
服务对外提供为HTTP
服务。
调用示例:
dubbo-http封面.jpg
准备工作
以下是本文所涉及到的一些知识点:
- Spring相关知识。
- Java反射相关知识。
- SpringMVC相关知识。
其实思路很简单,就是利用
SpringMVC
提供一个HTTP
接口。
在该接口中通过入参进行反射找到具体的
dubbo
服务实现进行调用。
HttpProviderConf配置类
首先需要定义一个HttpProviderConf
类用于保存声明需要对外提供服务的包名,毕竟我们反射时需要用到一个类的全限定名:
public class HttpProviderConf { /** * 提供http访问的包 */ private List<String> 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<String, Class<?>> cacheMap = new HashMap<String, Class<?>>(); 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; }