问题
最近在学习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 parameter = servletRequest.getParameter("method");
Assert.notNull(inf, "方法不能为空!");
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很强大. 代码地址 dubbo-invoke
2019-09-22