[C#]MemoryPackを使ってみたい2 - ASP.NET Coreで使う

Cysharp/MemoryPack

シリアライザの使い道でわかりやすいのはAPIサーバーとの間のやりとりです。

当然APIサーバーは、アプリ等との間で大量のデータをやりとりするわけで、シリアライザが仕事をする回数も相当な数になります。そこの部分を効率化できるとサーバーの負荷が減ったり、体感的なレスポンスがよくなったりすることが見込めます。

業務的なシステムを作っている場合だと、そういうところを細かく詰めたり、いろんな技を研究する余裕がなかったり、あまり一般的でない実装をしてしまうことで他の人(もしくは半年後の自分)が触れなくなってしまうことを恐れたりがあるので、どうしても公約数的な選択をせざるを得ないケースがあります。

そう、MemoryPackがそれを解決してくれる可能性があります。

ASP.NET Coreで使う

ASP.NET Core Web API(.NET 6)のプロジェクトをデフォルトの設定で作成します(OpenAPIも入れておく)。 MemoryPackのインストールに加えて、ASP.NET Core用のパッケージも入れます。

PM> Install-Package MemoryPack.AspNetCoreMvcFormatter

Program.csのAddControllersを下記のように変更します。

builder.Services.AddControllers(options =>
{
    options.InputFormatters.Insert(0, new MemoryPackInputFormatter());
    options.OutputFormatters.Insert(0, new MemoryPackOutputFormatter(true));
});

WeatherForecastクラスをpartialにして、MemoryPackable属性をつけます。

[MemoryPackable]
public partial class WeatherForecast
{
    public DateTime Date { get; set; }
    public int TemperatureC { get; set; }
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    public string? Summary { get; set; }
}

実行します。

1

Media typeにapplication/x-memorypackが追加されているのが見えます。

メソッドを呼び出してみます。

2

application/x-memorypackで返ってきました。Response bodyはバイナリデータがそのまま出ています。 もちろんMedia typeをJSONに変えれば読めるデータが返ってきます。

非常に簡単にMemoryPack化できます。

ちなみにbodyのサイズはJSONが493バイト、MemoryPackが150バイトぐらいです。

3

4

WeatherForecastはデータがランダムなので厳密ではありませんが小さくなるのは間違いありません。そりゃあ、レコード件数分フィールド名の文字列が削減されるんですから小さくなるのは当たり前で、フィールドが何十個もあって、どれもこれも長い名前なんかがついてたりしたら削減効果は結構あります。

これだけの作業で流れるデータが減るんですからすごい話です。

注意事項

上で書いたのと同じく、受ける側も型と並び順が一致したクラス定義でデシリアライズしないといけませんが、C#-C#の場合はクラス定義を別プロジェクトにしてお互いが参照するのが簡単です。

定義変更時はAPI側のバージョニングで対応したりでいけそうですが、レスポンスをバイナリのままキャッシュしたり、ローカルに書き出しているケースでは壊れてしまうので、やはり型と並び順は変えないというのが注意事項となります。

あと細かい話ですが、

new MemoryPackOutputFormatter(true)

OutputFormatterはcheckContentType=trueで生成しておかないと、MemoryPackが出力処理を全部横取りしますので、JSON指定してもバイナリが返ってきて泣くことになります。

まとめ

ASP.NET Coreで使えるフォーマッタが用意されているので導入も簡単でした。Acceptヘッダでレスポンスの形式を変えることでJSON等既存の形式を維持したまま導入が可能できますので、受け側の改修を段階的に進められると思います。

シリアライズの効率が上がると言うことはAPIサーバーのレスポンスにも効いてきますし、当然サイズが小さくなると通信にかかる時間も減るでしょうからまた然りでしょう。

とはいえサーバーはASP.NET Coreなんだけど、クライアント側がReactやVueなんだよなぁ、みたいなパターンは結構ありそうですが、それも大丈夫!なんとMemoryPackにはTypeScript用のシリアライザを吐き出してくれる機能が搭載されています。

次回はその紹介です。