Является ли пример чата веб-сокетов AWS API Gateway полностью асинхронным? - PullRequest
0 голосов
/ 10 октября 2019

Я смотрю на поддержку веб-сокетов AWS API Gateway, объявленную относительно недавно -

https://aws.amazon.com/blogs/compute/announcing-websocket-apis-in-amazon-api-gateway/

У них есть пример сервера чата -

https://github.com/aws-samples/simple-websockets-chat-app/blob/master/sendmessage/app.js

, который у меня запущен, очень хорошо.

Если вы отправляете сообщение, sendmessage Lambda передает это сообщение всем подключенным пользователям через следующее -

// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT-0

const AWS = require('aws-sdk');

const ddb = new AWS.DynamoDB.DocumentClient({ apiVersion: '2012-08-10' });

const { TABLE_NAME } = process.env;

exports.handler = async (event, context) => {
  let connectionData;

  try {
    connectionData = await ddb.scan({ TableName: TABLE_NAME, ProjectionExpression: 'connectionId' }).promise();
  } catch (e) {
    return { statusCode: 500, body: e.stack };
  }

  const apigwManagementApi = new AWS.ApiGatewayManagementApi({
    apiVersion: '2018-11-29',
    endpoint: event.requestContext.domainName + '/' + event.requestContext.stage
  });

  const postData = JSON.parse(event.body).data;

  const postCalls = connectionData.Items.map(async ({ connectionId }) => {
    try {
      await apigwManagementApi.postToConnection({ ConnectionId: connectionId, Data: postData }).promise();
    } catch (e) {
      if (e.statusCode === 410) {
        console.log(`Found stale connection, deleting ${connectionId}`);
        await ddb.delete({ TableName: TABLE_NAME, Key: { connectionId } }).promise();
      } else {
        throw e;
      }
    }
  });

  try {
    await Promise.all(postCalls);
  } catch (e) {
    return { statusCode: 500, body: e.stack };
  }

  return { statusCode: 200, body: 'Data sent.' };
};

Теперь, к сожалению (к счастью ??), у меня есть фон Python / Erlang, а не Javascript / nodejs. Таким образом, я могу видеть, что некоторые из этих действий делают, а именно перебирают соединения в таблице DynamoDB и отправляют ответ каждому. Кроме того, выглядит так, как будто он работает асинхронно, с использованием ключевых слов async и await, которые, я полагаю, являются Обещаниями. Но я не могу быть уверенным , это действует асинхронно, что меня беспокоит ... если у меня есть одна лямбда, которая перебирает большое количество соединений и делает синхронные вызовы, это не сработает.

Итак, особенно в отношении этой части кода,

 const postCalls = connectionData.Items.map(async ({ connectionId }) => {
    try {
      await apigwManagementApi.postToConnection({ ConnectionId: connectionId, Data: postData }).promise();
    } catch (e) {
      if (e.statusCode === 410) {
        console.log(`Found stale connection, deleting ${connectionId}`);
        await ddb.delete({ TableName: TABLE_NAME, Key: { connectionId } }).promise();
      } else {
        throw e;
      }
    }
  });

Могу ли я быть уверен, что это отправка postData всем соединениям в полностью асинхронном режиме? Нужно ли мне беспокоиться о том, что одна лямбда может передавать сообщения тысячам клиентов?

1 Ответ

1 голос
/ 10 октября 2019

Это не называется simple -websockets-chat-app даром:)

Читая код, он делает именно то, что вас беспокоит. Только один лямбда-экземпляр запускает сообщение для всех подключений.

Но это чат, распространено ли иметь тысячи пользователей?

Выглядит так, как будто он работает асинхроннос помощью ключевых слов async и await, которые, как я полагаю, называются Promises

Да, он работает асинхронно, но лямбда будет работать до тех пор, пока не будут отправлены все сообщения.


О

  await apigwManagementApi.postToConnection({ ConnectionId: connectionId, Data: postData }).promise();

Это оказывает очень небольшое давление на саму лямбду, делая только удаленный вызов за раз. И будучи асинхронным, он не ждет ответа, но продолжает посылать все больше и больше.

(примечание: я бы использовал forEach вместо map в этом случае)

Решение для чата с абсурдно высоким трафиком было бы:

  • В «DispatcherLambda» разделите сканирование из TABLE_NAME на несколько кусков, отправьте их в очередь SQS и подпишитесь на нее Lambda. Таким образом, будет несколько контейнеров, выполняющих этот код (в зависимости от выбранной вами степени детализации)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...