优化吞吐量目前我能想到的有 3 点。
“”
Docker 隔离
异步执行
增加内嵌 Tomcat 的最大连接数
Docker 隔离就不说了,很简单,先来看一下异步执行吧,很多人做异步执行的时候都写的千奇百怪的,其实 JDK 8 本身有一个非常好用的 Future 类——CompletableFuture。
先大概说一下 CompletableFuture 的用法
@AllArgsConstructor
public class AskThread implements Runnable{
private CompletableFuture<Integer> re = null;
public void run() {
int myRe = 0;
try {
myRe = re.get() * re.get();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(myRe);
}
public static void main(String[] args) throws InterruptedException {
final CompletableFuture<Integer> future = new CompletableFuture<>();
new Thread(new AskThread(future)).start();
//模拟长时间的计算过程
Thread.sleep(1000);
//告知完成结果
future.complete(60);
}
}
在该示例中,启动一个线程,此时 AskThread 对象还没有拿到它需要的数据,执行到 myRe = re.get() * re.get() 会阻塞。我们用休眠 1 秒来模拟一个长时间的计算过程,并将计算结果告诉 future 执行结果,AskThread 线程将会继续执行。
执行结果:
“3600 (此处是一个整数)
”
public class Calc {
public static Integer calc(Integer para) {
try {
//模拟一个长时间的执行
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return para * para;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
final CompletableFuture<Void> future = CompletableFuture.supplyAsync(() -> calc(50))
.thenApply((i) -> Integer.toString(i))
.thenApply((str) -> "\"" + str + "\"")
.thenAccept(System.out::println);
future.get();
}
}
CompletableFuture.supplyAsync 方法构造一个 CompletableFuture 实例,在 supplyAsync() 方法中,它会在一个新线程中,执行传入的参数。在这里它会执行 calc() 方法,这个方法可能是比较慢的,但这并不影响 CompletableFuture 实例的构造速度,supplyAsync() 会立即返回。而返回的 CompletableFuture 实例就可以作为这次调用的契约,在将来任何场合,用于获得最终的计算结果。supplyAsync 用于提供返回值的情况,CompletableFuture 还有一个不需要返回值的异步调用方法 runAsync(Runnable runnable), 一般我们在优化 Controller 时,使用这个方法比较多。这两个方法如果在不指定线程池的情况下,都是在 ForkJoinPool.common 线程池中执行,而这个线程池中的所有线程都是 Daemon(守护)线程,所以,当主线程结束时,这些线程无论执行完毕都会退出系统。
运行结果:
“"2500" (由于流式计算,此处是字符串)
”
这样,我们就可以在我们的 Controller 要异步执行的代码写成一个方法,放到 CompletableFuture 中,比如
CompletableFuture.runAsync(() ->
this.afterBetProcessor(betRequest,betDetailResult,appUser,id)
);
最后就是增加内嵌 Tomcat 的最大连接数,此处只针对 Springboot 2 的,Springboot 1.x 的是不同的,请勿使用。
首先在资源文件中注释掉 Server 下的配置
#server:
# port: 8005
# servlet:
# context-path: /api-g
写一个专门的 tomcat 的配置类
@Configuration
public class TomcatConfig {
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory tomcatFactory = new TomcatServletWebServerFactory();
tomcatFactory.addConnectorCustomizers(new MyTomcatConnectorCustomizer());
tomcatFactory.setPort(8005);
tomcatFactory.setContextPath("/api-g");
return tomcatFactory;
}
class MyTomcatConnectorCustomizer implements TomcatConnectorCustomizer {
public void customize(Connector connector) {
Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
//设置最大连接数
protocol.setMaxConnections(20000);
//设置最大线程数
protocol.setMaxThreads(2000);
protocol.setConnectionTimeout(30000);
}
}
}
经过以上的优化,Controller 的吞吐量大概可以提升 2 到 3 倍。
作者:算法之名
来源链接:
https://my.oschina.net/u/3768341/blog/3001731