hessian隱式傳參

背景

web應用的項目:分表現層portal端和業務服務層service端,使用dubbo框架,rpc使用hessian。dubbo裏的dubbo協議由於是socket長鏈接能夠attachment隱式帶參數,hessian協議則不能attachment參數。現有需求須要在每一個服務調用都多傳一個參數,因而考慮不在改動接口的狀況下,也在hessian協議下進行隱式傳參。web

同事說hessian也是走http,能夠在發起請求的時候把參數添加到http頭信息,服務端接收的時候再解析出來。還有就是hack一下hessian的二進制協議在序列化和反序列化的時候作點手腳。因而源碼看起、google百度相關的hessian源碼搭配更快地看懂源碼。途中就找在遠在2007年的一個帖子:Hessian源碼分析和Hack --讓Hessian攜帶遠程調用端的信息,裏面就說到隱式傳參的解決方案。因此本文不算原創。app

方案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頭信息socket

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一個參數google

    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)裏讀取參數後再讀取一個參數code

    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存放參數,代碼簡單直接上代碼:rem

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();
	}
}
相關文章
相關標籤/搜索