我正在执行一些测试,以评估使用基于可观察的反应式API,而不是阻塞传统API是否有真正的优势。
整个示例在Githug上可用
令人惊讶的是,结果显示,thoughput结果如下:
>
最好的:返回打包阻塞操作的可调用的延迟结果的REST服务。
还不错:阻止REST服务。
最糟糕的:返回结果由RxJava可观察设置的DeferredResult的REST服务。
这是我的Spring WebApp:
应用程序:
@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);
}
}
同步控制器:
@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:具有原始可调用和可观察endpoint
@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;
}
}
我设置了一个Thread.sleep(500)
延迟来增加服务响应时间。
负载测试的结果如下:
异步,可调用:700 rpm,无错误
>>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
阻塞:大约404 rps,但会产生错误
>>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 with Observable:不超过20 RPM,并且更快地出错
>>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
可观察对象以10的corePoolSize执行,但将其增加到50也没有任何改善。
可能是什么原因呢?
更新:根据akarnokd的建议,我做了以下更改。已从对象中移动。创建到对象。fromCallable在服务中,并在控制器中重用了调度器,但仍然得到了相同的结果。
该问题是由某个点的编程错误引起的。实际上,问题中的例子非常有效。
防止其他人出现问题的一个警告:小心使用Observable.just(func)
,func实际上是在可观察的创建中调用的。所以放在那里的任何Thread.sleep都会阻塞调用线程
@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;
}
我在RxJava谷歌小组开始了一场讨论,他们帮我解决了这个问题。
问题内容: 我正在尝试使用大型复杂的tomcat Java Web应用程序解决性能问题。目前最大的问题是,内存使用量有时会激增,应用程序将变得无响应。我已经修复了我可以使用日志探查器和日志文件的贝叶斯分析解决的所有问题。我正在考虑在生产的Tomcat服务器上运行探查器。 致读者的注意事项: 我了解有些人可能会发现对生产应用程序进行冒犯性分析的想法。请放心,我已经用尽了大多数其他选项。我正在考虑这样
到目前为止,我认为我已经掌握了async await如何使应用程序更具响应性的概念,但我有两点悬而未决: 层注意事项异步等待是否必须从存储库层一直到MVC或WCF层才能获得性能优势,或者我可以只对需要很长时间的存储库方法进行异步操作吗? “等待”用法如果我只能在存储库级别工作,有一部分我不明白。使用这种(低层)方法,线程能够在等待io绑定代码完成的同时为传入的客户端请求提供服务吗? 在我看来,当长
我见过一些APM,它只测量运行在WAS上的网络应用程序。 Elastic APM能否衡量纯Java应用程序等其他应用程序的性能? 如果没有,我可以使用吗https://www.elastic.co/guide/en/apm/agent/java/1.x/public-api.html(公共API),以便它可以测量非web应用程序的性能? 我将感谢你的建议。干杯
应用程序只是不会移动通过登录页面和错误指向一个内置的文档 我的代码
我试图在完全使用JavaConfig配置的Spring MVC应用程序中使用处理404错误。 Spring MVC版本为4.1.5 我读过这样的话: null SpringConfigurationInitializer 请注意,我正在使用 而且 MVC配置 存储配置 安全配置
在99%的情况下,当SQL server返回意外错误或文件系统中缺少文件时,我不能做任何合理的事情,我只想记录这种情况并将错误返回给用户。所以我看不出有什么理由应该返回“err”手动展开堆栈,实际上我会丢失stacktrace和上下文,并且更难找到错误的原因。 有没有什么我错过了,所以这种方法不会很好地工作?似乎大多数Go文章都建议不要使用panic/recover,但我不明白为什么。它看起来非常