我开发了一个Spring Boot微服务,它将被大量使用(接近10k点击/秒)。我使用Feign作为我的REST客户端,并使用CompletableFutures命中其他服务以异步方式获取数据。 在这一个场景中,我使用CompletableFutures在一个函数中为4个不同的对象命中另一个服务。
我的配置如下:
Tomcat线程大小:
server.tomcat.max-threads=250
执行程序线程池大小:
private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
private ExecutorService executorService = new ThreadPoolExecutor(1000, 1000, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), rejectedExecutionHandler);
我用来点击外部服务的代码:
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() ->
service1.function1(params) , executorService);
CompletableFuture<List<Object>> future2 = CompletableFuture.supplyAsync(() ->
service2.function2(params) , executorService);
CompletableFuture<Object> future3 = CompletableFuture.supplyAsync(() ->
service3.function3(params) , executorService);
CompletableFuture<Object> future4=CompletableFuture.supplyAsync(()->
service4.function4 (params) , executorService);
List<Object> response =Stream.of(future1, future2, future3, future4)
.parallel()
.map(CompletableFuture::join)
.collect(Collectors.toList());
这些服务是在内部使用Feign的SpringBoot服务。
@Autowired
Service1(AppConfig config)
{
this.config=config;
this.currentService=connect(config);
}
@Bean
private Service1 connect(AppConfig config) {
Logger logger = new Logger.JavaLogger();
return Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.logger(logger)
.logLevel(feign.Logger.Level.FULL)
.target(Service1.class, config.getApiUrl());
}
On使用jMeter加载测试此调用,jMeter在内部调用这4个外部服务调用,我能够在1个AWS实例上实现200次点击/秒的速率。如果我增加1个实例的负载,我开始得到RejectedExecutionException。 我在负载均衡器下尝试了10个AWS实例。期望它将线性扩展到2000次点击/秒。但是我只能达到1400次点击/秒。之后任何负载都会增加,导致RejectedExecutionExceptions返回。
这里有什么解决方案?我可以在配置中尝试调整吗? 还有,Feign可能成为这里的瓶颈吗?我应该尝试像Retrofit这样的其他客户吗?