2012-05-15 26 views
17

Kể từ khi khởi tạo dịch vụ khách hàng WS và cổng mất độ tuổi tôi muốn khởi tạo chúng một lần khi khởi động và tái sử dụng cùng một phiên bản của cổng. initalization sẽ giống như thế này:Chủ đề cuộc gọi khách hàng JAX-WS này có an toàn không?

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

Cuộc gọi nơi nào đó trong lớp học của tôi:

myPort.someFunctionCall(requestContext, "someValue"); 

Câu hỏi của tôi: cuộc gọi này sẽ được thread-an toàn không?

Jonny

+3

đã được trả lời ở đây: http://stackoverflow.com/questions/4385204/are-jax-ws-clients-thread-safe – kyiu

+0

Hi KHY , cảm ơn vì đã phản hồi sớm. Tôi đã thấy chủ đề này. Vấn đề của tôi là, tôi thiếu bất kỳ tuyên bố (chính thức) nào là luồng an toàn hay không (dịch vụ/cổng/etc). My usecase cũng khác với thread khác. Jonny – user871611

+1

Đây là câu trả lời tôi tìm thấy trên trang web của CXF: https://cwiki.apache.org/CXF/faq.html#FAQ-AreJAXWSclientproxiesthreadsafe%253F – kyiu

Trả lời

19

Theo CXF FAQ:

Are JAX-WS client proxy chủ đề an toàn không?

Official JAX-WS câu trả lời: số Theo spec JAX-WS, các proxy client KHÔNG đề an toàn. Để viết mã di động, bạn nên coi chúng là an toàn không phải luồng và đồng bộ hóa quyền truy cập hoặc sử dụng một nhóm các phiên bản hoặc tương tự.

Câu trả lời CXF: Proxy CXF là chủ đề an toàn cho nhiều trường hợp sử dụng. Các trường hợp ngoại lệ là:

  • Sử dụng ((BindingProvider)proxy).getRequestContext() - mỗi JAX-WS spec, bối cảnh yêu cầu là mỗi trường hợp. Do đó, mọi thứ được đặt ở đó sẽ ảnh hưởng đến các yêu cầu về các chủ đề khác. Với CXF, bạn có thể làm:

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

    và tương lai các cuộc gọi đến getRequestContext() sẽ sử dụng một sợi bối cảnh yêu cầu địa phương. Điều đó cho phép bối cảnh yêu cầu là an toàn chủ đề. (Lưu ý: bối cảnh câu trả lời luôn là thread địa phương trong CXF)

  • Cài đặt trên ống dẫn - nếu bạn sử dụng mã hoặc cấu hình trực tiếp thao tác các ống dẫn (thích để đặt thiết lập TLS hoặc tương tự), những không phải là thread an toàn. Ống dẫn là mỗi trường hợp và do đó những cài đặt này sẽ được chia sẻ. Ngoài ra, nếu bạn sử dụng tính năng FailoverFeature và LoadBalanceFeatures, ống dẫn sẽ được thay thế khi đang di chuyển. Do đó, các cài đặt được đặt trên ống dẫn có thể bị mất trước khi được sử dụng trên chuỗi cài đặt .

  • Hỗ trợ phiên - nếu bạn bật hỗ trợ phiên (xem thông số jaxws), cookie phiên được lưu trữ trong ống dẫn. Do đó, nó sẽ rơi vào các quy tắc trên về cài đặt ống dẫn và do đó được chia sẻ trên các chủ đề.
  • Mã thông báo WS-Security - Nếu sử dụng WS-SecureConversation hoặc WS-Trust, mã thông báo đã truy xuất được lưu trữ trong Endpoint/Proxy để tránh các cuộc gọi thêm (và đắt tiền) tới STS để nhận mã thông báo. Do đó, nhiều chuỗi sẽ chia sẻ mã thông báo. Nếu mỗi chuỗi có thông tin đăng nhập hoặc yêu cầu bảo mật khác nhau, bạn cần phải sử dụng các cá thể proxy riêng biệt.

Đối với các vấn đề về ống dẫn, bạn CÓ THỂ cài đặt ConduitSelector mới sử dụng một chuỗi cục bộ hoặc tương tự. Đó là một chút phức tạp mặc dù.

Đối với hầu hết các trường hợp sử dụng "đơn giản", bạn có thể sử dụng proxy CXF trên nhiều chủ đề . Ở trên phác thảo các cách giải quyết cho những người khác.

3

Nói chung, không.

Theo FAQ CXF http://cxf.apache.org/faq.html#FAQ-AreJAX-WSclientproxiesthreadsafe?

Official JAX-WS câu trả lời: số Theo spec JAX-WS, khách hàng proxy KHÔNG đề an toàn. Để viết mã di động, bạn nên coi chúng là an toàn không phải luồng và đồng bộ hóa quyền truy cập hoặc sử dụng một hồ bơi gồm trường hợp hoặc tương tự.

Câu trả lời CXF: Proxy CXF là chủ đề an toàn cho nhiều trường hợp sử dụng.

Để biết danh sách ngoại lệ, hãy xem Câu hỏi thường gặp.

3

Như bạn thấy từ các câu trả lời ở trên, các proxy máy khách JAX-WS không an toàn vì vậy tôi chỉ muốn chia sẻ việc triển khai của mình với những người khác để lưu trữ proxy của khách hàng. Tôi thực sự phải đối mặt với cùng một vấn đề và quyết định tạo một bean mùa xuân để lưu trữ bộ nhớ đệm của các máy khách JAX-WS Client. Bạn có thể xem chi tiết hơn 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(); 
} 
} 
0

Một giải pháp chung cho điều này là sử dụng nhiều đối tượng khách hàng trong một hồ bơi, sau đó sử dụng proxy hoạt động như một mặt tiền.

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

Để sử dụng proxy:

ServiceObjectPool<SomeNonThreadSafeService> servicePool = new ServiceObjectPool<>(createSomeNonThreadSafeService); 
nowSafeService = servicePool .getProxy(SomeNonThreadSafeService.class); 
Các vấn đề liên quan