2015-04-01 15 views
12

Başlamadan beri, her zaman InterruptedException ile nasıl başa çıkılacağı ve eğer çok fazla zaman alıyorlarsa http isteğinin nasıl iptal edileceği konusunda kafam karışmıştı. Müşterilerimize iki yöntem, senkronizasyon ve uyumsuzluk sağladığım bir kütüphanem var. Amaçları için uygun olduklarını düşündükleri yöntemi arayabilirler.AsyncRestTemplate HTTP isteği, çok fazla zaman alıyorlarsa nasıl iptal edilir?

  • executeSync() - Bir sonuç olana kadar bekler, sonuç verir.
  • executeAsync() - diğer şeyler yapıldıktan sonra gerekirse işlenebilir hangi derhal Geleceği döndürür.

Onlar kullanıcı kimliği ve zaman aşımı değeri vardır DataKey nesneyi geçecek. Hangi makinenin kullanıcı kimliğine göre çağrı yapılacağını anlayacağız ve daha sonra bu makineyle bir URL oluşturacağız ve AsyncRestTemplate kullanarak URL'ye http çağrısı yapacağız ve daha sonra yanıtın başarılı olup olmadığına bağlı olarak onlara geri göndereceğiz.

geri bir ListenableFuture döndürür AsyncRestTemplate ait exchange yöntemini kullanıyorum ve o isteği o ben AsyncRestTemplate gitti neden yüzden IO engelleme olmayan kullanır böylece MİT tabanlı istemci bağlantıları ile zaman uyumsuz olmayan engelleme mimarisini olmasını istedik. Bu yaklaşım problem tanımım için doğru geliyor mu? Bu kütüphane çok ağır yük altında üretimde kullanılacaktır. Aşağıda

benim arayüzü:

public interface Client { 
    // for synchronous 
    public DataResponse executeSync(DataKey key); 

    // for asynchronous 
    public ListenableFuture<DataResponse> executeAsync(DataKey key); 
} 

Ve aşağıda arabiriminin benim uygulamasıdır:

public class DataClient implements Client { 

    // using spring 4 AsyncRestTemplate 
    private final AsyncRestTemplate restTemplate = new AsyncRestTemplate(); 

    // for synchronous 
    @Override 
    public DataResponse executeSync(DataKey keys) { 
     Future<DataResponse> responseFuture = executeAsync(keys); 
     DataResponse response = null; 

     try { 
      response = responseFuture.get(keys.getTimeout(), TimeUnit.MILLISECONDS); 
     } catch (InterruptedException ex) { 
      // do we need to catch InterruptedException here and interrupt the thread? 
      Thread.currentThread().interrupt(); 
      // also do I need throw this RuntimeException at all? 
      throw new RuntimeException("Interrupted", ex); 
     } catch (TimeoutException ex) { 
      DataLogging.logEvents(ex, DataErrorEnum.CLIENT_TIMEOUT, keys); 
      response = new DataResponse(null, DataErrorEnum.CLIENT_TIMEOUT, DataStatusEnum.ERROR); 
      responseFuture.cancel(true); // terminating the tasks that got timed out so that they don't take up the resources? 
     } catch (Exception ex) { 
      DataLogging.logEvents(ex, DataErrorEnum.ERROR_CLIENT, keys); 
      response = new DataResponse(null, DataErrorEnum.ERROR_CLIENT, DataStatusEnum.ERROR); 
     } 

     return response; 
    } 

