From 0dc6a1d3a1b85d89866297f7fdbb623ae37fdf98 Mon Sep 17 00:00:00 2001 From: Simon Date: Thu, 23 Apr 2026 13:58:29 +0200 Subject: [PATCH 1/2] improve accept loop and clean logs --- .../main/java/unknow/server/nio/NIOServer.java | 14 +++++++------- .../main/java/unknow/server/nio/NIOWorker.java | 18 ++++++++++++++++-- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/unknow-server-nio/src/main/java/unknow/server/nio/NIOServer.java b/unknow-server-nio/src/main/java/unknow/server/nio/NIOServer.java index d64229bf..f267b809 100644 --- a/unknow-server-nio/src/main/java/unknow/server/nio/NIOServer.java +++ b/unknow-server-nio/src/main/java/unknow/server/nio/NIOServer.java @@ -65,20 +65,20 @@ protected void onStartup() { @SuppressWarnings("resource") @Override protected void selected(long now, SelectionKey key) throws IOException, InterruptedException { + ConnectionFactory factory = (ConnectionFactory) key.attachment(); + ServerSocketChannel ssc = (ServerSocketChannel) key.channel(); SocketChannel socket = null; - try { - ConnectionFactory factory = (ConnectionFactory) key.attachment(); - socket = ((ServerSocketChannel) key.channel()).accept(); - workers.register(socket, factory); - } catch (IOException e) { - if (socket != null) { + while ((socket = ssc.accept()) != null) { + try { + workers.register(socket, factory); + } catch (IOException e) { try { socket.close(); } catch (IOException ex) { e.addSuppressed(ex); } + logger.warn("Failed to accept", e); } - logger.warn("Failed to accept", e); } } diff --git a/unknow-server-nio/src/main/java/unknow/server/nio/NIOWorker.java b/unknow-server-nio/src/main/java/unknow/server/nio/NIOWorker.java index 9d96d3e7..59b71818 100644 --- a/unknow-server-nio/src/main/java/unknow/server/nio/NIOWorker.java +++ b/unknow-server-nio/src/main/java/unknow/server/nio/NIOWorker.java @@ -166,7 +166,7 @@ protected final void selected(long now, SelectionKey key) throws IOException { try { doWrite(co, now); } catch (Exception e) { - if (co.next != co) + if (co.next != co && !isConnectionIssue(e)) logger.error("failed to write {}", co, e); startClose(co, now); key.cancel(); @@ -180,7 +180,7 @@ protected final void selected(long now, SelectionKey key) throws IOException { try { doRead(co, now); } catch (Exception e) { - if (co.next != co) + if (co.next != co && !isConnectionIssue(e)) logger.error("failed to read {}", co, e); startClose(co, now); } finally { @@ -189,6 +189,20 @@ protected final void selected(long now, SelectionKey key) throws IOException { } } + private static boolean isConnectionIssue(Exception e) { + if (e instanceof java.net.SocketException) + return true; + if (e instanceof java.nio.channels.ClosedChannelException) + return true; + + String msg = e.getMessage(); + if (msg == null) + return false; + + msg = msg.toLowerCase(); + return msg.contains("connection reset") || msg.contains("broken pipe") || msg.contains("forcibly closed"); + } + private void doRead(NIOConnection co, long now) throws IOException { int i = 0; while (i++ < MAX_READS) { From 651edf3842caeff5f1bf08833e79c4921c1458cb Mon Sep 17 00:00:00 2001 From: Unknow Date: Thu, 23 Apr 2026 22:16:39 +0200 Subject: [PATCH 2/2] update roundrobin to remove synchronization --- .../unknow/server/nio/NIOServerBuilder.java | 5 +- .../java/unknow/server/nio/NIOWorkers.java | 47 ---------------- .../server/nio/worker/AbstractNIOWorkers.java | 54 +++++++++++++++++++ .../unknow/server/nio/worker/RoundRobin.java | 50 +++++++++++++++++ 4 files changed, 107 insertions(+), 49 deletions(-) create mode 100644 unknow-server-nio/src/main/java/unknow/server/nio/worker/AbstractNIOWorkers.java create mode 100644 unknow-server-nio/src/main/java/unknow/server/nio/worker/RoundRobin.java diff --git a/unknow-server-nio/src/main/java/unknow/server/nio/NIOServerBuilder.java b/unknow-server-nio/src/main/java/unknow/server/nio/NIOServerBuilder.java index 081f0ce9..56e40ece 100644 --- a/unknow-server-nio/src/main/java/unknow/server/nio/NIOServerBuilder.java +++ b/unknow-server-nio/src/main/java/unknow/server/nio/NIOServerBuilder.java @@ -26,7 +26,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import unknow.server.nio.NIOWorkers.RoundRobin; +import unknow.server.nio.worker.RoundRobin; /** builder for the an NIOServer */ public class NIOServerBuilder { @@ -180,6 +180,7 @@ public final NIOServer build(String... arg) throws Exception { return server; } + @SuppressWarnings("resource") private NIOWorkers createWorkers(int i, long selectTime, long closingTime, NIOServerListener l) throws IOException { ExecutorService executor = getExecutor(); if (i == 1) @@ -187,7 +188,7 @@ private NIOWorkers createWorkers(int i, long selectTime, long closingTime, NIOSe NIOWorker[] w = new NIOWorker[i]; while (i > 0) w[--i] = new NIOWorker(i, executor, l, selectTime, closingTime); - return new RoundRobin(w); + return RoundRobin.create(w); } protected ExecutorService getExecutor() { diff --git a/unknow-server-nio/src/main/java/unknow/server/nio/NIOWorkers.java b/unknow-server-nio/src/main/java/unknow/server/nio/NIOWorkers.java index 6eb1540c..10c99881 100644 --- a/unknow-server-nio/src/main/java/unknow/server/nio/NIOWorkers.java +++ b/unknow-server-nio/src/main/java/unknow/server/nio/NIOWorkers.java @@ -5,7 +5,6 @@ import java.io.IOException; import java.nio.channels.SocketChannel; -import java.util.Arrays; import java.util.Collection; import unknow.server.nio.NIOServer.ConnectionFactory; @@ -44,50 +43,4 @@ public interface NIOWorkers { * @return the workers */ Collection workers(); - - /** - * socket will register between workers in round robin - * - * @author unknow - */ - public static class RoundRobin implements NIOWorkers { - private final NIOWorker[] w; - private int o; - - /** @param workers the workers */ - public RoundRobin(NIOWorker[] workers) { - this.w = workers; - this.o = 0; - } - - @Override - public synchronized void register(SocketChannel socket, ConnectionFactory factory) throws IOException { - w[o++].register(socket, factory); - if (o == w.length) - o = 0; - } - - @Override - public void start() { - for (int i = 0; i < w.length; i++) - w[i].start(); - } - - @Override - public void stop() { - for (int i = 0; i < w.length; i++) - w[i].stop(); - } - - @Override - public void await() { - for (int i = 0; i < w.length; i++) - w[i].await(); - } - - @Override - public Collection workers() { - return Arrays.asList(w); - } - } } diff --git a/unknow-server-nio/src/main/java/unknow/server/nio/worker/AbstractNIOWorkers.java b/unknow-server-nio/src/main/java/unknow/server/nio/worker/AbstractNIOWorkers.java new file mode 100644 index 00000000..c477c49b --- /dev/null +++ b/unknow-server-nio/src/main/java/unknow/server/nio/worker/AbstractNIOWorkers.java @@ -0,0 +1,54 @@ +package unknow.server.nio.worker; + +import java.io.IOException; +import java.nio.channels.SocketChannel; +import java.util.Arrays; +import java.util.Collection; + +import unknow.server.nio.NIOServer.ConnectionFactory; +import unknow.server.nio.NIOWorker; +import unknow.server.nio.NIOWorkers; + +/** + * socket will register between workers in round robin + * + * @author unknow + */ +public abstract class AbstractNIOWorkers implements NIOWorkers { + protected final NIOWorker[] w; + + /** @param workers the workers */ + protected AbstractNIOWorkers(NIOWorker[] workers) { + this.w = workers; + } + + protected abstract NIOWorker next(); + + @Override + public synchronized void register(SocketChannel socket, ConnectionFactory factory) throws IOException { + next().register(socket, factory); + } + + @Override + public void start() { + for (int i = 0; i < w.length; i++) + w[i].start(); + } + + @Override + public void stop() { + for (int i = 0; i < w.length; i++) + w[i].stop(); + } + + @Override + public void await() { + for (int i = 0; i < w.length; i++) + w[i].await(); + } + + @Override + public Collection workers() { + return Arrays.asList(w); + } +} \ No newline at end of file diff --git a/unknow-server-nio/src/main/java/unknow/server/nio/worker/RoundRobin.java b/unknow-server-nio/src/main/java/unknow/server/nio/worker/RoundRobin.java new file mode 100644 index 00000000..dd658304 --- /dev/null +++ b/unknow-server-nio/src/main/java/unknow/server/nio/worker/RoundRobin.java @@ -0,0 +1,50 @@ +package unknow.server.nio.worker; + +import java.util.concurrent.atomic.AtomicInteger; + +import unknow.server.nio.NIOWorker; +import unknow.server.nio.NIOWorkers; + +/** + * socket will register between workers in round robin + * + * @author unknow + */ + +public final class RoundRobin extends AbstractNIOWorkers implements NIOWorkers { + private final AtomicInteger o; + + /** @param workers the workers */ + private RoundRobin(NIOWorker[] workers) { + super(workers); + this.o = new AtomicInteger(0); + } + + public static NIOWorkers create(NIOWorker[] w) { + if ((w.length & (w.length - 1)) == 0) + return new RoundRobinPow(w); + return new RoundRobin(w); + } + + @Override + protected NIOWorker next() { + return w[o.getAndIncrement() % w.length]; + } + + public static final class RoundRobinPow extends AbstractNIOWorkers implements NIOWorkers { + private final AtomicInteger o; + private final int m; + + /** @param workers the workers */ + private RoundRobinPow(NIOWorker[] workers) { + super(workers); + this.o = new AtomicInteger(0); + this.m = workers.length - 1; + } + + @Override + protected NIOWorker next() { + return w[o.getAndIncrement() & m]; + } + } +} \ No newline at end of file