今回はチャット機能を実装したので、備忘録として記事を残していきます。
AWS Lambdaの作成を行った後にフロント側の設定の記事を上げていこうかと思います。
AWS側はAPI Gateway + Lambda + DynamoDB
の構成で作成していきます。
※ 2020/08/29時点での作成画面
API Gatewayの設定
①API GatewayからAPI作成
API Gatewayから APIの作成
を押下し、
WebSocket APIを作成していきます。
②APIとルート選択式を設定
下記の内容でプロトコルを設定していきます。
プロトコル:WebSocket
項目 | 内容 |
---|---|
API名 | vue_chat_test |
ルート選択式 | $request.body.action |
説明 | vue_chat_test |
③ルートキーを設定内容
API作成後、ルートキーを作成する画面に遷移します。
今回は各ルートキーを下記で設定していきます。
キー名 | 用途 |
---|---|
$connect | クライアントがWebSocket通信を開始する時に利用 |
$disconnect | クライアントがWebSocket通信を終了する時に利用 |
$default | 該当するルートキーが無い場合に利用 |
sendmessage | メッセージ送信時に利用 |
getmessages | メッセージ取得時に利用 |
ただ、Lambda関数を作成していないとルートキーの作成ができないので、
先にdynamoDBのテーブル作成とLambda関数の作成を行っていきます。
dynamoDBでテーブル作成
dynamoDBは下記の内容で作成していきます。
構成は、自分宛てに届いたメッセージを見れるチャットの想定です。
- connections
項目 | 内容 | キー設定 |
---|---|---|
connection_id | コネクションID | パーテーションキー |
user_id | ユーザーID |
- messages
項目 | 内容 | キー設定 |
---|---|---|
user_id | コネクションID | パーテーションキー |
created_at | 作成日付 | ソートキー |
message | メッセージ |
※ 実際にチャット機能として作成する場合は
ルームID、誰から誰に送ったのか、既読情報などの情報も必要かと思いますが、
今回は簡易的なチャット機能なので、こちらの情報だけで作っていきます。
Lambda関数を作成
ランタイムは Python3.8
で下記のLambda関数を作成していきます。
関数 | 内容 | アクセス権限設定 |
---|---|---|
OnConnect | コネクションテーブルに コネクションID・ユーザーIDを保存 |
dynamoDB |
OnDisconnect | コネクションテーブルのレコード削除 | dynamoDB |
SendMessage | メッセージを保存 | dynamoDB,ExecuteAPI |
GetMessages | メッセージを取得 | dynamoDB,ExecuteAPI |
※ アクセス権限は一旦フルアクセス許可で設定
- OnConnect
import json
import boto3
# dynamoDBのテーブル取得
dynamodb = boto3.resource('dynamodb')
connections = dynamodb.Table('connections')
def lambda_handler(event, context):
# 「connectionId、userId」を取得
connection_id = event.get('requestContext',{}).get('connectionId')
user_id = event.get('queryStringParameters',{}).get('userId')
# connectionsテーブルに追加
result = connections.put_item(
Item={
'connection_id': connection_id,
'user_id': str(user_id)
}
)
return { 'statusCode': 200,'body': 'ok' }
- OnDisconnect
import json
import boto3
dynamodb = boto3.resource('dynamodb')
connections = dynamodb.Table('connections')
def lambda_handler(event, context):
connection_id = event.get('requestContext',{}).get('connectionId')
result = connections.delete_item(Key={ 'connection_id': connection_id })
return { 'statusCode': 200, 'body': 'ok' }
- SendMessage
import json
import boto3
import json
from boto3.dynamodb.conditions import Key, Attr
from datetime import datetime, timedelta, timezone
# dynamoDBのテーブル取得
dynamodb = boto3.resource('dynamodb')
connections = dynamodb.Table('connections')
messages = dynamodb.Table('messages')
# TimeZoneの設定
JST = timezone(timedelta(hours=+9), 'JST')
def lambda_handler(event, context):
# パラメータ取得
body = json.loads(event["body"])
user_id = body['user_id']
message = body['message']
# 作成日時
now = "{0:%Y-%m-%d %H:%M:%S}".format(datetime.now(JST))
# messageテーブルへ保存
messages.put_item(
Item={
'user_id': user_id,
'message': message,
'created_at': now
}
)
# ApiGatewayManagementApiの設定
domain_name = event.get('requestContext',{}).get('domainName')
stage = event.get('requestContext',{}).get('stage')
apigw_management = boto3.client('apigatewaymanagementapi',
endpoint_url=F"https://{domain_name}/{stage}")
# 送信先userのconnection_idを取得
connection_items = connections.scan(
ProjectionExpression="connection_id",
FilterExpression=Attr("user_id").eq(str(user_id))
).get('Items')
if len(connection_items) == 0:
return { 'statusCode': 200,'body': 'ok' }
# メッセージ情報を取得
get_message_items = messages.query(
KeyConditionExpression=Key("user_id").eq(str(user_id)) & Key("created_at").lte(now),
ScanIndexForward=True
).get('Items')
# メッセージがあればメッセージ、無ければ空配列を設定
content_all = [] if len(get_message_items) == 0 else get_message_items
# 送信先userにメッセージを返却
for item in connection_items:
_ = apigw_management.post_to_connection(ConnectionId=item['connection_id'],
Data=json.dumps(content_all))
return { 'statusCode': 200,'body': 'ok' }
- GetMessages
import boto3
import json
from boto3.dynamodb.conditions import Key, Attr
from datetime import datetime, timedelta, timezone
# テーブル定義
dynamodb_client = boto3.client('dynamodb')
dynamodb = boto3.resource('dynamodb')
connections = dynamodb.Table('connections')
messages = dynamodb.Table('messages')
# TimeZoneの設定
JST = timezone(timedelta(hours=+9), 'JST')
def lambda_handler(event, context):
# パラメータ情報を変数に設定
body = json.loads(event["body"])
user_id = body['user_id']
connection_id = event.get('requestContext',{}).get('connectionId')
domain_name = event.get('requestContext',{}).get('domainName')
stage = event.get('requestContext',{}).get('stage')
# コネクションIDが合っているか確認
connection_items = connections.query(
ProjectionExpression="connection_id",
KeyConditionExpression=Key("connection_id").eq(connection_id),
FilterExpression=Key('user_id').eq(str(user_id)),
).get('Items')
if len(connection_items) == 0:
return { 'statusCode': 500,'body': 'something went wrong' }
# ApiGatewayManagementApiの設定
apigw_management = boto3.client('apigatewaymanagementapi',
endpoint_url=F"https://{domain_name}/{stage}")
# 取得日時
now = "{0:%Y-%m-%d %H:%M:%S}".format(datetime.now(JST))
# メッセージ情報を取得
get_message_items = messages.query(
KeyConditionExpression=Key("user_id").eq(str(user_id)) & Key("created_at").lte(now),
ScanIndexForward=True
).get('Items')
# メッセージがあればメッセージ、無ければ空配列を設定
content_all = [] if len(get_message_items) == 0 else get_message_items
# コネクションにメッセージをPOST
_ = apigw_management.post_to_connection(
ConnectionId=connection_id,
Data=json.dumps(content_all)
)
return {
'statusCode': 200,
'body': 'ok'
}
ルートキーとLambdaの紐付け
作成したLambdaとAPI Gatewayのルートキーとを紐付けていきます。
ルートキー | Lambda関数 |
---|---|
$connect | OnConnect |
$disconnect | OnDisconnect |
$default | OnConnect |
sendmessage | SendMessage |
getmessages | GetMessages |
作成したWebSocket APIをデプロイ
ルートキーの紐付けが完了したら、WebSocket APIをデプロイしていきます。
今回はステージ名はTest
で作成します。
デプロイが完了したら、 WebSocket URL
と接続 URL
が発行されます。
フロントとの通信に必要なURLになります。
AWS側での設定は以上で、最後に動作確認だけ実施していきます。
wscatでのWebSocket APIを動作確認
wscatをインストール
$ npm install -g wscat
wscatを使って、動作を確認してdynamoDBにデータが保存されていることを確認する。
$ wscat -c [WebSocket URL]?userId=A
Connected (press CTRL+C to quit)
> {"action":"sendmessage","user_id": "A", "message": "Test"}
[{"message": "Test", "user_id": "A", "created_at": "2020-08-29 17:22:43"}]
> {"action":"getmessages", "user_id": "A"}
[{"message": "Test", "user_id": "A", "created_at": "2020-08-29 17:22:43"}]
dynamoDBの状態はこちら!
まとめ
Vue.js + AWS Lambda + dynamo DB
でチャット機能のAWSの設定を作成してみました。
今回は動作することをメインに作成したので、エラー時の処理はあまり入れれていないのと、実際に実装するには他にも考えないといけないことも多いかと思います。
次はVue.jsを使ってフロント画面を作成して、APIとの通信を実施していきます。
※ 投稿日時は未定
ご指摘などがあれば是非コメントお願いいたします!