AWS Aurora Serverless - 通信链接失败

11
我在我的Python代码中使用MySQL Aurora Serverless集群(启用了Data API),并且我遇到了“communications link failure”异常。通常情况下,这是由于集群静止一段时间所致。但是,一旦集群处于活动状态,我就不会遇到错误。每次在正常工作之前,我必须发送3-4个请求。
异常详细信息:
最后一个数据包成功发送到服务器的时间是0毫秒之前。驱动程序没有从服务器接收任何数据包。调用ExecuteStatement操作时发生错误(BadRequestException):通信链接失败。
我该如何解决这个问题?我正在使用标准的boto3库。
6个回答

11

这是来自AWS高级商业支持的答复。

Summary: It is an expected behavior

详细回答:

我看到您在 Aurora Serverless 实例处于非活动状态时会收到此错误,一旦实例处于活动状态并接受连接,您就不会再收到它。请注意,这是一种预期行为。总的来说,Aurora Serverless 的工作方式与预置的 Aurora 不同,在 Aurora Serverless 中,当集群处于“休眠”状态时,它没有分配计算资源,并且当接收到 db 连接时,计算资源将被分配。由于这种行为,您将不得不“唤醒”集群,并且第一个连接成功可能需要几分钟,就像您所看到的那样。

为了避免这种情况,您可以考虑增加客户端的超时时间。此外,如果您已经启用了暂停,您还可以考虑禁用它 [2]。禁用暂停后,您还可以将最小 Aurora 容量单元调整到更高的值,以确保您的集群始终具有足够的计算资源来服务新的连接 [3]。请注意,调整最小 ACU 可能会增加服务成本 [4]。

还请注意,仅建议对特定工作负载使用 Aurora Serverless [5]。如果您的工作负载高度可预测,并且您的应用程序需要定期访问数据库,我建议您使用预置的 Aurora 集群/实例来确保业务的高可用性。

[2] Aurora Serverless 工作原理 - Aurora Serverless 的自动暂停和恢复 - https://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/AuroraUserGuide/aurora-serverless.how-it-works.html#aurora-serverless.how-it-works.pause-resume

[3] 设置 Aurora Serverless DB 集群的容量 - https://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/AuroraUserGuide/aurora-serverless.setting-capacity.html

[4] Aurora Serverless 价格 - https://aws.amazon.com/zh_cn/rds/aurora/serverless/

[5] 使用 Amazon Aurora Serverless - Aurora Serverless 的使用案例 - https://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/AuroraUserGuide/aurora-serverless.html#aurora-serverless.use-cases


非常感谢您发布这个答案。虽然听起来很傻,但我从来没有想到过“尝试再次执行完全相同的aws rds-data execute-statement命令以查看通信链接故障是否消失”。但是在几分钟后再次运行该命令就完美解决了问题。 - Pat Needham
@john,如果您不想担心通信链路故障,我已经发布了另一个答案。它禁用了数据库的自动暂停(休眠)行为。我不知道这样做的影响,但这可能是您正在寻找的! - FelipeCruzV10

7
如果对某人有用,这就是我在管理Aurora Serverless唤醒时如何处理重试的方法。
客户端返回BadRequestException,因此即使更改客户端的配置,boto3也不会重试,请参见https://boto3.amazonaws.com/v1/documentation/api/latest/guide/retries.html
我的第一个选择是尝试使用Waiters,但RDSData没有任何waiter,然后我尝试创建一个带有错误匹配器的自定义Waiter,但仅尝试匹配错误代码,而忽略消息,并且由于可以通过sql语句中的错误引发BadRequestException,因此我需要验证消息,所以我使用了一种waiter函数:
def _wait_for_serverless():
    delay = 5
    max_attempts = 10

    attempt = 0
    while attempt < max_attempts:
        attempt += 1

        try:
            rds_data.execute_statement(
                database=DB_NAME,
                resourceArn=CLUSTER_ARN,
                secretArn=SECRET_ARN,
                sql_statement='SELECT * FROM dummy'
            )
            return
        except ClientError as ce:
            error_code = ce.response.get("Error").get('Code')
            error_msg = ce.response.get("Error").get('Message')

            # Aurora serverless is waking up
            if error_code == 'BadRequestException' and 'Communications link failure' in error_msg:
                logger.info('Sleeping ' + str(delay) + ' secs, waiting RDS connection')
                time.sleep(delay)
            else:
                raise ce

    raise Exception('Waited for RDS Data but still getting error')

我是这样使用它的:

def begin_rds_transaction():
    _wait_for_serverless()

    return rds_data.begin_transaction(
        database=DB_NAME,
        resourceArn=CLUSTER_ARN,
        secretArn=SECRET_ARN
    )

