【问题标题】:Is this JAX-WS client call thread safe?这个 JAX-WS 客户端调用线程安全吗?
【发布时间】:2012-05-22 22:03:19
【问题描述】:

由于 WS 客户端服务和端口的初始化需要很长时间,我喜欢在启动时将它们初始化一次并重用相同的端口实例。 初始化看起来像这样:

private static RequestContext requestContext = null;

static
{
    MyService service = new MyService(); 
    MyPort myPort = service.getMyServicePort(); 

    Map<String, Object> requestContextMap = ((BindingProvider) myPort).getRequestContext();
    requestContextMap = ((BindingProvider)myPort).getRequestContext(); 
    requestContextMap.put(BindingProvider.USERNAME_PROPERTY, uName); 
    requestContextMap.put(BindingProvider.PASSWORD_PROPERTY, pWord); 

    rc = new RequestContext();
    rc.setApplication("test");
    rc.setUserId("test");
}

我班上某处的电话:

myPort.someFunctionCall(requestContext, "someValue");

我的问题:这个调用是线程安全的吗?

【问题讨论】:

  • 已经在这里回答了:stackoverflow.com/questions/4385204/…
  • 嗨,KHY,感谢您的快速回复。我看到了这个线程。我的问题是,我缺乏任何(官方)声明什么是线程安全的(服务/端口/等)。我的用例也与其他线程不同。强尼
  • 这是我在 CXF 网站上找到的答案:cwiki.apache.org/CXF/…
  • 嗨,KHY,这似乎回答了我的问题。非常感谢。
  • 很高兴您发现它有帮助。我将复制我之前的评论作为答案,以便可以关闭这个问题。

标签: java jax-ws webservice-client java-metro-framework


【解决方案1】:

根据CXF FAQ

JAX-WS 客户端代理线程安全吗?

官方 JAX-WS 回答:不。 根据 JAX-WS 规范,客户端代理不是线程安全的。 要编写可移植代码,您应该将它们视为非线程安全的,并且 同步访问或使用实例池等。

CXF 答案: CXF 代理对于许多用例来说都是线程安全的。这 例外情况是:

  • 使用((BindingProvider)proxy).getRequestContext() - 根据 JAX-WS 规范, 请求上下文是 PER INSTANCE。因此,在那里设置的任何东西都会 影响其他线程上的请求。使用 CXF,您可以:

    ((BindingProvider)proxy).getRequestContext().put("thread.local.request.context","true");
    

    以后对 getRequestContext() 的调用将使用一个线程 本地请求上下文。这允许请求上下文是 线程安全。 (注意:响应上下文在 CXF 中始终是线程本地的)

  • 管道上的设置 - 如果您使用代码或配置直接 操纵管道(例如设置 TLS 设置或类似设置),那些 不是线程安全的。管道是每个实例的,因此那些 设置将被共享。此外,如果您使用 FailoverFeature 和 LoadBalanceFeatures,管道被即时更换。因此, 在导管上设置的设置可能会在使用之前丢失 设置线程。

  • 会话支持 - 如果您打开会话支持(请参阅 jaxws 规范),会话 cookie 存储在管道中。因此,它 将属于上述关于管道设置的规则,因此被共享 跨线程。
  • WS-Security 令牌 - 如果使用 WS-SecureConversation 或 WS-Trust,检索到的令牌缓存在 Endpoint/Proxy 中以避免 对 STS 的额外(和昂贵)调用以获取令牌。因此, 多个线程将共享令牌。如果每个线程有不同的 安全凭证或要求,您需要使用单独的代理 实例。

对于导管问题,您可以安装一个新的 使用本地线程或类似线程的 ConduitSelector。这有点 不过很复杂。

对于大多数“简单”用例,您可以在多个上使用 CXF 代理 线程。以上概述了其他人的解决方法。

