読者です 読者をやめる 読者になる 読者になる

xin9le.net

Microsoft の製品/技術が大好きな Microsoft MVP な管理人の技術ブログです。

Azure Web Sites + WebSocketにおけるSignalR Transport

Advent Calendar Azure SignalR Tips

One ASP.NET Advent Calendar 2013、3日目担当の@xin9leです。昨年に引き続き@chack411さんの後ろで光栄です。この1年はASP.NET MVCSignalRWeb APIなどなど、ASP.NETの技術を次々と業務に実践投入してEnjoyしてました。特にSignalRは毎日のように触れ合ってきて、「もうアナタがいないと生きていけないわ...」と言いたくなるくらい仲良くさせて頂きました。

WebSocket on Azure Web Sites

2013年11月中旬、Azure Web SitesでもWebSocketが利用できるようになりました。WebSocketはHTML 5時代の通信プロトコルで、これまでよりも効率的なデータの送受信ができます。SignalRはすでにWebSocketに対応しており、IIS 8.0以上が搭載されているオンプレミス・サーバーなどではWebSocketベースでの通信が可能でしたが、Azure Web Sitesにホストした場合はできませんでした。

Windows Azure WebサイトにWebSocketを導入する
米マイクロソフト、Windows Azure WebサイトでWebSocketに対応

Azure Web SitesでSignalR + WebSocketを利用にするには、アプリケーションをホストするWebサイトの [構成] 画面で以下の設定をします。

  • .NET Frameworkのバージョンを4.5にする
  • WebSocketを有効にする

Azure-WebSocket-Configulation

ただし、これでWebSocketを思い切り使えるようになるかと言うとそうではありません。WebSocketの機能には以下のような接続制限が掛けられています。

モード 1インスタンスあたりの最大接続可能数
無料 5
共有 35
標準 350

この最大接続数を超えた場合、HTTP Statusの503エラーが返される仕様のようです。なぜこんな ケチな 制約を付けたのかは分かりませんが、SignalRアプリケーション (例えばよくあるチャットアプリなど) をホストすることを考えると接続数がこんな少なくて事足りるハズはありません。ということで、Azure Web Sites上でSignalR + WebSocketのアプリケーションが接続制限を超える場合どうなるのか実験してみました。

検証コードの実装 (サーバー側)

前提としてVisual Studio 2013 + .NET Framework 4.5 + SignalR 2.0を利用するものとします。実装は非常に簡単で、以下の手順に従ってOWIN (Open Web Interface for .NET) のStartupコードを書くだけです。OWINやKatana Projectについては全然詳しくないので詳細は省きますが、とりあえず知らなくてもSignalRは動かせますし、開発できます(キリッ

  1. 空のASP.NETプロジェクトを作成
  2. NuGetからSignalRコンポーネントをインストール
  3. [新しい項目の追加] から [OWIN Startup クラス] を選択して作成
  4. Configurationメソッドに app.MapSignalR(); を記述
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(WebSocketOnAzureWebSite.Server.Startup))]

//--- 名前空間は適宜変更
namespace WebSocketOnAzureWebSite.Server
{
    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

以上の作業ができたら、ホスト先のAzure Web Sitesに発行します。

検証コードの実装 (クライアント側)

ホストしたSignalRアプリに接続するためのクライアントを作ります。SignalRアプリの例としてよく挙げられるのはブラウザベースのものが多いですが、普通のデスクトップアプリでもSignalRは扱えます。以下の手順で作成します。

  1. コンソールアプリケーションを作成
  2. NuGetからSignalRの.NET Clientコンポーネントをインストール
  3. Program.csに以下のコードを記述
using Microsoft.AspNet.SignalR.Client;
using System;
using System.Linq;

namespace WebSocketOnAzureWebSite.Client
{
    static class Program
    {
        static void Main()
        {
            Console.Title = "SignalR Transport Checker : WebSocket Enabled";

            Console.WriteLine("----- 接続数 : 3");
            CheckTransport(3);
            Console.WriteLine();

            Console.WriteLine("----- 接続数 : 7");
            CheckTransport(7);
            Console.ReadLine();
        }

        //--- 通信手段の確認
        static void CheckTransport(byte connectionCount)
        {
            var url = "http://hogehoge.azurewebsites.net/";
            var connections = Enumerable.Range(0, connectionCount)
                            .Select(_ => new HubConnection(url))
                            .ToArray();
            foreach (var x in connections)  x.Start().Wait();
            foreach (var x in connections)  Console.WriteLine("{0} : {1}", x.ConnectionId, x.Transport.Name);
            foreach (var x in connections)  x.Stop();
        }
    }
}

CheckTransportメソッドの引数で指定された数だけサーバーとの接続を行い、接続クライアント固有のID (ConnectionId) と、その接続に利用されている通信手段 (Transport) を表示するようになっています。

検証

まず、Azure Web SitesのWebSocket機能が無効になっている場合を試してみます。すべての接続がServer Sent Evnetsで通信されているのが分かります。

CheckTransport_DisableWebSocket

続いて、WebSocketの機能を有効にして接続数が3の場合と7の場合を試してみます。先の接続制限通り、最初の5接続まではWebSocketが利用されているのが確認できます。この値を超えた場合、SignalRは自動的にServer Sent Evnetsに切り替えて接続を試みてくれるようです。特に何もしなくても自動で最適なものを選んでくれるので安心です。

CheckTransport_EnableWebSocket

ただし、これはHubConnectionの接続が暗黙にAutoTransportクラスを利用しているからです。AutoTransportクラスは以下の順序で接続を試み、通信方法を選択/確立してくれます。

  1. WebSocket (.NET 4.5以降に限る)
  2. Server Sent Events
  3. Long Polling

HubConnection.Startメソッドで明示的にTransportを指定することができますが、以下のように明示的にWebSocketを利用するように設定すると接続制限を超えたときに例外が発生しますので注意してください。

var connection = new HubConnection("http://...");
await connection.Start(new WebSocketTransport());  //--- 接続制限数を超えると例外発生

Transportを明示的に指定しなければならないのっぴきならないケース以外は、そのままの設定で利用するのが吉ですね。

おまけ

この記事を書くのと時を同じくして@shibayanも全く同じ内容について書いていました。ブラウザベースで作るとレスポンスを簡単に細かく解析できていいなー。

Windows Azure Web サイトで WebSocket の同時接続数が上限を超えた時の挙動を調べてみた

ということで、本日はここまで。明日は@zio3さんです。よろしくお願い致します!