WebSocket で通信できない環境では Blazor Server は動作しないのか?

## Blazor Server は WebSocket 通信が必須?

Blazor Server は、サーバー側プロセスとブラウザ上の小さなランタイムとが常時接続してリアルタイムで双方向通信しながら動作します。その通信手段としては、通常は WebSocket が使われます。

しかし、サーバー側との通信の途中に WebSocket 未対応の Web プロクシサーバーが挟まっているなどのように、WebSocket でサーバー側と通信ができない環境の場合、Blazor Server は動作しないのでしょうか。

## WebSocket で通信できなくても大丈夫です!

結論としては、**大丈夫です、Blazor Server は WebSocket 通信ができない環境でもちゃんと動作します!**

Blazor Server の通信技術基盤としては、ASP.NET "SignalR" (シグナルアール) と呼ばれるライブラリが用いられています。

https://dotnet.microsoft.com/apps/aspnet/signalr

この SignalR は双方向通信の実現のために、可能であれば WebSocket をその通信手段に使うのですが、もしも WebSocket で通信できない場合は、Server Sent Events、Long Polling、と順に通信手段をフォールバックしていきます。Long Polling 方式は、本質的には一般的な HTTP 要求と応答に過ぎません。ですから、その Web アプリのブートストラップページに到達できる以上は、最低限、Long Polling による SignalR 通信が開設可能、というわけです。

ということで、この SignalR を通信基盤に採用している Blazor Server でも同じ様に、**WebSocket 通信ができない環境では、Long Polling にフォールバックして動作続行**します。

このことは下記リンク先の公式ドキュメントサイトでも説明されています。

https://learn.microsoft.com/aspnet/core/blazor/host-and-deploy/server#transports

なお、SignalR は双方向通信手段として WebSocket、Server Sent Events、Long Polling の3つの手段から順にフォールバックしていく仕様・実装のようなのですが、Blazor Server では、WebSocket か Long Polling かの2択のようで、Server Sent Events は採用されないようです。上記ドキュメントサイトにもそう書かれていますし、自分の手元で試した限りでは、確かに、素の SignalR 接続を使ったサンプルプログラムでは Server Sent Events にフォールバックする環境でも、Blazor Server のサンプルプログラムでは Long Polling にフォールバックしていました。Blazor Server では双方向通信手段に Server Sent Events が採用されない理由については自分はわかっていません。

## WebSocket と Long Polling で違いはないの?

さて、WebSocket 通信ができなくても、最低限、Long Polling にフォールバックして動作可能な Blazor Server ですが、しかし、双方向通信手段に WebSocket を使った場合と、Long Polling を使った場合とで違いはないのでしょうか?

結論としては、はい、もちろん違いはあります。

いくつかの視点・観点からの差異がありますが、わかりやすい例としては処理速度の違いが挙げられると思います。

もっとも、一般的な Blazor Server アプリケーションでは体験できるほどの差は出ないことが想像されます。とはいえ、サーバー・クライアント間の通信負荷が非常に高い Blazor Server アプリケーションを作って試してみると、その差が如実になります。

### ライフゲームで実験

今回は実際にその処理速度の違いを体験してみようと思い、[ライフゲーム](https://ja.wikipedia.org/wiki/%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B2%E3%83%BC%E3%83%A0) を Blazor Server アプリケーションとして実装し、実験してみました。

ライフゲーム on Blazor Server の実装ですが、幸いなことに下記リンク先のとおり、Blazor WebAssembly 上で動作するライフゲームの実装が MIT License で公開されていましたので、これを clone し、Blazor Server で動作するよう改造して実験しました。

https://github.com/Timmoth/GameOfLife

下図は実行中の画像です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/680e1f04-6213-cbb6-928c-324a83d87c26.png)

60 x 60 セルを、JavaScript 相互運用呼び出しを使って Canvas に描画する実装としたので、かつ1セル描画に3回の JavaScript コード呼び出しが発生する実装となったため、**1フレーム描画**するのに 60 x 60 x 3 = 10,800 回、**およそ1万回の JavaScript コード呼び出しが発生** するかなり負荷の高い Blazor Serfver アプリとなりました。

#### WebSocket の場合

