2015-12-15 27 views
13

Sto eseguendo alcuni test per valutare se c'è un reale vantaggio nell'utilizzo di API reattive basate su Observables, invece dei tradizionali di blocco.Inspiegabile mancanza di miglioramento delle prestazioni utilizzando RxJava Observables in applicazioni Web

Tutta esempio è available on Githug

Sorprendentemente i risultati mostrano che i risultati sono thoughput:

  • Le migliori: turismo Servizi che restituiscono un Callable/DeferredResult che avvolge le operazioni di blocco.

  • Non così male: Blocco dei servizi REST.

  • I peggiori: turismo Servizi che restituiscono un DeferredResult il cui risultato è fissato da un RxJava Observable.

Questa è la mia Primavera WebApp:

Applicazione:

@SpringBootApplication 
public class SpringNioRestApplication { 

    @Bean 
    public ThreadPoolTaskExecutor executor(){ 
     ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 
     executor.setCorePoolSize(10); 
     executor.setMaxPoolSize(20); 
     return executor; 
    } 

    public static void main(String[] args) { 
     SpringApplication.run(SpringNioRestApplication.class, args); 
    } 
} 

SyncController:

@RestController("SyncRestController") 
@Api(value="", description="Synchronous data controller") 
public class SyncRestController { 

    @Autowired 
    private DataService dataService; 

    @RequestMapping(value="/sync/data", method=RequestMethod.GET, produces="application/json") 
    @ApiOperation(value = "Gets data", notes="Gets data synchronously") 
    @ApiResponses(value={@ApiResponse(code=200, message="OK")}) 
    public List<Data> getData(){ 
     return dataService.loadData(); 
    } 
} 

AsyncController: Con entrambi Callable prime e gli endpoint osservabili

@RestController 
@Api(value="", description="Synchronous data controller") 
public class AsyncRestController { 

    @Autowired 
    private DataService dataService; 

    private Scheduler scheduler; 

    @Autowired 
    private TaskExecutor executor; 

    @PostConstruct 
    protected void initializeScheduler(){ 
     scheduler = Schedulers.from(executor); 
    } 

    @RequestMapping(value="/async/data", method=RequestMethod.GET, produces="application/json") 
    @ApiOperation(value = "Gets data", notes="Gets data asynchronously") 
    @ApiResponses(value={@ApiResponse(code=200, message="OK")}) 
    public Callable<List<Data>> getData(){ 
     return (() -> {return dataService.loadData();}); 
    } 

    @RequestMapping(value="/observable/data", method=RequestMethod.GET, produces="application/json") 
    @ApiOperation(value = "Gets data through Observable", notes="Gets data asynchronously through Observable") 
    @ApiResponses(value={@ApiResponse(code=200, message="OK")}) 
    public DeferredResult<List<Data>> getDataObservable(){ 
     DeferredResult<List<Data>> dr = new DeferredResult<List<Data>>(); 
     Observable<List<Data>> dataObservable = dataService.loadDataObservable(); 
     dataObservable.subscribeOn(scheduler).subscribe(dr::setResult, dr::setErrorResult); 
     return dr; 
    } 
} 

DataServiceImpl

@Service 
public class DataServiceImpl implements DataService{ 

    @Override 
    public List<Data> loadData() { 
     return generateData(); 
    } 

    @Override 
    public Observable<List<Data>> loadDataObservable() { 
     return Observable.create(s -> { 
      List<Data> dataList = generateData(); 
      s.onNext(dataList); 
      s.onCompleted(); 
     }); 
    } 

    private List<Data> generateData(){ 
     List<Data> dataList = new ArrayList<Data>(); 
     for (int i = 0; i < 20; i++) { 
      Data data = new Data("key"+i, "value"+i); 
      dataList.add(data); 
     } 
     //Processing time simulation 
     try { 
      Thread.sleep(500); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     return dataList; 
    } 
} 

ho impostato un ritardo Thread.sleep(500) per aumentare il tempo di risposta del servizio.

Ne risulta dalle prove di carico sono:

asincrono con Callable: 700 RPS, nessun errore

>>loadtest -c 15 -t 60 --rps 700 http://localhost:8080/async/data  
... 
Requests: 0, requests per second: 0, mean latency: 0 ms 
Requests: 2839, requests per second: 568, mean latency: 500 ms 
Requests: 6337, requests per second: 700, mean latency: 500 ms 
Requests: 9836, requests per second: 700, mean latency: 500 ms 
... 
Completed requests: 41337 
Total errors:  0 
Total time:   60.002348360999996 s 
Requests per second: 689 
Total time:   60.002348360999996 s 

Blocco: circa 404 rps ma produce errori

>>loadtest -c 15 -t 60 --rps 700 http://localhost:8080/sync/data  
... 
Requests: 7683, requests per second: 400, mean latency: 7420 ms 
Requests: 9683, requests per second: 400, mean latency: 9570 ms 
Requests: 11680, requests per second: 399, mean latency: 11720 ms 
Requests: 13699, requests per second: 404, mean latency: 13760 ms 
... 
Percentage of the requests served within a certain time 
    50%  8868 ms 
    90%  22434 ms 
    95%  24103 ms 
    99%  25351 ms 
100%  26055 ms (longest request) 

100%  26055 ms (longest request) 

