最近在想如何提高网关的性能。主要在思考两个问题
- IO 线程中被接口加解密占用时间太长
- IO 线程和其他线程争抢CPU
IO 线程中被接口加解密占用时间太长
先来说说为什么会有这个问题。
假设有两个业务接口 API-A 和 API-B-encrypted 。其中A只需要转发路由功能,API-B-encrypted 接口需要先解密再转发。 而每次解密需要消耗200ms左右。这样的话 API-B-encrypted 的流量较大时,就会导致 API-A的吞吐量急剧下降
针对这个问题比较好办
- 把 API-B-encrypted 的解密操作丢到另一个线程池里去,解密完了再回到 IO 线程。
- 针对 API-B-encrypted 设置限流,预定好它的阈值
IO 线程和其他线程争抢CPU
做完上面这些,问题基本解决了,但还是有点小瑕疵。就是IO和解密线程会争抢CPU,线程上下文的切换太多会导致CPU高和接口响应延迟不稳定。
首先想到的办法当然是设置线程优先级,应该优先保证网关整体的性能和吞吐量不受个别接口的影响。
但作为一个爱较真的人,即使设置了优先级,CPU core依然是公用的,它还是存在CPU争抢。于是就像,能否让IO和解密绑定在不同的CPU core上?
到这里有两个思路
- 解密动作放在专用的解密服务里,网关异步调用解密服务,解密后再继续转发后端
- 通过技术手段,限制线程只能在某个core上执行
今天不说第一个,第一个在现在的场景下有点复杂。今天主要来实验第二个办法。我在 Gayhub 上找到了这个项目Java-Thread-Affinity,可以拿来试试(这是我fork别人的)。
下面看看代码怎么验证,在例子中,我们指定两个线程t0和t1分别绑定在不同的cpu上,然后打印出它运行时所在的cpu编号
package com.wangsz.api.dudugou;
import net.openhft.affinity.Affinity;
import org.junit.jupiter.api.Test;
import java.util.Random;
public class ThreadAffinityTest {
@Test
public void testManual() throws InterruptedException {
int cpus = Runtime.getRuntime().availableProcessors();
Random random=new Random();
int cpuForTask0=random.nextInt(cpus);
int cpuForTask1=random.nextInt(cpus);
printCpuOnThread("main");
Thread t0 = new Thread(new MyTask(cpuForTask0, "task0"));
Thread t1 = new Thread(new MyTask(cpuForTask1, "task1"));
t0.start();
t1.start();
t0.join();
t1.join();
}
static class MyTask implements Runnable{
public MyTask(int cpu, String name) {
this.cpu = cpu;
this.name = name;
}
int cpu;
String name;
@Override
public void run() {
Affinity.setAffinity(cpu);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
printCpuOnThread(name);
}
}
static void printCpuOnThread(String name){
System.out.println(name +" is running on thread"+Thread.currentThread().toString()+" with cpu "+Affinity.getCpu());
}
}
一定要在linux上运行哦,在我的win10笔记本上运行没效果。可以看到task0和task1分别在不同的cpu上执行了。
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.wangsz.api.dudugou.ThreadAffinityTest
main is running on threadThread[main,5,main] with cpu 0
task0 is running on threadThread[Thread-1,5,main] with cpu 2
task1 is running on threadThread[Thread-2,5,main] with cpu 3
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.697 s - in com.wangsz.api.dudugou.ThreadAffinityTest