最近在想如何提高网关的性能。主要在思考两个问题

  1. IO 线程中被接口加解密占用时间太长
  2. IO 线程和其他线程争抢CPU

IO 线程中被接口加解密占用时间太长

先来说说为什么会有这个问题。

假设有两个业务接口 API-A 和 API-B-encrypted 。其中A只需要转发路由功能,API-B-encrypted 接口需要先解密再转发。 而每次解密需要消耗200ms左右。这样的话 API-B-encrypted 的流量较大时,就会导致 API-A的吞吐量急剧下降

针对这个问题比较好办

  1. 把 API-B-encrypted 的解密操作丢到另一个线程池里去,解密完了再回到 IO 线程。
  2. 针对 API-B-encrypted 设置限流,预定好它的阈值

IO 线程和其他线程争抢CPU

做完上面这些,问题基本解决了,但还是有点小瑕疵。就是IO和解密线程会争抢CPU,线程上下文的切换太多会导致CPU高和接口响应延迟不稳定。

首先想到的办法当然是设置线程优先级,应该优先保证网关整体的性能和吞吐量不受个别接口的影响。

但作为一个爱较真的人,即使设置了优先级,CPU core依然是公用的,它还是存在CPU争抢。于是就像,能否让IO和解密绑定在不同的CPU core上?

到这里有两个思路

  1. 解密动作放在专用的解密服务里,网关异步调用解密服务,解密后再继续转发后端
  2. 通过技术手段,限制线程只能在某个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