Amazon Web Services ブログ

[発表]Amazon API GatewayでWebsocketが利用可能

本日より、任意のサーバーをプロビジョニングして管理することなく、Amazon API GatewayでWebSocket APIを使用して双方向通信アプリケーションを構築できます。

HTTPベースのAPIは、リクエスト/レスポンスモデルを使用して、クライアントがサービスにリクエストを送信し、サービスがクライアントに同期して応答します。 WebSocketベースのAPIは本質的に双方向です。 これは、クライアントがメッセージをサービスに送信し、サービスが独立してメッセージをクライアントに送信できることを意味します。

この双方向の振る舞いにより、クライアント/サーバーとのやりとりがより豊富になります。これは、明示的なリクエストをする必要のないクライアントにデータをプッシュできるためです。 WebSocket APIは、チャットアプリケーション、コラボレーションプラットフォーム、マルチプレイヤーゲーム、金融取引プラットフォームなどのリアルタイムアプリケーションでよく使用されます。

このブログでは、WebSocket APIとAPI Gatewayを使用してサーバーレスのリアルタイムチャットアプリケーションを構築する方法について説明します。

概要

歴史的に、WebSocket APIを構築するためには、WebSocketプロトコルの基礎となる永続的な接続の管理を担当するホストの設定が必要でした。API Gatewayでは、これはもはや必要ではありません。 API Gatewayは、クライアントとサービス間の接続を処理します。 AWS Lambda、Amazon Kinesis、その他のHTTPエンドポイントなどのHTTPベースのバックエンドを使用してビジネスロジックを構築できます。

まず、API GatewayのWebSocket APIの概念をいくつか紹介します。 1つは、ルートと呼ばれる新しいリソースタイプです。ルートは、API Gatewayが特定のタイプのクライアントリクエストを処理する方法を記述し、ルートを識別するために提供する値であるrouteKeyパラメータを含みます。

WebSocket APIは、1つまたは複数のルートで構成されています。特定のインバウンドリクエストで使用するルートを決定するには、ルートセレクションエクスプレッション(選択式)を指定します。式は、ルートのrouteKey値の1つに対応する値を生成する着信リクエストに対して評価されます。

API Gatewayでルートに使用できる3つの特別なrouteKey値があります。

 

  • $default – 選択式がAPIルート内の他のrouteKeyに一致しない値を生成する場合に使用されます。 これは、例えば、一般的なエラー処理メカニズムを実装するために使用できます。
  • $connect – クライアントがWebSocket APIに最初に接続するときに、関連するルートが使用されます。
  • $disconnect – クライアントがAPIから切断すると、関連するルートが使用されます。 この呼び出しは、ベストエフォート方式で行われます。

これらの特別なrouteKey値のいずれかのルートを指定する必要はありません。

サーバレスなリアルタイムチャットアプリケーションの構築

API Gatewayの新しいWebSocket API機能の使い方を理解するために、リアルタイムチャットアプリの構築方法を紹介します。 簡単にするため、このアプリには1つのチャットルームしかないと仮定します。 アプリに含まれる機能は次のとおりです。

  • クライアントはWebSocket APIに接続するときにチャットルームに参加します。
  • バックエンドは、ユーザーがWebSocket APIに接続した後に提供されるコールバックURLを使用して、特定のユーザーにメッセージを送信できます。
  • ユーザーはルームにメッセージを送信できます。
  • 切断されたクライアントはチャットルームから削除されます。

リアルタイムチャットアプリケーションの概要は以下です。

アプリケーションは、クライアントとサーバー(1)間の接続を処理するAPI GatewayのWebSocket APIで構成されています。 クライアントがAPIに接続(2)または切断(5)すると、2つのAWS Lambdaが反応します。 sendMessage関数(3)は、クライアントがサーバーにメッセージを送信するときに呼び出されます。 サーバーは、新しいAPI Gateway管理APIを使用して、接続されているすべてのクライアント(4)にメッセージを送信します。 接続された各クライアントを追跡するには、DynamoDBテーブルを使用して接続識別子を保持します。

