問題
最近在學習SpringCloud , 以及将要在公司内部部署和推廣的過程中,發現網關既需要支援
http
,同時也需要支援
dubbo
,并且網關隻需要支援http即可,那麼在網關的内部就需要将http協定轉換成dubbo協定,在内部做又有2個處理方式
- 1、在網關層面處理
- 優點
- 直接利用dubbo的泛化功能
- 服務提供者不需要進行額外的處理
- 優點
-
- 缺點
- 在網關層需要進行dubbo的tcp連接配接,如果業務的網絡環境比較特殊,那麼這一套是較難維護的
- 接口的互動較為複雜,泛化需要将參數類型,參數等等進行傳遞,而這些服務提供者本身其實是存在的。
- 2、在dubbo#provider層面進行處理
- 優點
- 直接對接http協定,不必處理額外的網絡環境
- 僅需要傳遞dubbo服務需要的參數,不必傳遞額外的參數類型
- 缺點
- 如何讓服務提供者支援http轉dubbo.
- 優點
- 缺點
通過上述的比較,以及公司業務上處理,我們選擇了第二種進行處理.
開發
一開始我們的服務是通過tomcat或者内置容器的SpringBoot進行暴露的,如果通路dubbo的話,過程就是
http --> nginx ---> tomcat ---> springmvc ---> dubbo
這個過程,而現在我們需要做的就是将這個過程中的SpringMVC這一塊進行移除,變成
http --> nginx ---> tomcat ---> dubbo
,進而直接支援http被dubbo處理。于是我通過SpringMVC的處理機制将Controller這一塊移除掉,達到了我們的目的,接下來看如何一步一步實作的.
假設url = /dubbo/*
- 通過包裝Servlet統一處理對接的http。
<servlet>
<servlet-name>GatewayServlet</servlet-name>
<servlet-class>com.xxx.gateway.dubbo.web.GatewayServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>GatewayServlet</servlet-name>
<url-pattern>/dubbo/*</url-pattern>
</servlet-mapping>
- 通過http參數擷取dubbo服務的接口,版本等等資訊
String inf = servletRequest.getParameter("serviceName");
Assert.notNull(inf, "接口不能為空!");
String method = servletRequest.getParameter("method");
Assert.notNull(method, "方法不能為空!");
String uGroup = servletRequest.getParameter("group");
String vVersion = servletRequest.getParameter("version");
Assert.notNull(vVersion, "版本不能為空!");
- 擷取dubbo服務
String[] beanNamesForType = this.applicationContext.getBeanNamesForType(ServiceConfig.class);
Object ref = null;
for (String service : beanNamesForType) {
ServiceConfig serviceConfig = (ServiceConfig) this.applicationContext.getBean(service);
String version = serviceConfig.getVersion();
String anInterface = serviceConfig.getInterface();
String group = serviceConfig.getGroup();
if (!inf.equalsIgnoreCase(anInterface)) {
continue;
}
if (!vVersion.equalsIgnoreCase(version)) {
continue;
}
if (uGroup != null && !group.equalsIgnoreCase(uGroup)) {
continue;
}
ref = serviceConfig.getRef();
break;
}
- 利用SpringMVC的的處理機制将Controller移除
try {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
ServletInvocableHandlerMethod invocableMethod = new ServletInvocableHandlerMethod(handlerMethod);
WebDataBinderFactory binderFactory = getDataBinderFactory(invocableMethod);
invocableMethod.setDataBinderFactory(binderFactory);
ServletWebRequest webRequest = new ServletWebRequest(req, resp);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
HandlerMethodArgumentResolverComposite handlerMethodArgumentResolverComposite = new HandlerMethodArgumentResolverComposite();
handlerMethodArgumentResolverComposite.addResolvers(getDefaultArgumentResolvers());
invocableMethod.setHandlerMethodArgumentResolvers(handlerMethodArgumentResolverComposite);
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
HandlerMethodReturnValueHandlerComposite returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
invocableMethod.setHandlerMethodReturnValueHandlers(returnValueHandlers);
invocableMethod.invokeAndHandle(webRequest, mavContainer);
methodMap.put(handlerMethod, invocableMethod);
} catch (Exception e) {
fail(servletResponse, e);
}
- 如何突破傳遞參數的問題。 利用SpringMVC的
HandlerMethodArgumentResolver
即可解析
到這一步,http轉dubbo就處理好了,測試發現SpringMVC在3,4,5的幾個大版本中稍有變動,相容花費一點時間。後續跟新會上傳的github。 :)
結論
Spring很強大.