    // for asynchronous  
    @Override 
    public ListenableFuture<DataResponse> executeAsync(final DataKey keys) { 

     final SettableFuture<DataResponse> responseFuture = SettableFuture.create(); 
     final org.springframework.util.concurrent.ListenableFuture orig = 
      restTemplate.exchange(createURL(keys), HttpMethod.GET, keys.getEntity(), String.class); 

     orig.addCallback(
       new ListenableFutureCallback<ResponseEntity<String>>() { 
        @Override 
        public void onSuccess(ResponseEntity<String> result) { 
         responseFuture.set(new DataResponse(result.getBody(), DataErrorEnum.OK, 
           DataStatusEnum.SUCCESS)); 
        } 

        @Override 
        public void onFailure(Throwable ex) { 
         DataLogging.logErrors(ex, DataErrorEnum.ERROR_SERVER, keys); 
         responseFuture.set(new DataResponse(null, DataErrorEnum.ERROR_SERVER, 
           DataStatusEnum.ERROR)); 
        } 
       }); 

     // propagate cancellation back to the original request 
     responseFuture.addListener(new Runnable() { 
      @Override public void run() { 
      if (responseFuture.isCancelled()) { 
       orig.cancel(false); // I am keeping this false for now 
      } 
      } 
     }, MoreExecutors.directExecutor()); 
     return responseFuture; 
    } 
} 

Ve müşteri kendi kodundan böyle arayacaktır -

// if they are calling executeSync() method 
DataResponse response = DataClientFactory.getInstance().executeSync(dataKey); 

// and if they want to call executeAsync() method 
Future<DataResponse> response = DataClientFactory.getInstance().executeAsync(dataKey); 

Şimdi soru şu -

http istek çok uzun sürerse
  1. biz AsyncRestTemplate çağrı kesme miyim? Aslında executeSync yönteminde benim yukarıdaki kodda benim future üzerinde cancel arıyorum ama bunu yapmak doğrulamak nasıl emin değilim emin ne olması gerektiği yapıyor? Geri yüklemeyi orijinal geleceğe yaymak istiyorum, böylece ilgili http isteğini (muhtemelen kaynakları kaydetmek için yapmak istiyorum) iptal edebilirim, bu yüzden executeAsync yöntemimde bir dinleyici ekledim. RestTemplate çağrılarını kesemeyiz, ancak bunu yapıp yapamayacağımızı AsyncRestTemplate'dan emin olamayız. AsyncRestTemplate çağrılarını kesebileceğimizi söylersek, http çağrılarını kesmek için her şeyi doğru yapıyorum? Yoksa bunu yapmanın daha iyi/daha temiz bir yolu var mı? Veya şu anki tasarımımda Http isteğini AsyncRestTemplate ile iptal etme konusunda endişelenmeme gerek var mı? bu benim HTTP isteği anlamına mı ardından iptal edildi -

    akım kurulumla
    // propagate cancellation back to the original request 
        responseFuture.addListener(new Runnable() { 
         @Override public void run() { 
         if (responseFuture.isCancelled()) { 
          orig.cancel(false); // I am keeping this false for now 
         } 
         } 
        }, MoreExecutors.directExecutor()); 
    

    , ben bazen (değil her) bazı CancellationException atıyor görebilir?

  2. Ayrıca InterruptedException catch bloğunda executeSync yönteminde doğru şeyi yapıyorum? Eğer değilse, o zaman bununla başa çıkmanın doğru yolu nedir? Ve benim durumumda InterruptedException ile uğraşmak zorunda mıyım?
  3. Varsayılan olarak AsyncRestTamplete, çağrıları engellemek ve iş parçacığı başına istek kullanmakta doğru mu? Varsa, mevcut kurulumumda NIO tabanlı istemci bağlantılarına sahip olmanın bir yolu var mı?

Herhangi açıklamalar/kod önerileri büyük yardımı olacaktır.

+0

Tüm sorularınızı yanıtlayamıyorum, ancak "InterruptedException" catch bloğu içindeki geçerli dizgiyi kesmeniz gerekmediğini size söyleyebilirim. 'DataErrorEnum'unuza bir' CLIENT_INTERRUPTED 'değeri eklemek ve diğer catch bloklarınıza benzer bir hata yanıtı vermek isteyebilirsiniz. –

+1

Tüm sorularınızı yanıtlayamıyorum, ancak neden bu amaçla soket zaman aşımlarını kullanmadığınızı merak ettim, hat üzerinden veri gönderiliyor ve yine de bağlantıyı iptal etmek istiyor musunuz? –

cevap

8

