hessian隐式传参

背景

web应用的项目:分表现层portal端和业务服务层service端,使用dubbo框架,rpc使用hessian。dubbo里的dubbo协议因为是socket长连接可以attachment隐式带参数,hessian协议则不能attachment参数。现有需求需要在每个服务调用都多传一个参数,于是考虑不在改动接口的情况下,也在hessian协议下进行隐式传参。

同事说hessian也是走http,可以在发起请求的时候把参数添加到http头信息,服务端接收的时候再解析出来。还有就是hack一下hessian的二进制协议在序列化和反序列化的时候做点手脚。于是源码看起、google百度相关的hessian源码搭配更快地看懂源码。途中就找在远在2007年的一个帖子:Hessian源码分析和Hack --让Hessian携带远程调用端的信息,里面就说到隐式传参的解决方案。所以本文不算原创。

方案1:使用http头信息传参

  1. 在Hessian客户端HessianProxy调用方法发送请求中添加http头信息:
    public Object invoke(Object proxy, Method method, Object []args) -->
    protected HessianConnection sendRequest(String methodName, Object []args) -->
    protected void addRequestHeaders(HessianConnection conn) 里添加头信息

     protected void addRequestHeaders(HessianConnection conn)
         {
         conn.addHeader("Content-Type", "x-application/hessian");
         conn.addHeader("Accept-Encoding", "deflate");
         //从ThreadLocal里获取参数
         Map<String,String> context=HessianContext.getContext();
         if(context!=null)
         {
     	    for(Map.Entry<String, String> entry:context.entrySet())
     	        conn.addHeader(entry.getKey(), entry.getValue());
         }
    
         String basicAuth = _factory.getBasicAuth();
    
         if (basicAuth != null)
                 conn.addHeader("Authorization", basicAuth);
         }
    

2.在dubbo的HessianProtocol中,skeleton.invoke(request.getInputStream(), response.getOutputStream()); 前提取http头信息

private class HessianHandler implements HttpHandler 
{
    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());
            try {
            	Enumeration paramName=request.getHeaderNames();
            	String name;
            	Map<String,String> data=new HashMap<String, String>();
            	while(paramName.hasMoreElements())
            	{
            		name=(String)paramName.nextElement();
            		data.put(name, request.getHeader(name));
            	}
            	HessianContext.setContext(data);
                skeleton.invoke(request.getInputStream(), response.getOutputStream());
                HessianContext.removeContext();
            } catch (Throwable e) {
                throw new ServletException(e);
            }
        }
    }
}

3.这种用法需要同时升级hessian和dubbo包,但是不破坏hessian协议可以和原有的hessian兼容。在portal端调用service端方法前后需要HessianContext.setContext(...)HessianContext.removeContext(),service端用HessianContext.getContext()获取参数。

方案2:hessian多序列化反序列化一个参数

  1. hessian的HessianOutput和Hessian2Output在HessianProxy的sendRequest方法里用call方法拼接请求body。在call方法的拼接参数后再write一个参数

     public void call(String method, Object []args)
         throws IOException
       {
         int length = args != null ? args.length : 0;
    
         startCall(method, length);
    
         for (int i = 0; i < length; i++)
           writeObject(args[i]);
    
         //再write一个参数
         writeObject(HessianContext.getContext());
    
         completeCall();
       }
    
  2. 在HessianSkeleton里invoke(Object service,AbstractHessianInput in,AbstractHessianOutput out)里读取参数后再读取一个参数

     for (int i = 0; i < args.length; i++) {
       // XXX: needs Marshal object
       values[i] = in.readObject(args[i]);
     }
     HessianContext.setContext((Map<String, Object>) in.readObject());
     Object result = null;
    
     try {
       result = method.invoke(service, values);
     } catch (Exception e) {
       Throwable e1 = e;
    
  3. 这个方案只需升级hessian,但是破坏hessian的二进制规则,不能兼容原有的hessian,需要所有项目都升级,且不能外部使用。

HessianContext

HessianContext用ThreadLocal存放参数,代码简单直接上代码:

public class HessianContext {

	private static final ThreadLocal<HessianContext> _THREADLOCAL = new ThreadLocal<HessianContext>();
	private Map<String,Object> data;
	
	private HessianContext(){}
	
	
	public static void setContext(Map<String,Object> data)
	{
		if(data==null)
			return;
		
		HessianContext context=_THREADLOCAL.get();
		if(context==null)
		{
			context=new HessianContext();
			_THREADLOCAL.set(context);
		}
		context.data=data;
	}
	
	public static Map<String,Object> getAndRemoveContext()
	{
		Map<String,Object> data=getContext();
		removeContext();
		return data;
	}
	
	public static Map<String,Object> getContext()
	{
		HessianContext context=_THREADLOCAL.get();
		return context==null?null:context.data;
	}
	
	public static void removeContext()
	{
		_THREADLOCAL.remove();
	}
}

转载于:https://my.oschina.net/braveCS/blog/632323

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值