この Blazor Server アプリで、まずは **WebSocket** 通信での処理速度を測ってみました。50フレーム書き終えるまでを測定した結果は下図のとおりで、およそ **1フレーム描画するのに平均 0.5 秒ほど**かかっています (セルの描画数に左右されるため、世代が若いうちのセル数が多いタイミングでは1フレーム描画に1秒以上かかったりしますが、平均値ということで)。

![websocket.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/abc6a666-953b-9dce-be58-d6d41e068a17.png)

:::note
「Blazor Server って、60 x 60 の1フレーム描画に 500 msec もかかるの!?」と思われるかも知れませんが、これは実験の目的から、**あえて**処理時間がかかるよう・高負荷のかかるように実装したためです。ちゃんと作ればもっとまともな処理性能で動作しますから、どうか誤解の無きようお願いします!
:::


#### Long Polling の場合

では、同じ Blazor Server アプリケーションを、今度は **Long Polling** で動かしてみます。すると結果は下図のとおりで、**1フレーム描画に平均で 3秒以上** かかりました。

![LongPolling.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/b6c7070e-c6c3-0c13-7311-3f3442a636ff.png)

この結果を見る限り、**Long Polling 方式は WebSocket 方式と比べて、6倍ほど処理時間が延びている**ようです。

### 通信量にも差があるはず

自分の技術力では、ブラウザの WebSocket の通信量を測定する方法がわからず、結局数値比較できていないのですが、 **WebSocket 方式に対し、Long Polling のほうが通信量も増えているはず、** です。というのも、Long Polling 方式では、サーバー-クライアント間で何か通信するたびに HTTP 要求 x 1 件が消費され、そしてそれら HTTP 要求処理には**毎回、HTTP 要求ヘッダと応答ヘッダとが追加**されます。例えば、下記は要求ヘッダを含む HTTP 要求の例ですが、

```
GET https://localhost:5001/ HTTP/1.1
Host: localhost:5001
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
sec-ch-ua: "Not?A_Brand";v="8", "Chromium";v="108", "Microsoft Edge";v="108"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
DNT: 1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36 Edg/108.0.1462.42
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
```
こういう追加情報が毎回の通信のたびにクライアントからサーバーに送信されるのですから、これはなかなかに通信量が増えることが想像されます。

対して WebSocket では、ひとたび通信が開設した以後は、実際に通信したいデータ本体のみが通信経路を流れます。以上のことから Long Polling 方式のほうが通信負荷も高い、といえるでしょう。

前述のとおり、WebSocket 方式の場合の通信量を測定する方法が自分はわかっていませんが、Long Polling 方式の場合のサーバー側からクライアント側への通信量であれば、ブラウザの開発者ツールからざっくり知ることができます。以下に、上記ライフゲームを Long Polling 方式で動作させたときの、50フレーム描画終わった時点の開発者ツールのスクリーンショットを参考程度に貼っておきます。HTTP 要求回数が 10 万回を超えているのもすごいですし、通信量が 10 MB を超えているようですね。

![LongPolling - 通信量.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/0b2df7e1-4e26-b630-016b-827b44d09b89.png)

## まとめ

Blazor Server は、WebSocket では通信できない環境であっても、Long Polling 方式に動作を切り替えて、動作継続します。

ただし Long Polling 方式は、WebSocket 方式と比べると処理速度や通信量の面で不利であることは知っておくべきでしょう。

とはいえ、繰り返しになりますが、上記ライフゲームの例は、かなり JavaScript 相互運用を酷使するわざとらしい実験例です。一般的な Blaor Server アプリケーションでは Long Polling であっても、体感上は実運用に耐えられる程度の処理性能で動作すると想像されます。
仮に、Long Polling のほうが WebSocket より 6 倍遅いのだとしても、ユーザー操作のたびに WebSocket で 0.1 秒しかかからない処理ばかりであれば、Long Polling だとそのユーザー操作が 0.6 秒かかるようになるだけとも言える次第です。もちろん、0.6 秒という時間は、0.1 秒と比較したり、それとわかっていると、もたつきを感じられるかもしれない程度には微妙な秒数だとは思いますが。

以上、Blazor Server の配置運用のお役に立てば幸いです。

_Learn, Practice, Share :)_