mirror of https://github.com/dapr/docs.git
Resolving code review comments.
This commit is contained in:
parent
d03b40d9dc
commit
98d12b3a3a
|
@ -1,65 +0,0 @@
|
|||
# Actor Pattern with Dapr
|
||||
|
||||
Dapr is a new programming model that is based on RPC communication between the framework and the user code, namely HTTP. However, it does support developers to write their code using the Actor Pattern. Specifically, Dapr allows a contextual Id to be associated with messages. Dapr provides:
|
||||
|
||||
1) Routing by a contextual Id.
|
||||
2) Ensuring single-threaded access per contextual Id.
|
||||
3) Setting and getting state by a contextual Id is serialized.
|
||||
|
||||
The combination of above behaviors delivers essential characteristics of an Actor: uniquely identified, single-threaded, with isolated state.
|
||||
|
||||
## Authoring an Actor
|
||||
|
||||
In Dapr, authoring Actor code is no difference with writing any other service code. An Actor in this case is a web server that listens to any number of messages the actor expects to handle.
|
||||
|
||||
When a request is routed to a handler of the web server, a contextual Id may present in the request header. Dapr guarantees that requests with the same contextual Id are dispatched to the web server sequentially.
|
||||
|
||||
Handler code can set or retrieve state from the Dapr sidecar with the contextual Id attached.
|
||||
|
||||
## Talking to an Actor
|
||||
|
||||
Just like invoking any other Dapr instances, you can send a request to an Actor by doing a POST to the sidecar. The **to** address of your message will be in the format of:
|
||||
```yaml
|
||||
<Actor name>.<Contextual Id>
|
||||
```
|
||||
|
||||
> Dapr uses **virtual actors** pattern. You don't need to explicitly create or destroy Actor instances.
|
||||
|
||||
For example, the following POST request to **/set-temperature** sends a temperature payload to a **theromstat** with id **123**:
|
||||
```json
|
||||
{
|
||||
"to": [
|
||||
"theromstat.123"
|
||||
],
|
||||
"data": {
|
||||
"temperature": 72
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Your code can access the response from the actor through the HTTP response body.
|
||||
|
||||
Dapr also allows you to make direct calls to actors. The Dapr sidecar provides a special **/actor** route that can be used to route requests to actor instance. For example, to invoke the above actor directly, send a POST request to:
|
||||
```bash
|
||||
http://localhost:<sidecarport>/actors/theromstat/123
|
||||
```
|
||||
|
||||
## Key Differences From a Full Actor Framework
|
||||
|
||||
Dapr programming model is not an Actor programming model. Although it provides certain actor characteristics, Dapr differ from common Actor programming model in several ways.
|
||||
|
||||
### Single Activation
|
||||
|
||||
Many Actor frameworks requires single activation of an actor at any time. Dapr doesn’t offer such guarantees. There should be a single activation for the most of time. However, multiple activations may exist during failovers, scaling, and network partitions. Dapr will converge back to single activation when such conditions are resolved.
|
||||
|
||||
Dapr offers exact-once delivery within a configurable window. Dapr delivers requests for the same actor id to the same service instance, while serializing client requests as well as state access of an instance. The combination of these features offers a high-fidelity simulation of the single activation behavior. However, there could be corner cases that cause problems when multiple activations do occur.
|
||||
|
||||
### State Transaction Scope
|
||||
|
||||
Some Actor frameworks allow wrapping multiple state operations into an atomic transaction. In Dapr, each state operation is a separate transaction. Because Dapr doesn't dictate how user code is written, a user may trigger multiple state transactions in the code. If the code crashes between transactions, the state is left at the last committed transaction.
|
||||
|
||||
|
||||
## More Information
|
||||
To learn more about Dapr Actor Pattern support, consult the following topics:
|
||||
|
||||
* [Enabling the Actor Pattern](../../topics/enable_actor_pattern.md)
|
|
@ -1,11 +1,11 @@
|
|||
# Dapr Actors Runtime
|
||||
|
||||
Dapr Actors runtime provides capability to use Actor pattern features.
|
||||
Dapr Actors runtime provides following capabilities:
|
||||
|
||||
## Actor State Management
|
||||
Actors are single-threaded objects that can encapsulate both logic and state. Because Dapr provides the state management, so Actors can maintain state reliably by using the same persistence and replication mechanisms. This way, actors don't lose their state after failures, upon reactivation after garbage collection, or when they are moved around between pods in a cluster due to resource balancing or upgrades.
|
||||
Actors can save state reliably using state management capability.
|
||||
|
||||
You can interact with Dapr Actors runtime through Http/gRPC endpoints for state management.
|
||||
You can interact with Dapr through Http/gRPC endpoints for state management.
|
||||
|
||||
### Save the Actor State
|
||||
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
# Integration with Actor Frameworks
|
||||
This article introduces how Dapr integrate with existing Actor Frameworks such as [Orleans](https://github.com/dotnet/orleans), [Akka](https://akka.io/), and [Service Fabric Reliable Actors](https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-actors-introduction). With Dapr integration, these Actor frameworks will allow developers to write Actor code in any programming languages, and to plug their Actors into Dapr eventing pipeline that enables reliable messaging, pub-sub, binding to event sources and many other features.
|
||||
|
||||
## Client-side integration
|
||||
On the client side, Dapr acts as a shim in front of an Actor proxy. This allows Dapr to bring reliable messaging, binding and other features to the connected Actor framework.
|
||||

|
||||
|
||||
## Server-side integration
|
||||
On the server side, Dapr leverages Actor framework capabilities such as state management and single activation guarantee to offer complete Actor framework capabilities.
|
||||
Dapr defines an **Actor API**, which the integrated Actor frameworks are supposed to implement/support.
|
||||
|
||||
> Dapr comes with a built-in Actor framework implemenation through the same Actor API.
|
||||
|
||||
The following diagram illustrates two possible execution paths. 
|
||||
|
||||
### Client calling an Actor (green path)
|
||||
|
||||
1. A client makes a call to an Actor instance.
|
||||
2. Dapr calls an Actor proxy to forward the request.
|
||||
3. As the Actor runtime activates/invokes an Actor, it calls Dapr through the Actor API to invoke the user method.
|
||||
4. Dapr forwards the request to user code through Dapr API.
|
||||
5. As the response is returned, it was sent back to client as a response to the original request.
|
||||
|
||||
### A message triggering an Actor (yellow path)
|
||||
|
||||
1. A message is routed to the Dapr instance that is hosting the destination Actor.
|
||||
2. Dapr dispatches the message to the Actor runtime when possible.
|
||||
3. The rest of the steps are the same as the green path.
|
||||
|
||||
## Actor API
|
||||
At a high level, the Actor API shall contain the following methods:
|
||||
|
||||
* Dispatch to Actor when possible
|
||||
* Call method on an Actor
|
||||
* Load/save state
|
||||
* Create/remove
|
||||
* Locate & send message to an Actor
|
||||
|
||||
## Custom Programming Experience
|
||||
Custom programming experiences can be built on top of Dapr through a custom library. The Custom library adapts Dapr API to a custom API shape that the user code consumes, as shown in the following diagram:
|
||||

|
|
@ -1,76 +0,0 @@
|
|||
# Enabling the Actor Pattern
|
||||
The Actor Pattern has been broadly used in modeling complex distributed systems in which a large number of individual agents work together. For example, in many IoT solutions, actors are often used as digital presences of physical devices. Dapr supports the Actor Pattern by offering key characteristics of an actor. This document introduces architecturally how Dapr enables the Actor Pattern. To learn about how to use the Actor Pattern with Dapr, please read [this document](../concepts/actor/actor_pattern.md).
|
||||
|
||||
## Overview
|
||||
Dapr uses an off-path **partition table** to assign actor ids to service instances so that requests to the same actor id is always routed to the same instance. The partition table is dynamically adjusted when re-partitioning or failover happens. Changes to the table are broadcasted to Dapr sidecars to update their own settings to match with the global table.
|
||||
|
||||
A client can talk to an actor through either reliable messaging or direct invocation, as illustrated by the following diagram:
|
||||

|
||||
|
||||
### Calling an actor through messaging
|
||||
1. When a process pair of user code and Dapr sidecar comes up, the sidecar registers the pair (such as a Pod on Kubernetes) with a **partition table** hosted on the Dapr control plane.
|
||||
2. The partition table updates itself to reflect the topology change.Then, it broadcasts a change notification to all Dapr sidecars.
|
||||
3. The Dapr sidecar updates its own settings to poll messages from corresponding partitions of a reliable queue.
|
||||
4. The client code sends requests to the reliable queue, which puts the requests in corresponding partitions based on the associated contextual ids. Then, the messages are picked up and consumed during the next poll.
|
||||
|
||||
### Calling an actor through direct invocation
|
||||
1. The client sidecar reads the partition table to find actor id assignments.
|
||||
2. The sidecar figures out a direct route to the target service instance and sends the request direct to the destination sidecar.
|
||||
|
||||
## Messaging
|
||||
|
||||
When enabled, each Dapr sidecar has a designated reliable message queue. This allows the sidecar to sequentialize requests to a specific actor id. Dapr supports at-least-once delivery and exact-once delivery within a configurable time window. Please see [reliable messaging](TBD) for more details.
|
||||
|
||||
Reliable messing is not used for direct invocations. In such a case, user code is expected to handle response errors and retry the request as needed.
|
||||
|
||||
## State Management
|
||||
|
||||
All state access are encapsulated in a **state provider**. Dapr expects a state provider to offer state partitioning for scale and replication for high availability and reliability.
|
||||
|
||||
Because all requests to an actor instance are routed to the same Dapr service instance, the user code can use local variables as local caches of its state.
|
||||
|
||||
When Dapr routes an actor instance to a new Dapr service instance, it invokes the **/state** endpoint so that the actor instance can restore state from the state store.
|
||||
|
||||
## Partition Table
|
||||
|
||||
Dapr uses different strategies to manage and use the partition table for different scenarios. Although partition table doesn’t sit on active data path, Dapr tries to employ any possible optimizations to minimize the overheads of table management and lookups.
|
||||
|
||||
### Skipping the table
|
||||
|
||||
If an Dapr service is stateless, and if only direct invocation is needed, the partition table is skipped. In this case, requests to actor instances are distributed by the service load balancer.
|
||||
|
||||
If an Dapr service is stateless, and is known to have light traffic so that messaging system partition is not needed, the table can also be skipped even when reliable messaging is used, because all Dapr instances will simply compete for the latest messages from the same partition.
|
||||
|
||||
If Dapr services have stable identifiers (i.e. doesn’t change when restarted or relocated), and both the possible range of actor Ids and all Dapr service ids are known, a predefined algorithm can be used to decide actor partitions instead of looking up the partition table. However, in such a case, dynamic partitioning is not allowed.
|
||||
|
||||
### Range-only table
|
||||
|
||||
Range-only table contains rows of actor id ranges only. When the service replica number changes, the table is updated so that the possible range is evenly distributed across all service replicas.
|
||||
|
||||
The partition table can change its assignments based on partition loads. For example, when it finds the first partition is heavily used than others, it can reassign multiple service instances to the partition through **dynamic assignments**, or re-partition through **dynamic ranges**, as shown by the following diagram:
|
||||
|
||||

|
||||
|
||||
Dynamic assignments requires less sidecar configuration changes, but it requires a sidecar being able to handle multiple partitions.
|
||||
|
||||
### Per-actor table
|
||||
|
||||
Per-actor table maintains a detailed mapping between actor ids and service instances. Per-actor table is quite expensive to maintain. It’s most useful when the actor code fluctuates in resource consumption when given different workloads. In such case, a more sophisticated scheduling algorithm, such as a greedy algorithm or a dynamic programming algorithm, can be used to place the actor instances.
|
||||
|
||||
In addition to the increased maintenance burden, per-actor table also triggers sidecar configuration updates much more frequently. And the sidecar needs to be able to handle scattered id ranges, when could be hard to be implemented against certain messaging systems.
|
||||
|
||||
In the above discussion, it's been assumed that service instances have stable identifiers. If this is not the case, a check-in-check-out mechanism is needed for a service instance to associate itself to a given partition. In such as case, the partition table defines partition ranges but doesn’t define assignments. Instead, all sidecars come in as competing consumers and attempt to check out partitions. A partition can be explicitly checked in for scenarios such controlled upgrades. Otherwise, a checkout expires after certain time window and the partition is made available for checkouts again.
|
||||
|
||||
## Sequential Access
|
||||
|
||||
When configured to run as actors, Dapr sidecar dispatches messages to user code. The user code is free to use whatever threading model of choice to handle these messages. When the user code tries to preserve state, the state write requests are again dispatched to the underlying state store sequentially.
|
||||
|
||||
Dapr allows read-only requests (signified by the GET verb) to be routed to any service instances. This allows the single-writer-multiple-reader pattern, in which readers get eventual consistent (but almost always up-to-date - the delta is the single in-flight transaction) reads on actor states.
|
||||
|
||||
Such sequential access pattern is broken when multiple activations occur. For example, due to network partition, two service instances get different partition table data and try to poll data from the same partition. One message for an actor goes to intance A and the subsquent message for the same actor goes to instance B. This may lead to consistency issues.
|
||||
|
||||
One way to partially mitigate this is to add a auto-increment version tag on state data so that the user code can check for conflicting updates while submitting its changes.
|
||||
|
||||
## Summary
|
||||
|
||||
Dapr can be used as building blocks to build a complete Actor framework but itself isn’t one. As a developer, you should be aware that when you use Dapr by itself, you can configure it to offer some characteristics of an Actor, but you should not assume it gives a complete feature set as a complete Actor framework.
|
Loading…
Reference in New Issue