未完待续

背景

朋友有这样的一个需求,在5分钟发起200万次请求 ,就尝试一下

简单计算下

  1. 2000000 / 300 大概每秒 7000次请求 同步请求无法
  2. 按照每个API 20字节 网络流量是 20 * 7000 140k

    技术方案

  3. Java线程池 (试试阿里开源JDK 启用协程试试)

  4. 异步请求 (okhttp 异步调用 http线程池)
  5. 操作系统调优

代码案例

服务端代码(建议使用Go)

  1. package main
  2. import (
  3. "log"
  4. "net/http"
  5. "runtime"
  6. )
  7. const (
  8. httpPort = "8088"
  9. )
  10. func main() {
  11. runtime.GOMAXPROCS(runtime.NumCPU() - 1)
  12. hello := func(w http.ResponseWriter, req *http.Request) {
  13. data := "Hello, World!"
  14. w.Header().Add("Server", "golang")
  15. w.Write([]byte(data))
  16. return
  17. }
  18. http.HandleFunc("/", hello)
  19. err := http.ListenAndServe(":"+httpPort, nil)
  20. if err != nil {
  21. log.Fatal("ListenAndServe: ", err)
  22. }
  23. }

客户端代码

  1. <dependency>
  2. <groupId>com.squareup.okhttp3</groupId>
  3. <artifactId>okhttp</artifactId>
  4. <version>4.9.3</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.google.guava</groupId>
  8. <artifactId>guava</artifactId>
  9. <version>31.1-jre</version>
  10. </dependency>
  1. package ren.shuaipeng.oom.basic;
  2. import com.google.common.util.concurrent.ThreadFactoryBuilder;
  3. import okhttp3.*;
  4. import org.jetbrains.annotations.NotNull;
  5. import java.io.File;
  6. import java.io.IOException;
  7. import java.util.concurrent.LinkedBlockingQueue;
  8. import java.util.concurrent.ThreadPoolExecutor;
  9. import java.util.concurrent.TimeUnit;
  10. import java.util.concurrent.atomic.AtomicLong;
  11. public class Diaobao {
  12. public static OkHttpClient client = new OkHttpClient.Builder()
  13. .cache(new Cache(new File(System.getProperty("java.io.tmpdir") , "http_cache"),50L * 1024L * 1024L))
  14. .eventListenerFactory(PrintingEventListener.FACTORY)
  15. .connectTimeout(10,TimeUnit.SECONDS)
  16. .readTimeout(10,TimeUnit.SECONDS)
  17. .connectionPool(new ConnectionPool(50,5,TimeUnit.MINUTES))
  18. .build();
  19. public static void main(String[] args) {
  20. ThreadPoolExecutor executor = new ThreadPoolExecutor(
  21. Runtime.getRuntime().availableProcessors(),
  22. Runtime.getRuntime().availableProcessors() * 2,
  23. 60, TimeUnit.SECONDS,
  24. new LinkedBlockingQueue<>(1000000),
  25. new ThreadFactoryBuilder().setDaemon(true).setNameFormat("demo-pool-%d").build(),
  26. new ThreadPoolExecutor.AbortPolicy()
  27. );
  28. executor.execute(new CallApiCountTask());
  29. for (int i = 0; i< 999999;i++) {
  30. executor.execute(new CallApiTask());
  31. }
  32. executor.shutdown();
  33. }
  34. static final AtomicLong secondCount = new AtomicLong(0L);
  35. static class CallApiCountTask implements Runnable {
  36. @Override
  37. public void run() {
  38. while (true) {
  39. try {
  40. secondCount.set(0L);
  41. Thread.sleep(1000L);
  42. System.out.println(secondCount.get());
  43. } catch (InterruptedException e) {
  44. e.printStackTrace();
  45. }
  46. }
  47. }
  48. }
  49. static class CallApiTask implements Runnable {
  50. @Override
  51. public void run() {
  52. Request washingtonPostRequest = new Request.Builder()
  53. .url("http://127.0.0.1:8088")
  54. .build();
  55. client.newCall(washingtonPostRequest).enqueue(new Callback() {
  56. @Override
  57. public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
  58. String demo = response.body().string();
  59. }
  60. @Override
  61. public void onFailure(@NotNull Call call, @NotNull IOException e) {
  62. e.printStackTrace();
  63. }
  64. });
  65. }
  66. }
  67. static class PrintingEventListener extends EventListener {
  68. public static final Factory FACTORY = new Factory() {
  69. final AtomicLong nextCallId = new AtomicLong(1L);
  70. @Override public EventListener create(Call call) {
  71. long callId = nextCallId.getAndIncrement();
  72. return new PrintingEventListener(callId, System.nanoTime());
  73. }
  74. };
  75. final long callId;
  76. final long callStartNanos;
  77. public PrintingEventListener(long callId, long callStartNanos) {
  78. this.callId = callId;
  79. this.callStartNanos = callStartNanos;
  80. }
  81. private void printEvent(String name) {
  82. // System.out.printf("第 %d 个请求 时间是 %s \n", callId,System.nanoTime() );
  83. }
  84. @Override public void callStart(Call call) {
  85. printEvent("callStart");
  86. secondCount.incrementAndGet();
  87. }
  88. @Override public void callEnd(Call call) {
  89. printEvent("callEnd");
  90. }
  91. }
  92. }

测试结果

image.png
每秒发起请求数达到预想的一半,后面看看还能不能继续优化一波

对比Jmeter 就3个线程就搞了这么多
image.png

参考资料