分布式应用上下文(Distributed ThreadLocal)

来源:互联网 发布:什么叫网络打印机 编辑:程序博客网 时间:2024/05/16 16:04
http://badqiu.iteye.com/blog/858903

1.问题

单机应用内,在进程内部,我们可以使用ThreadLocal传递应用上下文的方式. 当前的 Spring Secrucity , Spring TransactionManager,  Log4J MDC, Struts2 ActionContext等等应用场景随处可见.

 

但在是分布式系统下,由于不是在同一个进程内,所以无法使用ThreadLocal. 那么什么是分布式ThreadLocal呢?就是将一个系统中的ThreadLocal信息可以传递至下一个系统,将两者的调用可以关联起来。如对应用有一个调用,我们生成一个请求ID (traceId),在后面所有分布式系统调用中,可以通过这个traceId将所有调用关联起来,这样查找调用日志都将十分方便.

 

2.实现方式

我们现在使用的通讯协议,一般都包含两部分:Header,Body. 如 Soap Header,Http Header. 通过自定义Header,可以带上我们的自定义信息。 然后在服务器端解析Header,再得到自定义信息。那么就可以完成Distributed ThreadLocal的功能。

 

 

 

如上图,通过两个拦截器,client在调用之前,将DistrbiutedThreadLocal中的信息放在soap header中,在服务端方法调用之前,从soap header中取回 DistrbiutedThreadLocal信息。

 

 

3. 实现代码.

以下为CXF webservice的实现代码,一个DistributedThreadLocal及增加了两个拦截器. hessian 也可以自定义Header,完成传递.

 

DistributedThreadLocal

 

Java代码  收藏代码
  1. /** 
  2.  * 分布式 ThreadLocal, 存放在ThreadLocal中的数据可以传输至另外一台机器上 
  3.  * @author badqiu 
  4.  */  
  5. public class DistributedThreadLocal {  
  6.     public static String DISTRIBUTED_THREAD_LOCAL_KEY_PREFIX = "tl_";  
  7.       
  8.     public static ThreadLocal<Map<String, String>> threadLocal = new ThreadLocal<Map<String, String>>();  
  9.   
  10.     public static void putAll(Map<String, String> map) {  
  11.         getMap().putAll(map);  
  12.     }  
  13.       
  14.     public static void put(String key, String value) {  
  15.         getMap().put(key, value);  
  16.     }  
  17.   
  18.     public static String get(String key) {  
  19.         Map<String, String> map = threadLocal.get();  
  20.         if (map == null)  
  21.             return null;  
  22.         return (String) map.get(key);  
  23.     }  
  24.   
  25.     public static Map<String, String> getMap() {  
  26.         Map<String, String> map = threadLocal.get();  
  27.         if (map == null) {  
  28.             map = new HashMap();  
  29.             threadLocal.set(map);  
  30.         }  
  31.         return map;  
  32.     }  
  33.   
  34.     public static void clear() {  
  35.         threadLocal.set(null);  
  36.     }  
  37.   
  38. }  

 

DistributedThreadLocalInSOAPHeaderInterceptor

 

Java代码  收藏代码
  1. /** 
  2.  * 输入(In)拦截器,用于从 WebService SOAP 的Header中取回DistributedThreadLocal中的信息,并存放在DistributedThreadLocal中 
  3.  *  
  4.  * @author badqiu 
  5.  */  
  6. public class DistributedThreadLocalInSOAPHeaderInterceptor extends AbstractSoapInterceptor {  
  7.       
  8.     private SAAJInInterceptor saajIn = new SAAJInInterceptor();    
  9.       
  10.     public DistributedThreadLocalInSOAPHeaderInterceptor() {    
  11.         super(Phase.PRE_PROTOCOL);    
  12.         getAfter().add(SAAJInInterceptor.class.getName());    
  13.     }    
  14.   
  15.     public void handleMessage(SoapMessage message) throws Fault {  
  16.         SOAPMessage doc = message.getContent(SOAPMessage.class);    
  17.         if (doc == null) {    
  18.             saajIn.handleMessage(message);    
  19.             doc = message.getContent(SOAPMessage.class);    
  20.         }    
  21.           
  22.         Map<String,String> headers = toHeadersMap(doc);    
  23.         DistributedThreadLocal.putAll(headers);  
  24.           
  25.     }  
  26.   
  27.     private Map toHeadersMap(SOAPMessage doc) {  
  28.         SOAPHeader header = getSOAPHeader(doc);    
  29.         if (header == null) {    
  30.             return new HashMap(0);    
  31.         }   
  32.           
  33.         Map<String,String> headersMap = new HashMap();  
  34.         NodeList nodes = header.getChildNodes();  
  35.         for(int i=0; i<nodes.getLength(); i++) {    
  36.             Node item = nodes.item(i);  
  37.             if(item.hasChildNodes()) {  
  38.                 headersMap.put(item.getLocalName(), item.getFirstChild().getNodeValue());  
  39.             }  
  40.         }  
  41.         return headersMap;  
  42.     }  
  43.   
  44.     private SOAPHeader getSOAPHeader(SOAPMessage doc) {  
  45.         SOAPHeader header;  
  46.         try {  
  47.             header = doc.getSOAPHeader();  
  48.         } catch (SOAPException e) {  
  49.             throw new RuntimeException(e);  
  50.         }  
  51.         return header;  
  52.     }  
  53.   
  54. }  
 

