From b8ce5c6cc7c1f456afa091a2b87a8c8af655fd67 Mon Sep 17 00:00:00 2001 From: Chris Gillum Date: Thu, 2 Feb 2023 15:25:24 -0800 Subject: [PATCH] Update async HTTP pattern Signed-off-by: Chris Gillum --- .../workflow/workflow-patterns.md | 38 ++++++++++++++++-- .../workflow-async-request-response.png | Bin 0 -> 27068 bytes 2 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 daprdocs/static/images/workflow-overview/workflow-async-request-response.png diff --git a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-patterns.md b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-patterns.md index 2a16d1e46..dc29c6ef1 100644 --- a/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-patterns.md +++ b/daprdocs/content/en/developing-applications/building-blocks/workflow/workflow-patterns.md @@ -124,13 +124,43 @@ While not shown in the example, it's possible to go further and limit the degree ## Async HTTP APIs -In an asynchronous HTTP API pattern, you coordinate non-blocking requests and responses with external clients. This increases performance and scalability. One way to implement an asynchronous API is to use an event-driven architecture, where the server listens for incoming requests and triggers an event to handle each request as it comes in. Another way is to use asynchronous programming libraries or frameworks, which allow you to write non-blocking code using callbacks, promises, or async/await. +Asynchronous HTTP APIs are typically implemented using the [Asynchronous Request-Reply pattern](https://learn.microsoft.com/azure/architecture/patterns/async-request-reply). Implementing this pattern traditionally involves the following: -TODO: DIAGRAM? +1. A client sends a request to an HTTP API endpoint (the _start API_) +1. The _start API_ writes a message to a backend queue, which triggers the start of a long-running operation +1. Immediately after scheduling the backend operation, the _start API_ returns an HTTP 202 response to the client with a `Location` header for the operation's _status API_ +1. The _status API_ queries a database that contains the status of the long-running operation +1. The client repeatedly polls the _status API_ either until some timeout expires or it receives a "completion" response -Dapr Workflows simplifies or even removing the code you need to write to interact with long-running function executions. +The end-to-end flow is illustrated in the following diagram. -TODO: CODE EXAMPLE +Diagram showing how the async request response pattern works + +The challenge with implementing the asynchronous request-reply pattern is that it involves the use of multiple APIs and state stores. It also involves implementing the protocol correctly so that the client knows how to automatically poll for status and know when the operation is complete. + +The Dapr workflow management HTTP API supports the asynchronous request-reply pattern out-of-the box, without requiring you to write any code or do any state management. The following `curl` commands illustrate how the workflow APIs support this pattern. + +```bash +curl -i -X TODO +``` +```http +HTTP/1.1 202 Accepted +Content-Type: application/json + +{TODO} +``` + +The HTTP client can then poll the endpoint in the `Location` response header repeatedly until it sees the "COMPLETE", "FAILURE", or "TERMINATED" status in the payload. + +```bash +curl -i -X TODO +``` +```http +HTTP/1.1 200 OK +Content-Type: application/json + +{TODO} +``` ## Monitor diff --git a/daprdocs/static/images/workflow-overview/workflow-async-request-response.png b/daprdocs/static/images/workflow-overview/workflow-async-request-response.png new file mode 100644 index 0000000000000000000000000000000000000000..02230bc58f6cd2b74f194a20f27015631bb2566a GIT binary patch literal 27068 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 MYw2qiYh1nmU$8|v)c^nh literal 0 HcmV?d00001