【讨论】:

    【解决方案2】:

    一般来说不会。

    根据 CXF 常见问题解答http://cxf.apache.org/faq.html#FAQ-AreJAX-WSclientproxiesthreadsafe?

    官方 JAX-WS 回答: 不。根据 JAX-WS 规范,客户端 代理不是线程安全的。要编写可移植代码,您应该对待 它们是非线程安全的并同步访问或使用一个池 实例或类似情况。

    CXF 答案: CXF 代理对于许多用例来说都是线程安全的。

    有关例外列表,请参阅常见问题解答。

    【讨论】:

      【解决方案3】:

      正如您从上面的答案中看到的那样,JAX-WS 客户端代理不是线程安全的,所以我只是想与其他人分享我的实现来缓存客户端代理。 我实际上遇到了同样的问题,并决定创建一个 Spring bean 来缓存 JAX-WS 客户端代理。您可以查看更多详情http://programtalk.com/java/using-spring-and-scheduler-to-store/

      import java.util.Map;
      import java.util.concurrent.ConcurrentHashMap;
      import java.util.concurrent.Executors;
      import java.util.concurrent.ScheduledExecutorService;
      import java.util.concurrent.TimeUnit;
      
      import javax.annotation.PostConstruct;
      
      import org.apache.commons.lang3.concurrent.BasicThreadFactory;
      import org.apache.logging.log4j.Logger;
      import org.springframework.stereotype.Component;
      
      /**
       * This keeps the cache of MAX_CUNCURRENT_THREADS number of
       * appConnections and tries to shares them equally amongst the threads. All the
       * connections are created right at the start and if an error occurs then the
       * cache is created again.
       *
       */
      /*
       *
       * Are JAX-WS client proxies thread safe? <br/> 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.
       *
       */
      @Component
      public class AppConnectionCache {
      
       private static final Logger logger = org.apache.logging.log4j.LogManager.getLogger(AppConnectionCache.class);
      
       private final Map<Integer, MyService> connectionCache = new ConcurrentHashMap<Integer, MyService>();
      
       private int cachedConnectionId = 1;
      
       private static final int MAX_CUNCURRENT_THREADS = 20;
      
       private ScheduledExecutorService scheduler;
      
       private boolean forceRecaching = true; // first time cache
      
       @PostConstruct
       public void init() {
        logger.info("starting appConnectionCache");
        logger.info("start caching connections"); ;;
        BasicThreadFactory factory = new BasicThreadFactory.Builder()
          .namingPattern("appconnectioncache-scheduler-thread-%d").build();
        scheduler = Executors.newScheduledThreadPool(1, factory);
      
        scheduler.scheduleAtFixedRate(new Runnable() {
         @Override
         public void run() {
          initializeCache();
         }
      
        }, 0, 10, TimeUnit.MINUTES);
      
       }
      
       public void destroy() {
        scheduler.shutdownNow();
       }
      
       private void initializeCache() {
        if (!forceRecaching) {
         return;
        }
        try {
         loadCache();
         forceRecaching = false; // this flag is used for initializing
         logger.info("connections creation finished successfully!");
        } catch (MyAppException e) {
         logger.error("error while initializing the cache");
        }
       }
      
       private void loadCache() throws MyAppException {
        logger.info("create and cache appservice connections");
        for (int i = 0; i < MAX_CUNCURRENT_THREADS; i++) {
         tryConnect(i, true);
        }
       }
      
       public MyPort getMyPort() throws MyAppException {
        if (cachedConnectionId++ == MAX_CUNCURRENT_THREADS) {
         cachedConnectionId = 1;
        }
        return tryConnect(cachedConnectionId, forceRecaching);
       }
      
       private MyPort tryConnect(int threadNum, boolean forceConnect) throws MyAppException {
        boolean connect = true;
        int tryNum = 0;
        MyPort app = null;
        while (connect && !Thread.currentThread().isInterrupted()) {
         try {
          app = doConnect(threadNum, forceConnect);
          connect = false;
         } catch (Exception e) {
          tryNum = tryReconnect(tryNum, e);
         }
        }
        return app;
       }
      
       private int tryReconnect(int tryNum, Exception e) throws MyAppException {
        logger.warn(Thread.currentThread().getName() + " appservice service not available! : " + e);
        // try 10 times, if
        if (tryNum++ < 10) {
         try {
          logger.warn(Thread.currentThread().getName() + " wait 1 second");
          Thread.sleep(1000);
         } catch (InterruptedException f) {
          // restore interrupt
          Thread.currentThread().interrupt();
         }
        } else {
         logger.warn(" appservice could not connect, number of times tried: " + (tryNum - 1));
         this.forceRecaching = true;
         throw new MyAppException(e);
        }
        logger.info(" try reconnect number: " + tryNum);
        return tryNum;
       }
      
       private MyPort doConnect(int threadNum, boolean forceConnect) throws InterruptedException {
        MyService service = connectionCache.get(threadNum);
        if (service == null || forceConnect) {
         logger.info("app service connects : " + (threadNum + 1) );
         service = new MyService();
         connectionCache.put(threadNum, service);
         logger.info("connect done for " + (threadNum + 1));
        }
        return service.getAppPort();
       }
      }
      

      【讨论】:

        【解决方案4】:

        对此的一般解决方案是在池中使用多个客户端对象,然后使用充当外观的代理。

        import org.apache.commons.pool2.BasePooledObjectFactory;
        import org.apache.commons.pool2.PooledObject;
        import org.apache.commons.pool2.impl.DefaultPooledObject;
        import org.apache.commons.pool2.impl.GenericObjectPool;
        
        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        import java.lang.reflect.Proxy;
        
        class ServiceObjectPool<T> extends GenericObjectPool<T> {
                public ServiceObjectPool(java.util.function.Supplier<T> factory) {
                    super(new BasePooledObjectFactory<T>() {
                        @Override
                        public T create() throws Exception {
                            return factory.get();
                        }
                    @Override
                    public PooledObject<T> wrap(T obj) {
                        return new DefaultPooledObject<>(obj);
                    }
                });
            }
        
            public static class PooledServiceProxy<T> implements InvocationHandler {
                private ServiceObjectPool<T> pool;
        
                public PooledServiceProxy(ServiceObjectPool<T> pool) {
                    this.pool = pool;
                }
        
        
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    T t = null;
                    try {
                        t = this.pool.borrowObject();
                        return method.invoke(t, args);
                    } finally {
                        if (t != null)
                            this.pool.returnObject(t);
                    }
                }
            }
        
            @SuppressWarnings("unchecked")
            public T getProxy(Class<? super T> interfaceType) {
                PooledServiceProxy<T> handler = new PooledServiceProxy<>(this);
                return (T) Proxy.newProxyInstance(interfaceType.getClassLoader(),
                                                  new Class<?>[]{interfaceType}, handler);
            }
        }
        

        要使用代理:

        ServiceObjectPool<SomeNonThreadSafeService> servicePool = new ServiceObjectPool<>(createSomeNonThreadSafeService);
        nowSafeService = servicePool .getProxy(SomeNonThreadSafeService.class);
        

        【讨论】:

          猜你喜欢
          • 2011-05-22
          • 1970-01-01
          • 1970-01-01
          • 2014-08-07
          • 2019-04-07
          • 2019-03-20
          • 1970-01-01
          • 1970-01-01
          • 2010-11-09
          相关资源
          最近更新 更多