.NET8でgRPCのパイプ通信を評価してみる&ついでに既存プロジェクトを.NET8へ移行

3.3K Views

November 30, 23

スライド概要

【オンライン】.NET Conf 後! C# Tokyo イベント - connpass
https://csharp-tokyo.connpass.com/event/292517/
このイベントで発表した資料です。

profile-image

会社勤めのSE・プログラマです。個人としての情報発信も行っており、このアカウントはその用途で使用します。同一ID「suusanex」でGitHub・はてな等でも発信しています。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

.NET8でgRPCのパイプ通信 を評価してみる &ついでに既存プロジェクトを.NET8へ移行 2023/11/30 須藤(suusanex)

2.

自己紹介  ID:suusanex( connpass・Twitter・GitHub共通)  名前:須藤圭太  サイエンスパーク株式会社という独立系ソフトウェアベンダーに所属  4年ほど受託開発で、上流から下流まで全部を回す  ここ10年ほどは、自社製品開発も担当  Windowsアプリ開発のネタが多い

3.

概要  Windowsアプリでは、プロセス間で通信をしたい場合がある  .NET 8でgRPCのパイプ通信が公式サポートされた  ということで、このような話をしてみます  話の前提:プロセス間通信したいケースと、gRPC  標準のTCP通信をパイプ通信に移行  そのメリットと実測データ  パイプ通信への移行の具体的な実装方法  既存のプロジェクトを.NET 8に移行

4.

話の前提:プロセス間通信したい  Windowsアプリでは、プロセス1つで完結できず、他のプロセスと通信したい ケースがある  例えば、バックグラウンドで動くWindowsサービスと、GUIを持つユーザー セッションアプリで通信したい場合など

5.

話の前提: gRPCをプロセス間通信に使う  gRPC:通信内容を専用構文で定義することで、クロスプラットフォームの通信がで きる(通常はTCP)  同一プラットフォーム内でプロセス間通信に使うだけでもメリットはある   最初から通信内容と通信方式が分離されていて、コード生成なども手厚い  つまり他のプロセス間通信手法より手軽 TCP通信でもlocalhostが対象なら十分に速い

6.

TCPからパイプ通信へ  .NET 8で、gRPCでのパイプ通信を公式サポート  通信内容の定義はそのままに、通信方式をパイプ通信にできる

7.

なぜパイプ通信?  TCP通信と比べてのメリットは?  他ソフトとのポート番号の衝突を気にしなくて良い  パイプは文字列なので衝突する可能性は低い  ファイルと同様にACLでの権限管理ができる  「TCPでも十分に速い」とは言ったが、より高速な用途ではパイプが優れる

8.

いくつかのケースでの実測1  「接続・送信・受信・切断」の繰り返し  100ms間隔で「接続・送信・受信・切断」を繰り返し、1回ごとの所要時間を計測  TCP:約2000ms/1回  パイプ:約1ms /1回  通信確立の所要時間の違いか、圧倒的な差が付く  数秒オーダーよりも頻繁に通信を繰り返す場合、パイプ通信が有効

9.

いくつかのケースでの実測2   ストリーム通信を確立し、その上で通信を繰り返し  1,ループ1回の所要時間と、  2,送信元の通信データ作成から、送信先の通信データ取得までの所要時間を測定  10ms間隔でループ 結果は次ページ

10.

いくつかのケースでの実測2   1,ループ1回の所要時間  TCP:約15.65ms  パイプ:約15.55ms 2, 送信元の通信データ作成から、送信先の通信データ取得までの所要時間  TCP:約0.25ms  パイプ:約0.15ms  通信繰り返しの所要時間はほぼ同じ  通信内容そのものの到達時間にはわずかに差がある(通信全体の所要時間から すると誤差に近い)  よほど通信遅延のタイミングにシビアな用途でなければ、差は無いと言って良 い

11.

速度のメリットが得られるケース まとめ  ストリームを常時確立せずに単発の通信をしたく、かつ数秒間隔では性能不足 のケースにはぴったり  ストリーム通信の場合はTCPと大差無い

12.
[beta]
実装方法は割と簡単 クライアント側


TCPの場合

var channel = GrpcChannel.ForAddress("http://localhost:50100/Connect1");
パイプの場合、パイプ接続処理をオプションへ渡すだけ
var channel = GrpcChannel.ForAddress("http://localhost/Connect1", new
{
HttpHandler = new
{
ConnectCallback = async (context, token) =>
{
var clientStream = new NamedPipeClientStream(/*パイプ名など、諸々のオプション*/);
await clientStream.ConnectAsync().ConfigureAwait(false);
return clientStream;
}
}
});
 色々省略してるので、詳細はMS Docsをどうぞ


13.
[beta]
実装方法は割と簡単 サーバー側


Generic Host前提で

IHost host = Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(options =>
{
//TCPの場合
options.Listen(IPAddress.Loopback, 50100, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
});
//パイプの場合
options.ListenNamedPipe(“(パイプ名)", listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
});
});

14.

サーバー側注意点  Windowsサービス(LocalSystem権限)からパイプを作ると、デフォルトではユー ザー権限の書き込み不可  ユーザーセッションからユーザー権限で操作したい場合は、Win32APIを使って パイプにACLを設定する必要あり   CreateFileで開いてGet/SetSecurityInfoなど コードは複雑なので省略、興味のある人はこちらをどうぞ  https://github.com/suusanex/sample_winservice_pipe_duplex_wcf_and_grpc/blob /master/gRPCWinServiceSample

15.

.NET8より前の既存プロジェクトからの 移行は?    WPF・クラスライブラリなど(C#)  プロジェクトの「ターゲットフレームワーク」で「.NET 8.0」を選択するだけ。  もちろんファイルの<TargetFramework>を直接書き換えてもOK C++/CLI  プロジェクトの「.NETターゲットフレームワーク」で「.NET 8.0」を・・・選択肢にない。  コンボボックスに、手入力で「net8.0」と書く。 Win UI 3(C#)  未サポート・・・  https://learn.microsoft.com/ja-jp/windows/apps/windows-app-sdk/stable-channel (一部引用)この問題のため、さらに .NET 8 がまだ正式にリリースされていないため、 Windows App SDK 1.4 では .NET 8 が正式にサポートされていません。 ただし、このバー ジョンの App SDK で .NET 8 のプレリリース バージョンを引き続きターゲットとしたい場 合は、次の手順をお勧めします。

16.

まとめ  PC内のプロセス間通信に、gRPCのパイプ通信を使うのは十分あり  .NET8より前のプロジェクトでTCPを使っている場合の移行も割と容易  特に接続・切断を数秒より速く繰り返すケースではメリット大  当てはまる場合は.NET 8移行の動機にもなりそう  WinUI 3、息してる?