dubbo的Hessian协议下隐式传参碰到的坑

1,451 阅读2分钟

我的需求是要将consumer的数据传到provinder层

我定义了2个拦截器

  • consumer
RpcContext.getContext().setAttachment()
  • provinder
RpcContext.getContext().getAttachment()

我使用的dubbo版本是2.5.3

放到服务器运行后,部分程序接口就报错了。我们观察到dubbo协议下,没有任何问题,但是在hessian协议下,provinder无法获取到数据。

官网似乎没有这个的说明,网上看到一篇文章 htchz.cc/1632594101.… 里讲到,dubbo在2.6.3下解决了这个问题,它将数据保存到headers里面,并贴上了源码,但是文章中并没有说清楚保存到headers后,provinder方如果获取。

于是我想,既然是http请求,我要想办法获取到request对象,然后就可以获取到request的headers,试过了Filter,Interceptor,Handler,想在provinder的Filter拦截器前面,获取到request,但是程序运行起来,发现拦截器根本拦截不到(可能是自己问题,我确实没找到办法)

于是只能翻看dubbo源码,看看源码里能不能自定义扩展下,找了半天没找到,正想吐槽这dubbo源码太垃圾了,没有提供扩展的时候, 发现了如下代码

这个类定义在HessianProtocol类中,由servlet调用,他获取到headers的数据,然后重新放入到Attachment中。

private class HessianHandler implements HttpHandler {

        @Override
        public void handle(HttpServletRequest request, HttpServletResponse response)
                throws IOException, ServletException {
            String uri = request.getRequestURI();
            HessianSkeleton skeleton = skeletonMap.get(uri);
            if (!request.getMethod().equalsIgnoreCase("POST")) {
                response.setStatus(500);
            } else {
                RpcContext.getContext().setRemoteAddress(request.getRemoteAddr(), request.getRemotePort());
                <!--将headers的数据取出来,重新放入到Attachment中 -->
                Enumeration<String> enumeration = request.getHeaderNames();
                while (enumeration.hasMoreElements()) {
                    String key = enumeration.nextElement();
                    if (key.startsWith(Constants.DEFAULT_EXCHANGER)) {
                        RpcContext.getContext().setAttachment(key.substring(Constants.DEFAULT_EXCHANGER.length()),
                                request.getHeader(key));
                    }
                }

                try {
                    skeleton.invoke(request.getInputStream(), response.getOutputStream());
                } catch (Throwable e) {
                    throw new ServletException(e);
                }
            }
        }

    }

系统在初始化的时候,调用addHttpHandler()方法设置了handlers的值,handlers里放的是不同的协议对应的hander,这里根据请求类型request.getLocalPort(),获取到响应的handler进行后续处理。

public class DispatcherServlet extends HttpServlet {

    private static final long serialVersionUID = 5766349180380479888L;
    private static final Map<Integer, HttpHandler> handlers = new ConcurrentHashMap<Integer, HttpHandler>();
    private static DispatcherServlet INSTANCE;

    public DispatcherServlet() {
        DispatcherServlet.INSTANCE = this;
    }

    public static void addHttpHandler(int port, HttpHandler processor) {
        handlers.put(port, processor);
    }

    public static void removeHttpHandler(int port) {
        handlers.remove(port);
    }

    public static DispatcherServlet getInstance() {
        return INSTANCE;
    }

    @Override
    protected void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
          <!--  获取到对应的hander -->
        HttpHandler handler = handlers.get(request.getLocalPort());
        if (handler == null) {// service not found.
            response.sendError(HttpServletResponse.SC_NOT_FOUND, "Service not found.");
        } else {
        	<!-- 调用【HessianHandler】执行具体的逻辑处理  -->
            handler.handle(request, response);
        }
    }

}

我继续想,既然值放进去了,那为啥获取不到,最后通过打断点发现了问题,原来在设置头的手,将字母的大写变成了小写;(因为最开始是放到服务器试运行的,所以没有debug)

原来dubbo在2.6.3下面已经帮我们解决了这个问题,只是这个大写变小写真是有点坑, 为啥变小写,我也不知道,有知道的告诉我,谢谢啦!