異步api
by Garrett Vargas
通過Garrett Vargas
如何設計無服務器異步API (How To Design a Serverless Async API)
I recently ran a workshop to teach developers how to create an Alexa skill. The workshop material centered around a project to return car rental search results. Because I wanted to focus the learning on developing the conversational flow and not the mechanics of doing a car search, I decided to encapsulate the search logic behind an API. In addition, since the car search request can take 10 or more seconds to complete, I wanted the call to be asynchronous so we could build a conversation like:
我最近舉辦了一個講習班,教開發人員如何創建Alexa技能。 研討會資料圍繞一個項目返回了租車搜索結果。 因為我想將學習重點放在開發對話流程上,而不是在進行汽車搜索的機制上,所以我決定將搜索邏輯封裝在API后面。 此外,由于汽車搜索請求可能需要10秒鐘或更長時間才能完成,因此我希望通話是異步的,因此我們可以建立如下對話:
“Find a Car in New York next weekend”
“下周末在紐約找車”
“What size car would you like for your trip in New York next weekend?”
“下周末您要在紐約旅行時選擇哪種尺寸的汽車?”
“A small car”
“一輛小汽車”
“Is there a specific company you’d like to rent from?”
“您想租一家特定的公司嗎?”
“No”
“沒有”
The implementation of the asynchronous API was a pretty interesting project in and of itself, and in this blog post I’m going to tell you how I did this in a serverless way using API Gateway, Lambda functions, and S3.
異步API的實現本身就是一個非常有趣的項目,在這篇博客文章中,我將告訴您如何使用API??網關,Lambda函數和S3以無服務器方式進行此操作。
S3水桶 (S3 Bucket)
The S3 bucket in this architecture serves as a cache that stores search results to be retrieved later. Callers of the API are given a token when they start a search, and the basic design is to use that token as part of the S3 object name to allow you to retrieve the contents on a subsequent call. To prevent the bucket from filling up with search results, you can set an expiration policy that is appropriate for the lifecycle of your API results (i.e. how long do you want the asynchronous results to stay alive?).
此體系結構中的S3存儲桶用作緩存,用于存儲以后要檢索的搜索結果。 API的調用者開始搜索時會得到一個令牌,并且基本設計是將該令牌用作S3對象名稱的一部分,以允許您在后續調用中檢索內容。 為了防止存儲桶填滿搜索結果,您可以設置一個適用于您的API結果生命周期的過期策略(即,您希望異步結果保持多長時間?)。
Note that the expiration policy can only be set in day increments, so even if you have results that should be considered stale after 30 minutes, with this approach you’ll still have the object in storage for at least a day.
請注意,過期策略只能以天為單位進行設置,因此即使您將30分鐘后的結果認為是過時的,使用這種方法,您仍將對象存儲至少一天。
You can associate a timestamp with the object and check it in your code to make sure that the result is ignored if it is more than a certain age, but the object itself will persist for at least a day.
您可以將時間戳記與對象關聯,并在代碼中對其進行檢查,以確保結果(如果超過一定期限)將被忽略,但是對象本身將持續至少一天。
To set up your bucket, take the following steps in the AWS Management Console:
要設置存儲桶,請在AWS管理控制臺中執行以下步驟:
Click Create Bucket from S3
單擊從S3 創建存儲桶
- Enter the name of the bucket and make note of which region you create it in (you’ll need to make sure your Lambda functions and API Gateway are all set up in this same region). Note that S3 bucket names are globally unique, which means a name like “test” will likely be taken. You’ll need to pick something that no other user has created before, so something that incorporates your name or the current date would be a good starting point 輸入存儲桶的名稱,并記下創建存儲桶的區域(您需要確保在同一區域中都設置了Lambda函數和API網關)。 請注意,S3存儲桶名稱是全局唯一的,這意味著可能會使用“ test”之類的名稱。 您需要選擇以前沒有其他用戶創建過的內容,因此可以使用包含您的姓名或當前日期的內容作為一個很好的起點
- You can keep the bucket with the default permissions and no versioning — you’ll explicitly grant your Lambda function permission to this bucket, so don’t expose it publicly to the world (in fact, that would be a bad idea!) 您可以將存儲桶保留為默認權限,而無需進行版本控制-您將向該存儲桶明確授予Lambda函數權限,因此不要將其公開暴露給世界(實際上,這是個壞主意!)
Once the bucket is created, click on it and go to the Management tab
創建存儲桶后,單擊它并轉到“ 管理”選項卡
Enter a lifecycle by clicking Add Lifecycle Rule
通過單擊添加生命周期規則輸入生命周期
Enter a name and skip through the Transitions screen to end up on the Expiration screen
輸入名稱并跳過“過渡”屏幕以顯示在“ 到期”屏幕上
Since we didn’t add versioning to our S3 bucket, you only need to configure an expiration rule for the Current Version as illustrated below
由于我們沒有向S3存儲桶添加版本控制,因此您只需為“ 當前版本”配置到期規則,如下所示
Lambda函數 (Lambda Functions)
I had the basic idea to use a Lambda function to perform a search, return a token to the caller, and write the results into an S3 bucket. The results could then be retrieved by a subsequent call, passing in the token and any additional filtering information (for example, “small car” in the example above).
我的基本想法是使用Lambda函數執行搜索,將令牌返回給調用者,然后將結果寫入S3存儲桶。 然后可以通過后續調用,傳遞令牌和任何其他過濾信息(例如,上例中的“小型汽車”)來檢索結果。
However, I quickly realized that my Lambda function would return after I validated the input parameters and called back with a token, which meant that I wasn’t able to keep it alive to write the search results out to S3.
但是,我很快意識到,在驗證輸入參數并使用令牌進行回調之后,Lambda函數將返回,這意味著我無法保持活動狀態,無法將搜索結果寫入S3。
What I needed was a way to continue code execution after I had the token so I could return to the API caller. To do this, I created two Lambda functions, one to validate the parameters, and the other to perform the search and lookup the cached results.
我需要的是在獲得令牌后繼續執行代碼的方法,這樣我就可以返回到API調用者。 為此,我創建了兩個 Lambda函數,一個用于驗證參數,另一個用于執行搜索和查找緩存結果。
The first function validates the parameters, and once it has done so, it invokes the second Lambda function asynchronously to kick off the search, then returns with a generated token back to the caller while the second Lambda function churns away. The token that my generateToken
function used in my workshop was just a timestamp since I didn’t have scalability considerations, but it could also be a UUID or other generated ID.
第一個函數驗證參數,一旦完成驗證,它將異步調用第二個Lambda函數以開始搜索,然后在第二個Lambda函數失效時以生成的令牌返回給調用者。 由于我沒有可伸縮性考慮,所以我的generateToken
函數在我的講習班中使用的令牌只是一個時間戳,但是它也可以是UUID或其他生成的ID。
Because this Lambda function invokes another Lambda function, you’ll need to give it the appropriate permissions to make this call. You do this by creating the appropriate IAM role following these steps:
由于此Lambda函數將調用另一個Lambda函數,因此您需要為其授予適當的權限才能進行此調用。 您可以按照以下步驟通過創建適當的IAM角色來做到這一點:
In your Lambda function, under Execution role, select Create a Custom Role in the dropdown
在Lambda函數的“執行角色”下,從下拉列表中選擇“ 創建自定義角色 ”
- This will launch IAM in a new tab 這將在新標簽頁中啟動IAM
Select Create a new IAM Role from the dropdown for IAM Role and provide it with a name
從“ IAM角色 ”下拉列表中選擇“ 創建新的IAM角色” ,并為其提供名稱。
In the full list of IAM roles, select this new role and click Attach Policies
在IAM角色的完整列表中,選擇此新角色,然后單擊“ 附加策略”。
Filter for the AWSLambdaRole policy and add it to this role. Optionally, you can modify the JSON to give it access only to the second Lambda function after you create it in the following step, by referring to its ARN in the Resource field
過濾AWSLambdaRole策略并將其添加到該角色。 (可選)您可以修改JSON,以使其在接下來的步驟中通過在Resource字段中引用其ARN使其僅訪問第二個Lambda函數。
The second Lambda function has two responsibilities — to perform the search and save the results into an S3 bucket, and to retrieve the results from an S3 bucket when called with a valid token. I could have separated this logic and created three Lambda functions, but I felt that it was a better design to have the logic that accessed the cache and knew how to encode/decode the object in one place.
Lambda的第二個功能有兩個職責-執行搜索并將結果保存到S3存儲桶中,以及在使用有效令牌調用時從S3存儲桶中檢索結果。 我本可以分開這個邏輯并創建三個 Lambda函數,但是我認為讓邏輯訪問緩存并知道如何在一處編碼/解碼對象是一個更好的設計。
Because API Gateway allows you to map query parameters, you’ll find it easy to differentiate between the cases when this function is being called to perform a new search and when it is being asked to retrieve a search result (I’ll demonstrate how to do that later). Note that this function calls a lengthy internal doSearch
function which writes results to S3 based on token provided from the previous function.
由于API Gateway允許您映射查詢參數,因此您將很容易區分在調用此函數以執行新搜索和要求其檢索搜索結果的情況之間(我將演示如何稍后再執行)。 請注意,此函數調用一個冗長的內部doSearch
函數,該函數根據前一個函數提供的令牌將結果寫入S3。
Because this Lambda function makes a call to read and write from S3, you’ll have to set the appropriate permissions for this Lambda function — which will be different than the first one. Follow the same set of steps to create an IAM role, only this time rather than the AWSLambdaRole policy, you’ll want to associate the AmazonS3FullAccess policy — again optionally providing the ARN of the specific S3 bucket that you want to provide full access to.
由于此Lambda函數會調用S3進行讀取和寫入操作,因此您必須為此Lambda函數設置適當的權限-這將與第一個函數不同。 遵循同一組步驟來創建IAM角色,只是這次而不是AWSLambdaRole策略,您將要關聯AmazonS3FullAccess策略-再次有選擇地提供要提供完全訪問權限的特定S3存儲桶的ARN。
API網關 (API Gateway)
With the Lambda functions out of the way, the next step is to create an API Gateway around these functions so that a user has a REST API to call into. Remember, the flow I wanted to build from a client perspective was:
在不使用Lambda函數的情況下,下一步是圍繞這些函數創建API網關,以便用戶可以調用REST API。 記住,我要從客戶角度構建的流程是:
- POST call to the API with the desired search parameters 使用所需的搜索參數對API進行POST調用
- Get a token back as a response 取回令牌作為回應
- Ask the user some additional questions to help narrow the results 向用戶詢問一些其他問題,以幫助縮小結果范圍
- GET call to the API with the token and additional filter criteria to get the actual result set 使用令牌和其他過濾條件對API進行GET調用,以獲取實際結果集
API Gateway makes it easy and convenient to access your Lambda functions as desired.
API網關使您可以根據需要輕松便捷地訪問Lambda函數。
The first step is to create your new API. In the AWS Management Console, you can navigate to API Gateway and click Create API to create a new API. Once you’ve given it a name, you need to create the methods that you want to use in accessing the API. In my case, that meant selecting POST and GET as new methods from the Actions drop down menu.
第一步是創建新的API。 在AWS管理控制臺中,您可以導航到API網關,然后單擊創建API以創建新的API。 為其命名后,需要創建要用于訪問API的方法。 就我而言,這意味著從“ 操作”下拉菜單中選擇POST和GET作為新方法。
Let’s start with the POST. Once you’ve selected the POST method, you’ll be asked the Integration Type you’d like to use. Select Lambda Function, and then fill in the details with the first Lambda function you created to validate parameters and kick off the search. You don’t need to point API Gateway to the second Lambda function that does the search — that is done by by the validation function for you.
讓我們從POST開始。 選擇POST方法后,系統將詢問您要使用的集成類型 。 選擇Lambda Function ,然后使用您創建的第一個Lambda函數填寫詳細信息以驗證參數并開始搜索。 您無需將API Gateway指向執行搜索的第二個Lambda函數,這是由驗證函數為您完成的。
After you set these parameters, you’ll see the full API flow, along with a TEST sidebar that will allow you to pass a test payload to your API to see if it executes properly.
設置完這些參數后,您將看到完整的API流程以及一個TEST側邊欄,該邊欄將允許您將測試有效負載傳遞給API,以查看其是否正常執行。
For the GET call, do similar, though in this case you’re going to call the second Lambda function passing in a token so it knows to retrieve the results from your S3 cache. Also, in this case you won’t have a JSON payload to pass on — rather the expectation is that the customer supplies query parameters in the URL. API Gateway allows you to do this transformation easily via a Mapping Template.
對于GET調用,請執行類似操作,盡管在這種情況下,您將調用傳遞令牌的第二個Lambda函數,以便它知道從S3緩存中檢索結果。 同樣,在這種情況下,您將沒有傳遞JSON有效負載的方法,而是期望客戶在URL中提供查詢參數。 API Gateway使您可以通過映射模板輕松進行此轉換。
The basic steps are as follows:
基本步驟如下:
Add a GET method under the Actions drop down menu
在“ 操作”下拉菜單下添加GET方法
Set Integration Type to Lambda Function
將積分類型設置為Lambda函數
- Enter in the details of the second Lambda function 輸入第二個Lambda函數的詳細信息
Once created, click on the Integration Request step of your GET method execution
創建完成后,單擊GET方法執行的Integration Request步驟
Expand the Mapping Templates section and click Add mapping template
展開“ 映射模板”部分,然后單擊“ 添加映射模板”。
Type application/json into the edit box and click the check to confirm
在編輯框中輸入application / json ,然后單擊檢查以確認
Enter in the mapping from query parameters to JSON request — you’ll do this with keys of the form
"field": “$input.params('queryparam')"
This will map the query param namedqueryparam
to a field namedfield
輸入從查詢參數到JSON請求的映射-您將使用
"field": “$input.params('queryparam')"
形式的鍵來執行此操作"field": “$input.params('queryparam')"
這會將名為queryparam
的查詢參數queryparam
到名為field
The nice thing here is that you don’t have to have the same name for the query parameters exposed to your client as for internal usage in your Lambda function. For example, in my case I expect parameters of User, CarSize, SupplierRating, and UpgradeClass but I map these to id, size, rating, and upgrade respectively for internal use.
這里的好處是,不必為暴露給客戶端的查詢參數使用與Lambda函數內部使用的名稱相同的名稱。 例如,在我的情況下,我希望使用User,CarSize,SupplierRating和UpgradeClass的參數,但是我將它們分別映射到id,大小,等級和升級以供內部使用。
構建和部署 (Build and Deploy)
Now that you’ve integrated your Lambda function into API Gateway, you’re ready to build and deploy. Under the Actions menu, select Deploy API. API Gateway will ask you for a Deployment Stage; choose [New Stage] to create a new stage, and provide it a name like Beta. After you deploy the API, API Gateway will tell you the URL to use to invoke your API. You use the same URL for both the POST and GET functions. Pretty easy, isn’t it?
現在,您已將Lambda函數集成到API Gateway中,就可以開始構建和部署了。 在“操作”菜單下,選擇“ 部署API” 。 API網關將要求您進行部署; 選擇[新階段]創建一個新階段,并提供一個類似Beta的名稱。 部署API后,API網關將告訴您用于調用API的URL。 POST和GET函數使用相同的URL。 很簡單,不是嗎?
結語 (Wrap Up)
What I described here is the foundation for an asynchronous serverless API. There are a lot of edge cases and error handling that I glossed over, as well as techniques within API Gateway to harden the API that I didn’t address such as validating that all required parameters are set before invoking the Lambda function or requiring an access token as opposed to creating a wide-open API.
我在這里描述的是異步無服務器API的基礎。 我掩蓋了很多極端情況和錯誤處理,以及API網關中用于強化我未解決的API的技術,例如在調用Lambda函數或要求訪問之前驗證是否已設置所有必需的參數令牌而不是創建一個廣泛開放的API。
In addition, this use case was for a small workshop environment. You’d have to look at your own use case to understand the scale you need and whether this approach would work for you. You’d want to pay particular attention to the concurrent execution settings for your Lambda function. If the underlying call you are trying to make takes a minute to run for example, even with a limit of 1000 concurrent executions you’d only be able to make 16 calls per second, which may not prove sufficient for a full production workload.
另外,該用例用于小型車間環境。 您必須查看自己的用例,以了解所需的規模以及此方法是否對您有用。 您需要特別注意Lambda函數的并發執行設置。 例如,如果您要進行的基礎調用需要一分鐘才能運行,即使限制了1000次并發執行,則您每秒只能進行16次調用,這可能不足以滿足全部生產工作負荷。
Caveats aside, for the right use cases this approach can be a simple and powerful way to create an async API without having to stand up dedicated servers or a caching solution.
除了警告之外,對于正確的用例,此方法可以是一種創建異步API的簡單而強大的方法,而無需使用專用服務器或緩存解決方案。
翻譯自: https://www.freecodecamp.org/news/how-to-design-a-serverless-async-api-6cfd68939459/
異步api