    -1: 7559 errors 
Requests: 31193, requests per second: 689, mean latency: 14350 ms 
Errors: 1534, accumulated errors: 7559, 24.2% of total requests 

Async con Observable: non più di 20 rps, e ottiene gli errori prima

>>loadtest -c 15 -t 60 --rps 700 http://localhost:8080/observable/data 
Requests: 0, requests per second: 0, mean latency: 0 ms 
Requests: 90, requests per second: 18, mean latency: 2250 ms 
Requests: 187, requests per second: 20, mean latency: 6770 ms 
Requests: 265, requests per second: 16, mean latency: 11870 ms 
Requests: 2872, requests per second: 521, mean latency: 1560 ms 
Errors: 2518, accumulated errors: 2518, 87.7% of total requests 
Requests: 6373, requests per second: 700, mean latency: 1590 ms 
Errors: 3401, accumulated errors: 5919, 92.9% of total requests 

Le esegue osservabili con corePoolSize di 10, ma portandolo a 50 non ha migliorato niente.

Quale potrebbe essere la spiegazione?

UPDATE: Come suggerito da akarnokd ho apportato le seguenti modifiche. Spostato da Object.create a Object.fromCallable nel servizio e riutilizzato lo Scheduler nel controller, ma ottengo comunque gli stessi risultati.

+0

Potresti usare "Observable.fromCallable" invece di "Observable.create'? Il tuo uso di 'create' sembra strano. Inoltre, Thread.sleep non garantisce l'importo del sonno in modo esatto ma dipende dal sistema operativo. In 'getVideoInfoAsync' stai creando il wrapper dell'Utilità di pianificazione più e più volte. – akarnokd

+0

Ciao akarnokd, grazie per il tuo commento. Un paio di cose, cosa c'è di sbagliato nell'usare Observable.create? Inoltre non capisco cosa intendi per "creare il wrapper dello Scheduler più e più volte". Per implementarlo ho seguito quello che ho visto [qui in dzone] (https://dzone.com/articles/rx-java-subscribeon-and) – codependent

+0

Non si chiama s.onCompleted() per l'avvio, ma la mancanza la gestione della disiscrizione potrebbe essere anche problematica. Inoltre, dovresti vedere quale è l'errore che potrebbe anche indicare l'origine della perdita di prestazioni. Hai un TaskExecutor come un campo membro, ma poi lo avvolgi con Scheduler.wrap per ogni chiamata di 'getVideoInfoAsync' che suppongo accada centinaia di volte al secondo. – akarnokd

risposta

4

Il problema è stato causato da un errore di programmazione a un certo punto. In realtà l'esempio nella domanda funziona perfettamente.

Un avvertimento per impedire ad altri di avere problemi: attenzione all'utilizzo di Observable.just(func), func è effettivamente chiamato su Creazione osservabile. Quindi, qualsiasi Thread.sleep posto ci sarà bloccare il thread chiamante

@Override 
public Observable<List<Data>> loadDataObservable() { 
    return Observable.just(generateData()).delay(500, TimeUnit.MILLISECONDS); 
} 

private List<Data> generateData(){ 
    List<Data> dataList = new ArrayList<Data>(); 
    for (int i = 0; i < 20; i++) { 
     Data data = new Data("key"+i, "value"+i); 
     dataList.add(data); 
    } 
    return dataList; 
} 

ho iniziato una discussione in RxJava Google group dove mi hanno aiutato a lavorare fuori.

+1

Quindi, qual è il risultato finale? – Rekin

+1

Il risultato è che le prestazioni di Observable sono leggermente migliori rispetto all'utilizzo di Callable. – codependent