`

CXF client在并发下的线程安全问题

阅读更多

这个是发生在上周周末的真实案例,因为cxf client 端线程安全导致的错误,总结出来希望其他使用cxf的兄弟注意。

    首先描述一下背景,简单的说就是使用cxf作为web service的客户端,运行在weblogic上,连接外部的服务器。为了测试需要,开发了一个简单的模拟器模拟服务器端,准备在release之前跑稳定性测试。

    结果出问题了,在排除掉一些干扰和诸如网络环境,设置等之后问题依旧,由于系统负责,包括ws的模拟器也是出了一个之前没有试过的方法,因此费了不少时间来查找问题。过程很枯燥,应该很多人经历过,在一个大的系统中找到一个小错误的出处,可以说是一门学问,技术耐心和运气都是需要的.....跳出这个过程,由于问题表现在web service的网络连接在这个异常上,在服务器端模拟器的日志中有大量的这种异常信息:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
2009 - 07 - 24   19 : 23 : 22 , 898  DEBUG (                    :  ) (tomcat - exec - 56 ) [Http11NioProcessor]  -  Error parsing HTTP request header
java.io.EOFException: Unexpected EOF read on the socket
        at org.apache.coyote.http11.InternalNioInputBuffer.readSocket(InternalNioInputBuffer.java:
589 )
        at org.apache.coyote.http11.InternalNioInputBuffer.parseRequestLine(InternalNioInputBuffer.java:
425 )
        at org.apache.coyote.http11.Http11NioProcessor.process(Http11NioProcessor.java:
825 )
        at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:
719 )
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:
2080 )
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:
885 )
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:
907 )
        at java.lang.Thread.run(Thread.java:
619 )
2009 - 07 - 24   19 : 23 : 22 , 898  DEBUG (                    :  ) (tomcat - exec - 56 ) [Http11NioProcessor]  -  Error parsing HTTP request header
java.io.EOFException: Unexpected EOF read on the socket
        at org.apache.coyote.http11.InternalNioInputBuffer.readSocket(InternalNioInputBuffer.java:
589 )
        at org.apache.coyote.http11.InternalNioInputBuffer.parseRequestLine(InternalNioInputBuffer.java:
425 )
        at org.apache.coyote.http11.Http11NioProcessor.process(Http11NioProcessor.java:
825 )
        at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:
719 )
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:
2080 )
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:
885 )
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:
907 )
        at java.lang.Thread.run(Thread.java:
619 )


    而服务器端模拟器这次是我们第一次使用tomcat和coyote,因此怀疑是tomcat的问题,在再三追查代码无果的情况下,决定换一个服务器端模拟器来确认问题所在:到底是cxf的客户端的问题,还是服务器端模拟器。一个简单的模拟器写出来了,一个跳过所有业务逻辑直接调用cxf客户端实现代码的测试小程序写出来了,测试之后发现,问题依旧。于是将目光集中到cxf的客户端上。

    在测试中发现这样一个规律,在上述服务器端的异常发生前,在客户端中总是会有规律的出现下面这个异常:

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
Jul 
24 2009   10 : 36 : 18  PM org.apache.cxf.phase.PhaseInterceptorChain doIntercept
INFO: Interceptor has thrown exception, unwinding now 
null
tps 
=   25
Exception in thread 
" Thread-41 "   2009 - 07 - 24   22 : 36 : 19 , 925   149585  [Thread - 41 ] ( ******** Impl.java: 459 ) ERROR junit.framework.Test   -  Got an exception when invoking  ****  service:javax.xml.ws.WebServiceException: java.lang.NullPointerException

     (这里的信息是和业务相关的,不方便打出,总之和我们讨论的问题无关)
    at test.TestMci.execute(TestMci.java:
84 )
    at test.TestMci.access$
1 (TestMci.java: 81 )
    at test.TestMci$TestThread.run(TestMci.java:
90 )
    at java.lang.Thread.run(Thread.java:
595 )
Caused by: javax.xml.ws.WebServiceException: java.lang.NullPointerException
    at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:
142 )
    at $Proxy40.authorizeAndPurchase(Unknown Source)
    at 
*********************
      
6  more
Caused by: java.lang.NullPointerException
    at org.apache.cxf.transport.http.HTTPConduit.prepare(HTTPConduit.java:
483 )
    at org.apache.cxf.interceptor.MessageSenderInterceptor.handleMessage(MessageSenderInterceptor.java:
46 )
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:
226 )
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:
469 )
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:
299 )
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:
251 )
    at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:
73 )
    at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:
124 )
      
8  more


    看来问题是出现在这里了。

    进一步的测试发现,低压力下比如2-3个工作线程,基本不会有任何问题,因此可以解释为什么功能测试时不出现问题。工作线程加到10个,基本上还算问题,只有极其偶然的会出现一次两次这个异常。进一步加大工作线程,由于受到测试机器的性能限制,10个工作线程和100个工作线程的tps基本相同,都大体在300TPS左右(客户端在笔记本上跑的,cpu已经90%+了)。测试的结果是上面的异常开始变的有规律,恩,非常搞笑的规律,大概每10000次左右的请求就发生一次上述异常,非常的稳定而执着的重现。服了,这么有规律而重现性极好的错误,还真是难得一见........

    分析一下问题,在tps基本保持不变的情况下,客户端线程从10增加到100问题就变得明显。因此问题的焦点直指线程安全这个老大难问题,重新审视我们使用cxf的代码,发现有个地方

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
    
/**
     * the server service
     
*/
    MessagingChannelServiceImplService mcsis 
=   null ;
    
/**
     * the sever service port
     
*/
    MessagingChannelService mcs 
=   null ;


            mcsis 
=   new  MessagingChannelServiceImplService(serviceWsdlUrl);
            mcs 
=  mcsis.getMessagingChannelServiceImplPort();


    MessagingChannelService是cxf自动生成的,这个是@WebService,其他的业务代码都是调用它上面的业务方法来实现。由于serviceWsdlUrl不变,因此我们重用了一些东西,避免每次都初始化一次。看来问题出现在这里,试着将代码修改为threadlocal,让每个线程都初始化一次然后保存给自己使用。

    修改后的客户端代码在之后的测试中,非常稳定,没有再出现上面的异常,问题算是解决了。

    我对cxf不是很熟悉,找了一下也没有找到到底是那里造成的线程不安全,google了一下找到几个地方,但是似乎还不能完全说明问题。先列出来慢慢研究:

1) Are JAX-WS client proxies thread safe?

    装载自这里:http://cxf.apache.org/faq.html#FAQ-AreJAXWSclientproxiesthreadsafe%253F

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->
Official JAX-WS answer: No. According to the JAX-WS spec, the client proxies are NOT thread safe. To write portable code, you should treat them as non-thread safe and synchronize access or use a pool of instances or similar.

CXF answer: CXF proxies are thread safe for MANY use cases. The exceptions are:

    * Use of ((BindingProvider)proxy).getRequestContext() - per JAX-WS spec, the request context is PER INSTANCE. Thus, anything set there will affect requests on other threads. With CXF, you can do:
      ((BindingProvider)proxy).getRequestContext().put("thread.local.request.context", "true");
      ((BindingProvider)proxy).getRequestContext().put("thread.local.request.context", "true");

      and future calls to getRequestContext() will use a thread local request context. That allows the request context to be threadsafe. (Note: the response context is always thread local in CXF)

    * Settings on the conduit - if you use code or configuration to directly manipulate the conduit (like to set TLS settings or similar), those are not thread safe. The conduit is per-instance and thus those settings would be shared.

    * Session support - if you turn on sessions support (see jaxws spec), the session cookie is stored in the conduit. Thus, it would fall into the above rules on conduit settings and thus be shared across threads.

For the conduit issues, you COULD install a new ConduitSelector that uses a thread local or similar. That's a bit complex though.

For most "simple" use cases, you can use CXF proxies on multiple threads. The above outlines the workarounds for the others.


2) cxf的wiki中谈到Client API中的Proxy-based API

    wiki 地址: http://cwiki.apache.org/CXF20DOC/jax-rs.html

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->Limitations

Proxy methods can not have @Context method parameters and subresource methods returning Objects can not be invoked - perhaps it is actually not too bad at all - please inject contexts as field or bean properties and have subresource methods returning typed classes : interfaces, abstract classes or concrete implementations.

Proxies are currently not thread-safe.


3) thread safe issue caused by XMLOutputFactoryImpl

    找到的一个cxf的bug,https://issues.apache.org/jira/browse/CXF-2229

<!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --> Description    
Currently CXF calls StaxUtils.getXMLOutputFactory() to get the cached instance of XMLOutputFactoryImpl. But XMLOutputFactoryImpl.createXMLStreamWriter is not thread
- safe. See below.

javax.xml.stream.XMLStreamWriter createXMLStreamWriter(javax.xml.transform.stream.StreamResult sr, String encoding) 
throws  javax.xml.stream.XMLStreamException {
         
try {
           
if (fReuseInstance  &&  fStreamWriter  !=   null   &&  fStreamWriter.canReuse()  &&   ! fPropertyChanged){
               fStreamWriter.reset();
               fStreamWriter.setOutput(sr, encoding);
               
if (DEBUG)System.out.println( " reusing instance, object id :  "   +  fStreamWriter);
               
return  fStreamWriter;
           }
           
return  fStreamWriter  =   new  XMLStreamWriterImpl(sr, encoding,  new  PropertyManager(fPropertyManager));  --   this  is not thread safe, since the  new  instance is assigned to the field fStreamWriter first, then it is possible that different threads get the same XMLStreamWriterImpl when they call  this  method at the same time.
         }
catch (java.io.IOException io){
           
throw   new  XMLStreamException(io);
       }
   }

The solution might be, StaxUtils.getXMLOutputFactory() method creates a 
new  instance of XMLOutputFactory every time, don ' t cache it.

 

0
0
分享到:
评论
1 楼 chxiaowu 2012-02-17  
虽然还不是太明白 ,但貌 似有用,先收 藏

相关推荐

    cxf+spring+client

    在这个"**cxf+spring+client**"的主题中,我们将深入探讨CXF与Spring框架结合创建客户端服务的细节,以及如何利用Spring MVC来增强应用程序的交互性。 首先,让我们关注CXF。CXF允许开发者使用Java编程语言来定义和...

    jdk5 下 CXF 开发 WEB Service client 所需jar 包

    同时,CXF提供了丰富的日志配置,便于在开发过程中定位问题。 7. **最佳实践** - 使用最新的CXF版本,尽管支持JDK 5,但较新版本通常会修复更多bug并提供新特性。 - 在生产环境中,考虑使用服务发现机制,如服务...

    cxf-rt-rs-client-3.0.1-API文档-中文版.zip

    赠送jar包:cxf-rt-rs-client-3.0.1.jar; 赠送原API文档:cxf-rt-rs-client-3.0.1-javadoc.jar; 赠送源代码:cxf-rt-rs-client-3.0.1-sources.jar; 赠送Maven依赖信息文件:cxf-rt-rs-client-3.0.1.pom; 包含...

    使用CXF实现带header的soap服务

    标题“使用CXF实现带header的soap服务”指的是在Java环境中,通过Apache CXF框架...通过学习这个主题,开发者可以掌握如何在CXF中创建和管理带有自定义header的SOAP服务,这对于实现安全和复杂的Web服务交互至关重要。

    CXF Web Service & client

    在提供的文件列表中,"WebService"可能包含了服务器端的代码和配置,而"WebserviceClient"则可能包含客户端的相关实现。通过这两个文件,你可以深入了解CXF如何应用于实际项目中,实践上述的知识点。 总结,Apache ...

    CXF 一个完整的webService(整合客户端和服务端)

    - 日志和调试:CXF支持日志记录和调试工具,帮助开发者定位问题。 **5. RESTful服务与CXF** 除了传统的SOAP服务,CXF也支持RESTful服务的开发。REST(Representational State Transfer)是一种轻量级的架构风格,...

    cxf入门例子(安全认证)

    在安全认证方面,CXF 支持多种机制,如基本认证、 Digest 认证、OAuth 等。为了设置安全认证,我们需要配置 CXF 的 Spring 容器,添加相关的拦截器或过滤器,比如 `org.apache.cxf.security.authentication....

    springboot整合CXF发布webservice和客户端调用

    SpringBoot整合CXF是将流行的Java Web服务框架CXF与SpringBoot轻量级框架结合,以便更方便地创建和消费Web服务。这个项目提供了一个很好的示例,通过详细注释帮助开发者理解如何在SpringBoot应用中发布和调用Web服务...

    CXF实现SSL安全验证

    ### CXF实现SSL安全验证 在现代网络应用中,安全通信是至关重要的。Apache CXF 是一个开源项目,提供了一套强大的工具和服务框架,用于构建和开发基于标准的服务(如 Web Services)。本文将详细介绍如何使用 CXF ...

    j2ee.zip_cxf j2ee_java并发

    Java并发编程是Java平台中的一个重要领域,它涉及到如何在多线程环境下有效地执行任务。在本项目中,可能会有多个线程同时访问数据库或处理XML数据,这就需要我们掌握并发控制机制,如synchronized关键字、Lock接口...

    client cxf

    - 安全性:CXF支持多种安全机制,如基本认证、 Digest认证、WS-Security等,可以通过配置文件或代码实现。 五、高级特性 1. 异步调用:CXF客户端支持异步调用,这在处理大流量或者长时间运行的服务调用时非常有用。...

    CXF V3.2.4 实现的WebService调用(带安全认证)

    在本文中,我们将深入探讨如何使用Apache CXF V3.2.4实现带有安全认证的Web服务调用。Apache CXF是一个开源框架,它允许开发者创建和消费各种Web服务,包括SOAP和RESTful API。CXF 3.2版本引入了许多增强功能,包括...

    Apache Cxf 安全认证

    在客户端,`client-beans.xml`文件中配置了多个拦截器,其中包括日志输出拦截器`LoggingOutInterceptor`、SOAP消息处理拦截器`SAAJOutInterceptor`,以及核心的`WSS4JOutInterceptor`用于实现安全认证。`WSS4...

    cxf 2.1.3 client

    CXF提供了丰富的异常处理机制,确保在出现问题时能向客户端发送适当的错误信息。 标签"xcf"和"xfire"指的是这两个相关的Web服务框架,而"client"和"server"则强调了服务的调用和提供角色。在这个例子中,"client"指...

    CXF实例源代码(客户端)

    6. **测试和调试**:CXF提供了丰富的测试工具,如CXF TestSupport和模拟服务功能,可以帮助开发者在不依赖真实服务的情况下测试客户端代码。 7. **性能优化**:对于生产环境,关注性能优化很重要。这可能涉及到缓存...

    cxf 安全验证例子

    【标题】"CXF安全验证例子"涉及到的是Apache CXF框架在实现Web服务时的安全验证机制。Apache CXF是一个开源的Java框架,用于构建和部署Web服务。它提供了丰富的功能,包括SOAP、RESTful API、WS-*规范支持以及安全...

    websphere下部署CXF项目jar包冲突问题解决方式

    在websphere8.5 下部署含有CXFwebservice的war包无法正常启动,而相应的war包在tomcat上是可以正常启动的,通过后台的日志分析大致可以定位为相关的cxf类无法找到,其实这些类在项目的lib目录下都是存在的,莫名其妙...

    cxf+spring webservice demo client

    这个示例项目`CXFClient`很可能包含了上述步骤的代码实现,你可以通过阅读和分析代码来深入理解CXF和Spring如何协同工作,创建和消费Web服务。这将帮助你更好地理解和应用这两种强大工具在实际项目中的集成技巧。 ...

    cxf spring server client demo.rar

    这个压缩包包含了一个服务器端(cxfserver)和一个客户端(cxfclient),用于展示如何使用CXF和Spring来创建、消费SOAP Web服务。 【描述】在描述中提到,“soap webservice”表明此项目专注于SOAP协议,这是一种...

    CXF契约优先开发方式之客户端实现(client)

    这些文件可能需要被加载到CXF客户端,以便正确地处理与服务交互的安全和性能问题。例如,如果服务需要进行SOAP消息签名,那么对应的WSS4J策略文件就会在这里。 总的来说,CXF契约优先的客户端实现涉及到WSDL的编写...

Global site tag (gtag.js) - Google Analytics