This article is the second day article of NTT TechnoCross Advent Calendar 2019.
Hello. My name is Yasuda. I am in charge of developing new AI-related products at NTT TechnoCross. Although it deviates from the main subject immediately, drawing manga is a recent break, and I am drawing the following manga.
Data linkage understood by manga Enterprise architecture in the age of AI understood by manga IT Strategy Understood by Manga
... By the way, in this article, I would like to see how to do "WebSocket" without AWS server. It seems that AWS API Gateway has been able to establish WebSocket communication since December 2018, and I wanted to try it someday, so I tried it. When I looked it up, I found a lot of sample programs that use node.js in AWS Lambda, but I couldn't find any Python samples ... I'm good at Python, so I'd like to write Python code.
Make a web chat. When you connect to the web page and write a message, the message is delivered in real time to all the terminals connected to the page (having a WebSocket session) at that time.
It is created by combining API Gateway, Lambda, and DynamoDB, which are typical elements of AWS serverless. The web page to be published works even if you put the HTML file on the PC local, or you can easily publish it by putting it on the static hosting of S3.
Select WebSocket to create a new API Gateway.
The route selection formula is "$ request.body.action" here. You can change this later.
Create the route when connecting (connect), the route when disconnecting (disconnnect), and the route when sending a message (sendMessage) one by one.
But before that, I have to create DynamoDB and IAM roles.
I haven't created any routes yet, but I'll deploy and create a stage.
Where it is erased in blue, a unique character string is entered. This unique string will be used the next time you create an IAM role.
First, add "AWS LambdaBasicExecutionRole" to run Lambda and "AmazonDynamoDBFullAccess" to run DynamoDB. * It does not have to be Full Access, but it is easy.
After that, set the following inline policy. You can now access your API Gatteway from Lambda.
Inline policy
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"execute-api:ManageConnections"
],
"Resource": [
"arn:aws:execute-api:ap-northeast-1:<Tenant ID here>:<Write the API Gateway unique character string you set earlier here.>/*"
],
"Effect": "Allow"
}
]
}
Where the blue part in the image of the above role is erased, try setting the unique character string that appeared when you first deployed API Gateway.
Should the primary key be the string "id"?
Create a new Lambda / Python 3.8. Set up the IAM role you used earlier. The program is simple because there are only two lines in effect, without considering the abnormal system. (Actually, it should be made in consideration of the abnormal system.) Just get the connected connection_id and register it in DynamoDB.
ac19_onConnect/lambda_function.py
import json
import os
import boto3
dynamodb = boto3.resource('dynamodb')
connections = dynamodb.Table(os.environ['CONNECTION_TABLE'])
def lambda_handler(event, context):
connection_id = event.get('requestContext',{}).get('connectionId')
result = connections.put_item(Item={ 'id': connection_id })
return { 'statusCode': 200,'body': 'ok' }
Set the name of the DynamoDB created earlier in the Lambda environment variable "CONNECTION_TABLE". The timeout time should be 3 seconds, but I set it to about 1 minute.
Integrate the above Lambda "ac19_onConnect" with the connect root of API Gateway created earlier.
The procedure is the same when integrating the disconnect and sendMessage roots described below with Lambda. Don't forget to deploy the stage once it's integrated.
Follow the same procedure as the connect route, first create a Lambda program and integrate it with the API Gateway disconnect route. If you don't think about the anomaly, the program is simple, just get the disconnected connection_id and remove it from DynamoDB. Other settings of Lambda (role, timeout, environment variables) are the same as the previous onConnect program.
ac19_onDisconnect/lambda_function.py
import json
import os
import logging
import boto3
dynamodb = boto3.resource('dynamodb')
connections = dynamodb.Table(os.environ['CONNECTION_TABLE'])
def lambda_handler(event, context):
connection_id = event.get('requestContext',{}).get('connectionId')
result = connections.delete_item(Key={ 'id': connection_id })
return { 'statusCode': 200, 'body': 'ok' }
Only sendMessage has a few lines, but it's easy to do. When you receive a sendMessage, it just delivers the message to each connection registered in DynamoDB. Follow the same steps as before, first create a Lambda program, create a sendMessage route in API Gateway, and integrate it with that route. After that, don't forget to deploy API Geteway to the stage. Other Lambda settings (role, timeout, environment variables) are the same as the previous two Lambda.
ac19_sendMessage/lambda_function.py
import json
import os
import sys
import logging
import boto3
import botocore
dynamodb = boto3.resource('dynamodb')
connections = dynamodb.Table(os.environ['CONNECTION_TABLE'])
def lambda_handler(event, context):
post_data = json.loads(event.get('body', '{}')).get('data')
print(post_data)
domain_name = event.get('requestContext',{}).get('domainName')
stage = event.get('requestContext',{}).get('stage')
items = connections.scan(ProjectionExpression='id').get('Items')
if items is None:
return { 'statusCode': 500,'body': 'something went wrong' }
apigw_management = boto3.client('apigatewaymanagementapi',
endpoint_url=F"https://{domain_name}/{stage}")
for item in items:
try:
print(item)
_ = apigw_management.post_to_connection(ConnectionId=item['id'],
Data=post_data)
except:
pass
return { 'statusCode': 200,'body': 'ok' }
In the place of "wss: //" in the program, write the unique character string of API Gateway that you set earlier. This HTML can be run even if it is placed locally on the PC, or even if it is published on static hosting of AWS S3.
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>"WebSocket" trial chat!</title>
</head>
<body>
<H3>"WebSocket" trial chat!</H3>
<input id="input" type="text" />
<button onclick="send()">Send</button>
<pre id="output"></pre>
<script>
var input = document.getElementById('input');
var output = document.getElementById('output');
var socket = new WebSocket("wss://<Write the API Gateway unique character string you set earlier here.>.execute-api.ap-northeast-1.amazonaws.com/v01");
socket.onopen = function() {
output.innerHTML += "I was able to connect!\n";
};
socket.onmessage = function(e) {
output.innerHTML += "Received:" + e.data + "\n";
};
function send() {
socket.send(JSON.stringify(
{
"action":"sendMessage",
"data": input.value
}
));
input.value = "";
};
</script>
</body>
</html>
Alright! That's all there is to it! Let's try it!
I mainly made it while looking at the AWS page. https://aws.amazon.com/jp/blogs/news/announcing-websocket-apis-in-amazon-api-gateway/
Now that I can set up WebSocket without AWS server, I am excited to be able to make various things. Then, Day 3 of NTT TechnoCross Advent Calendar 2019 Please continue to enjoy 5bc108bdf5068ef7be2f)!
Recommended Posts