これをより迅速に実行できるように、簡単にデプロイできるAWS Serverless Application Repository(SAR)のsimple websocket-chat-app(Lambda関数、DynamoDBテーブル定義、IAMロールなど)を提供しました )。 さらに、GitHubリポジトリのAWS SAMアプリケーションで同じアプリを使用できます。

 

新しいWebsocket APIの作成

  1. API Gatewayコンソールで、Create APINew APIを選択します。
  2. Choose the protocolで、WebSocketを選択します。
  3. API Nameには、My Chat APIを入力します。
  4. Route Selection Expressionには、$ request.body.actionと入力します。
  5. 必要に応じて説明を入力し、Create APIをクリックします。

ルート選択式の値で記述された属性は、クライアントがAPIに送信するすべてのメッセージに存在する必要があります。 リクエストメッセージの一例を以下に示します。

{
    "action": "sendmessage",
    “data”: "Hello, I am using WebSocket APIs in API Gateway.”
}

WebSocket APIは、メッセージの内容に基づいてメッセージをルーティングするために、ルーティングキーとして機能するJSON形式のメッセージを必要とします。

ルートの管理

ここで、リクエストに応答するように新しいAPIを構成します。 このアプリでは、前述のように3つのルートを作成します。 まず、Lambda統合を使用してsendmessageルートを構成します。

  1. API GatewayコンソールのMy Chat APIRoutesを選択します。
  2. New Route Keysendmessageと入力して確認します。

各ルートには、そのモデルスキーマなどのルート固有情報と、ターゲット統合の参照情報が含まれています。 sendmessageルートが作成されたら、Lambda関数をメッセージの送信を担当する統合として使用します。

この時点で、AWS Serverless Application Repository backend をデプロイするか、AWS SAMを使用して自分自身でデプロイしました。 Lambdaのコンソールで、sendMessage関数を見てください。 この関数は、クライアントの1つが送信したデータを受け取り、現在接続されているすべてのクライアントを検索し、それぞれに提供されたデータを送信します。 Lambda関数のスニペットは次のとおりです。

