REST API 响应时间在负载下增加 [Tomcat]
REST API Response time increases under load [Tomcat]
我们有一个 REST API (GET) 可以被很多移动用户同时调用。我们目前的用户群约为 30 万,但预计将增长约 100 万。
API很简单。它使用 Akka 发出 3 个并行请求,并且 returns 是组合结果。主要代码如下所示:
Future<List<CardDTO>> pnrFuture = null;
Future<List<CardDTO>> newsFuture = null;
ExecutionContext ec = ExecutionContexts.fromExecutorService(executor);
final List<CardDTO> combinedDTOs = new ArrayList<CardDTO>();
// Array list of futures
List<Future<List<CardDTO>>> futures = new ArrayList<Future<List<CardDTO>>>();
futures.add(future(new PNRFuture(pnrService, userId), ec));
futures.add(future(new NewsFuture(newsService, userId), ec));
futures.add(future(new SettingsFuture(userPreferenceManager, userId), ec));
Future<Iterable<List<CardDTO>>> futuresSequence = sequence(futures, ec);
// combine the cards
Future<List<CardDTO>> futureSum = futuresSequence.map(
new Mapper<Iterable<List<CardDTO>>, List<CardDTO>>() {
@Override
public List<CardDTO> apply(Iterable<List<CardDTO>> allDTOs) {
for (List<CardDTO> cardDTOs : allDTOs) {
if(cardDTOs!=null)
combinedDTOs.addAll(cardDTOs);
}
Collections.sort(combinedDTOs);
return combinedDTOs;
}
}
);
Await.result(futureSum, Duration.Inf());
return combinedDTOs;
这 3 个期货是简单的 select 来自我的 SQL 数据库的语句,执行时间不到一毫秒。我们在这里使用 Spring + Hibernate。
整个API平均需要50毫秒才能return结果。
现在,当我们对 3 台服务器进行性能测试时,我们得出的结论是,在大约 200 个请求/秒后,API 的响应时间开始线性增加。它在负载下高达 3 -5 秒。令人惊讶的是,CPU 当时的使用率约为 20%,并且 JVM 内存中没有发生任何重大变化。内存使用量约为 700 MB。我们有 16 GB
我找不到瓶颈在哪里。我怎样才能将这个 API 扩展到至少 1000 个请求/秒。我至少在寻找从哪里开始的指示。我探索了 top
、 visualvm
等工具,但没有发现任何令人担忧的东西。
这是我们在 Java 7
上的 JVM 设置
export JAVA_OPTS="$JAVA_OPTS -Djava.awt.headless=true -server -Xms4g
-Xmx16g -XX:MaxPermSize=1g -XX:PermSize=512m -XX:MaxNewSize=4g -XX:NewSize=512m -XX:SurvivorRatio=16 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=0 -XX:CMSInitiatingOccupancyFraction=60 -XX:+CMSParallelRemarkEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:ParallelGCThreads=12 -XX:LargePageSizeInBytes=256m -Dspring.profiles.active=staging -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9899 -Djava.rmi.server.hostname=$HOSTNAME -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"
这些问题我都看过了,看来是大势所趋。切换到 node.js 或 Erlang 等其他框架会有帮助吗?
Response time Increases as concurrency increases in Java
Tomcat response time is increasing as concurrency is increased in apache bench
无法确定您的性能问题出在哪里,但我发现一般是因为(根据您的描述):
- 存在争用问题的线程或连接池(数据库连接池或 tomcat 请求池)
- 同步的 variable/code 或 BlockingQueue(可以是上述的超集)。
- 错误的负载平衡器或配置
- 网络不好
我建议你做的是尽可能地隔离。先证明不是数据库连接池。那是 运行 相同的并发负载,但只执行数据库部分。请记住,三台服务器需要 3 个连接。
接下来 运行 1-3 个服务器在不使用负载平衡器和 w/o 进行任何数据处理的情况下进行模拟响应。您会惊讶于负载平衡器/网络导致问题的频率。
继续分离事物。测试、观察、重复。
最后,当你把它真正隔离开来时 Tomcat 你可能想读一下 Netflix does。
我们有一个 REST API (GET) 可以被很多移动用户同时调用。我们目前的用户群约为 30 万,但预计将增长约 100 万。
API很简单。它使用 Akka 发出 3 个并行请求,并且 returns 是组合结果。主要代码如下所示:
Future<List<CardDTO>> pnrFuture = null;
Future<List<CardDTO>> newsFuture = null;
ExecutionContext ec = ExecutionContexts.fromExecutorService(executor);
final List<CardDTO> combinedDTOs = new ArrayList<CardDTO>();
// Array list of futures
List<Future<List<CardDTO>>> futures = new ArrayList<Future<List<CardDTO>>>();
futures.add(future(new PNRFuture(pnrService, userId), ec));
futures.add(future(new NewsFuture(newsService, userId), ec));
futures.add(future(new SettingsFuture(userPreferenceManager, userId), ec));
Future<Iterable<List<CardDTO>>> futuresSequence = sequence(futures, ec);
// combine the cards
Future<List<CardDTO>> futureSum = futuresSequence.map(
new Mapper<Iterable<List<CardDTO>>, List<CardDTO>>() {
@Override
public List<CardDTO> apply(Iterable<List<CardDTO>> allDTOs) {
for (List<CardDTO> cardDTOs : allDTOs) {
if(cardDTOs!=null)
combinedDTOs.addAll(cardDTOs);
}
Collections.sort(combinedDTOs);
return combinedDTOs;
}
}
);
Await.result(futureSum, Duration.Inf());
return combinedDTOs;
这 3 个期货是简单的 select 来自我的 SQL 数据库的语句,执行时间不到一毫秒。我们在这里使用 Spring + Hibernate。
整个API平均需要50毫秒才能return结果。
现在,当我们对 3 台服务器进行性能测试时,我们得出的结论是,在大约 200 个请求/秒后,API 的响应时间开始线性增加。它在负载下高达 3 -5 秒。令人惊讶的是,CPU 当时的使用率约为 20%,并且 JVM 内存中没有发生任何重大变化。内存使用量约为 700 MB。我们有 16 GB
我找不到瓶颈在哪里。我怎样才能将这个 API 扩展到至少 1000 个请求/秒。我至少在寻找从哪里开始的指示。我探索了 top
、 visualvm
等工具,但没有发现任何令人担忧的东西。
这是我们在 Java 7
上的 JVM 设置export JAVA_OPTS="$JAVA_OPTS -Djava.awt.headless=true -server -Xms4g -Xmx16g -XX:MaxPermSize=1g -XX:PermSize=512m -XX:MaxNewSize=4g -XX:NewSize=512m -XX:SurvivorRatio=16 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=0 -XX:CMSInitiatingOccupancyFraction=60 -XX:+CMSParallelRemarkEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:ParallelGCThreads=12 -XX:LargePageSizeInBytes=256m -Dspring.profiles.active=staging -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9899 -Djava.rmi.server.hostname=$HOSTNAME -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false"
这些问题我都看过了,看来是大势所趋。切换到 node.js 或 Erlang 等其他框架会有帮助吗?
Response time Increases as concurrency increases in Java
Tomcat response time is increasing as concurrency is increased in apache bench
无法确定您的性能问题出在哪里,但我发现一般是因为(根据您的描述):
- 存在争用问题的线程或连接池(数据库连接池或 tomcat 请求池)
- 同步的 variable/code 或 BlockingQueue(可以是上述的超集)。
- 错误的负载平衡器或配置
- 网络不好
我建议你做的是尽可能地隔离。先证明不是数据库连接池。那是 运行 相同的并发负载,但只执行数据库部分。请记住,三台服务器需要 3 个连接。
接下来 运行 1-3 个服务器在不使用负载平衡器和 w/o 进行任何数据处理的情况下进行模拟响应。您会惊讶于负载平衡器/网络导致问题的频率。
继续分离事物。测试、观察、重复。
最后,当你把它真正隔离开来时 Tomcat 你可能想读一下 Netflix does。