dubbo暴露出HTTP服务
dubbo暴露出HTTP服務(wù)
點(diǎn)關(guān)注不迷路,歡迎再訪!精簡(jiǎn)博客內(nèi)容,盡量已專業(yè)術(shù)語(yǔ)來(lái)分享。
努力做到對(duì)每一位認(rèn)可自己的讀者負(fù)責(zé)。
幫助別人的同時(shí)更是豐富自己的良機(jī)。
目錄
- dubbo暴露出HTTP服務(wù)
- 前言
- 準(zhǔn)備工作
- HttpProviderConf配置類
- 請(qǐng)求響應(yīng)入?yún)ⅰ⒊鰠?/li>
- HttpRequest入?yún)?/li>
- HttpResponse 響應(yīng)
- 暴露服務(wù)controller
最近接觸dubbo+zuul,涉及到將dubbo服務(wù)暴露為http,簡(jiǎn)單記錄下。
前言
通常來(lái)說(shuō)一個(gè)dubbo服務(wù)都是對(duì)內(nèi)給內(nèi)部調(diào)用的,但也有可能一個(gè)服務(wù)就是需要提供給外部使用,并且還不能有使用語(yǔ)言的局限性。本章講到的沒(méi)有那么復(fù)雜,就只是把一個(gè)不需要各種權(quán)限檢驗(yàn)的dubbo服務(wù)對(duì)外提供為HTTP服務(wù)。
準(zhǔn)備工作
以下是本文所涉及到的一些知識(shí)點(diǎn):
Spring相關(guān)知識(shí)
Java反射相關(guān)知識(shí)
SpringMVC相關(guān)知識(shí)
HttpProviderConf配置類
首先需要定義一個(gè)HttpProviderConf類用于保存聲明需要對(duì)外提供服務(wù)的包名,畢竟我們反射時(shí)需要用到一個(gè)類的全限定名:
public class HttpProviderConf {/*** 提供http訪問(wèn)的包*/private List<String> usePackage ;public List<String> getUsePackage() {return usePackage;}public void setUsePackage(List<String> usePackage) {this.usePackage = usePackage;}}請(qǐng)求響應(yīng)入?yún)ⅰ⒊鰠?/h3>
HttpRequest入?yún)?/h4>
public class HttpRequest {private String param ;//入?yún)?/span>private String service ;//請(qǐng)求serviceprivate String method ;//請(qǐng)求方法public String getParam() {return param;}public void setParam(String param) {this.param = param;}public String getService() {return service;}public void setService(String service) {this.service = service;}public String getMethod() {return method;}public void setMethod(String method) {this.method = method;}
}
其中param是用于存放真正調(diào)用dubbo服務(wù)時(shí)的入?yún)?#xff0c;傳入json在調(diào)用的時(shí)候解析成具體的參數(shù)對(duì)象。
service存放dubbo服務(wù)聲明的interface API的包名。
method則是真正調(diào)用的方法名稱。
HttpResponse 響應(yīng)
public class HttpResponse implements Serializable{private static final long serialVersionUID = -6296842759601736401L; private boolean success;// 成功標(biāo)志private String code;// 信息碼private String description;// 描述public boolean isSuccess() {return success;}public void setSuccess(boolean success) {this.success = success;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}
}
這里只是封裝了常用的HTTP服務(wù)的響應(yīng)數(shù)據(jù)。
暴露服務(wù)controller
@Controller
@RequestMapping("/dubboAPI")
public class DubboController implements ApplicationContextAware {private final static Logger logger = LoggerFactory.getLogger(DubboController.class);@Autowiredprivate HttpProviderConf httpProviderConf;// 緩存作用的mapprivate final Map<String, Class<?>> cacheMap = new HashMap<String, Class<?>>();protected ApplicationContext applicationContext;@ResponseBody@RequestMapping(value = "/{service}/{method}", method = RequestMethod.GET)public String api(HttpRequest httpRequest, HttpServletRequest request, @PathVariable String service,@PathVariable String method) {logger.info("ip:{}-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) {// 調(diào)用的是未經(jīng)配置的包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));// 設(shè)置緩存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) {// 沒(méi)有參數(shù)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;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}
}
-
首先是定義了一個(gè)DubboController,并使用了SpringMVC的注解對(duì)外暴露HTTP服務(wù)。
-
實(shí)現(xiàn)了org.springframework.context.ApplicationContextAware類, 實(shí)現(xiàn)了setApplicationContext()方法用于初始化Spring上下文對(duì)象,在之后可以獲取到容器里的相應(yīng)對(duì)象。
-
核心的invoke()方法。
-
調(diào)用時(shí):http://127.0.0.1:8080/dubbo-provider/dubboAPI/com.dubbo.service.IDubboService/getUser。
-
具體如上文的調(diào)用實(shí)例。先將com.dubbo.service.IDubboService、getUser賦值到httpRequest入?yún)⒅小?/p>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://code.alibabatech.com/schema/dubbohttp://code.alibabatech.com/schema/dubbo/dubbo.xsd"><!-- 定義了提供方應(yīng)用信息,用于計(jì)算依賴關(guān)系。在dubbo-admin 或 dubbo-monitor 會(huì)顯示這個(gè)名字,方便識(shí)別 --><dubbo:application name="admin-provider" owner="admin" organization="dubbox"/><!-- 使用zookeeper 注冊(cè)中心暴露服務(wù),注意要先開(kāi)啟 zookeeper --><dubbo:registry address="zookeeper://127.0.0.1:2181"/><!-- 用dubbo協(xié)議在20880端口暴露服務(wù) --><dubbo:protocol name="dubbo" port="20880"/><!-- 用dubbo 協(xié)議實(shí)現(xiàn)定義好的 api 接口 --><dubbo:service interface="com.dubbo.service.IDubboService" ref="dubboService" protocol="dubbo"/><dubbo:service interface="com.dubbo.service.IUserService" ref="userService" protocol="dubbo"/><!--dubbo服務(wù)暴露為http服務(wù)--><bean class="com.dubbo.http.conf.HttpProviderConf"><property name="usePackage"><list><!--需要暴露服務(wù)的接口包名,可多個(gè)--><value>com.dubbo.service</value></list></property></bean>
</beans>
其中的com.dubbo.service就是自己需要暴露的包名,可以多個(gè)。
- 接著在緩存map中取出反射獲取到的接口類類型,如果獲取不到則通過(guò)反射獲取,并將值設(shè)置到緩存map中,這樣不用每次都反射獲取,可以節(jié)省系統(tǒng)開(kāi)銷(反射很耗系統(tǒng)資源)。
- 接著也是判斷該接口中是否有傳入的getUser方法。
- 取出該方法的參數(shù)列表,如果沒(méi)有參數(shù)則直接調(diào)用。
- 如果有參數(shù),判斷個(gè)數(shù)。這里最多只運(yùn)行一個(gè)參數(shù)。也就是說(shuō)在真正的dubbo調(diào)用的時(shí)候只能傳遞一個(gè)BO類型,具體的參數(shù)列表可以寫到BO中。因?yàn)槿绻卸鄠€(gè)在進(jìn)行json解析的時(shí)候是無(wú)法賦值到兩個(gè)參數(shù)對(duì)象中去的。
- 之后進(jìn)行調(diào)用,將調(diào)用返回的數(shù)據(jù)進(jìn)行返回即可。
總結(jié)
以上是生活随笔為你收集整理的dubbo暴露出HTTP服务的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
其中param是用于存放真正調(diào)用dubbo服務(wù)時(shí)的入?yún)?#xff0c;傳入json在調(diào)用的時(shí)候解析成具體的參數(shù)對(duì)象。
service存放dubbo服務(wù)聲明的interface API的包名。
method則是真正調(diào)用的方法名稱。
HttpResponse 響應(yīng)
public class HttpResponse implements Serializable{private static final long serialVersionUID = -6296842759601736401L; private boolean success;// 成功標(biāo)志private String code;// 信息碼private String description;// 描述public boolean isSuccess() {return success;}public void setSuccess(boolean success) {this.success = success;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;} }這里只是封裝了常用的HTTP服務(wù)的響應(yīng)數(shù)據(jù)。
暴露服務(wù)controller
@Controller @RequestMapping("/dubboAPI") public class DubboController implements ApplicationContextAware {private final static Logger logger = LoggerFactory.getLogger(DubboController.class);@Autowiredprivate HttpProviderConf httpProviderConf;// 緩存作用的mapprivate final Map<String, Class<?>> cacheMap = new HashMap<String, Class<?>>();protected ApplicationContext applicationContext;@ResponseBody@RequestMapping(value = "/{service}/{method}", method = RequestMethod.GET)public String api(HttpRequest httpRequest, HttpServletRequest request, @PathVariable String service,@PathVariable String method) {logger.info("ip:{}-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) {// 調(diào)用的是未經(jīng)配置的包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));// 設(shè)置緩存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) {// 沒(méi)有參數(shù)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;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;} }-
首先是定義了一個(gè)DubboController,并使用了SpringMVC的注解對(duì)外暴露HTTP服務(wù)。
-
實(shí)現(xiàn)了org.springframework.context.ApplicationContextAware類, 實(shí)現(xiàn)了setApplicationContext()方法用于初始化Spring上下文對(duì)象,在之后可以獲取到容器里的相應(yīng)對(duì)象。
-
核心的invoke()方法。
-
調(diào)用時(shí):http://127.0.0.1:8080/dubbo-provider/dubboAPI/com.dubbo.service.IDubboService/getUser。
-
具體如上文的調(diào)用實(shí)例。先將com.dubbo.service.IDubboService、getUser賦值到httpRequest入?yún)⒅小?/p>
其中的com.dubbo.service就是自己需要暴露的包名,可以多個(gè)。
- 接著在緩存map中取出反射獲取到的接口類類型,如果獲取不到則通過(guò)反射獲取,并將值設(shè)置到緩存map中,這樣不用每次都反射獲取,可以節(jié)省系統(tǒng)開(kāi)銷(反射很耗系統(tǒng)資源)。
- 接著也是判斷該接口中是否有傳入的getUser方法。
- 取出該方法的參數(shù)列表,如果沒(méi)有參數(shù)則直接調(diào)用。
- 如果有參數(shù),判斷個(gè)數(shù)。這里最多只運(yùn)行一個(gè)參數(shù)。也就是說(shuō)在真正的dubbo調(diào)用的時(shí)候只能傳遞一個(gè)BO類型,具體的參數(shù)列表可以寫到BO中。因?yàn)槿绻卸鄠€(gè)在進(jìn)行json解析的時(shí)候是無(wú)法賦值到兩個(gè)參數(shù)對(duì)象中去的。
- 之后進(jìn)行調(diào)用,將調(diào)用返回的數(shù)據(jù)進(jìn)行返回即可。
總結(jié)
以上是生活随笔為你收集整理的dubbo暴露出HTTP服务的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: UMAP介绍和代码实例
- 下一篇: 1111