Kuinka luoda reaaliaikaisia ​​sovelluksia WebSocketsin avulla AWS API -yhdyskäytävän ja Lambdan kanssa

Äskettäin AWS on ilmoittanut käynnistävänsä laajalti vaaditun ominaisuuden: WebSockets for Amazon API Gateway. WebSocketsin avulla pystymme luomaan kaksisuuntaisen viestintälinjan, jota voidaan käyttää monissa tilanteissa, kuten reaaliaikaisissa sovelluksissa. Tämä herättää kysymyksen: mitä ovat reaaliaikaiset sovellukset? Joten vastaamme ensin tähän kysymykseen.

Suurin osa tällä hetkellä toiminnassa olevista sovelluksista käyttää asiakas-palvelin-arkkitehtuuria. Asiakas-palvelin-arkkitehtuurissa asiakas lähettää pyynnöt Internetin kautta verkkoyhteyden kautta ja sitten palvelin käsittelee pyynnöt ja lähettää vastauksen asiakkaalle.

Täällä voit nähdä, että asiakas on aloittanut yhteyden palvelimen kanssa. Joten ensin asiakas aloittaa viestinnän ja palvelin vastaa palvelimen lähettämään pyyntöön. Joten mitä jos palvelin haluaa aloittaa viestinnän ja lähettää vastauksia ilman, että asiakas pyytää niitä ensin? Siellä reaaliaikaiset sovellukset tulevat peliin.

Reaaliaikaiset sovellukset ovat sovelluksia, joissa palvelin saa mahdollisuuden työntää asiakkaita ilman, että asiakas pyytää ensin tietoja. Oletetaan, että meillä on chat-sovellus, jossa kaksi chat-asiakasta voi kommunikoida palvelimen kautta. Tässä tilanteessa on tuhlausta, jos kaikki chat-asiakkaat pyytävät tietoja palvelimelta kuten joka sekunti. Tehokkaampaa on, jos palvelin lähettää tietoja asiakas chat-sovelluksiin, kun chat on vastaanotettu. Tämä toiminto voidaan tehdä reaaliaikaisilla sovelluksilla.

Amazon ilmoitti tukevansa WebSocketsia API-yhdyskäytävässä AWS re: Invent 2018: ssä. Myöhemmin joulukuussa he julkaisivat sen API-yhdyskäytävässä. Joten nyt AWS-infrastruktuuria käyttämällä voimme luoda reaaliaikaisia ​​sovelluksia API-yhdyskäytävän avulla.

Tässä viestissä aiomme luoda yksinkertaisen chat-sovelluksen käyttämällä API Gateway WebSocketsia. Ennen kuin aloitamme chat-sovelluksen käyttöönoton, on joitain käsitteitä, jotka meidän on ymmärrettävä reaaliaikaisista sovelluksista ja API-yhdyskäytävästä.

WebSocket API -konseptit

WebSocket-sovellusliittymä koostuu yhdestä tai useammasta reitistä. Reitin valintaan ilmaisu on olemassa määrittää, mikä reitti tietyn saapuva pyyntö tulisi käyttää, joka tullaan antamaan saapuvan pyynnön. Lauseketta verrataan saapuvaan pyyntöön tuottaa arvo, joka vastaa yhtä reittisi routeKey- arvoista. Esimerkiksi, jos JSON-viestit sisältävät ominaisuuskutsutoiminnon ja haluat suorittaa erilaisia ​​toimintoja tämän ominaisuuden perusteella, reitinvalintalauseke voi olla ${request.body.action}.

Esimerkiksi: Jos JSON-viestisi näyttää tältä: {"action": "onMessage", "message": "Hei kaikki"}, tälle pyynnölle valitaan onMessage-reitti.

Oletuksena on kolme reittiä, jotka on jo määritelty WebSocket-sovellusliittymässä. Alla mainittujen reittien lisäksi voimme lisätä räätälöityjä reittejä tarpeisiimme.

  • $ default - Käytetään, kun reitinvalintalauseke tuottaa arvon, joka ei vastaa mitään muita API-reittien reittinäppäimiä. Tätä voidaan käyttää esimerkiksi yleisen virhekäsittelymekanismin toteuttamiseen.
  • $ connect - Liitettyä reittiä käytetään, kun asiakas muodostaa ensimmäisen kerran yhteyden WebSocket-sovellusliittymään.
  • $ disconnect - Asiaan liittyvää reittiä käytetään, kun asiakas katkaisee yhteyden sovellusliittymään.

Kun laite on yhdistetty onnistuneesti WebSocket API: n kautta, laitteelle varataan yksilöllinen yhteystunnus. Tämä yhteystunnus säilyy koko eliniän ajan, jos yhteys. Viestien lähettämiseksi takaisin laitteelle meidän on käytettävä seuraavaa POST-pyyntöä käyttämällä yhteystunnusta.

POST //{api-id}.execute-api.us-east 1.amazonaws.com/{stage}/@connections/{connection_id}

Chat-sovelluksen käyttöönotto

