33

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");

私の質問:この呼び出しはスレッドセーフですか?

4

4 に答える 4

37

CXF FAQによると:

JAX-WSクライアントプロキシはスレッドセーフですか?

JAX-WSの公式回答:いいえ。JAX-WS仕様によると、クライアントプロキシはスレッドセーフではありません。ポータブルコードを作成するには、それらを非スレッドセーフとして扱い、アクセスを同期するか、インスタンスのプールなどを使用する必要があります。

CXFの回答: CXFプロキシは、多くのユースケースでスレッドセーフです。例外は次のとおりです。

  • --を使用すると、((BindingProvider)proxy).getRequestContext()JAX-WS仕様に従って、リクエストコンテキストはPERINSTANCEになります。したがって、そこに設定されたものはすべて、他のスレッドの要求に影響します。CXFを使用すると、次のことができます。

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

    getRequestContext()の今後の呼び出しでは、スレッドローカルリクエストコンテキストが使用されます。これにより、リクエストコンテキストをスレッドセーフにすることができます。(注:応答コンテキストは、CXFでは常にスレッドローカルです)

  • コンジットの設定-コードまたは構成を使用してコンジットを直接操作する場合(TLS設定などを設定する場合など)、これらはスレッドセーフではありません。コンジットはインスタンスごとであるため、これらの設定は共有されます。また、FailoverFeatureとLoadBalanceFeaturesを使用する場合、コンジットはオンザフライで置き換えられます。したがって、コンジットに設定された設定は、設定スレッドで使用される前に失われる可能性があります。

  • セッションサポート-セッションサポートをオンにすると(jaxws仕様を参照)、セッションCookieがコンジットに保存されます。したがって、コンジット設定に関する上記のルールに該当するため、スレッド間で共有されます。
  • WS-Securityトークン-WS-SecureConversationまたはWS-Trustを使用する場合、取得したトークンはエンドポイント/プロキシにキャッシュされ、トークンを取得するためのSTSへの余分な(そして高価な)呼び出しを回避します。したがって、複数のスレッドがトークンを共有します。各スレッドに異なるセキュリティクレデンシャルまたは要件がある場合は、個別のプロキシインスタンスを使用する必要があります。

コンジットの問題については、ローカルスレッドまたは同様のスレッドを使用する新しいConduitSelectorをインストールできます。しかし、それは少し複雑です。

ほとんどの「単純な」ユースケースでは、複数のスレッドでCXFプロキシを使用できます。上記は、他の人の回避策の概要です。

于 2012-05-15T13:35:47.297 に答える
3

一般的に、いいえ。

CXF FAQによるとhttp://cxf.apache.org/faq.html#FAQ-AreJAX-WSclientproxiesthreadsafe?

JAX-WSの公式回答:いいえ。JAX-WS仕様によると、クライアントプロキシはスレッドセーフではありません。ポータブルコードを作成するには、それらを非スレッドセーフとして扱い、アクセスを同期するか、インスタンスのプールなどを使用する必要があります。

CXFの回答: CXFプロキシは、多くのユースケースでスレッドセーフです。

例外のリストについては、FAQを参照してください。

于 2014-06-11T12:54:36.117 に答える
3

上記の回答からわかるように、JAX-WSクライアントプロキシはスレッドセーフではないため、他の人がクライアントプロキシをキャッシュするように実装を共有したかっただけです。私は実際に同じ問題に直面し、JAX-WSクライアントプロキシのキャッシュを実行するSpringBeanを作成することにしました。詳細については、 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();
 }
}
于 2015-11-17T16:30:04.987 に答える
1

このための一般的な解決策は、プール内の複数のクライアントオブジェクトを使用してから、ファサードとして機能するプロキシを使用することです。

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);
于 2018-02-15T14:13:17.010 に答える