Let's make a web chat using WebSocket with AWS serverless (Python)!

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 240_news012.jpg

... 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.

What to make

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. qiita作るもの.png

System configuration

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.

qiita作るもの.png

coding

1. First, prepare API Gateway!

1-1. Create a new API Gateway for "WebSocket"

Select WebSocket to create a new API Gateway. キャプチャ.PNG

The route selection formula is "$ request.body.action" here. You can change this later.

1-2. I am making routes one by one from here

Create the route when connecting (connect), the route when disconnecting (disconnnect), and the route when sending a message (sendMessage) one by one. キャプチャ.PNG

But before that, I have to create DynamoDB and IAM roles.

1-3. Deploy and create a stage

I haven't created any routes yet, but I'll deploy and create a stage. キャプチャ.PNG

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.

2. Make an IAM role for Lambda

First, add "AWS LambdaBasicExecutionRole" to run Lambda and "AmazonDynamoDBFullAccess" to run DynamoDB. * It does not have to be Full Access, but it is easy. キャプチャ.PNG

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.

3. Create a table (DynamoDB) that manages connection information

Should the primary key be the string "id"? キャプチャ.PNG

4. Create a connect when connecting

4-1. Make Lambda

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.

4-2. Integrate API Gateway and Lambda

Integrate the above Lambda "ac19_onConnect" with the connect root of API Gateway created earlier. キャプチャ.PNG

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.

5. Create a root (disconnect) when disconnecting

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' }

6. Create a route (sendMessage) when a sendMessage is received

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' }

7. Create an HTML web page

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! qiita作るもの.png

reference

I mainly made it while looking at the AWS page. https://aws.amazon.com/jp/blogs/news/announcing-websocket-apis-in-amazon-api-gateway/

in conclusion

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

Let's make a web chat using WebSocket with AWS serverless (Python)!
Let's make a web framework with Python! (2)
[Chat De Tornado] Make a chat using WebSocket with Tornado
Let's make a GUI with python.
Let's make a graph with python! !!
Let's make a websocket client with Python. (Access token authentication)
Let's make a shiritori game with Python
Let's make a voice slowly with Python
Let's make a Twitter Bot with Python!
Let's replace UWSC with Python (5) Let's make a Robot
Let's make a module for Python using SWIG
[Let's play with Python] Make a household account book
Let's make a simple game with Python 3 and iPhone
Make a fortune with Python
[Super easy] Let's make a LINE BOT with Python.
Let's make a breakout with wxPython
Make a recommender system with python
Let's make a supercomputer with xCAT
If you know Python, you can make a web application with Django
[AWS Hands-on] Let's create a celebrity identification service with a serverless architecture!
Let's make a WEB application for phone book with flask Part 2
Let's make a WEB application for phone book with flask Part 3
Make a scraping app with Python + Django + AWS and change jobs
I made a poker game server chat-holdem using websocket with python
[AWS] Using ini files with Lambda [Python]
Let's create a free group with Python
Daemonize a Python web app with Supervisor
Let's make a simple language with PLY 1
[Python] A quick web application with Bottle!
[Python] Let's make matplotlib compatible with Japanese
Let's make a multilingual site using flask-babel
Run a Python web application with Docker
Let's make a tic-tac-toe AI with Pylearn 2
Let's make a combination calculation in Python
Make a desktop app with Python with Electron
Touch AWS with Serverless Framework and Python
Let's create a chat function with Vue.js + AWS Lambda + dynamo DB [AWS settings]
[Ev3dev] Let's make a remote control program by Python with RPyC protocol
[Streamlit] I hate JavaScript, so I make a web application only with Python
Let's make 3D animation only with Python without using Blender! [FBX SDK Python]
I tried to make a url shortening service serverless with AWS CDK
I want to make a web application using React and Python flask
Implement a simple application with Python full scratch without using a web framework.
Make a Twitter trend bot with heroku + Python
[Python] Make a game with Pyxel-Use an editor-
I want to make a game with Python
Serverless scraping using selenium with [AWS Lambda] -Part 1-
Start a simple Python web server with Docker
Create a web map using Python and GDAL
[Python] Make a simple maze game with Pyxel
Launch a web server with Python and Flask
Let's do web scraping with Python (weather forecast)
Let's do web scraping with Python (stock price)
Extract data from a web page with Python
Let's make an A to B conversion web application with Flask! From scratch ...
WEB scraping with python and try to make a word cloud from reviews
If you want to make a discord bot with python, let's use a framework
Make one repeating string with a Python regular expression.
Try to make a command standby tool with python
(Python) Try to develop a web application using Django
[Practice] Make a Watson app with Python! # 2 [Translation function]