DDB.scan(scanParams, function (err, data) {
  // some code omitted for brevity

  var apigwManagementApi = new AWS.APIGatewayManagementAPI({
    apiVersion: "2018-11-29",
    endpoint: event.requestContext.domainName " /" + event.requestContext.stage
  });
  var postParams = {
    Data: JSON.parse(event.body).data
  };

  data.Items.forEach(function (element) {
    postParams.ConnectionId = element.connectionId.S;
    apigwManagementApi.postToConnection(postParams, function (err, data) {
      // some code omitted for brevity
    });
  });

クライアントのうちの1人が “sendmessage”アクションでメッセージを送信すると、この関数はDynamoDBテーブルをスキャンして、現在接続されているすべてのクライアントを検索します。 それぞれに対して、提供されたデータとのpostToConnection呼び出しを行います。

メッセージを正しく送信するには、クライアントが接続されているAPIを知っている必要があります。 これは、APIを指すようにSDKエンドポイントを明示的に設定することを意味します。

残っているのは、チャットルームに接続しているクライアントを適切に追跡することです。 そのためには、2つの特別なrouteKey値を利用し、$connect$disconnectのルートを実装します。

onConnect関数は、requestContextからのconnectionId値をDynamoDBテーブルに挿入します。

exports.handler = function(event, context, callback) {
  var putParams = {
    TableName: process.env.TABLE_NAME,
    Item: {
      connectionId: { S: event.requestContext.connectionId }
    }
  };

  DDB.putItem(putParams, function(err, data) {
    callback(null, {
      statusCode: err ? 500 : 200,
      body: err ? "Failed to connect: " + JSON.stringify(err) : "Connected"
    });
  });
};

onDisconnect関数は、指定されたconnectionId値に対応するレコードを削除します。

exports.handler = function(event, context, callback) {
  var deleteParams = {
    TableName: process.env.TABLE_NAME,
    Key: {
      connectionId: { S: event.requestContext.connectionId }
    }
  };

  DDB.deleteItem(deleteParams, function(err) {
    callback(null, {
      statusCode: err ? 500 : 200,
      body: err ? "Failed to disconnect: " + JSON.stringify(err) : "Disconnected."
    });
  });
};

前述のように、onDisconnect関数は呼び出されないことがあります。 再度使用できなくなる接続を再試行しないようにするには、postToConnection呼び出しが成功しない場合に備えて、追加のロジックを追加します。

if (err.statusCode === 410) {
  console.log("Found stale connection, deleting " + postParams.connectionId);
  DDB.deleteItem({ TableName: process.env.TABLE_NAME,
                   Key: { connectionId: { S: postParams.connectionId } } });
} else {
  console.log("Failed to post. Error: " + JSON.stringify(err));
}

接続がもはや利用できなくなると、API Gatewayは410 GONEのステータスを返します。 このような場合は、DynamoDBテーブルから識別子を削除してください。

接続されたクライアントに呼び出しを行うには、アプリケーションに新しいアクセス権 “execute-api:ManageConnections”が必要です。 これは、AWS SAMテンプレートで処理されます。

Websocket APIのデプロイ

次のステップは、WebSocket APIをデプロイすることです。 はじめてAPIをデプロイするので、「dev」などのステージを作成し、サンプルの説明を付けます。

ステージエディタ画面には、設定、ログ/トレース、ステージ変数、履歴など、WebSocket APIを管理するためのすべての情報が表示されます。 また、API Gatewayのスロットルに関する記載を確認してください。

新しく作成されたWebSocket API用のダッシュボードを使用してテストを監視するようにしてください。

チャットAPIのテスト

WebSocket APIをテストするには、オープンソースのコマンドラインツールwscatを使用できます。

1. NPMをインストールします。

2. wscatをインストールします。

$ npm install -g wscat

3. コンソールで、次のコマンドを実行して公開APIエンドポイントに接続します。

$ wscat -c wss://{YOUR-API-ID}.execute-api.{YOUR-REGION}.amazonaws.com/{STAGE}

4. sendMessage関数をテストするには、次の例のようなJSONメッセージを送信します。 Lambda関数はコールバックURLを使用してそれを返します:

$ wscat -c wss://{YOUR-API-ID}.execute-api.{YOUR-REGION}.amazonaws.com/dev
connected (press CTRL+C to quit)
> {"action":"sendmessage", "data":"hello world"}
< hello world

wscatを複数のコンソールで実行すると、それぞれが “hello world”を受信します。

これで、WebSocket APIからメッセージを送信したり、応答を返すことができます。

結論

この記事では、API GatewayでWebSocket APIを使用する方法を紹介しました。これには、ルートとルート選択式を使用したクライアントとサーバー間のメッセージ交換が含まれます。 LambdaとDynamoDBを使用して、サーバーレスのリアルタイムチャットアプリケーションを作成しました。 この記事では可能な限りのことしか書かれていませんでしたが、サーバーを一切管理する必要はありません。

API Gatewayで独自のWebSocket APIを作成することは今日から可能です。 詳細については、Amazon API Gateway Developer Guideを参照してください。 George Maoが主催するAmazon API Gateway webinarでサポートされているWebSocket APIを使用して、Real-Timeアプリケーションを構築することもできます。

新しいWebSocket APIをどのように使用して新しいアプリケーションが登場するのか、とても嬉しく思っています。 私は、AWSのフォーラム、またはTwitterでのフィードバックを歓迎します。

Happy coding!

 

このブログは、AWS Senior Solutions Architect – World Wide Public Sector-Canada & JT Thompson, AWS Principal Software Development Engineer – Amazon API Gatewayによって投稿されています。

 

飜訳:SA 小梁川が担当しました。

原文はこちら