具有CXF客户端竞争条件/连接超时的Spring Boot

时间:2018-03-05 16:17:27

标签: spring-boot cxf jax-ws

我在Spring Boot应用程序中配置了一个CXF客户端,如下所示:

Caused by: java.io.IOException: Timed out waiting for response to operation {http://theservice.com}SearchConsumer.
    at org.apache.cxf.endpoint.ClientImpl.waitResponse(ClientImpl.java:685) ~[cxf-core-3.2.0.jar:3.2.0]
    at org.apache.cxf.endpoint.ClientImpl.processResult(ClientImpl.java:608) ~[cxf-core-3.2.0.jar:3.2.0]

同一个服务bean由同一应用程序序列中的两个不同线程访问。如果我连续10次执行此特定序列,我将从服务调用至少3次获得连接超时。我所看到的是:

expect(new Set([1,2,3])).toEqual(new Set([3,2,1]))

如果我更改序列以使其中一个线程不调用此服务,则错误消失。所以,似乎在这里发生了某种竞争条件。如果我查看我们的代理管理器中的日志以获取此服务,我可以看到两个服务调用确实很快返回响应,但是第二个服务调用似乎卡在代码中的某个地方而从未实际放过连接,直到达到超时值。我一直试图追查其原因很长一段时间,但都没有成功。

我已经阅读了一些关于CXF客户端代理是否是线程安全的混合意见,但我的印象是他们是。如果事实并非如此,我应该为每次调用创建一个新的客户端代理,或者使用代理池?

1 个答案:

答案 0 :(得分:0)

事实证明,代理不是线程安全的问题。我最后做的是利用一种解决方案,就像在帖子底部发布的那样:Is this JAX-WS client call thread safe? - 我为代理创建了一个池,我用它以线程安全的方式从多个线程访问代理。这似乎很好。

public class JaxWSServiceProxyPool<T> extends GenericObjectPool<T> {
    JaxWSServiceProxyPool(Supplier<T> factory, GenericObjectPoolConfig poolConfig) {
        super(new BasePooledObjectFactory<T>() {
            @Override
            public T create() throws Exception {
                return factory.get();
            }

            @Override
            public PooledObject<T> wrap(T t) {
                return new DefaultPooledObject<>(t);
            }
        }, poolConfig != null ? poolConfig : new GenericObjectPoolConfig());
    }
}
然后我创建了一个简单的&#34;注册表&#34; class以保持对各种池的引用。

@Component
public class JaxWSServiceProxyPoolRegistry {
    private static final Map<Class, JaxWSServiceProxyPool> registry = new HashMap<>();

    public synchronized <T> void register(Class<T> serviceTypeClass, Supplier<T> factory, GenericObjectPoolConfig poolConfig) {
        Assert.notNull(serviceTypeClass);
        Assert.notNull(factory);
        if (!registry.containsKey(serviceTypeClass)) {
            registry.put(serviceTypeClass, new JaxWSServiceProxyPool<>(factory, poolConfig));
        }
    }

    public <T> void register(Class<T> serviceTypeClass, Supplier<T> factory) {
        register(serviceTypeClass, factory, null);
    }

    @SuppressWarnings("unchecked")
    public <T> JaxWSServiceProxyPool<T> getServiceProxyPool(Class<T> serviceTypeClass) {
        Assert.notNull(serviceTypeClass);
        return registry.get(serviceTypeClass);
    }
}

要使用它,我做了:

JaxWSServiceProxyPoolRegistry jaxWSServiceProxyPoolRegistry = new JaxWSServiceProxyPoolRegistry();
jaxWSServiceProxyPoolRegistry.register(ConsumerSupportService.class,
            this::buildConsumerSupportServiceClient,
            getConsumerSupportServicePoolConfig());

buildConsumerSupportServiceClient使用JaxWsProxyFactoryBean来构建客户端。

要从池中检索实例,我会注入我的注册表类,然后执行:

JaxWSServiceProxyPool<ConsumerSupportService> consumerSupportServiceJaxWSServiceProxyPool = jaxWSServiceProxyPoolRegistry.getServiceProxyPool(ConsumerSupportService.class);

然后根据需要从/向池中借用/返回对象。

到目前为止,这似乎运作良好。我已经针对它执行了一些相当重的负载测试并且它已经停止了。