1

在开发使用外部 Web 服务的应用程序时,我从 wsdl-url 生成了源代码,然后创建了一个客户端:

GeoIPServiceClient service = new GeoIPServiceClient();
GeoIPServiceSoap geoIPClient = service.getGeoIPServiceSoap();

由于创建此代理需要一些时间,我将客户端设置为我的服务类中的一个属性。

但是我担心客户端不是线程安全的,并且并发线程(webapp)在应用程序中大量使用了这个webservice。我找不到任何关于此的文档。

作为预防措施,我开始使用肥皂客户端的对象池而不是共享客户端。

这是不必要的预防措施吗?编写 xfire 客户端时的最佳实践是什么?

我怀疑 xfire 存在某种并发问题,因为我经常在高负载下获得阻塞的线程,因此应用程序崩溃。这是部分线程转储:

"http-xx.xx.xx.xx-80-17" daemon prio=10 tid=0x00007f560d437000 nid=0x66cb waiting for monitor entry [0x00000000412b8000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.sun.xml.bind.v2.runtime.reflect.opt.Injector.inject(Injector.java:174)
    - waiting to lock <0x00007f561d44e1c0> (a com.sun.xml.bind.v2.runtime.reflect.opt.Injector)
    at com.sun.xml.bind.v2.runtime.reflect.opt.Injector.inject(Injector.java:85)
    at com.sun.xml.bind.v2.runtime.reflect.opt.AccessorInjector.prepare(AccessorInjector.java:87)
    at com.sun.xml.bind.v2.runtime.reflect.opt.OptimizedAccessorFactory.get(OptimizedAccessorFactory.java:165)
    at com.sun.xml.bind.v2.runtime.reflect.Accessor$FieldReflection.optimize(Accessor.java:253)
    at com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor$CompositeTransducedAccessorImpl.<init>(TransducedAccessor.java:231)
    at com.sun.xml.bind.v2.runtime.reflect.TransducedAccessor.get(TransducedAccessor.java:173)
    at com.sun.xml.bind.v2.runtime.property.SingleElementLeafProperty.<init>(SingleElementLeafProperty.java:83)
    at sun.reflect.GeneratedConstructorAccessor165.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at com.sun.xml.bind.v2.runtime.property.PropertyFactory.create(PropertyFactory.java:124)
    at com.sun.xml.bind.v2.runtime.ClassBeanInfoImpl.<init>(ClassBeanInfoImpl.java:171)
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getOrCreate(JAXBContextImpl.java:481)
    at com.sun.xml.bind.v2.runtime.JAXBContextImpl.<init>(JAXBContextImpl.java:315)
    at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:139)
    at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:117)
    at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:188)
    at sun.reflect.GeneratedMethodAccessor176.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:128)
    at javax.xml.bind.ContextFinder.find(ContextFinder.java:277)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:372)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:337)
    at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:244)
    at org.codehaus.xfire.jaxb2.JaxbType.getJAXBContext(JaxbType.java:306)
    - locked <0x00007f565b3aee60> (a org.codehaus.xfire.jaxb2.JaxbType)
    at org.codehaus.xfire.jaxb2.JaxbType.writeObject(JaxbType.java:230)
    at org.codehaus.xfire.aegis.AegisBindingProvider.writeParameter(AegisBindingProvider.java:229)
    at org.codehaus.xfire.service.binding.AbstractBinding.writeParameter(AbstractBinding.java:273)
    at org.codehaus.xfire.service.binding.WrappedBinding.writeMessage(WrappedBinding.java:90)
    at org.codehaus.xfire.soap.SoapSerializer.writeMessage(SoapSerializer.java:80)
    at org.codehaus.xfire.transport.http.HttpChannel.writeWithoutAttachments(HttpChannel.java:56)
    at org.codehaus.xfire.transport.http.OutMessageRequestEntity.writeRequest(OutMessageRequestEntity.java:51)
    at org.apache.commons.httpclient.methods.EntityEnclosingMethod.writeRequestBody(EntityEnclosingMethod.java:499)
    at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2114)
    at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)
    at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)
    at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
    at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
    at org.codehaus.xfire.transport.http.CommonsHttpMessageSender.send(CommonsHttpMessageSender.java:369)
    at org.codehaus.xfire.transport.http.HttpChannel.sendViaClient(HttpChannel.java:123)
    at org.codehaus.xfire.transport.http.HttpChannel.send(HttpChannel.java:48)
    at org.codehaus.xfire.handler.OutMessageSender.invoke(OutMessageSender.java:26)
    at org.codehaus.xfire.handler.HandlerPipeline.invoke(HandlerPipeline.java:131)
    at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:79)
    at org.codehaus.xfire.client.Invocation.invoke(Invocation.java:114)
    at org.codehaus.xfire.client.Client.invoke(Client.java:336)
    at org.codehaus.xfire.client.XFireProxy.handleRequest(XFireProxy.java:77)
    at org.codehaus.xfire.client.XFireProxy.invoke(XFireProxy.java:57)
    at $Proxy143.getMyMethod(Unknown Source)

线程转储包含许多看起来像这样的阻塞线程。

4

2 回答 2

1

取决于您使用的 Xfire 版本,因为它们在 1.2.5 版本中修复了一些线程安全问题。您可以在http://jira.codehaus.org/browse/XFIRE-886查看提出的错误,并在 hxxp://xfire.codehaus.org/XFire+1.2.5+Release+ 上查看有关发行说明的更多详细信息笔记

于 2009-09-09T08:33:48.840 回答
1

我猜当你得到很多阻塞的线程时,客户端实际上是线程安全的,因为对象数据没有损坏:)。但我同意它没有很好地处理并发。

1) 一个观察结果是最终锁定似乎在 JAXB 实现中,而不是在 XFire 中。如果您尝试使用不同的 JAXB 实现,例如JaxMe会怎样?

2) JaxbType 中的getJAXBContext方法也是同步的。而且很可能是因为您的线程正在访问相同的 JaxbType 实例,它们可能会被阻止。

查看该方法,我实际上会在检查上下文存在后将同步移到该方法中:

if (context == null) {
    synchronized (this)  {
         ...

这将允许已经初始化 JAXBContext 的客户端跳过昂贵的同步。

我的建议是要么尝试自己修复代码并进行测试,要么向 XFire 提交错误,或者两者都做:)。

于 2009-09-09T21:43:20.637 回答