Java 这是JAX-WS客户端调用线程安全吗?

Java 这是JAX-WS客户端调用线程安全吗?,java,jax-ws,webservice-client,java-metro-framework,Java,Jax Ws,Webservice Client,Java Metro Framework,由于WS-client服务和端口的初始化需要很长时间,我喜欢在启动时对它们进行一次初始化,并重用端口的同一实例。 初始化将如下所示: private static RequestContext requestContext = null; static { MyService service = new MyService(); MyPort myPort = service.getMyServicePort(); Map<String, Object>

由于WS-client服务和端口的初始化需要很长时间,我喜欢在启动时对它们进行一次初始化,并重用端口的同一实例。 初始化将如下所示:

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");
}
我的问题:这个调用是线程安全的吗?

根据:

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

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

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

  • 根据JAX-WS规范使用
    ((BindingProvider)proxy).getRequestContext()
    , 请求上下文是每个实例的上下文。因此,任何设置都将 影响其他线程上的请求。使用CXF,您可以执行以下操作:

    ((BindingProvider)proxy).getRequestContext().put("thread.local.request.context","true");
    
    将来对getRequestContext()的调用将使用线程 本地请求上下文。这样就可以创建请求上下文 线程安全。(注意:响应上下文在CXF中始终是线程本地的)

  • 导管上的设置-如果使用代码或配置直接 操纵导管(如设置TLS设置或类似设置),这些 不是线程安全的。导管为每个实例,因此 设置将被共享。此外,如果您使用故障切换功能和 负载平衡功能,导管可随时更换。因此 在导管上设置的设置在用于导管之前可能会丢失 设置线程

  • 会话支持-如果启用会话支持(请参阅 jaxws-spec),会话cookie存储在管道中。因此, 将属于上述导管设置规则,因此将共享 跨越线程
  • WS-Security令牌-如果使用WS-SecureConversation或 WS-Trust,检索到的令牌缓存在端点/代理中以避免 额外(且昂贵)呼叫STS以获取令牌。因此 多个线程将共享该令牌。如果每个线程有不同的 安全凭据或要求,您需要使用单独的代理 实例
对于导管问题,您可以安装新的 使用本地线程或类似线程的ConduitSelector。那有点 不过很复杂

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

一般来说,没有

根据CXF常见问题解答

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

CXF回答:CXF代理对于许多用例都是线程安全的


有关异常列表,请参阅常见问题。

正如您从上面的答案中看到的,JAX-WS客户端代理不是线程安全的,因此我只想与其他人分享我的实现,以缓存客户端代理。 我实际上也面临同样的问题,并决定创建一个Springbean来缓存JAX-WS客户机代理。你可以看到更多的细节

import java.util.Map;
导入java.util.concurrent.ConcurrentHashMap;
导入java.util.concurrent.Executors;
导入java.util.concurrent.ScheduledExecutorService;
导入java.util.concurrent.TimeUnit;
导入javax.annotation.PostConstruct;
导入org.apache.commons.lang3.concurrent.BasicThreadFactory;
导入org.apache.logging.log4j.Logger;
导入org.springframework.stereotype.Component;
/**
*这将保持最大线程数的缓存当前线程数
*appConnections并尝试在线程之间平均共享它们。所有的
*连接是在开始时创建的,如果发生错误,则
*缓存将再次创建。
*
*/
/*
*
*JAX-WS客户端代理是线程安全的吗
根据JAX-WS规范, *客户端代理不是线程安全的。要编写可移植代码,您应该 *将它们视为非线程安全的,并同步访问或使用线程池 *实例或类似实例。 * */ @组成部分 公共类AppConnectionCache{ 私有静态最终记录器Logger=org.apache.logging.log4j.LogManager.getLogger(AppConnectionCache.class); 私有最终映射连接缓存=新的ConcurrentHashMap(); 私有int cachedConnectionId=1; 私有静态最终int MAX\u cun当前线程=20; 专用ScheduledExecutorService调度器; 私有布尔forceRecaching=true;//第一次缓存 @施工后 公共void init(){ logger.info(“启动appConnectionCache”); info(“开始缓存连接”);; BasicThreadFactory=新的BasicThreadFactory.Builder() .namingPattern(“appconnectioncache调度程序线程-%d”).build(); scheduler=Executors.newScheduledThreadPool(1,工厂); scheduleAtFixedRate(新的Runnable(){ @凌驾 公开募捐{ initializeCache(); } },0,10,时间单位(分钟); } 公共空间销毁(){ scheduler.shutdownNow(); } private void initializeCache(){ 如果(!强制重新校准){ 返回; } 试一试{ loadCache(); forceRecaching=false;//此标志用于初始化 logger.info(“连接创建成功完成!”); }捕获(MyAppException e){ logger.error(“初始化缓存时出错”); } } 私有void loadCache()引发MyAppException{ info(“创建和缓存appservice连接”); 对于(int i=0;i((BindingProvider)proxy).getRequestContext().put("thread.local.request.context","true");
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();
 }
}
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);