これは、なにをしたくて書いたもの?
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
Linuxにおける非同期IOの実装について - Qiita
io_uringで高速IO処理(?) | κeenのHappy Hacκing Blog
ソケットAPIが遅すぎる?新たなio_uringを試す!. 新しいAPIが作られるたびに、私たちは、古いAPIを置き換えるだけで高速化という… | by FUJITA Tomonori | nttlabs | Medium
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.
パフォーマンス面では、カーネルとユーザー空間でキューが共有されているため以下の点が有利なようです。
- データのコピーを避けることができる
- システムコールを減らすことができる
- ユーザー空間のキューとやり取りすればよい
- 複数のリクエストをまとめてカーネルに送信することができる
- 送信したエントリー(SQE)をカーネルにポーリングさせることができる
Submission Queue Polling — Lord of the io_uring documentation
ライブラリとしては、liburingを使うことが勧められています。
GitHub - axboe/liburing
Netty io_uring
Nettyのincubatorプロジェクトとして、io_uringベースのトランスポートライブラリが作られています。
GitHub - netty/netty-incubator-transport-io_uring
現時点でのバージョンは、0.0.16.Finalです。
io_uring APIを直接使っているみたいですね。
https://github.com/netty/netty-incubator-transport-io_uring/blob/netty-incubator-transport-parent-io_uring-0.0.16.Final/transport-native-io_uring/src/main/c/syscall.c
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>
<mavencompilersource>17</mavencompilersource>
<mavencompilertarget>17</mavencompilertarget>
<projectbuildsourceEncoding>UTF-8</projectbuildsourceEncoding>
<projectreportingoutputEncoding>UTF-8</projectreportingoutputEncoding>
</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 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) {
}
}
@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 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のクライアントサンプルを参考にしています。
https://github.com/netty/netty/blob/netty-4.1.85.Final/example/src/main/java/io/netty/example/memcache/binary/MemcacheClient.java
動作確認
では、動作確認してみましょう。
サーバー側を起動。
$ 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を使ったプログラムを久しぶりに書いたので、これもこれでけっこう苦労しました。