Instructions for the technical lab at Microsoft Ready (Winter 2019).
Instructors:
- Mike Budzynski
- Elizabeth Graham
- David Barkol
Please, take a look at a blog about using Azure API Management with serverless microservices.
- Navigate to the Azure Portal. You should see a resource group with the following resources:
- API Management: store-api-...
- Application Insights: store-logs
- Service Bus Namespace: store-msg-...
- Function App: store-order-...
- Function App: store-product-...
- Logic App: store-shipping
-
Navigate to the Function App store-order-....
-
Add new HTTP trigger Function. Click on the + icon next to Functions. Select In portal -> Continue -> More templates -> Finish.
-
Click on the Http Trigger tile.
-
Name the Function Order and leave authorization level as Function.
-
Click Create.
-
Navigate to Integrate tab.
-
Unselect GET in Selected HTTP methods and click Save.
-
Click on + New Output. Pick Azure Service Bus and click on Select.
-
In the Extensions not Installed section, click Install.
-
Click new in the Service Bus connection section. Choose store-msg-... as the namespace and click Select.
-
Choose Service Bus queue in the Message type section.
-
Set Queue name to store-msg-queue-1.
-
Important: wait at least 1-2 minutes.
-
Click Save.
-
Go to the Function implementation. Replace the code of the Function with:
public static string Run(HttpRequest req, ILogger log, out string outputSbMsg) { // Read the body from the HTTP request var message = new StreamReader(req.Body).ReadToEnd(); // Log for debugging log.LogInformation($"C# function processed: {message}"); // Queue message in the Service Bus outputSbMsg = message; // Return HTTP response return message; }
-
Click Save.
-
Navigate to the Function App store-product-....
-
Add new HTTP trigger Function. Name it Product and leave authorization level as Function. Click Create.
-
Go to the Function implementation. Replace the code of the Function with:
#r "Newtonsoft.Json" using System.Net; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Primitives; using Newtonsoft.Json; public class Product { public string Name { get; private set; } public string Description { get; private set; } public Product(string name, string description = "") { this.Name = name; this.Description = description; } } public static async Task<IActionResult> Run(HttpRequest req, ILogger log) { // Retrieve product name from the query parameter string name = req.Query["name"]; // Create product object (this is a mock, normally it would come from a database). // Then, serialize it and return. var product = new Product(name ?? string.Empty, "Coming soon..."); var productSerialized = JsonConvert.SerializeObject(product); return (ActionResult) new OkObjectResult(productSerialized); }
-
Click Save and run to see if the Function works.
-
On the right sidebar, in the Query section, click + Add parameter. Set name to name and value to Microsoft Surface.
-
Select the GET operation from the dropdown.
-
Click Run in the right sidebar and you should see the response:
{"Name":"Microsoft Surface","Description":"Coming soon..."}
- Navigate to the API Management instance store-api-... located in your resource group.
- Click on APIs on the left sidebar.
- Select Function App in the Add a new API view.
- Click Browse to locate the Function App. Select the store-product-... Function App.
- Set Display name to Store API.
- Set Name to store-api.
- Set API URL suffix to api.
- Click Create.
- Click ... next to your API name on the left sidebar. Select Import.
- Browse for the store-order-... app and confirm import.
Unfortunately, there is a bug in Azure Functions, which most likely prevented your Order Function from correct activation. To make sure your Function works, follow the steps below:
- Navigate to the Function App store-order-....
- Go to the Function implementation. Click Run to see if the Function works. You should see the response:
{ "name": "Azure" }
- If you do not see the response, you need to recreate the Function's output - continue with the steps below. If you see the correct response, you can skip to the next section Test the API.
- Navigate to Integrate tab.
- Click on the existing Service Bus output.
- Select Delete.
- Click on + New Output. Pick Azure Service Bus and click on Select.
- In the Extensions not Installed section, click Install.
- The Service Bus connection should already be propagated with store-msg-....
- Choose Service Bus queue in the Message type section.
- Set Queue name to store-msg-queue-1.
- Click Save.
- Wait 2 minutes for the changes to propagate.
- Go to the Function implementation. Click Run to see if the Function works. You should see the response:
{ "name": "Azure" }
-
Navigate to the API Management instance store-api-... located in your resource group.
-
Click on APIs on the left sidebar and go to the Store API.
-
Go to the Test tab in the top menu.
-
Select POST Order from the API operations list.
-
In the Request body paste:
{ "id": "14157194", "address": "One Microsoft Way, Redmond, WA, USA", "products": [ "31", "145" ] }
-
Click Send.
-
You should receive a 200 OK response:
HTTP/1.1 200 OK cache-control: private ... x-powered-by: ASP.NET,ASP.NET { "id": "14157194", "address": "One Microsoft Way, Redmond, WA, USA", "products": [ "31", "145" ] }
-
Go to the Design view of the Store API in your API Management instance.
-
Make sure you have selected All operations on the left bar with API operations.
-
Click the </> icon in the Inbound processing section.
-
Place a cursor in the
<inbound>
section:<inbound> <base /> | </inbound>
-
Select Limit call rate per key from the right sidebar. It will automatically add the rate-limit-by-key line in the
<inbound>...</inbound>
section as follows:<inbound> <base /> <rate-limit-by-key calls="number" renewal-period="seconds" counter-key="@()" /> </inbound>
-
Fill the placeholders in the policy with the information below:
<rate-limit-by-key calls="5" renewal-period="30" counter-key="@(context.Subscription.Key)" />
-
Click Save.
-
Go to the Test tab in the top menu.
-
Select GET Product from the API operations list.
-
Click + Add parameter. Set name to name, value to Microsoft Surface.
-
Click Send.
-
You should a 200 OK response.
-
Click Send a few more times. You should see a response:
HTTP/1.1 429 Too many requests cache-control: private ... x-powered-by: ASP.NET { "statusCode": 429, "message": "Rate limit is exceeded. Try again in 28 seconds." }
- Go to the Design view of the Store API in your API Management instance.
- Click ... next to your API name on the left sidebar. Select Add version.
- Set name to store-api-v2.
- Set version identifier to v2.
- Click Create.
- Make sure v2 is selected on the left side bar. Click on Original and then v2 to load it.
- Click ... next to the POST Product API operation.
- Select Delete and click Yes.
- Go to the Design view.
- Select ... next to
rate-limit-by-key
in the Inbound processing section and click Delete. - Click Save at the bottom.
- Compare the versions. Original should have the POST Product operation, while v2 shouldn't.
- Go to the Design view of the Store API in your API Management instance, and click All operations.
- Click + Add policy in the Inbound processing section.
- Select the Allow cross-origin resource sharing (CORS) tile.
- Click Save.
- Go to the Design view of the Store API in your API Management instance.
- Select the GET Product operation.
- Click + Add policy.
- Select Set query parameters.
- Set name to name, value to Microsoft Xbox, action to skip.
- Click Save.
-
Go to the Test tab in the top menu.
-
Select GET Product from the API operations list.
-
Click Send.
-
You should see a 200 OK response:
HTTP/1.1 200 OK cache-control: private ... x-powered-by: ASP.NET,ASP.NET {"Name":"Microsoft Xbox","Description":"Coming soon..."}
- Navigate to the API Management instance store-api-... located in your resource group.
- Click Application Insights from the menu on the left.
- Click + Add.
- Select store-logs as your Application Insights instance.
- Click Create.
- Select APIs from the menu on the left and navigate to the Store API, v2.
- Click on the Settings tab on the top.
- Scroll down to the Diagnostics logs section and select Enable in the Application Insights view.
- Set Destination to store-logs.
- Select Sampling to 100% for testing purposes.
- Click Save.
- Navigate to the Application Insights instance store-logs located in your resource group.
- Look at the dashboards in the overview.
- Go to Application map from the menu on the left.
- You should be able to see two Function Apps and a Service Bus there.
- Navigate back to the API Management instance store-api-... located in your resource group.
- Select APIs from the menu on the left and navigate to the Store API, v2.
- Make sure at least 2 minutes have passed since you completed the steps in Enable logging to Application Insights.
- Go to the Test tab in the top menu.
- Select GET Product from the API operations list.
- Click Send a few times.
- Navigate back to the Application Insights instance store-logs located in your resource group.
- You need to wait a few minutes since generating the API usage.
- Look at the dashboards in the overview.
- Go to Application map from the menu on the left.
- Now you should be able to see another node for API Management: storeapi.... Click on it and then on the Investigate performance on the right.
- Click on the Drill into (xx) samples button in the bottom right corner.
- Select a sample from the list and view the properties of the request.
- Navigate to the API Management instance store-api-... located in your resource group.
- Select External cache from the menu on the left.
- Click + Add.
- Select store-cache-... as the cache instance.
- Make sure the Used from field is set to Default or points to the region of your API Management instance.
- Click Save.
- Select APIs from the menu on the left and navigate to the Store API, v2.
- Select the GET Product operation.
- Click + Add policy.
- Select the Cache responses tile.
- Specify duration as 3600 seconds.
- Switch to the Full view.
- Click + Add query parameter and set it to name.
- Click Save.
- Go to the Test tab in the top menu.
- Select GET Product from the API operations list.
- Click + Add parameter. Set name to name, value to Microsoft Surface.
- Click Send.
- You should a 200 OK response.
- Go to the Trace tab. At the end of the Inbound section you should see:
cache-lookup (214.292 ms) { "message": "Cache lookup resulted in a miss. Cache headers listed below were removed from the request to prompt the backend service to send back a complete response.", "cacheKey": "...", "cacheHeaders": [ { "name": "Cache-Control", "value": "no-cache, no-store" } ] }
- Click Send again.
- Now in the Trace tab you should see:
cache-lookup (67.351 ms) { "message": "Cache lookup resulted in a hit! Cached response will be used. Processing will continue from the step in the response pipeline that is after the corresponding `cache-store`.", "cacheKey": "..." }