1
有趣的方法。我有点惊讶实现甚至需要这个。我认为AWS会简单地保留请求直到启动。当然,延迟可能会很长,但考虑到无服务器的方法,这是可以预料的...我猜他们说失败得快,让用户决定该怎么做更好? - Jimbo
无论如何,我的真正评论是,我认为最好先尝试您的实际请求,如果失败了,再在重试真正代码之前进行等待代码。这样,在理想情况下,您可以避免等待开销,而不是每次都需要等待。你有什么想法? - Jimbo
我认为 AWS 中没有活动 ACU 不是一个生产场景。对于我的用例,Aurora 大部分时间都处于休眠状态,因此开始验证它是否处于活动状态是合理的。首先尝试您的真实请求将取决于 Aurora 的使用频率。 - Arless

0

可能有点晚了,但是有一种方法可以停用数据库的休眠行为。

在使用CDK创建集群时,您可以按以下方式配置属性:

new rds.ServerlessCluster(
  this,
  'id',
  {
    engine: rds.DatabaseClusterEngine.AURORA_MYSQL,
    defaultDatabaseName: 'name',
    vpc,
    scaling:{
      autoPause:Duration.millis(0) //Set to 0 to disable
    }
  }
)

属性是autoPause。默认值为5分钟(如果在5分钟内不使用数据库,可能会出现通信链路故障消息)。最大值为24小时。但是,您可以将值设置为0,这将禁用自动关闭。此后,即使没有连接,数据库也不会进入休眠状态。

从AWS(RDS ->数据库 ->'实例' ->配置 ->容量设置)查看配置时,您会注意到此属性没有值(如果设置为0):AWS attribute

最后,如果您不希望数据库一直处于开启状态,请设置您自己的autoPause值,以便其按预期行为。


0

我也遇到了这个问题,在借鉴Arless的解决方案并与Jimbo交流后,我想出了以下的解决方法。

我定义了一个装饰器,它会在可配置的重试时间内重试无服务器RDS请求。

import logging
import functools
from sqlalchemy import exc
import time

logger = logging.getLogger()


def retry_if_db_inactive(max_attempts, initial_interval, backoff_rate):
    """
    Retry the function if the serverless DB is still in the process of 'waking up'.
    The configration retries follows the same concepts as AWS Step Function retries.
    :param max_attempts: The maximum number of retry attempts
    :param initial_interval: The initial duration to wait (in seconds) when the first 'Communications link failure' error is encountered
    :param backoff_rate: The factor to use to multiply the previous interval duration, for the next interval
    :return:
    """

    def decorate_retry_if_db_inactive(func):

        @functools.wraps(func)
        def wrapper_retry_if_inactive(*args, **kwargs):
            interval_secs = initial_interval
            attempt = 0
            while attempt < max_attempts:
                attempt += 1
                try:
                    return func(*args, **kwargs)

                except exc.StatementError as err:
                    if hasattr(err.orig, 'response'):
                        error_code = err.orig.response["Error"]['Code']
                        error_msg = err.orig.response["Error"]['Message']

                        # Aurora serverless is waking up
                        if error_code == 'BadRequestException' and 'Communications link failure' in error_msg:
                            logger.info('Sleeping for ' + str(interval_secs) + ' secs, awaiting RDS connection')
                            time.sleep(interval_secs)
                            interval_secs = interval_secs * backoff_rate
                        else:
                            raise err
                    else:
                        raise err

            raise Exception('Waited for RDS Data but still getting error')

        return wrapper_retry_if_inactive

    return decorate_retry_if_db_inactive

然后可以像这样使用:

@retry_if_db_inactive(max_attempts=4, initial_interval=10, backoff_rate=2)
def insert_alert_to_db(sqs_alert):
    with db_session_scope() as session:
        # your db code
        session.add(sqs_alert)

    return None

请注意,我正在使用SQLAlchemy,因此代码需要进行调整以适应特定的目的,但希望它作为一个起点是有用的。

0

0
在您的CloudFormation/SAM模板中,在ScalingConfiguration中添加AutoPause = false。这是我模板中的一小段代码。
docs
 Type: 'AWS::RDS::DBCluster'
 Properties:
   DatabaseName: !Ref DBName
   DBClusterIdentifier: name
   Engine: aurora-mysql
   EngineVersion: 5.7.mysql_aurora.2.11.3
   EngineMode: serverless
   EnableHttpEndpoint: true
   ScalingConfiguration:
     MinCapacity: 1
     MaxCapacity: 64
     AutoPause: false

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接