Kun olet oppinut WebSocket API: n peruskäsitteet, katsotaanpa, miten voimme luoda reaaliaikaisen sovelluksen WebSocket API: n avulla. Tässä viestissä aiomme toteuttaa yksinkertaisen chat-sovelluksen käyttämällä WebSocket API: ta, AWS LAmbdaa ja DynamoDB: tä. Seuraava kaavio näyttää reaaliaikaisen sovelluksemme arkkitehtuurin.

Sovelluksessamme laitteet kytketään API-yhdyskäytävään. Kun laite kytketään, lambda-toiminto tallentaa yhteystunnuksen DynamoDB-taulukkoon. Jos haluamme lähettää viestin takaisin laitteelle, toinen lambda-toiminto hakee yhteystunnuksen ja POST-tiedot takaisin laitteeseen takaisinsoitto-URL-osoitteen avulla.

WebSocket-sovellusliittymän luominen

WebSocket-sovellusliittymän luomiseksi meidän on ensin siirryttävä Amazon API Gateway -palveluun konsolin avulla. Valitse siellä luoda uusi sovellusliittymä. Napsauta WebSocket luodaksesi WebSocket-sovellusliittymän, antamalla sovellusliittymän nimen ja reittivalintalausekkeemme. Meidän tapauksessamme lisää $ request.body.action valintalausekkeeksi ja paina Luo sovellusliittymä.

API: n luomisen jälkeen meidät ohjataan reittisivulle. Täällä voimme nähdä jo ennalta määritellyt kolme reittiä: $ connect, $ disconnect ja $ default. Luomme myös mukautetun reitin $ onMessage. Arkkitehtuurissamme $ connect- ja $ disconnect -reitit saavuttavat seuraavat tehtävät:

  • $ connect - kun tätä reittiä kutsutaan, Lambda-toiminto lisää liitetyn laitteen yhteystunnuksen DynamoDB: hen.
  • $ disconnect - kun tätä reittiä kutsutaan, Lambda-toiminto poistaa irrotetun laitteen yhteysnumeron DynamoDB: stä.
  • onMessage - kun tätä reittiä kutsutaan, viestin runko lähetetään kaikille laitteille, jotka ovat yhteydessä toisiinsa.

Ennen reitin lisäämistä yllä olevan mukaisesti meidän on tehtävä neljä tehtävää:

  • Luo DynamoDB-taulukko
  • Luo connect lambda -toiminto
  • Luo katkaise lambda-toiminto
  • Luo onMessage lambda -toiminto

Luo ensin DynamoDB-taulukko. Siirry DynamoDB-palveluun ja luo uusi taulukko nimeltä Chat. Lisää ensisijainen avain nimellä "connectionid".

Seuraavaksi luodaan connect Lambda -toiminto. Luo Lambda-toiminto siirtymällä Lambda-palveluihin ja valitsemalla Luo toiminto. Valitse Kirjoittaja alusta alkaen ja anna nimeksi ChatRoomConnectFunction ja rooli tarvittavilla käyttöoikeuksilla. (Roolilla on oltava lupa hankkia, laittaa ja poistaa kohteita DynamoDB: stä, soittaa API-kutsuja API-yhdyskäytävässä.)

Lisää seuraava koodi lambda-funktion koodiin. Tämä koodi lisää yhdistetyn laitteen yhteystunnuksen luomallemme DynamoDB-taulukkoon.

exports.handler = (event, context, callback) => { const connectionId = event.requestContext.connectionId; addConnectionId(connectionId).then(() => { callback(null, { statusCode: 200, }) });}
function addConnectionId(connectionId) { return ddb.put({ TableName: 'Chat', Item: { connectionid : connectionId }, }).promise();}

Seuraavaksi luodaan myös katkaise lambda-toiminto. Luo uusi lambda-funktio nimeltä Samat vaiheet

'ChatRoomDonnectFunction'. Lisää seuraava koodi funktioon. Tämä koodi poistaa yhteystunnuksen DynamoDB-taulukosta, kun laite katkaistaan.

const AWS = require('aws-sdk');const ddb = new AWS.DynamoDB.DocumentClient();
exports.handler = (event, context, callback) => { const connectionId = event.requestContext.connectionId; addConnectionId(connectionId).then(() => { callback(null, { statusCode: 200, }) });}
function addConnectionId(connectionId) { return ddb.delete({ TableName: 'Chat', Key: { connectionid : connectionId, }, }).promise();}

Nyt olemme luoneet DynamoDB-taulukon ja kaksi lambda-toimintoa. Ennen kuin luot kolmannen lambda-toiminnon, palataan takaisin API-yhdyskäytävään ja määritetään reitit luomiemme lambda-toimintojen avulla. Napsauta ensin $ connect route. Valitse integrointityypiksi Lambda-toiminto ja valitse ChatRoomConnectionFunction.

Voimme tehdä saman myös $ disconnect -reitillä, missä lambda-toiminto on ChatRoomDisconnectionFunction:

