发布于: Nov 30, 2022
【概要】本文提供一种跨区域形式的高可用性实现方法,保证能够在某一区域中的机器人或支持实现 API 不可用时,使用来自其他区域的资源以继续响应客户呼叫。
大家需要在主区域与辅助区域中创建相同的对话机器人。在本文中,我们将 us-east-1 作为主区域,us-west-2 作为辅助区域。接下来,先使用主区域 us-east-1 创建机器人:
- 在 Amazon Lex 控制台上,点击 Create。
- 在 Try a Sample 部分,选择 OrderFlowers,而后在 COPPA 中选择 No。
- 其他设置项皆保留默认值,点击 Create。
- 此机器人的创建与构建操作将自动进行。
- 在机器人构建完成(约需 1 至 2 分钟)后,选择 Publish。
- 创建一个别名,名称为 ver_one。对 us-west-2 区域重复上述步骤。现在,您已经在 us-east-1 与 us-west-2 中建立起能够正常运行的 Amazon Lex 机器人。
请确保您当前处于 us-east-1 区域内。
- 在 DynamoDB 控制台处,选择 Create。
- 在 Table name 部分,输入 lexDR。
- 在 Primary key 部分,输入 connectRegion 且类型为 String。
- 其他各项保留默认值,而后选择 Create。
- 在 Items 选项卡中,选择 Create item。
- 将 connectRegion 的值设置为 us-east-1,而后 Append 一个类型为 String、名称为 lexRegion 的新列,并将其值设置为 us-east-1。
- 点击 Save。
在此步骤中,我们将为两项 Lambda 函数创建一个 Amazon Web Services 身份与访问管理(Amazon Identity and Access Management,简称 IAM)角色。
- 在 IAM 控制台上,点击 Access management 并选择 Policies。
- 点击 Create Policy。
- 点击 JSON。
- 粘贴以下自定义 IAM 策略,此策略允许对 DynamoDB 表 lexDR 进行读取/写入访问。请将策略中的 “xxxxxxxxxxxx” 部分替换为您的 Amazon Web Services 账户编号。
{ "Version": "2012-10-17", "Statement": [{ "Sid": "VisualEditor0", "Effect": "Allow", "Action": ["dynamodb:GetItem", "dynamodb:UpdateItem"], "Resource": "arn:aws:dynamodb:us-east-1:xxxxxxxxxxxx:table/lexDR" }] }
- 点击 Review Policy。
- 将其命名为 DynamoDBReadWrite,而后点击 Create Policy。
- 在 IAM 控制台上,点击 Access management 下的 Roles ,而后点击 Create Role。
- 为该服务选择 Lambda,而后点击 Next。
- 附加以下权限策略:
- AmazonLambdaBasicExecutionRole
- AmazonLexRunBotsOnly
- DynamoDBReadWrite
- 点击 Next: Tags。接下来,点击 Next: Review 以跳过 Tags 页面。
- 将角色命名为 lexDRRole,而后点击 Save。
我们首先需要创建一项 Lambda 函数,用于从 DynamoDB 表中读取记录,借此判断哪个 Amazon Lex 机器人与 Amazon Connect 实例处于同一区域当中。Amazon Connect 或者使用此机器人的应用程序后续将调用此函数。
- 在 Lambda 控制台上,选择 Create function。
- 在 Function name 部分,输入 lexDRGetRegion。
- 在 Runtime 部分,选择 Python 3.8。
- 在 Permissions 之下,选择 Use an existing role。
- 选择角色 lexDRRole。
- 选择 Create function。
- 在 Lambda 代码编辑器中,输入以下代码(下载自 lexDRGetRegion.zip):
import json import boto3 import os import logging dynamo_client=boto3.client('dynamodb') logger = logging.getLogger() logger.setLevel(logging.DEBUG) def getCurrentPrimaryRegion(key): result = dynamo_client.get_item( TableName=os.environ['TABLE_NAME'], Key = { "connectRegion": {"S": key } } ) logger.debug(result['Item']['lexRegion']['S'] ) return result['Item']['lexRegion']['S'] def lambda_handler(event, context): logger.debug(event) region = event["Details"]["Parameters"]["region"] return { 'statusCode': 200, 'primaryCode': getCurrentPrimaryRegion(region) }
- 在 Environment variables 部分,选择 Edit。
- 添加一项环境变量,其中 Key 为 TABLE_NAME,Value 为 lexDR。
- 点击 Save 以保存该环境变量。
- 点击 Save 以保存该 Lambda 函数。
在 us-east-1 当中创建另一项 Lambda 函数,用以实现运行状况检查功能。
- 在 Lambda 控制台上,选择 Create function。
- 在 Function name 部分,输入 lexDRTest。
- 在 Runtime 部分,选择 Python 3.8。
- 在 Permissions 之下,选择 Use an existing role。
- 选择 lexDRRole。
- 选择 Create function。
- 在 Lambda 代码编辑器中,输入以下代码(下载自 lexDRTest.zip):
import json import boto3 import sys import os dynamo_client = boto3.client('dynamodb') primaryRegion = os.environ['PRIMARY_REGION'] secondaryRegion = os.environ['SECONDARY_REGION'] tableName = os.environ['TABLE_NAME'] primaryRegion_client = boto3.client('lex-runtime',region_name=primaryRegion) secondaryRegion_client = boto3.client('lex-runtime',region_name=secondaryRegion) def getCurrentPrimaryRegion(): result = dynamo_client.get_item( TableName=tableName, Key={ 'connectRegion': {'S': primaryRegion} } ) return result['Item']['lexRegion']['S'] def updateTable(region): result = dynamo_client.update_item( TableName= tableName, Key={ 'connectRegion': {'S': primaryRegion } }, UpdateExpression='set lexRegion = :region', ExpressionAttributeValues={ ':region': {'S':region} } ) #SEND MESSAGE/PUT SESSION ENV VA def put_session(botname, botalias, user, region): print(region,botname, botalias) client = primaryRegion_client if region == secondaryRegion: client = secondaryRegion_client try: response = client.put_session(botName=botname, botAlias=botalias, userId=user) if (response['ResponseMetadata'] and response['ResponseMetadata']['HTTPStatusCode'] and response['ResponseMetadata']['HTTPStatusCode'] != 200) or (not response['sessionId']): return 501 else: if getCurrentPrimaryRegion != region: updateTable(region) return 200 except: print('ERROR: {}',sys.exc_info()[0]) return 501 def send_message(botname, botalias, user, region): print(region,botname, botalias) client = primaryRegion_client if region == secondaryRegion: client = secondaryRegion_client try: message = os.environ['SAMPLE_UTTERANCE'] expectedOutput = os.environ['EXPECTED_RESPONSE'] response = client.post_text(botName=botname, botAlias=botalias, userId=user, inputText=message) if response['message']!=expectedOutput: print('ERROR: Expected_Response=Success, Response_Received='+response['message']) return 500 else: if getCurrentPrimaryRegion != region: updateTable(region) return 200 except: print('ERROR: {}',sys.exc_info()[0]) return 501 def lambda_handler(event, context): print(event) botName = os.environ['BOTNAME'] botAlias = os.environ['BOT_ALIAS'] testUser = os.environ['TEST_USER'] testMethod = os.environ['TEST_METHOD'] if testMethod == 'send_message': primaryRegion_response = send_message(botName, botAlias, testUser, primaryRegion) else: primaryRegion_response = put_session(botName, botAlias, testUser, primaryRegion) if primaryRegion_response != 501: primaryRegion_client.delete_session(botName=botName, botAlias=botAlias, userId=testUser) if primaryRegion_response != 200: if testMethod == 'send_message': secondaryRegion_response = send_message(botName, botAlias, testUser, secondaryRegion) else: secondaryRegion_response = put_session(botName, botAlias, testUser, secondaryRegion) if secondaryRegion_response != 501: secondaryRegion_client.delete_session(botName=botName, botAlias=botAlias, userId=testUser) if secondaryRegion_response != 200: updateTable('err') #deleteSessions(botName, botAlias, testUser) return {'statusCode': 200,'body': 'Success'}
- 在 Environment variables 部分,选择 Edit,而后添加以下环境变量:
- BOTNAME – OrderFlowers
- BOT_ALIAS – ver_one
- SAMPLE_UTTERANCE – I would like to order some flowers. (向机器人发送的示例话语。)
- EXPECTED_RESPONSE – What type of flowers would you like to order? (机器人在收到以上示例话语后应做出的预期响应。)
- PRIMARY_REGION – us-east-1
- SECONDARY_REGION – us-west-2
- TABLE_NAME – lexDR
- TEST_METHOD – put_session or send_message
- send_message : 此方法将调用 Lex 运行时函数 postText,该函数将提取语音并将其映射至训练得出的某一 intent。postText 将测试 Lex 的自然语言理解能力,每项请求的使用成本为 0.00075 美元,几乎可以忽略不计。
- put_session: 此方法将调用 Lex 运行时函数 put_session,该函数为用户创建一个新的会话。put_session 不会测试 Lex 的自然语言理解能力。
- TEST_USER – test
- 点击 Save 以保存此环境变量。
- 在 Basic Settings section 当中,将 Timeout 的值更新为 15 秒。
- 点击 Save 以保存此 Lambda 函数。
为了每 5 分钟触发一次运行状况检查函数,我们需要创建一条 Amazon CloudWatch 规则。
- 在 CloudWatch 控制台的 Events 之下,选择 Rules。
- 选择 Create rule。
- 在 Event Source 之下,将选项切换为 Schedule。
- 将 Fixed rate of 设置为 5 minutes。
- 在 Targets 之下,选择 Add target。
- 选择目标 Lambda function。
- 在 Function 部分,选择 lexDRTest。
- 在 Configure input 之下,选择 Constant (JSON text),而后输入 {}
- 选择 Configure details。
- 在 Rule definition 之下的 Name 部分,输入 lexHealthCheckRule。
- 选择 Create rule。
现在,您应该已经建立起 lexHealthCheckRule CloudWatch 规则,能够每 5 分钟调用一次 lexDRTest 函数。这项操作将检查您主机器人的运行状况是否正常,并将结果对应更新至 DynamoDB 表。
现在,我们需要创建一个 Amazon Connect 实例,借此在创建 lexDRTest 函数的同一区域之内测试机器人的多区域模式。
- 如果您还没有 Amazon Connect 实例,请首先创建一个。
- 在 Amazon Connect 控制台上,选择作为 Amazon Connect 传输流目标的实例别名。
- 选择 Contact flows。
- 在 Amazon Lex 之下,从 us-east-1 区域中选择 OrderFlowers 机器人,而后点击 Add Lex Bot。
- 在 us-west-2 区域中选择 OrderFlowers 机器人,而后点击 Add Lex Bot。
- 在 Amazon Lambda 之下,选择 lexDRGetRegion 并点击 Add Lambda Function。
- 点击左侧面板中的 Overview 再点击登录链接,借此登录至您的 Amazon Connect 实例。
- 点击左侧面板中的 Routing,而后点击下拉菜单中的 Contact Flows。
- 点击 Create Contact Flow 按钮。
- 点击 Save 按钮旁的向下箭头按钮,再点击 Import Flow。
- 下载联系流程 Flower DR Flow。在 Import Flow 对话框中上传此文件。
- 在 Contact Flow 中,点击 Inovke Amazon Lambda Function 部分,借此在屏幕右侧打开一个属性面板。
- 选择 lexDRGetRegion 并点击 Save。
- 点击 Publish 按钮,发布当前联系流程。
接下来,我们需要将电话号码关联至联系流程,借此调用并测试 OrderFlowers 机器人。
- 点击左侧导航栏中的 Routing 选项。
- 点击 Phone Numbers。
- 点击 Claim Number。
- 选择您的国家代码并选择电话号码。
- 在 Contact flow/IVR 选择框中,选择我们在之前步骤中导入的联系流程 Flower DR Flow 。
- 等待几分钟,而后呼叫该号码以与 OrderFlowers 机器人交互。
要测试这套解决方案,您可以执行以下操作以模拟 us-east-1 区域发生故障的场景:
- 在 us-east-1 区域中打开 Amazon Lex 控制台。
- 选择 OrderFlowers 机器人。
- 点击 Settings。
- 删除机器人别名 ver_one
在下一次进行运行状况检查时,解决方案将尝试与 us-east-1 区域的 Lex 机器人进行通信。由于机器人别名不存在,因此无法获得成功响应。因此,本示例随后会呼叫辅助区域 us-west-2 并收到成功响应。在收到响应后,示例将使用 us-west-2 更新 lexDR 以及 DynamoDB 表中的 lexRegion 列。
接下来,所有指向 us-east-1 区域内 Connect 的后续呼叫都将与 us-west-2 区域内的 Lex 机器人进行实际交互。通过这一自动切换,可以证明当前架构模式确实能够在发生服务故障时保障业务连续性。
在删除机器人别名到下一次运行状况检查之间的时段内,对 Amazon Connect 的呼叫都将失败。但在运行状况检查之后,系统将自动实现业务连续性保障。因此,每一轮运行状况检查之间的间隔越短,则停机时间越短。您可以通过编辑 Amazon CloudWatch 规则 lexHealthCheckRule 以调整每次运行状况检查之间的间隔时长。
要让 us-east-1 区域再次通过运行状况检查,请在 us-east-1 中重新创建 OrderFlowers 机器人的别名 ver_one。
为了避免产生不必要的额外成本,请删除本示例中创建的所有资源。
- 创建在 us-east-1 与 us-west-2 当中的 Amazon Lex 机器人OrderFlowers
- CloudWatch 规则 lexHealthCheckRule
- DynamoDB 表 lexDR
- Lambda 函数 lexDRTest 与 lexDRGetRegion
- IAM 角色 lexDRRole
- 联系流程 Flower DR Flow
配合 Amazon Lex 提供的自助服务,Amazon Connect 将帮助您轻松创建便捷直观的客户服务体验。本文提供一种跨区域形式的高可用性实现方法,保证能够在某一区域中的机器人或支持实现 API 不可用时,使用来自其他区域的资源以继续响应客户呼叫。
相关文章