これは、なにをしたくて書いたもの?
Nettyのio_uringを少し見てみたいな、ということで。
io_uring
まずは、io_uring自体について。
io_uringは、非同期IO用のAPIです。カーネル5.1から導入されたそうです。
https://kernel.dk/io_uring.pdf
io_uringについては、以下あたりも見てみました。
Ubuntu Manpage: io_uring - Asynchronous I/O facility
Welcome to Lord of the io_uring — Lord of the io_uring documentation
io_uringで高速IO処理(?) | κeenのHappy Hacκing Blog
io_uringの作者が書いたこちらのページが、io_uringの概要を把握するのによいかもしれません。
What is io_uring? — Lord of the io_uring documentation
どういうものかというと、以下のように2つのキューを使う仕組みのようです。
- リクエスト送信用、リクエスト完了通知用の2つのキューを持つ
- この2つのキューは、カーネルとユーザー空間で共有される
- タスク(ファイルの読み書き、クライアント接続の受け入れなど)があると、送信キューエントリー(SQE)として送信キューの末尾に追加する
- カーネルは送信されたリクエストを処理して、完了キューイベント(CQE)として完了キューの末尾に追加する
なので、io_uringという名前はカーネルとユーザー空間の間でコミュニケーションを行うリングバッファーをインターフェースとすることに
由来するようです。
The very name io_uring comes from the fact that the interfaces uses ring buffers as the main interface for kernel-user space communication.
パフォーマンス面では、カーネルとユーザー空間でキューが共有されているため以下の点が有利なようです。
- データのコピーを避けることができる
- システムコールを減らすことができる
Submission Queue Polling — Lord of the io_uring documentation
ライブラリとしては、liburingを使うことが勧められています。
Netty io_uring
Nettyのincubatorプロジェクトとして、io_uringベースのトランスポートライブラリが作られています。
GitHub - netty/netty-incubator-transport-io_uring
現時点でのバージョンは、0.0.16.Finalです。
io_uring APIを直接使っているみたいですね。
Nettyでのリリースに関するブログエントリー。
Netty.news: Netty/Incubator/Transport/Native/io_uring 0.0.1.Final released
Netty.news: Netty/Incubator/Transport/Native/io_uring 0.0.3.Final released
情報がほとんどないのですが、GitHubのREADME.md
やブログエントリーを見る限り、既存のNettyの使い方から大きく変わりそうな
雰囲気はないので、サンプルを参考にTCP Echo Server/Clientを書いてみることにしましょう。
https://github.com/netty/netty/tree/netty-4.1.85.Final/example/src/main/java/io/netty/example
https://github.com/netty/netty/tree/netty-4.1.85.Final/example/src/main/java/io/netty/example/echo
環境
今回の環境は、こちら。
Ubuntu Linux 22.04 LTSです。
$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.1 LTS Release: 22.04 Codename: jammy $ uname -srvmpio Linux 5.15.0-58-generic #64-Ubuntu SMP Thu Jan 5 11:43:13 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
カーネル5.1以上ですね。
Java。
$ java --version openjdk 17.0.5 2022-10-18 OpenJDK Runtime Environment (build 17.0.5+8-Ubuntu-2ubuntu122.04) OpenJDK 64-Bit Server VM (build 17.0.5+8-Ubuntu-2ubuntu122.04, mixed mode, sharing) $ mvn --version Apache Maven 3.8.7 (b89d5959fcde851dcb1c8946a785a163f14e1e29) Maven home: $HOME/.sdkman/candidates/maven/current Java version: 17.0.5, vendor: Private Build, runtime: /usr/lib/jvm/java-17-openjdk-amd64 Default locale: ja_JP, platform encoding: UTF-8 OS name: "linux", version: "5.15.0-58-generic", arch: "amd64", family: "unix"
準備
Maven依存関係などは、こちら。
<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> </properties> <dependencies> <dependency> <groupId>io.netty.incubator</groupId> <artifactId>netty-incubator-transport-native-io_uring</artifactId> <version>0.0.16.Final</version> <classifier>linux-x86_64</classifier> </dependency> <dependency> <groupId>io.netty</groupId> <artifactId>netty-handler</artifactId> <version>4.1.85.Final</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.2</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>3.24.2</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>3.0.0-M7</version> </plugin> </plugins> </build>
Nettyでio_uringを使うには、netty-incubator-transport-native-io_uring
があればOKです。
netty-handler
は、ログ出力で使うLoggingHandler
クラスを使うために入れています。
netty-incubator-transport-native-io_uring
を使う時には、classifier
を指定する必要があります。
<dependency> <groupId>io.netty.incubator</groupId> <artifactId>netty-incubator-transport-native-io_uring</artifactId> <version>0.0.16.Final</version> <classifier>linux-x86_64</classifier> </dependency>
現時点で指定可能なのは、linux-x86_64
とlinux-aarch_64
の2つです。
最後にテストコードも書くので、JUnitとAssertJも入れています。
サンプルコードを書く
それでは、こちらを参考にTCP Echo Server/Clientを書いてみましょう。
https://github.com/netty/netty/tree/netty-4.1.85.Final/example/src/main/java/io/netty/example/echo
サーバー側から書きます。
ハンドラー。
src/main/java/org/littlewings/netty/iouring/EchoServerHandler.java
package org.littlewings.netty.iouring; import java.nio.charset.StandardCharsets; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; @ChannelHandler.Sharable public class EchoServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { String msgAsString = ((ByteBuf) msg).toString(StandardCharsets.UTF_8); if (msgAsString.endsWith("\r\n")) { msgAsString = msgAsString.substring(0, msgAsString.length() - 2); } else if (msgAsString.endsWith("\n")) { msgAsString = msgAsString.substring(0, msgAsString.length() - 1); } System.out.printf("received message = %s%n", msgAsString); ByteBuf sendMessage = Unpooled.wrappedBuffer(String.format("★%s★", msgAsString).getBytes(StandardCharsets.UTF_8)); ctx.write(sendMessage); } @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }
メッセージを返す時には、装飾するようにしておきました。
@Override public void channelRead(ChannelHandlerContext ctx, Object msg) { String msgAsString = ((ByteBuf) msg).toString(StandardCharsets.UTF_8); if (msgAsString.endsWith("\r\n")) { msgAsString = msgAsString.substring(0, msgAsString.length() - 2); } else if (msgAsString.endsWith("\n")) { msgAsString = msgAsString.substring(0, msgAsString.length() - 1); } System.out.printf("received message = %s%n", msgAsString); ByteBuf sendMessage = Unpooled.wrappedBuffer(String.format("★%s★", msgAsString).getBytes(StandardCharsets.UTF_8)); ctx.write(sendMessage); }
Bootstrap。
src/main/java/org/littlewings/netty/iouring/IoUringEchoServer.java
package org.littlewings.netty.iouring; import java.util.concurrent.ExecutionException; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.incubator.channel.uring.IOUringEventLoopGroup; import io.netty.incubator.channel.uring.IOUringServerSocketChannel; public class IoUringEchoServer implements AutoCloseable { int port; EventLoopGroup bossGroup; EventLoopGroup workerGroup; public static void main(String... args) throws InterruptedException, ExecutionException { try (IoUringEchoServer server = IoUringEchoServer.newServer(8080)) { server.start(true); } } public static IoUringEchoServer newServer(int port) { IoUringEchoServer server = new IoUringEchoServer(); server.port = port; return server; } public void start(boolean block) throws InterruptedException { bossGroup = new IOUringEventLoopGroup(); workerGroup = new IOUringEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap .group(bossGroup, workerGroup) .channel(IOUringServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) .option(ChannelOption.SO_REUSEADDR, true) .childHandler(new ChannelInitializer<>() { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // pipeline.addLast(new LoggingHandler(LogLevel.INFO)); pipeline.addLast(new EchoServerHandler()); } }); ChannelFuture future = bootstrap.bind(port).sync(); if (block) { future.channel().closeFuture().sync(); } } @Override public void close() throws ExecutionException, InterruptedException { if (bossGroup != null) { bossGroup.shutdownGracefully().get(); } if (workerGroup != null) { workerGroup.shutdownGracefully().get(); } } }
ポイントは、EventLoopGroup
としてIOUringEventLoopGroup
クラスを使うことと、
bossGroup = new IOUringEventLoopGroup(); workerGroup = new IOUringEventLoopGroup();
Channel
としてIOUringServerSocketChannel
クラスを指定することですね。
ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap .group(bossGroup, workerGroup) .channel(IOUringServerSocketChannel.class)
起動を行うstart
メソッドの呼び出し時に、true
かfalse
を渡すかでブロックするかどうかを指定できるようにしました。
if (block) {
future.channel().closeFuture().sync();
}
true
だとstart
メソッドはブロックします。
クライアント側。
ハンドラー。
src/main/java/org/littlewings/netty/iouring/EchoClientHandler.java
package org.littlewings.netty.iouring; import java.nio.charset.StandardCharsets; import java.util.concurrent.BlockingQueue; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class EchoClientHandler extends ChannelInboundHandlerAdapter { BlockingQueue<String> queue; public EchoClientHandler(BlockingQueue<String> queue) { this.queue = queue; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { String msgAsString = ((ByteBuf) msg).toString(StandardCharsets.UTF_8); System.out.printf("received message = %s%n", msgAsString); try { queue.put(msgAsString); } catch (InterruptedException e) { // no-op } } @Override public void channelReadComplete(ChannelHandlerContext ctx) { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { cause.printStackTrace(); ctx.close(); } }
Bootstrap。
src/main/java/org/littlewings/netty/iouring/IoUringEchoClient.java
package org.littlewings.netty.iouring; import java.io.Console; import java.nio.charset.StandardCharsets; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingDeque; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.incubator.channel.uring.IOUringEventLoopGroup; import io.netty.incubator.channel.uring.IOUringSocketChannel; public class IoUringEchoClient implements AutoCloseable { String host; int port; BlockingQueue<String> queue = new ArrayBlockingQueue<>(10); EventLoopGroup group; Channel channel; public static void main(String... args) throws InterruptedException { try (IoUringEchoClient client = IoUringEchoClient.newClient("localhost", 8080)) { client.start(); Console console = System.console(); while (true) { String message = console.readLine("> "); if ("exit".equals(message)) { break; } else { client.sendMessage(message); TimeUnit.MILLISECONDS.sleep(500L); } } } } public static IoUringEchoClient newClient(String host, int port) { IoUringEchoClient client = new IoUringEchoClient(); client.host = host; client.port = port; return client; } public void start() throws InterruptedException { group = new IOUringEventLoopGroup(); Bootstrap bootstrap = new Bootstrap(); bootstrap .group(group) .channel(IOUringSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // pipeline.addLast(new LoggingHandler(LogLevel.INFO)); pipeline.addLast(new EchoClientHandler(queue)); } }); channel = bootstrap.connect(host, port).sync().channel(); } public String sendMessage(String message) throws InterruptedException { ByteBuf msg = Unpooled.wrappedBuffer(message.getBytes(StandardCharsets.UTF_8)); channel.writeAndFlush(msg); return queue.take(); } @Override public void close() throws InterruptedException { if (channel != null) { channel.close().sync(); } if (group != null) { group.shutdownGracefully().sync(); } } }
io_uringと全然関係ないですが、BlockingQueue
を使ってサーバー側から返ってきたメッセージを呼び出し元に返せるようにしました。
public String sendMessage(String message) throws InterruptedException { ByteBuf msg = Unpooled.wrappedBuffer(message.getBytes(StandardCharsets.UTF_8)); channel.writeAndFlush(msg); return queue.take(); }
main
メソッドから起動した時は、対話的なアプリケーションにしているのですが
public static void main(String... args) throws InterruptedException { try (IoUringEchoClient client = IoUringEchoClient.newClient("localhost", 8080)) { client.start(); Console console = System.console(); while (true) { String message = console.readLine("> "); if ("exit".equals(message)) { break; } else { client.sendMessage(message); TimeUnit.MILLISECONDS.sleep(500L); } } } }
これは、Memcachedのクライアントサンプルを参考にしています。
動作確認
では、動作確認してみましょう。
サーバー側を起動。
$ mvn compile exec:java -Dexec.mainClass=org.littlewings.netty.iouring.IoUringEchoServer
telnet
で確認。
$ telnet localhost 8080 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'.
結果。
Hello World ★Hello World★
この時、サーバー側ではコンソールにこのように出力されます。
received message = Hello World
次に、作成したクライアントを使ってみます。
$ mvn compile exec:java -Dexec.mainClass=org.littlewings.netty.iouring.IoUringEchoClient
こんな感じになりますね。
> Hello World received message = ★Hello World★ > こんにちは、世界 received message = ★こんにちは、世界★
exit
で終了します。
> exit
それから、サーバー・クライアント双方とも以下のコメントアウトを解除すると
pipeline.addLast(new LoggingHandler(LogLevel.INFO));
Nettyのログが出力されるようになります。
サーバー側。
1月 22, 2023 12:09:54 午前 io.netty.handler.logging.LoggingHandler channelRegistered 情報: [id: 0xfc878418, L:/127.0.0.1:8080 - R:/127.0.0.1:54800] REGISTERED 1月 22, 2023 12:09:54 午前 io.netty.handler.logging.LoggingHandler channelActive 情報: [id: 0xfc878418, L:/127.0.0.1:8080 - R:/127.0.0.1:54800] ACTIVE 1月 22, 2023 12:09:58 午前 io.netty.handler.logging.LoggingHandler channelRead 情報: [id: 0xfc878418, L:/127.0.0.1:8080 - R:/127.0.0.1:54800] READ: 11B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| 48 65 6c 6c 6f 20 57 6f 72 6c 64 |Hello World | +--------+-------------------------------------------------+----------------+ received message = Hello World 1月 22, 2023 12:09:58 午前 io.netty.handler.logging.LoggingHandler write 情報: [id: 0xfc878418, L:/127.0.0.1:8080 - R:/127.0.0.1:54800] WRITE: 17B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| e2 98 85 48 65 6c 6c 6f 20 57 6f 72 6c 64 e2 98 |...Hello World..| |00000010| 85 |. | +--------+-------------------------------------------------+----------------+ 1月 22, 2023 12:09:58 午前 io.netty.handler.logging.LoggingHandler channelReadComplete 情報: [id: 0xfc878418, L:/127.0.0.1:8080 - R:/127.0.0.1:54800] READ COMPLETE 1月 22, 2023 12:09:58 午前 io.netty.handler.logging.LoggingHandler flush 情報: [id: 0xfc878418, L:/127.0.0.1:8080 - R:/127.0.0.1:54800] FLUSH 1月 22, 2023 12:10:04 午前 io.netty.handler.logging.LoggingHandler channelRead 情報: [id: 0xfc878418, L:/127.0.0.1:8080 - R:/127.0.0.1:54800] READ: 24B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| e3 81 93 e3 82 93 e3 81 ab e3 81 a1 e3 81 af e3 |................| |00000010| 80 81 e4 b8 96 e7 95 8c |........ | +--------+-------------------------------------------------+----------------+ received message = こんにちは、世界 1月 22, 2023 12:10:04 午前 io.netty.handler.logging.LoggingHandler write 情報: [id: 0xfc878418, L:/127.0.0.1:8080 - R:/127.0.0.1:54800] WRITE: 30B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| e2 98 85 e3 81 93 e3 82 93 e3 81 ab e3 81 a1 e3 |................| |00000010| 81 af e3 80 81 e4 b8 96 e7 95 8c e2 98 85 |.............. | +--------+-------------------------------------------------+----------------+ 1月 22, 2023 12:10:04 午前 io.netty.handler.logging.LoggingHandler channelReadComplete 情報: [id: 0xfc878418, L:/127.0.0.1:8080 - R:/127.0.0.1:54800] READ COMPLETE 1月 22, 2023 12:10:04 午前 io.netty.handler.logging.LoggingHandler flush 情報: [id: 0xfc878418, L:/127.0.0.1:8080 - R:/127.0.0.1:54800] FLUSH 1月 22, 2023 12:10:07 午前 io.netty.handler.logging.LoggingHandler channelReadComplete 情報: [id: 0xfc878418, L:/127.0.0.1:8080 ! R:/127.0.0.1:54800] READ COMPLETE 1月 22, 2023 12:10:07 午前 io.netty.handler.logging.LoggingHandler flush 情報: [id: 0xfc878418, L:/127.0.0.1:8080 ! R:/127.0.0.1:54800] FLUSH 1月 22, 2023 12:10:07 午前 io.netty.handler.logging.LoggingHandler channelInactive 情報: [id: 0xfc878418, L:/127.0.0.1:8080 ! R:/127.0.0.1:54800] INACTIVE 1月 22, 2023 12:10:07 午前 io.netty.handler.logging.LoggingHandler channelUnregistered 情報: [id: 0xfc878418, L:/127.0.0.1:8080 ! R:/127.0.0.1:54800] UNREGISTERED
クライアント側。
1月 22, 2023 12:09:54 午前 io.netty.handler.logging.LoggingHandler channelRegistered 情報: [id: 0xbe67cbc7] REGISTERED 1月 22, 2023 12:09:54 午前 io.netty.handler.logging.LoggingHandler connect 情報: [id: 0xbe67cbc7] CONNECT: localhost/127.0.0.1:8080 1月 22, 2023 12:09:54 午前 io.netty.handler.logging.LoggingHandler channelActive 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 - R:localhost/127.0.0.1:8080] ACTIVE > Hello World 1月 22, 2023 12:09:58 午前 io.netty.handler.logging.LoggingHandler write 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 - R:localhost/127.0.0.1:8080] WRITE: 11B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| 48 65 6c 6c 6f 20 57 6f 72 6c 64 |Hello World | +--------+-------------------------------------------------+----------------+ 1月 22, 2023 12:09:58 午前 io.netty.handler.logging.LoggingHandler flush 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 - R:localhost/127.0.0.1:8080] FLUSH 1月 22, 2023 12:09:58 午前 io.netty.handler.logging.LoggingHandler channelRead 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 - R:localhost/127.0.0.1:8080] READ: 17B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| e2 98 85 48 65 6c 6c 6f 20 57 6f 72 6c 64 e2 98 |...Hello World..| |00000010| 85 |. | +--------+-------------------------------------------------+----------------+ received message = ★Hello World★ 1月 22, 2023 12:09:58 午前 io.netty.handler.logging.LoggingHandler channelReadComplete 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 - R:localhost/127.0.0.1:8080] READ COMPLETE 1月 22, 2023 12:09:58 午前 io.netty.handler.logging.LoggingHandler flush 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 - R:localhost/127.0.0.1:8080] FLUSH > こんにちは、世界 1月 22, 2023 12:10:04 午前 io.netty.handler.logging.LoggingHandler write 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 - R:localhost/127.0.0.1:8080] WRITE: 24B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| e3 81 93 e3 82 93 e3 81 ab e3 81 a1 e3 81 af e3 |................| |00000010| 80 81 e4 b8 96 e7 95 8c |........ | +--------+-------------------------------------------------+----------------+ 1月 22, 2023 12:10:04 午前 io.netty.handler.logging.LoggingHandler flush 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 - R:localhost/127.0.0.1:8080] FLUSH 1月 22, 2023 12:10:04 午前 io.netty.handler.logging.LoggingHandler channelRead 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 - R:localhost/127.0.0.1:8080] READ: 30B +-------------------------------------------------+ | 0 1 2 3 4 5 6 7 8 9 a b c d e f | +--------+-------------------------------------------------+----------------+ |00000000| e2 98 85 e3 81 93 e3 82 93 e3 81 ab e3 81 a1 e3 |................| |00000010| 81 af e3 80 81 e4 b8 96 e7 95 8c e2 98 85 |.............. | +--------+-------------------------------------------------+----------------+ received message = ★こんにちは、世界★ 1月 22, 2023 12:10:04 午前 io.netty.handler.logging.LoggingHandler channelReadComplete 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 - R:localhost/127.0.0.1:8080] READ COMPLETE 1月 22, 2023 12:10:04 午前 io.netty.handler.logging.LoggingHandler flush 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 - R:localhost/127.0.0.1:8080] FLUSH > exit 1月 22, 2023 12:10:05 午前 io.netty.handler.logging.LoggingHandler close 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 - R:localhost/127.0.0.1:8080] CLOSE 1月 22, 2023 12:10:05 午前 io.netty.handler.logging.LoggingHandler channelInactive 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 ! R:localhost/127.0.0.1:8080] INACTIVE 1月 22, 2023 12:10:05 午前 io.netty.handler.logging.LoggingHandler channelUnregistered 情報: [id: 0xbe67cbc7, L:/127.0.0.1:54800 ! R:localhost/127.0.0.1:8080] UNREGISTERED
テストを書く
最後に、テストコードを書いておきます。
src/test/java/org/littlewings/netty/iouring/IoUringEchoTest.java
package org.littlewings.netty.iouring; import java.util.concurrent.ExecutionException; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; public class IoUringEchoTest { @Test void echoClientServer() throws InterruptedException, ExecutionException { String host = "localhost"; int port = 8080; try (IoUringEchoServer server = IoUringEchoServer.newServer(port); IoUringEchoClient client = IoUringEchoClient.newClient(host, port)) { server.start(false); client.start(); assertThat(client.sendMessage("Hello World")) .isEqualTo("★Hello World★"); assertThat(client.sendMessage("Hello Netty")) .isEqualTo("★Hello Netty★"); assertThat(client.sendMessage("こんにちは 世界")) .isEqualTo("★こんにちは 世界★"); } } }
こんなところでしょうか。
まとめ
Netty io_uringを試してみました。
そもそもio_uringをよく知らなかったので、io_uring自体を調べるにそれなりに時間を使いましたが。直接は使っていないので、理解が
進んだかというと微妙なところです。
こういう世界にも、いずれ踏み込んだ方がいいのかな、とは時々思います。
あと、Nettyを使ったプログラムを久しぶりに書いたので、これもこれでけっこう苦労しました。