IOの基礎知識
IOの読み取りと書き込みの基本原則
read
システムコールとwrite
システムコールは、カーネルバッファとプロセスバッファに対する単なる操作です。
読み取りシステムコールは、カーネルバッファ内のデータをプロセスバッファにコピーします
writeシステムコールは、プロセスバッファデータをカーネルバッファにコピーします。
至于具体的内核缓冲区到磁盘的过程则由操作系统内核进行
。
ソケットやファイルIOなどのユーザープログラムのIO操作はすべて上位レベルのアプリケーション開発であり、それらの入出力処理はプログラミングプロセスで一貫していることがわかります。
バッファの役割
物理デバイスとの頻繁なデータ交換を減らします。ディスクへのデータ書き込みの過程で中断が発生します。システムが中断されると、システムは対応するプロセス情報を維持し、中断後に復元する必要があります。バッファは、システムへのこの割り込みのオーバーヘッドを削減します。
4つの主要なIOモデル
- ブロッキングIO:ユーザースペースプログラムの状態を参照します。カーネルIO操作が完全に完了すると、ユーザースペースに戻ってユーザー操作を実行します。Javaでデフォルトで作成されたソケットはIOをブロックしています
- 同期IOと非同期IO:
同期IOは、ユーザースペースプログラムのスレッドがIOを開始するパーティであり、カーネルスペースがパッシブに受け入れるパーティであることを意味します。
非同期IOは、カーネルスペースでIOを開始するパーティを指し、ユーザースペースプログラムは受け入れパーティです。
同步阻塞IO(Blocking IO)
:ユーザープログラムは、続行する前にカーネルIO操作が完了するのを待つ必要があります。
同步非阻塞IO(Non-bolcking IO):
ユーザープログラムは、次の操作を実行するためにカーネルIO操作の完了を待つ必要はありません。このプロセス中に、カーネルはステータス値をユーザースペースに返します。
JAVAのNIOではない(新しいIO)
IO多路复用
:又称为异步阻塞IO,就是Reactor反应器模式,Java中的Selector选择器和Linux中的epoll都是这种模型。
异步IO(Asynchronous IO)
:ユーザープロセスのスレッドは、カーネルによってアクティブに呼び出されるさまざまなIOイベントコールバック関数をカーネル空間に登録します。
合理的な構成を通じて数百万の同時接続をサポートする方法
同時実行性の高いシステムを開発する場合は、Linuxファイルハンドルの制限を解除する必要があります。Linuxのデフォルトは1024です。つまり、プロセスは1024ソケット接続を受け入れることができます。
ファイルハンドル。ファイル記述子とも呼ばれます。
文件句柄是内核为了高效管理已经被打开的文件所常创建的索引。
Linux通过ulimite -n 文件句柄数量 来修改文件句柄数
。ただし、この方法は、ユーザーがログインした場合にのみ有効です。
永続的に変更する場合は、/etc/rc.local
スタートアップファイルを変更して、ulimite -SHn
数量を追加する必要があります。次のコンテンツ
を変更して直接etc/secuity/limites.conf’
追加することもできます。
soft nofile 1000000 //(最大值为100万)
hard nofile 1000000
//soft nofile表示软性极限,
//hard nofile表示硬性极限,
Java NIO
Java NIO(New IO)は、ストリーム指向の同期ブロッキングの問題を解決するためにJava 4で導入されたため、多くの人が非ブロックIOと呼んでいます。
Java NIOは、IO多重化モデルにも属しています。
NIOは、次の3つの主要コンポーネントで構成されています。
チャネル
バッファ
セレクタセレクタ
NIOとOIOの違い
OIOは、Java4、ストリーム指向の同期ブロッキングIO、バイトストリームまたは文字ストリーム、バイトストリーム(文字ストリーム)からデータを読み取る前に使用されていました。一度に1つ以上を読み取ると、ポインタの位置を自由に変更することはできません。OIOにはセレクターの概念がありません
NIOは、同期ブロッキングIO、バッファ指向の非ブロッキングIOを解決するためにJava4によって導入されました。各読み取りはチャネルからバッファへ、書き込み操作はバッファからチャネルへです。
バッファ内のどこからでもデータを読み取ることができます。
- NIOはどのようにしてノンブロッキングを実現しますか?
チャネルおよびチャネル多重化テクノロジーを介して- NIOにはセレクターの概念があり、NIOの実装はシステムのセレクター呼び出しに基づいています。
バッファの使い方
- バッファオブジェクトを作成するには、allocate()メソッドを使用します
- putを使用してデータをバッファプールに書き込みます
- filp()を使用して、書き込みモードを読み取りモードに変更します
- get()を使用してバッファからデータを読み取ります
- 読み取り後、Buffer.clear()またはBuffe.compat()メソッドを使用して、バッファーを書き込みモードに切り替えることができます。
チャネルタイプ
FileChannel文件通道
、ファイルのデータの読み取りと書き込みに使用されますSocketChannel套接字通道
、ソケットソケットTCP接続およびデータの読み取りと書き込みに使用されます。接続に一般的に使用される分散型ServerSoketChannel。ServerSocketChannel服务器套接字通道
、TCP接続要求をリッスンし、接続要求ごとにSockeTChannelを作成できます。DatagramChannl数据报通道
:UDPデータの読み取りと書き込みに使用されます。
SocketChannelを使用する場合、デフォルトで同期ブロッキングが使用
configureBlocking(false)
されるため、非同期の非ブロッキングIOを設定して取得するために呼び出す必要があります。
NIOセレクター
セレクターの役割は、IO多重化を完了することです。チャネルは接続を表し、複数の接続チャネルを監視する機能はセレクターを介して実現できます。1つのスレッドが複数のチャネルの監視を完了できるようにします。
- セレクターIOイベントタイプ
- OP_READを読む
- 書き込み可能なOP_WRITE
- OP_CONNECTを接続すると、反対側のハンドシェイクが完了し、
连接就绪
状態になります。 - OP_ACCEPTを受信すると、新しい接続が検出されると
接收就绪
遷移状態になります。
FileChannel
継承SelectableChannel
がないため、セレクターで管理することはできません。
セレクター使用プロセス
- セレクターインスタンスを取得します
- チャネルをセレクターに登録します
- 関心のあるIO対応イベント(選択キーのセット)をポーリングします
原子炉原子炉モード
リアクターモードとは何ですか?
リアクターモードは、リアクターリアクタースレッドとハンドラープロセッサーで構成されます。
- リアクターリアクタースレッドの責任:IOイベントへの応答を担当し、ハンドラープロセッサーに分散され
ますプロセッサーの責任を処理します:ビジネスロジックの非ブロッキング実行。実際の接続の確立、チャネルの読み取り、ビジネスロジックの処理を完了し、結果をチャネルに書き込む責任があります。
Netty
Nettyとは
Nettyは、NIO(JavaのNEW IO)に基づくクライアントサーバーフレームワークであり、ネットワークアプリケーションを迅速かつ簡単に開発するために使用できます。
Nettyの何がそんなに良いのですか?
Nettyは、JDKに付属するNIOよりも整然としています。
- 複数のプロトコル、FTP、SMTP、HTTP、およびさまざまなバイナリおよびテキストベースの従来のプロトコルをサポートします。
- シンプルで強力なスレッドモデル
- 複数のコーデックが付属しています
- 真のコネクションレス型パケットソケットのサポート
- SSL、/ TLS、StartTLSの完全サポート
- 開発コミュニティ環境は良好であり、多くのオープンソースプロジェクトソリューションがあります。
Nettyのコアコンポーネント
Channel
、インターフェイスはネットワーク操作用のNettyの抽象クラスであり、bind()、connect()、read()、write()などの基本的なI / O操作が含まれています。
より一般的な実装クラスは、NioServerSocketChannelサーバーであるNioSocketChannel(クライアント)です。EventLoop(事件循环)接口
、Nettyのコアコンセプト。主要作用实际就是负责监听网络事件并调用事件处理器进行相关 I/O 操作的处理。
ChannelFuture
Nettyは非同期で非ブロッキングであり、すべてのI / O操作は非同期です。また、ChannelFutureはNettyの非同期ソリューションであり、これを使用して値を返し、操作が正常に実行されたかどうかを知ることができます。
ChannelFutureインターフェイスのaddListener()メソッドを介してChannelFutureListenerを登録します。操作が成功または失敗すると、リスナーは自動的に結果を返します。
また、ChannelFutureのchannel()メソッドを介して関連するチャネルを取得することもできます
-
ChannelHandler
:メッセージ固有のプロセッサ。読み取りと書き込みの操作、クライアント接続などを担当します。 -
ChannelPipeline(通道流水线)
:ChannelHandlerチェーンの場合、コンテナーが提供され、チェーンに沿ってインバウンドおよびアウトバウンドのイベントフローを伝播するためのAPIが定義されます。当 Channel 被创建时,它会被自动地分配到它专属的ChannelPipeline。
チャネルにバインドされた複数のハンドラプロセッサがチャネルに追加されます。
ChannelPiplineは二重リンクリストとして設計されており、ハンドラーはそのノードです。
Nettyでのリアクトルリアクターモードの使用の実施形態は何ですか?
NettyのReactorのreactorモードは次のように反映されます。
Channel
:まず、NettyはJava NIOのチャネルコンポーネントを直接使用しませんが、非同期IOとブロッキングIOの両方を維持しながら、複数のプロトコルに適したものにするためにそれをカプセル化します。Channelの最下層は、SelectableChannelの最下層をカプセル化します。Netty中的Reactor反应器
:Nettyのリアクターの名前は:NioEventLoopです。その役割はJavaNIOのreactorの役割と似ており、スレッドスレッドとIOイベントのポーリングがあります。Netty中的Handler
:Nettyのハンドラプロセッサは2つのカテゴリに分けられます。1つはChannelInboundHandlerチャネルのスタック内プロセッサで、もう1つはChannelOutboundHandlerチャネルのアウトバウンドプロセッサです。すべてChannelHandlerから継承されます。
EventloopGroupは理解していますか?EventLoopとの関係は何ですか?
- Nettyのリアクターはマルチスレッドリアクターです。EventLoopはサブリアクターと同等です。EventLoopGroupは、これらのサブリアクターを管理するスレッドグループです。
- Nettyを使用する場合、単一のEventLoopの代わりにスレッドグループEventLoopGroupが使用されます。EventLoopGroupの構築パラメーターは、スレッド数(デフォルトはCPUの2倍)を指定でき、スレッド数はEventLoopに1対1で対応します。EventLoopには、IOイベントを処理するための専用スレッドがあります。(モニター、呼び出しプロセッサー)
上の図からわかるように、クライアントが接続メソッドを介してサーバーに接続すると、bossGroupはクライアント接続要求を処理します。クライアントの処理が完了すると、接続は処理のためにworkerGroupに送信され、workerGroupはIO関連の操作の処理を担当します。
BootstrapとServerBootstrapを知っていますか?
- Bootstrapは、クライアントのスタートアップブートクラス/補助クラスです。
EventLoopGroup group = new NioEventLoopGroup();
try {
//创建客户端启动引导/辅助类:Bootstrap
Bootstrap b = new Bootstrap();
//指定线程模型
b.group(group).
......
// 尝试建立连接
ChannelFuture f = b.connect(host, port).sync();
f.channel().closeFuture().sync();
} finally {
// 优雅关闭相关线程组资源
group.shutdownGracefully();
}
ブートストラップは通常、connet()メソッドを使用して、NettyTCPプロトコル通信のクライアントとしてリモートホストおよびポートに接続します。ブートストラップは、UDPプロトコル通信の一端としてbind()メソッドを介してローカルポートをバインドすることもできます。
- ServerBootstrapサーバー側の起動ブートクラス/補助クラス
/1.创建线程反应组
//bossGroup 处理连接监听IO事件
//workerGroup 用于负责数据IO事件和Handler业务处理
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//.创建服务端启动引导/辅助类:ServerBootstrap
ServerBootstrap b = new ServerBootstrap();
//.给引导类配置两大线程组,确定了线程模型
b.group(bossGroup, workerGroup).
/2.设置通道的IO类型
b.channel(NioServerSocketChannel.class)
/3.设置监听端口
b.localAddress(new InetSocketAddress(port))
/4.设置通道传输参数
b.option(给父通道接收连接通道设置的选项)
/5.装配流水线
b.pipeline().addLast(new Handler)
/ 6.绑定服务器新连接的端口号
ChannelFuture f = b.bind(port).sync();
// 等待连接关闭
f.channel().closeFuture().sync();
} finally {
//7.优雅关闭相关线程组资源
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
ServerBootstrapは通常、bind()メソッドを使用してローカルポートにバインドし、クライアントが接続するのを待ちます。
Bootstrapは1つのスレッドグループ(EventLoopGroup)を構成するだけでよく、ServerBootstrapは2つのスレッドグループ(EventLoopGroup)を構成する必要があります。1つは接続の受信用で、もう1つは特定の処理用です。
NioEventLoopGroupのデフォルトコンストラクターはいくつのスレッドを開始しますか?
デフォルトはですがCPU*2
、コンストラクターを介して変更できます。
Nettyのスレッドモデル
Nettyでは、NioEventLoopGroupスレッドプールは主に特定のスレッドモデル(リアクターモード)を実現するために使用されます。
Reactorモードはイベント駆動型に基づいており、多重化を使用してイベントを対応するハンドラーに配布して処理します。これは、大規模なIOシナリオの処理に非常に適しています。
TCPディップ/アンパックとは何ですか?彼の解決策は?
- TCPの固定/解凍とは、TCPに基づいてデータを送信する場合、複数の文字列を「固定」する場合、または1つの文字列を「分解」する場合です。
- Nettyでのディッピングとアンパックの問題:毎回基になるデータを読み取る容量が制限されています。基になるTCPデータグラムが比較的大きい場合、1つの基になるデータグラムがサブパッケージ化およびコピーされ、結果としてハーフパケットになります。
- TCP最下層によってバッファリングされたデータパケットが比較的小さい場合、一度に複数のカーネルバッファパケットを処理し、プログラムバッファにダーティパケットを読み取らせます。
- 解決?
(1)Nettyが提供するデコーダーを使用できます。
- LineBasedFrameDecoder:送信者がデータパケットを送信するとき、各データパケットは改行文字で区切られます。LineBasedFrameDecoderの動作原理は、ByteBufの読み取り可能なバイトを順番にトラバースし、改行文字があるかどうかを判断して、対応するインターセプトを実行することです。
- DelimiterBasedFrameDecoder:区切り文字デコーダーをカスタマイズできます。
- LineBasedFrameDecoderは、実際には特別なDelimiterBasedFrameDecoderデコーダーです。
- FixedLengthFrameDecoder:固定長デコーダー。指定された長さに応じてメッセージを解凍できます。
- LengthFieldBasedFrameDecoder:カスタム長のパケットデコーダー。
(2)カスタムシリアル化デコーダーパケタイザー:JSONシリアル化など。
Nettyの長い接続
TCPが読み取りと書き込みを行う前に、サーバーとクライアントの間で事前に接続を確立する必要があることを私たちは知っています。接続を確立するプロセスには、私たちがよく言う3方向のハンドシェイクが必要であり、接続を解放/閉じるには4つの手の波が必要です。このプロセスはネットワークリソースを消費し、時間遅延があります。
したがって、TCPは長い接続を使用します。
ネティの心拍メカニズム
長い接続のTCPでさまざまな異常状態が発生し、サーバーが切断される可能性があります。ハートビートメカニズムがない場合、クライアントはサーバーがダウンしていることを認識せず、サーバーの再起動後、クライアントが接続されていると思ったことを認識しません。クライアントが誤って切断した場合、基盤となるTCPが切断されたことをサーバーが認識しないと、接続は偽の接続になり、多くのリソースを占有します。したがって、それを解決するにはハートビートメカニズムが必要です。
TCPには実際には長い接続オプションがあり、TCPのオプションであるハートビートパケットメカニズムもあります:SO_KEEPALIVE。ただし、TCPプロトコルレベルでの長い接続の柔軟性は十分ではありません。したがって、一般に、アプリケーション層プロトコルにカスタムハートビートメカニズムを実装します。つまり、Nettyレベルでコーディングします。ハートビートメカニズムがNettyを介して実装されている場合、コアクラスはIdleStateHandlerです。
ネティのゼロコピー
ゼロコピー(英語:ゼロコピー。ゼロコピーとしても翻訳されます)
- テクノロジーとは、コンピューターが操作を実行するときに、CPUが最初にメモリから別の特定の領域にデータをコピーする必要がないことを意味します。この手法は通常、ネットワーク経由でファイルを転送するときにCPUサイクルとメモリ帯域幅を節約するために使用されます。
- オペレーティングシステムレベルのアーキテクチャからは、ユーザーモードとカーネルモードの間でデータを前後にコピーしないようにすることを意味します。
Nettyのゼロコピーは主に以下に反映されます。
- Nettyが提供するCompositeByteBufクラスを使用すると、複数のByteBufを1つの論理ByteBufにマージして、各ByteBuf間のコピーを回避できます。
- ByteBufはスライス操作をサポートしているため、ByteBufを複数のByteBufに分解して、同じストレージ領域を共有し、メモリのコピーを回避できます。
- ファイル転送は、FileRegionによってラップされたFileChannel.tranferToを介して実現されます。これにより、ファイルバッファー内のデータをターゲットチャネルに直接送信できるため、従来の循環書き込み方式によって引き起こされるメモリコピーの問題を回避できます。