DistributedThreadLocalOutSOAPHeaderInterceptor

 

Java代码  收藏代码
  1. /** 
  2.  * 输出(Out)拦截器,用于将DistributedThreadLocal中的信息存放在 WebService SOAP 的Header中 
  3.  *  
  4.  * @author badqiu 
  5.  */  
  6. public class DistributedThreadLocalOutSOAPHeaderInterceptor extends AbstractSoapInterceptor {  
  7.       
  8.     public DistributedThreadLocalOutSOAPHeaderInterceptor() {  
  9.         super(Phase.WRITE);  
  10.     }  
  11.   
  12.     public void handleMessage(SoapMessage message) throws Fault {  
  13.           
  14.         List<Header> headers = message.getHeaders();  
  15.         Map<String,String> threadlocalMap = DistributedThreadLocal.getMap();  
  16.           
  17.         for(Map.Entry<String, String> entry : threadlocalMap.entrySet()) {  
  18.             headers.add(getHeader(entry.getKey(), entry.getValue()));  
  19.         }  
  20.     }  
  21.   
  22.     private Header getHeader(String key, String value) {  
  23.         QName qName = new QName(key);  
  24.         Document document = DOMUtils.createDocument();  
  25.         Element element = document.createElement(key);  
  26.         element.appendChild(document.createTextNode(value));  
  27.         SoapHeader header = new SoapHeader(qName, element);  
  28.         return (header);  
  29.     }  
  30. }  
 

CXF spring配置文件:

 

server端: 

 

Java代码  收藏代码
  1. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.     xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:cxf="http://cxf.apache.org/core"  
  3.     xsi:schemaLocation="http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"  
  4.     default-lazy-init="true">  
  5.   
  6.     <description>Apache CXF的Web Service配置</description>  
  7.   
  8.     <import resource="classpath:META-INF/cxf/cxf.xml" />  
  9.     <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />  
  10.     <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml" />  
  11.   
  12.     <!-- jax-ws endpoint定义  -->  
  13.     <jaxws:endpoint address="/hello" >  
  14.         <jaxws:implementor ref="hello" />  
  15.         <jaxws:inInterceptors>  
  16.             <bean class="cn.org.rapid_framework.distributed.threadlocal.cfx.TraceIdInSOAPHeaderInterceptor"/>  
  17.         </jaxws:inInterceptors>  
  18.     </jaxws:endpoint>  
  19.       
  20.     <!-- WebService的实现Bean定义 -->  
  21.     <bean id="hello" class="cn.org.rapid_framework.hessian.HessianTest.HelloImpl" />  
  22. </beans>  

 

client端:

 

Java代码  收藏代码
  1. <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  2.     xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:cxf="http://cxf.apache.org/core"  
  3.     xsi:schemaLocation="http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"  
  4.     default-lazy-init="true">  
  5.     <description>Apache CXF Web Service Client端配置</description>  
  6.   
  7.     <jaxws:client id="hello" serviceClass="cn.org.rapid_framework.hessian.HessianTest.Hello"  
  8.         address="http://localhost:8080/service/hello" >  
  9.         <jaxws:outInterceptors>  
  10.             <bean class="cn.org.rapid_framework.distributed.threadlocal.cfx.TraceIdOutSOAPHeaderInterceptor"/>  
  11.         </jaxws:outInterceptors>  
  12.     </jaxws:client>  
  13.   
  14. </beans>  
 

4. 应用场景.

通过分布式应用上下文,暂时想到的几个应用场景.

 

1. Log4j MDC traceId传递.  通过一个traceId,将所有相关的 操作所有的日志信息关联起来。

2. sessionId 传递, 让我们的应用也有状态,可以使用session什么的

3. Security(username,password)传递. 在需要安全调用的地方,避免污染接口,需要显式的在接口传递username,password. 相对应的 WSSecurity也可以走这个通道

 

 

 

 

 

 

分布式应用上下文的概念,全球首创,欢迎转载(因为google 搜索不到相关文章,或许早已经有相同的概念了,欢迎提醒我)。

原创粉丝点击