Day707.Jetty性能调优的思路 -深入拆解 Tomcat & Jetty
Posted 阿昌喜欢吃黄桃
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day707.Jetty性能调优的思路 -深入拆解 Tomcat & Jetty相关的知识,希望对你有一定的参考价值。
Jetty性能调优的思路
Hi,我是阿昌
,今天学习记录的是关于Jetty性能调优的思路
一、操作系统层面调优
对于 Linux 操作系统调优来说,我们需要加大一些默认的限制值,这些参数主要可以在/etc/security/limits.conf
中或通过sysctl命令
进行配置,其实这些配置对于 Tomcat 来说也是适用的,下面我来详细介绍一下这些参数。
1、TCP 缓冲区大小
TCP 的发送和接收缓冲区最好加大到 16MB,可以通过下面的命令配置:
sysctl -w net.core.rmem_max = 16777216
sysctl -w net.core.wmem_max = 16777216
sysctl -w net.ipv4.tcp_rmem =“4096 87380 16777216”
sysctl -w net.ipv4.tcp_wmem =“4096 16384 16777216”
2、TCP 队列大小
net.core.somaxconn
控制 TCP 连接队列的大小,默认值为 128,在高并发情况下明显不够用,会出现拒绝连接的错误。
但是这个值也不能调得过高,因为过多积压的 TCP 连接会消耗服务端的资源,并且会造成请求处理的延迟,给用户带来不好的体验。
因此建议适当调大,推荐设置为 4096。
sysctl -w net.core.somaxconn = 4096
net.core.netdev_max_backlog
用来控制 Java 程序传入数据包队列的大小,可以适当调大。
sysctl -w net.core.netdev_max_backlog = 16384
sysctl -w net.ipv4.tcp_max_syn_backlog = 8192
sysctl -w net.ipv4.tcp_syncookies = 1
3、端口
如果 Web 应用程序作为客户端向远程服务器建立了很多 TCP 连接,可能会出现 TCP 端口
不足的情况。
因此最好增加使用的端口范围,并允许在 TIME_WAIT 中重用套接字:
sysctl -w net.ipv4.ip_local_port_range =“1024 65535”
sysctl -w net.ipv4.tcp_tw_recycle = 1
4、文件句柄数
高负载服务器的文件句柄数
很容易耗尽,这是因为系统默认值通常比较低,我们可以在/etc/security/limits.conf
中为特定用户增加文件句柄数:
用户名 hard nofile 40000
用户名 soft nofile 40000
5、拥塞控制
Linux 内核支持可插拔的拥塞控制算法
,如果要获取内核可用的拥塞控制算法列表,可以通过下面的命令:
sysctl net.ipv4.tcp_available_congestion_control
这里我推荐将拥塞控制算法设置为 cubic
:
sysctl -w net.ipv4.tcp_congestion_control = cubic
二、Jetty 本身的调优
Jetty 本身的调优,主要是设置不同类型的线程的数量,包括 Acceptor
和 Thread Pool
。
1、Acceptors
Acceptor 的个数 accepts 应该设置为大于等于 1,并且小于等于 CPU 核数。
2、Thread Pool
限制 Jetty 的任务队列非常重要。
默认情况下,队列是无限的!
因此,如果在高负载下超过 Web 应用的处理能力,Jetty 将在队列上积压大量待处理的请求。并且即使负载高峰过去了,Jetty 也不能正常响应新的请求,这是因为仍然有很多请求在队列等着被处理。
因此对于一个高可靠性的系统,我们应该通过使用有界队列立即拒绝过多的请求(也叫快速失败)。那队列的长度设置成多大呢,应该根据 Web 应用的处理速度而定。
比如,如果 Web 应用每秒可以处理 100 个请求,当负载高峰到来,我们允许一个请求可以在队列积压 60 秒,那么我们就可以把队列长度设置为 60 × 100 = 6000。如果设置得太低,Jetty 将很快拒绝请求,无法处理正常的高峰负载,以下是配置示例:
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Set name="ThreadPool">
<New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
<!-- specify a bounded queue -->
<Arg>
<New class="java.util.concurrent.ArrayBlockingQueue">
<Arg type="int">6000</Arg>
</New>
</Arg>
<Set name="minThreads">10</Set>
<Set name="maxThreads">200</Set>
<Set name="detailedDump">false</Set>
</New>
</Set>
</Configure>
那如何配置 Jetty 的线程池中的线程数呢?
跟 Tomcat 一样,你可以根据实际压测,如果 I/O 越密集,线程阻塞越严重,那么线程数就可以配置多一些。
通常情况,增加线程数需要更多的内存,因此内存的最大值也要跟着调整,所以一般来说,Jetty 的最大线程数应该在 50 到 500 之间。
三、Jetty 性能测试
通过一个实验来测试一下 Jetty 的性能。我们可以在这里下载 Jetty 的 JAR 包。
第二步我们创建一个 Handler,这个 Handler 用来向客户端返回“Hello World”,并实现一个 main 方法,根据传入的参数创建相应数量的线程池。
public class HelloWorld extends AbstractHandler
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
response.setContentType("text/html; charset=utf-8");
response.setStatus(HttpServletResponse.SC_OK);
response.getWriter().println("<h1>Hello World</h1>");
baseRequest.setHandled(true);
public static void main(String[] args) throws Exception
//根据传入的参数控制线程池中最大线程数的大小
int maxThreads = Integer.parseInt(args[0]);
System.out.println("maxThreads:" + maxThreads);
//创建线程池
QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setMaxThreads(maxThreads);
Server server = new Server(threadPool);
ServerConnector http = new ServerConnector(server,
new HttpConnectionFactory(new HttpConfiguration()));
http.setPort(8000);
server.addConnector(http);
server.start();
server.join();
第三步,我们编译这个 Handler,得到 HelloWorld.class。
javac -cp jetty.jar HelloWorld.java
第四步,启动 Jetty server,并且指定最大线程数为 4。
java -cp .:jetty.jar HelloWorld 4
第五步,启动压测工具 Apache Bench
。关于 Apache Bench 的使用,参考这里。
ab -n 200000 -c 100 http://localhost:8000/
上面命令的意思是向 Jetty server 发出 20 万个请求
,开启 100 个线程
同时发送。经过多次压测,测试结果稳定以后,在 Linux 4 核
机器上得到的结果是这样的:
从上面的测试结果我们可以看到,20 万个请求在 9.99 秒内处理完成,RPS 达到了 20020。
不知道你是否好奇,为什么我把最大线程数设置为 4 呢?是不是有点小?
接下来试着逐步加大最大线程数,直到找到最佳值。
下面这个表格显示了在其他条件不变的情况下,只调整线程数对 RPS
的影响。
一个有意思的现象,线程数从 4 增加到 6,RPS 确实增加了。
但是线程数从 6 开始继续增加,RPS 不但没有跟着上升,反而下降了,而且线程数越多,RPS 越低。
发生这个现象的原因是,测试机器的 CPU 只有 4 核,而我们测试的程序做得事情比较简单,没有 I/O 阻塞,属于 CPU 密集型程序。对于这种程序,最大线程数可以设置为比 CPU 核心稍微大一点点。
那具体设置成多少是最佳值呢,我们需要根据实验里的步骤反复测试。
在我们这个实验中,当最大线程数为 6,也就 CPU 核数的 1.5 倍
时,性能达到最佳。
四、总结
学习了 Jetty 调优的基本思路,主要分为·操作系统级别·的调优和 ·Jetty 本身·的调优,其中操作系统级别也适用于 Tomcat。
通过一个实例来寻找 Jetty 的最佳线程数,在测试中我们发现,对于 ·CPU 密集型·应用,将·最大线程数设置 CPU 核数的 1.5 倍是最佳的·。
因此,在我们的实际工作中,切勿将线程池直接设置得很大,因为程序所需要的线程数可能会比我们想象的要小。
上面说到,Jetty 的最大线程数应该在 50 到 500 之间。但是我们的实验中测试发现,最大线程数为 6 时最佳,这是不是矛盾了?
不矛盾,这个案例里面没有IO操作。
有IO操作的时候,用这个公式:(线程IO阻塞时间+线程CPU时间) / 线程CPU时间
以上是关于Day707.Jetty性能调优的思路 -深入拆解 Tomcat & Jetty的主要内容,如果未能解决你的问题,请参考以下文章