Her şeyden önce, neden SettableFuture kullanıyorsunuz? AsyncRestTemplate tarafından döndürülen ListenableFuture'u neden iade edemiyoruz?

1. Can we interrupt AsyncRestTemplate call if http request is taking too long? 

Tabii ki yaparsınız! Sadece Future.cancel yöntemini çağırmanız yeterlidir. Bu yöntem, AsyncRestTemplate'in aslında kullandığı dahili RestTemplate uygulamasının çalışmasını kesintiye uğratır.

2. Also am I doing the right thing in catch block of InterruptedException in executeSync method? 

Phil ve Danilo'nun dediği gibi, InterruptedException catch bloğu içindeki geçerli dizgiyi kesmeniz gerekmez. Sadece isteğin yürütülmesi iptal edilmesi gerektiğinde ne yapmanız gerekiyorsa yapın. Aslında, bu davranışı ele alan bir yöntem oluşturmanızı öneririm, handleInterruption gibi bir şey ve bu yöntemi hem TimeoutException hem de InterruptedException için kullanın. Üste |

( ) AsyncRestTamplete'un varsayılan kurucusu, dahili olarak SimpleClientHttpRequestFactory ve SimpleAsyncTaskExecutor'dur.

Bu TaskExecutor zaman her görev için bir tehdit başlar ve konu asla kullanmayın, bu yüzden çok verimsiz:

* TaskExecutor implementation that fires up a new Thread for each task, 
* executing it asynchronously. 
* 
* Supports limiting concurrent threads through the "concurrencyLimit" 
* bean property. By default, the number of concurrent threads is unlimited. 
* 
* NOTE: This implementation does not reuse threads! Consider a 
* thread-pooling TaskExecutor implementation instead, in particular for 
* executing a large number of short-lived tasks. 
* 

Sana AsyncRestTemplate başka yapılandırma kullanmanızı öneriyoruz.

Başka TaskExecutor kullanan AsyncRestTemplate yapıcısı kullanmalıdır: Mesela

public AsyncRestTemplate(AsyncListenableTaskExecutor taskExecutor) 

:

AsyncRestTemplate template = new AsyncRestTemplate(new ConcurrentTaskExecutor(Executors.newCachedThreadPool())); 

Bu ExecutorService (Executors.newCachedThreadPool()) gerektiği gibi yeni konuları oluşturur, ancak Mevcut olduklarında önceden oluşturulmuş konuları yeniden kullanır.

Veya daha da iyisi, başka bir RequestFactory kullanabilirsiniz. Örneğin, sadece AsyncRestTemplate düzgün kurucu çağırmak, içten MİT kullandığı, HttpComponentsAsyncClientHttpRequestFactory kullanabilirsiniz:

new AsyncRestTemplate(new HttpComponentsAsyncClientHttpRequestFactory()) 

Eğer nesne oluşturmak nasıl bağlı olacaktır AsyncRestTemplate iç davranışı unutmayın.

+0

Öneriniz için çok teşekkürler. Yardımınız için teşekkür ederiz. Birkaç sorum var - AsyncRestTemplate' çağrılarını kesebileceğimizden emin misin? Anlayışım gereği, RestTemplate'ın çağrılarını hiç kesemeyiz. Bkz. [This] (http://stackoverflow.com/a/29192089/2809564). Yanılıyorsam haberim olsun. Ve şunu söyleyelim, eğer kesebilirsek, ne yapıyorum doğruysa? Gelecekte zaten 'cancel' i çağırıyorum ve ayrıca iptal işlemini koddaki orijinal talebe geri iletiyorum. Bu konuda bahsettiğim bir sorudan bahsetmiştim. Mümkünse – john

+0

..., ne yapıyorum ne olursa olsun sağ mümkünse noktasında bir açık olduğundan emin yapabilir? Ayrıca, mevcut tasarım ile, http isteği iptal etme konusunda endişelenmeli miyim? – john

+0

Evet 2 için, şimdi ne yapmam gerektiğini anladım, böylece bununla ilgilenebiliyorum. – john