Now that we have configured our $connect and $disconnect routes, we can actually test whether out WebSocket API is working. To do that we must first to deploy the API. In the Actions button, click on Deploy API to deploy. Give a stage name such as Test since we are only deploying the API for testing.

After deploying, we will be presented with two URLs. The first URL is called WebSocket URL and the second is called Connection URL.

The WebSocket URL is the URL that is used to connect through WebSockets to our API by devices. And the second URL, which is Connection URL, is the URL which we will use to call back to the devices which are connected. Since we have not yet configured call back to devices, let’s first only test the $connect and $disconnect routes.

To call through WebSockets we can use the wscat tool. To install it, we need to just issue the npm install -g wscat command in the command line. After installing, we can use the tool using wscat command. To connect to our WebSocket API, issue the following command. Make sure to replace the WebSocket URL with the correct URL provided to you.

wscat -c wss://bh5a9s7j1e.execute-api.us-east-1.amazonaws.com/Test

When the connection is successful, a connected message will be displayed on the terminal. To check whether our lambda function is working, we can go to DynamoDB and look in the table for the connection id of the connected terminal.

As above, we can test the disconnect as well by pressing CTRL + C which will simulate a disconnection.

Now that we have tested our two routes, let us look into the custom route onMessage. What this custom route will do is it will get a message from the device and send the message to all the devices that are connected to the WebSocket API. To achieve this we are going to need another lambda function which will query our DynamoDB table, get all the connection ids, and send the message to them.

Let’s first create the lambda function in the same way we created other two lambda functions. Name the lambda function ChatRoomOnMessageFunction and copy the following code to the function code.

const AWS = require('aws-sdk');const ddb = new AWS.DynamoDB.DocumentClient();require('./patch.js');
let send = undefined;function init(event) { console.log(event) const apigwManagementApi = new AWS.ApiGatewayManagementApi({ apiVersion: '2018-11-29', endpoint: event.requestContext.domainName + '/' + event.requestContext.stage }); send = async (connectionId, data) => { await apigwManagementApi.postToConnection({ ConnectionId: connectionId, Data: `Echo: ${data}` }).promise(); }}
exports.handler = (event, context, callback) => { init(event); let message = JSON.parse(event.body).message getConnections().then((data) => { console.log(data.Items); data.Items.forEach(function(connection) { console.log("Connection " +connection.connectionid) send(connection.connectionid, message); }); }); return {}};
function getConnections(){ return ddb.scan({ TableName: 'Chat', }).promise();}

The above code will scan the DynamoDB to get all the available records in the table. For each record, it will POST a message using the Connection URL provided to us in the API. In the code, we expect that the devices will send the message in the attribute named ‘message’ which the lambda function will parse and send to others.

Since WebSockets API is still new there are some things we need to do manually. Create a new file named patch.js and add the following code inside it.

require('aws-sdk/lib/node_loader');var AWS = require('aws-sdk/lib/core');var Service = AWS.Service;var apiLoader = AWS.apiLoader;
apiLoader.services['apigatewaymanagementapi'] = {};AWS.ApiGatewayManagementApi = Service.defineService('apigatewaymanagementapi', ['2018-11-29']);Object.defineProperty(apiLoader.services['apigatewaymanagementapi'], '2018-11-29', { get: function get() { var model = { "metadata": { "apiVersion": "2018-11-29", "endpointPrefix": "execute-api", "signingName": "execute-api", "serviceFullName": "AmazonApiGatewayManagementApi", "serviceId": "ApiGatewayManagementApi", "protocol": "rest-json", "jsonVersion": "1.1", "uid": "apigatewaymanagementapi-2018-11-29", "signatureVersion": "v4" }, "operations": { "PostToConnection": { "http": { "requestUri": "/@connections/{connectionId}", "responseCode": 200 }, "input": { "type": "structure", "members": { "Data": { "type": "blob" }, "ConnectionId": { "location": "uri", "locationName": "connectionId" } }, "required": [ "ConnectionId", "Data" ], "payload": "Data" } } }, "shapes": {} } model.paginators = { "pagination": {} } return model; }, enumerable: true, configurable: true});
module.exports = AWS.ApiGatewayManagementApi;

I took the above code from this article. The functionality of this code is to automatically create the Callback URL for our API and send the POST request.

Now that we have created the lambda function we can go ahead and create our custom route in API Gateway. In the New Route Key, add ‘OnMessage’ as a route and add the custom route. As configurations were done for other routes, add our lambda function to this custom route and deploy the API.

Now we have completed our WebSocket API and we can fully test the application. To test that sending messages works for multiple devices, we can open and connect using multiple terminals.

After connecting, issue the following JSON to send messages:

{"action" : "onMessage" , "message" : "Hello everyone"}

Here, the action is the custom route we defined and the message is the data that need to be sent to other devices.

Se on yksinkertainen chat-sovelluksemme, joka käyttää AWS WebSocket API: ta. Emme ole itse määrittäneet $ defalut -reittiä, jota kutsutaan joka kerta, kun reittiä ei löydy. Jätän tämän reitin toteuttamisen sinulle. Kiitos ja nähdään toisessa viestissä. :)