背景
朋友有这样的一个需求,在5分钟发起200万次请求 ,就尝试一下
简单计算下
- 2000000 / 300 大概每秒 7000次请求 同步请求无法
按照每个API 20字节 网络流量是 20 * 7000 140k
技术方案
Java线程池 (试试阿里开源JDK 启用协程试试)
- 异步请求 (okhttp 异步调用 http线程池)
- 操作系统调优
代码案例
服务端代码(建议使用Go)
package main
import (
"log"
"net/http"
"runtime"
)
const (
httpPort = "8088"
)
func main() {
runtime.GOMAXPROCS(runtime.NumCPU() - 1)
hello := func(w http.ResponseWriter, req *http.Request) {
data := "Hello, World!"
w.Header().Add("Server", "golang")
w.Write([]byte(data))
return
}
http.HandleFunc("/", hello)
err := http.ListenAndServe(":"+httpPort, nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
客户端代码
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.9.3</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
package ren.shuaipeng.oom.basic;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class Diaobao {
public static OkHttpClient client = new OkHttpClient.Builder()
.cache(new Cache(new File(System.getProperty("java.io.tmpdir") , "http_cache"),50L * 1024L * 1024L))
.eventListenerFactory(PrintingEventListener.FACTORY)
.connectTimeout(10,TimeUnit.SECONDS)
.readTimeout(10,TimeUnit.SECONDS)
.connectionPool(new ConnectionPool(50,5,TimeUnit.MINUTES))
.build();
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors() * 2,
60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000000),
new ThreadFactoryBuilder().setDaemon(true).setNameFormat("demo-pool-%d").build(),
new ThreadPoolExecutor.AbortPolicy()
);
executor.execute(new CallApiCountTask());
for (int i = 0; i< 999999;i++) {
executor.execute(new CallApiTask());
}
executor.shutdown();
}
static final AtomicLong secondCount = new AtomicLong(0L);
static class CallApiCountTask implements Runnable {
@Override
public void run() {
while (true) {
try {
secondCount.set(0L);
Thread.sleep(1000L);
System.out.println(secondCount.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class CallApiTask implements Runnable {
@Override
public void run() {
Request washingtonPostRequest = new Request.Builder()
.url("http://127.0.0.1:8088")
.build();
client.newCall(washingtonPostRequest).enqueue(new Callback() {
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
String demo = response.body().string();
}
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
e.printStackTrace();
}
});
}
}
static class PrintingEventListener extends EventListener {
public static final Factory FACTORY = new Factory() {
final AtomicLong nextCallId = new AtomicLong(1L);
@Override public EventListener create(Call call) {
long callId = nextCallId.getAndIncrement();
return new PrintingEventListener(callId, System.nanoTime());
}
};
final long callId;
final long callStartNanos;
public PrintingEventListener(long callId, long callStartNanos) {
this.callId = callId;
this.callStartNanos = callStartNanos;
}
private void printEvent(String name) {
// System.out.printf("第 %d 个请求 时间是 %s \n", callId,System.nanoTime() );
}
@Override public void callStart(Call call) {
printEvent("callStart");
secondCount.incrementAndGet();
}
@Override public void callEnd(Call call) {
printEvent("callEnd");
}
}
}
测试结果
每秒发起请求数达到预想的一半,后面看看还能不能继续优化一波
对比Jmeter 就3个线程就搞了这么多