使用 JDK19 虚拟线程实现5百万持久连接

使用Project Loom 虚拟线程实现5M 持久连接| 黑客新闻

记得十几年前 erlang 火的时候,单机 20万连接、100万连接不停刷新,后来 golang 也开始炫耀,抛开连结构化错误处理都没有不谈 golang 还是挺不错的,尤其是 channel 设计的很棒。多年以后 Java 终于也可以飞一把了。和 erlang/golang 一样,今后 Java 世界的网络编程模型将变得非常简单,一个请求一个线程、一个任务一个线程还不简单吗?

这么多年各种回调带来的折腾尤其式丑陋的 reactive 终于没有理由再活着了。byebye reactive,byebye vertx!

受益的还有 graal 带的一堆脚本,比如 js, python, groovy 等等,都可以放飞自我了,当然我的 d2js 也可以放飞一把,想想已经太久没有摸服务器端了。

跑 500万连接的 Echo 服务器代码,和 erlang 一个样

// https://github.com/ebarlas/project-loom-c5m/blob/main/src/main/java/loomtest/EchoServer.java
package loomtest;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.StandardSocketOptions;
import java.time.Duration;
import java.util.concurrent.atomic.LongAdder;

public class EchoServer {

    final Args args;
    final LongAdder connections;
    final LongAdder messages;

    EchoServer(Args args) {
        this.args = args;
        connections = new LongAdder();
        messages = new LongAdder();
    }

    void run() throws InterruptedException {
        for (int i = 0; i < args.portCount; i++) {
            int port = args.port + i;
            Thread.startVirtualThread(() -> serve(port));
        }
        long start = System.nanoTime();
        while (true) {
            long elapsed = Duration.ofNanos(System.nanoTime() - start).toMillis();
            System.out.printf("[%d] connections=%d, messages=%d\n", elapsed, connections.sum(), messages.sum());
            Thread.sleep(1_000);
        }
    }

    void serve(int port) {
        try (ServerSocket serverSocket = new ServerSocket(port, args.backlog, InetAddress.getByName(args.host))) {
            serverSocket.setOption(StandardSocketOptions.SO_REUSEADDR, true);
            serverSocket.setOption(StandardSocketOptions.SO_REUSEPORT, true);
            while (true) {
                Socket socket = serverSocket.accept();
                connections.increment();
                Thread.startVirtualThread(() -> handle(socket));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void handle(Socket socket) {
        try (Socket s = socket) {
            byte[] buffer = new byte[args.bufferSize];
            InputStream in = s.getInputStream();
            OutputStream out = s.getOutputStream();
            while (true) {
                int numBytes = in.read(buffer);
                if (numBytes < 0) {
                    break;
                }
                out.write(buffer, 0, numBytes);
                messages.increment();
            }
        } catch (Exception ignore) {
            // auto-close
        } finally {
            connections.decrement();
        }
    }

    record Args(String host, int port, int portCount, int backlog, int bufferSize) {
        static Args parse(String[] args) {
            return new Args(
                    args.length >= 1 ? args[0] : "0.0.0.0",
                    args.length >= 2 ? Integer.parseInt(args[1]) : 8000,
                    args.length >= 3 ? Integer.parseInt(args[2]) : 1,
                    args.length >= 4 ? Integer.parseInt(args[3]) : 0,
                    args.length >= 5 ? Integer.parseInt(args[4]) : 32);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Args a = Args.parse(args);
        System.out.println(a);
        new EchoServer(a).run();
    }

}
posted @ 2022-10-19 16:41  Inshua  阅读(180)  评论(0编辑  收藏  举报