(一)Lambda功能介紹
AWS Step Functions 将多個AWS服務協調為無伺服器工作流,以便可以快速建構和更新應用程式。使用Step Functions,可以設計和運作将AWS Lambda 和 Amazon ECS等服務整合到功能豐富的應用程式中的工作流。工作流由一系列步驟組成,一個步驟的輸出充當下一個步驟的輸入。 使用Step Functions,應用程式開發更簡單、更直覺,因為它将工作流轉換為易于了解、易于向其他人說明且易于更改的狀态機示意圖。可以監控執行的每個步驟,這意味着可以快速識别并解決問題。 Step Functions可以自動觸發和跟蹤各個步驟,并在出現錯誤時重試,是以的應用程式能夠按照預期順序執行。
(二)demo實踐
為了友善大家了解AWS Step Functions,以下我們将通過一個實際案例的動手操作,來幫忙大家了解AWS Step Functions,以及AWS SNS/AWS Dynamodb等服務。測試Demo架構如下:

2.1 AWS Step Function 狀态機的工作流程
2.1.1工作流如圖所示:
2.1.2工作流描述:
1)調用 Input Lottery Winners 函數,傳入 num_of_winners,進入第二步。
2)Random Select Winners 根據 Input Lottery Winners 的輸出(body)調用 Random Select Winners,生成兩個獲獎号碼,進入第三步。
3)Validate Winners 根據第二步輸出查詢 Winners 表判斷是否重複,重複則傳出 status:1,否則傳出 status:0。
4)Is Winner In Past Draw 接受第三步 status 并判斷,當 status 為1,則重新調用 Random Select Winners 進入第二步,當 status 為0,則調用 Notify Winners 和 Record Winner Queue,給 5)SNS topic 發送通知,把獲獎者寫入 Winner 表。
在整個工作流程中,當 Catch 接收到錯誤,都直接進入 Failed 步驟,輸出異常并中斷 step function。
2.2 建立過程及步驟
2.2.1 建立IAM角色
執行 lambda的角色需要以下政策:
AmazonDynamoDBFullAccess
AWSLambdaBasicExecutionRole
AmazonSNSFullAccess
AWSStepFunctionsFullAccess
在AWS的Console建立角色的過程如下:
2.2.2建立Lambda
Input Lottery Winners
為了實作Step Functions狀态機流轉下的任務,我們這次實作會用到AWS Lambda作為我們業務的實作環境
1)進入AWS控制台,選擇服務然後輸入Lambda進入AWS Lambda控制台
2)選擇建立函數,然後選擇從頭開始創作來自定義我們的實驗程式
3)首先我們需要建立狀态機中的第一個狀态任務Input Lottery Winners,輸入函數名稱Lottery-InputWinners來定義幸運兒的數量。運作語言選擇Python 3.7。同時需要選擇函數執行的權限,
這裡我們選擇使用現有角色,選擇之前建立的IAM角色
4)點選建立函數
5)在函數代碼欄目下輸入如下代碼塊,修改代碼中的 region_name為目前使用的AWS的region
6)建立函數時複制頁面右上角的ARN,為後面建立狀态機準備
為Lottery-InputWinners函數準備代碼塊如下:
import json
class CustomError(Exception):
pass
def lambda_handler(event, context):
num_of_winners = event['input']
# Trigger the Failed process
if 'exception' in event:
raise CustomError("An error occurred!!")
return {
"body": {
"num_of_winners": num_of_winners
}
}
接下來我們還需要建立另外三個需要定義的狀态機業務邏輯,建立過程和上面的Lottery-InputWinners函數一緻,下面是具體的狀态機的代碼塊
Lottery-RandomSelectWinners的函數代碼塊:
import json
import boto3
from random import randint
from boto3.dynamodb.conditions import Key, Attr
TOTAL_NUM = 10
def lambda_handler(event, context):
# variables
num_of_winners = event['num_of_winners']
# query in dynamodb
dynamodb = boto3.resource('dynamodb', region_name='')
table = dynamodb.Table('Lottery-Employee')
# random select the winners, if has duplicate value, re-run the process
while True:
lottery_serials = [randint(1,TOTAL_NUM) for i in range(num_of_winners)]
if len(lottery_serials) == len(set(lottery_serials)):
break
# retrieve the employee details from dynamodb
results = [table.query(KeyConditionExpression=Key('lottery_serial').eq(serial), IndexName='lottery_serial-index') for serial in lottery_serials]
# format results
winner_details = [result['Items'][0] for result in results]
return {
"body": {
"num_of_winners": num_of_winners,
"winner_details": winner_details
}
}
Lottery-ValidateWinners的函數代碼塊:
import json
import boto3
from boto3.dynamodb.conditions import Key, Attr
def lambda_handler(event, context):
# variables
num_of_winners = event['num_of_winners']
winner_details = event['winner_details']
# query in dynamodb
dynamodb = boto3.resource('dynamodb', region_name='')
table = dynamodb.Table('Lottery-Winners')
# valiate whether the winner has already been selected in the past draw
winners_employee_id = [winner['employee_id'] for winner in winner_details]
results = [table.query(KeyConditionExpression=Key('employee_id').eq(employee_id)) for employee_id in winners_employee_id]
output = [result['Items'] for result in results if result['Count'] > 0]
# if winner is in the past draw, return 0 else return 1
has_winner_in_queue = 1 if len(output) > 0 else 0
# format the winner details in sns
winner_names = [winner['employee_name'] for winner in winner_details]
name_s = ""
for name in winner_names:
name_s += name
name_s += " "
return {
"body": {
"num_of_winners": num_of_winners,
"winner_details": winner_details
},
"status": has_winner_in_queue,
"sns": "Congrats! [{}] You have selected as the Lucky Champ!".format(name_s.strip())
}
Lottery-RecordWinners函數代碼塊:
import json
import boto3
from boto3.dynamodb.conditions import Key, Attr
def lambda_handler(event, context):
# variables
winner_details = event['winner_details']
# retrieve the winners' employee id
employee_ids = [winner['employee_id'] for winner in winner_details]
# save the records in dynamodb
dynamodb = boto3.resource('dynamodb', region_name='')
table = dynamodb.Table('Lottery-Winners')
for employee_id in employee_ids:
table.put_item(Item={
'employee_id': employee_id
})
return {
"body": {
"winners": winner_details
},
"status_code": "SUCCESS"
}
2.2.3建立SNS通知服務
1)進入AWS控制台,在服務中搜尋SNS
2)在SNS控制台中,選擇主題, 然後選擇建立主題
3)在建立新主題彈框中,輸入如下内容:
- 主題名稱: Lottery-Notification
- 顯示名稱: Lottery
1)建立主題後,會進入主題詳細資訊頁面,這時候我們需要建立訂閱來對接我們的消息服務,例如郵件服務(本次實驗使用郵件服務來作為消息服務)
2)點選建立訂閱, 在彈框中選擇
- 協定: Email
- 終端節點: <填入自己的郵箱位址>
1)點選請求确認, 然後到上面填寫的郵箱位址中确認收到資訊,表示确認該郵箱可以接收來自AWS SNS該主題的通知消息
2)複制主題詳細頁面的主題ARN,之後替換Step Functions狀态機下的<Notification:ARN>
2.2.4建立Amazon Dynamodb服務
本次實驗需要建立兩張Dynamodb表來記錄員工資訊和幸運兒資訊。使用Dynamodb能更快地通過托管的方式記錄資料同時免去資料庫運維的壓力。
1)進入AWS控制台,在服務中搜尋Dynamodb
2)在左側控制欄中選在表, 然後在首頁面中選擇建立表
3)在建立Dynamodb表中,填入如下資訊
- 表名稱:Lottery-Winners
- 主鍵:employee_id
1)表設定中确認勾選使用預設設定,點選建立
2)同樣的設定步驟,點選建立表,在建立Dynamodb表中,填入如下資訊
- 表名稱:Lottery-Employee
2)等待表建立完成後, 通過本附件中的request-items.json檔案導入資料到Lottery-Employee
$ aws dynamodb batch-write-item --request-items file://request-items.json
1)選擇表Lottery-Employee Tab頁面中的索引, 點選建立索引
- 主鍵:lottery_serial, 字段類型選擇數字
- 索引名稱:lottery_serial-index
2.2.5建立AWS Step Functions 狀态機
1)進入AWS控制台,在服務中搜尋Step Functions
2)進入Step Functions服務後,點選左側的活動欄,并點選狀态機
3)進入狀态機首頁面後,選擇建立狀态機
4)在定義狀态機欄目下,選擇預設使用代碼段創作。同時在詳細資訊欄目輸入狀态機名稱:Lottery
5)在狀态機定義欄目下,複制如下狀态機定義檔案,通過Amazon States Language來定義狀态機的狀态流轉
狀态機名稱:Lottery
{
"Comment": "A simple AWS Step Functions state machine that simulates the lottery session",
"StartAt": "Input Lottery Winners",
"States": {
"Input Lottery Winners": {
"Type": "Task",
"Resource": "<InputWinners:ARN>",
"ResultPath": "$",
"Catch": [
{
"ErrorEquals": [ "CustomError" ],
"Next": "Failed"
},
{
"ErrorEquals": [ "States.ALL" ],
"Next": "Failed"
}
],
"Next": "Random Select Winners"
},
"Random Select Winners": {
"Type": "Task",
"InputPath": "$.body",
"Resource": "<RandomSelectWinners:ARN>",
"Catch": [
{
"ErrorEquals": [ "States.ALL" ],
"Next": "Failed"
}
],
"Retry": [
{
"ErrorEquals": [ "States.ALL"],
"IntervalSeconds": 1,
"MaxAttempts": 2
}
],
"Next": "Validate Winners"
},
"Validate Winners": {
"Type": "Task",
"InputPath": "$.body",
"Resource": "<ValidateWinners:ARN>",
"Catch": [
{
"ErrorEquals": [ "States.ALL" ],
"Next": "Failed"
}
],
"Retry": [
{
"ErrorEquals": [ "States.ALL"],
"IntervalSeconds": 1,
"MaxAttempts": 2
}
],
"Next": "Is Winner In Past Draw"
},
"Is Winner In Past Draw": {
"Type" : "Choice",
"Choices": [
{
"Variable": "$.status",
"NumericEquals": 0,
"Next": "Send SNS and Record In Dynamodb"
},
{
"Variable": "$.status",
"NumericEquals": 1,
"Next": "Random Select Winners"
}
]
},
"Send SNS and Record In Dynamodb": {
"Type": "Parallel",
"End": true,
"Catch": [
{
"ErrorEquals": [ "States.ALL" ],
"Next": "Failed"
}
],
"Retry": [
{
"ErrorEquals": [ "States.ALL"],
"IntervalSeconds": 1,
"MaxAttempts": 2
}
],
"Branches": [
{
"StartAt": "Notify Winners",
"States": {
"Notify Winners": {
"Type": "Task",
"Resource": "arn:aws:states:::sns:publish",
"Parameters": {
"TopicArn": "<Notification:ARN>",
"Message.$": "$.sns"
},
"End": true
}
}
},
{
"StartAt": "Record Winner Queue",
"States": {
"Record Winner Queue": {
"Type": "Task",
"InputPath": "$.body",
"Resource":
"<RecordWinners:ARN>",
"TimeoutSeconds": 300,
"End": true
}
}
}
]
},
"Failed": {
"Type": "Fail"
}
}
}
1)在狀态機定義欄目的右側,點選重新整理按鈕,可以看到狀态機流轉的流程圖。使用之前的 lambda ARN(4個),SNS topic ARN(1個),對應替換狀态機 json 檔案中的 <InputWinners:ARN>,<RandomSelectWinners:ARN>,<ValidateWinners:ARN>,<RecordWinners:ARN>,<Notification:ARN>,點選下一步。
2)在配置設定下,選擇為我建立IAM角色, 輸入自定義的IAM角色名稱MyStepFunctionsExecutionRole,并且附加AmazonSNSFullAccess權限
3)點選建立狀态機完成建立過程
(三)執行 Step Function 狀态機
1)入AWS控制台,在服務中搜尋Step Functions
2)進入之前建立的狀态機Lottery
3)點選啟動執行
4)在彈框中填入輸入的json 文本,這裡的input代表在本次實驗中需要抽取的獲獎人數
{
"input": 2
}
5)點選啟動執行
(四) 實驗結果
1)Dynamodb表中Lottery-Winners記錄獲獎者
2)郵件會收取到辛運兒的資訊
(五)總結描述