Bloque de creación de administración de estado de Dapr
Sugerencia
Este contenido es un extracto del libro electrónico "Dapr for .NET Developers" (Dapr para desarrolladores de .NET), disponible en Documentación de .NET o como un PDF descargable gratuito que se puede leer sin conexión.
Las aplicaciones distribuidas se componen de servicios independientes. Aunque cada servicio no debe tener estado, algunos servicios deben realizar un seguimiento del estado para completar las operaciones empresariales. Considere un servicio de cesta de la compra de un sitio de comercio electrónico. Si el servicio no puede realizar un seguimiento del estado, el cliente podría perder el contenido de la cesta de la compra cuando sale del sitio web, lo que da lugar a una venta perdida y a una experiencia de cliente poco satisfactoria. En estos escenarios, el estado debe conservarse en un almacén de estado distribuido. El bloque de creación de administración de estado de Dapr simplifica el seguimiento del estado y ofrece características avanzadas en varios almacenes de datos.
Para probar el bloque de creación de administración de estado, consulte el ejemplo de aplicación de contador en el capítulo 3.
Qué hace
El seguimiento del estado en una aplicación distribuida puede ser complicado. Por ejemplo:
- La aplicación puede requerir diferentes tipos de almacenes de datos.
- Es posible que se necesiten distintos niveles de coherencia para acceder a los datos y actualizarlos.
- Varios usuarios pueden actualizar los datos al mismo tiempo, lo que requiere resolución de conflictos.
- Los servicios deben reintentar los errores transitorios de corta duración que se produzcan al interactuar con el almacén de datos.
El bloque de creación de administración de estado de Dapr aborda estos desafíos. Así, simplifica el seguimiento del estado sin dependencias o una curva de aprendizaje de los SDK de almacenamiento de terceros.
Importante
La administración de estado de Dapr ofrece una API de clave-valor. La característica no admite el almacenamiento de datos relacional o de grafos.
Cómo funciona
La aplicación interactúa con un sidecar de Dapr para almacenar y recuperar datos de clave-valor. En segundo plano, la API sidecar consume un componente de almacén de estado configurable para conservar los datos. Los desarrolladores pueden elegir entre una creciente colección de almacenes de estado admitidos que incluyen Azure Cosmos DB, SQL Server y Cassandra.
Se puede llamar a la API con HTTP o gRPC. Use la siguiente dirección URL para llamar a la API HTTP:
http://localhost:<dapr-port>/v1.0/state/<store-name>/
<dapr-port>
: el puerto HTTP en el que Dapr escucha.<store-name>
: el nombre del componente de almacén de estado que se va a usar.
En la figura 5-1 se muestra cómo un servicio de cesta de la compra habilitado para Dapr almacena un par clave-valor mediante el componente de almacén de estado de Dapr denominado statestore
.
Figura 5-1. Almacenamiento de un par clave-valor en un almacén de estado de Dapr.
Tenga en cuenta los pasos de la ilustración anterior:
- El servicio de cesta de la compra llama a la API de administración de estado en el sidecar de Dapr. El cuerpo de la solicitud incluye una matriz JSON que puede contener varios pares clave-valor.
- El sidecar de Dapr determina el almacén de estado en función del archivo de configuración del componente. En este caso, es un almacén de estado de caché de Redis.
- El archivo sidecar conserva los datos en la caché de Redis.
Recuperar los datos almacenados es una llamada API parecida. En el ejemplo siguiente, un comando curl recupera los datos mediante una llamada a la API sidecar de Dapr:
curl http://localhost:3500/v1.0/state/statestore/basket1
El comando devuelve el estado almacenado en el cuerpo de la respuesta:
{
"items": [
{
"itemId": "DaprHoodie",
"quantity": 1
}
],
"customerId": 1
}
En las secciones siguientes se explica cómo usar las características más avanzadas del bloque de creación de administración de estado.
Coherencia
El teorema CAP es un conjunto de principios que se aplican a los sistemas distribuidos que almacenan el estado. En la figura 5-2 se muestran las tres propiedades del teorema CAP.
Figura 5-2. Teorema CAP.
El teorema indica que los sistemas de datos distribuidos ofrecen un equilibrio entre la coherencia, la disponibilidad y la tolerancia a particiones. Además, cualquier almacén de datos solo puede garantizar dos de las tres propiedades:
Coherencia (C). Cada nodo del clúster responde con los datos más recientes, incluso si el sistema debe bloquear la solicitud hasta que se actualicen todas las réplicas. Si consulta un "sistema coherente" para un artículo que se está actualizando actualmente, no recibirá una respuesta hasta que todas las réplicas se actualicen correctamente. Sin embargo, siempre recibirá los datos más actuales.
Disponibilidad (A [availability]). Cada nodo devuelve una respuesta inmediata, incluso si esa respuesta no son los datos más recientes. Si consulta un "sistema disponible" para un artículo que se está actualizando, obtendrá la mejor respuesta posible que el servicio puede proporcionar en ese momento.
Tolerancia a particiones (P). Garantiza que el sistema sigue funcionando incluso si se produce un error en un nodo de datos replicado o se pierde la conectividad con otros nodos de datos replicados.
Las aplicaciones distribuidas deben controlar la propiedad P. A medida que los servicios se comunican entre sí con llamadas de red, se producirán interrupciones de red (P). Teniendo esto en cuenta, las aplicaciones distribuidas deben ser AP o CP.
Las aplicaciones AP eligen la disponibilidad por encima de la coherencia. Dapr admite esta opción con su estrategia de coherencia posible. Considere un almacén de datos subyacente, como Azure CosmosDB, que almacena datos redundantes en varias réplicas. Con la coherencia posible, el almacén de estado escribe la actualización en una réplica y completa la solicitud de escritura con el cliente. Después de este tiempo, el almacén actualizará asincrónicamente sus réplicas. Las solicitudes de lectura pueden devolver datos de cualquiera de las réplicas, incluidas las réplicas que aún no han recibido la actualización más reciente.
Las aplicaciones CP eligen la coherencia por encima de la disponibilidad. Dapr admite esta opción con su estrategia de coherencia fuerte . En este escenario, el almacén de estado actualizará de forma sincrónica todas las réplicas necesarias (o, en algunos casos, un cuórum de estas) antes de completar la solicitud de escritura. Las operaciones de lectura devolverán los datos más actualizados de forma coherente entre réplicas.
El nivel de coherencia de una operación de estado se especifica asociando una sugerencia de coherencia a la operación. El siguiente comando curl escribe un par clave-valor Hello=World
en un almacén de estado mediante una sugerencia de coherencia fuerte:
curl -X POST http://localhost:3500/v1.0/state/<store-name> \
-H "Content-Type: application/json" \
-d '[
{
"key": "Hello",
"value": "World",
"options": {
"consistency": "strong"
}
}
]'
Importante
Corresponde al componente de almacén de estado de Dapr cumplir con la sugerencia de coherencia asociada a la operación. No todos los almacenes de datos admiten ambos niveles de coherencia. Si no se establece ninguna sugerencia de coherencia, el comportamiento predeterminado es posible.
Simultaneidad
En una aplicación multiusuario, existe la posibilidad de que varios usuarios actualicen los mismos datos simultáneamente (al mismo tiempo). Dapr admite el control de simultaneidad optimista (OCC) para administrar conflictos. OCC se basa en una suposición de que los conflictos de actualización son poco habituales porque los usuarios trabajan en diferentes partes de los datos. Es más eficaz suponer que una actualización se realizará correctamente y se reintentará si no lo hace. La alternativa, implementar el bloqueo pesimista, puede afectar al rendimiento con bloqueos de larga duración que provocan la contención de datos.
Dapr admite el control de simultaneidad optimista (OCC) mediante ETags. Una ETag es un valor asociado a una versión específica de un par clave-valor almacenado. Cada vez que se actualiza un par clave-valor, también se actualiza el valor de ETag. Cuando un cliente recupera un par clave-valor, la respuesta incluye el valor de ETag actual. Cuando un cliente actualiza o elimina un par clave-valor, debe devolver ese valor de ETag en el cuerpo de la solicitud. Si otro cliente ha actualizado los datos mientras tanto, las ETags no coincidirán y se producirá un error en la solicitud. En este momento, el cliente debe recuperar los datos actualizados, volver a realizar el cambio y reenviar la actualización. Esta estrategia se conoce como la primera escritura gana.
Dapr también admite una estrategia la última escritura gana. Con este enfoque, el cliente no asocia una ETag a la solicitud de escritura. El componente de almacén de estado siempre permitirá la actualización, incluso si el valor subyacente ha cambiado durante la sesión. La última escritura gana es útil en escenarios de escritura de alto rendimiento con contención de datos baja. Además, se puede tolerar la sobrescritura de una actualización de usuario ocasional.
Transacciones
Dapr puede escribir cambios de varios elementos en un almacén de datos como una sola operación implementada como una transacción. Esta funcionalidad solo está disponible para almacenes de datos que admiten transacciones ACID. En el momento de redactar este artículo, estos almacenes incluyen Redis, MongoDB, PostgreSQL, SQL Server y Azure CosmosDB.
En el ejemplo siguiente, se envía una operación de varios elementos al almacén de estado en una sola transacción. Todas las operaciones deben realizarse correctamente para que la transacción se confirme. Si se produce un error en una o varias de las operaciones, se revierte toda la transacción.
curl -X POST http://localhost:3500/v1.0/state/<store-name>/transaction \
-H "Content-Type: application/json" \
-d '{
"operations": [
{
"operation": "upsert",
"request": { "key": "Key1", "value": "Value1"
}
},
{
"operation": "delete",
"request": { "key": "Key2" }
}
]
}'
En el caso de los almacenes de datos que no admiten transacciones, se pueden enviar varias claves como una sola solicitud. En el ejemplo siguiente se muestra una operación de escritura masiva:
curl -X POST http://localhost:3500/v1.0/state/<store-name> \
-H "Content-Type: application/json" \
-d '[
{ "key": "Key1", "value": "Value1" },
{ "key": "Key2", "value": "Value2" }
]'
En el caso de las operaciones masivas, Dapr enviará al almacén de datos cada actualización de los pares clave-valor como una solicitud independiente.
Uso del SDK de .NET de Dapr
El SDK de .NET de Dapr proporciona compatibilidad específica del lenguaje con la plataforma .NET. Los desarrolladores pueden usar la clase DaprClient
introducida en el capítulo 3 para leer y escribir datos. En el ejemplo siguiente se muestra cómo usar el método DaprClient.GetStateAsync<TValue>
para leer datos de un almacén de estado. El método espera el nombre del almacén, statestore
, y la clave, AMS
, como parámetros:
var weatherForecast = await daprClient.GetStateAsync<WeatherForecast>("statestore", "AMS");
Si el almacén de estado no contiene datos para la clave AMS
, el resultado será default(WeatherForecast)
.
Para escribir datos en el almacén de datos, use el método DaprClient.SaveStateAsync<TValue>
:
daprClient.SaveStateAsync("statestore", "AMS", weatherForecast);
En el ejemplo se usa la estrategia la última escritura gana dado que no se pasa un valor de ETag al componente de almacén de estado. Para usar el control de simultaneidad optimista (OCC) con una estrategia la primera escritura gana, primero recupere la ETag actual mediante el método DaprClient.GetStateAndETagAsync
. A continuación, escriba el valor actualizado y pase la ETag recuperada mediante el método DaprClient.TrySaveStateAsync
.
var (weatherForecast, etag) = await daprClient.GetStateAndETagAsync<WeatherForecast>("statestore", city);
// ... make some changes to the retrieved weather forecast
var result = await daprClient.TrySaveStateAsync("statestore", city, weatherForecast, etag);
El método DaprClient.TrySaveStateAsync
produce un error cuando los datos (y la ETag asociada) se han cambiado en el almacén de estado después de recuperar los datos. El método devuelve un valor booleano para indicar si la llamada se realizó correctamente. Una estrategia para controlar el error es simplemente volver a cargar los datos actualizados del almacén de estado, volver a realizar el cambio y reenviar la actualización.
Si siempre quiere que una escritura se realice correctamente independientemente de otros cambios en los datos, use la estrategia la última escritura gana.
El SDK proporciona otros métodos para recuperar datos de forma masiva, eliminar datos y ejecutar transacciones. Para más información, consulte el repositorio del SDK de .NET para Dapr.
Integración de ASP.NET Core
Dapr también admite ASP.NET Core, un marco multiplataforma para crear aplicaciones web modernas basadas en la nube. El SDK de Dapr integra las funcionalidades de administración de estado directamente en las funcionalidades de enlace de modelos de ASP.NET Core. La configuración es sencilla. En el archivo Program.cs
, llame al siguiente método de extensión del generador WebApplication
:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers().AddDapr();
Una vez configurado, Dapr puede insertar un par clave-valor directamente en una acción del controlador mediante el atributo FromState
de ASP.NET Core. Ya no es necesario hacer referencia al objeto DaprClient
. En el ejemplo siguiente se muestra una API web que devuelve la previsión meteorológica de una ciudad determinada:
[HttpGet("{city}")]
public ActionResult<WeatherForecast> Get([FromState("statestore", "city")] StateEntry<WeatherForecast> forecast)
{
if (forecast.Value == null)
{
return NotFound();
}
return forecast.Value;
}
En el ejemplo, el controlador carga la previsión meteorológica mediante el atributo FromState
. El primer parámetro de atributo es el almacén de estado, statestore
. El segundo parámetro de atributo, city
, es el nombre de la variable plantilla de ruta para obtener la clave de estado. Si omite el segundo parámetro, se usa el nombre del parámetro de método enlazado (forecast
) para buscar la variable de plantilla de ruta.
La clase StateEntry
contiene propiedades de toda la información que se recupera para un único par clave-valor: StoreName
, Key
, Value
y ETag
. La ETag es útil para implementar la estrategia de control de simultaneidad optimista (OCC). La clase también proporciona métodos para eliminar o actualizar los datos de clave y valor recuperados sin necesidad de una instancia de DaprClient
. En el ejemplo siguiente, el método TrySaveAsync
se usa para actualizar la previsión meteorológica recuperada mediante OCC.
[HttpPut("{city}")]
public async Task Put(WeatherForecast updatedForecast, [FromState("statestore", "city")] StateEntry<WeatherForecast> currentForecast)
{
// update cached current forecast with updated forecast passed into service endpoint
currentForecast.Value = updatedForecast;
// update state store
var success = await currentForecast.TrySaveAsync();
// ... check result
}
Componentes del almacén de estado
En el momento de redactar este documento, Dapr proporciona compatibilidad con los siguientes almacenes de estado transaccionales:
- Azure CosmosDB
- Azure SQL Server
- CockroachDB
- En memoria
- MongoDB
- MySQL
- Base de datos de Oracle
- PostgreSQL
- Redis
- RethinkDB
Dapr también incluye compatibilidad con almacenes de estado que admiten operaciones CRUD, pero no funcionalidades transaccionales:
- Aerospike
- Apache Cassandra
- AWS DynamoDB
- Azure Blob Storage
- Azure Table Storage
- Couchbase
- GCP Firestore
- Hashicorp Consul
- Hazelcast
- JetStream KV
- Memcached
- Almacenamiento de objetos de Oracle
- Zookeeper
Configuración
Cuando se inicializa para el desarrollo local autohospedado, Dapr registra Redis como el almacén de estado predeterminado. Este es un ejemplo de la configuración del almacén de estado predeterminado. Anote el nombre predeterminado, statestore
:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""
- name: actorStateStore
value: "true"
Nota
Muchos almacenes de estado se pueden registrar en una sola aplicación cada uno con un nombre diferente.
El almacén de estado de Redis requiere los metadatos redisHost
y redisPassword
para conectarse a la instancia de Redis. En el ejemplo anterior, la contraseña de Redis (que es una cadena vacía de forma predeterminada) se almacena como una cadena sin formato. El procedimiento recomendado es evitar cadenas de texto no cifrado y usar siempre referencias a secretos. Para más información sobre la administración de secretos, consulte el capítulo 10.
El otro campo de metadatos, actorStateStore
, indica si el bloque de creación de actores puede consumir el almacén de estado.
Estrategias de prefijo de clave
Los componentes del almacén de estado permiten diferentes estrategias para almacenar pares clave-valor en el almacén subyacente. Recuerde el ejemplo anterior de un servicio de cesta de la compra que almacena artículos que un cliente desea comprar:
curl -X POST http://localhost:3500/v1.0/state/statestore \
-H "Content-Type: application/json" \
-d '[{
"key": "basket1",
"value": {
"customerId": 1,
"items": [
{ "itemId": "DaprHoodie", "quantity": 1 }
]
}
}]'
Con la herramienta Redis Console, busque dentro de la caché de Redis para ver cómo el componente del almacén de estado de Redis conserva los datos:
127.0.0.1:6379> KEYS *
1) "basketservice||basket1"
127.0.0.1:6379> HGETALL basketservice||basket1
1) "data"
2) "{\"items\":[{\"itemId\":\"DaprHoodie\",\"quantity\":1}],\"customerId\":1}"
3) "version"
4) "1"
La salida muestra la clave completa de Redis para los datos como basketservice||basket1
. De forma predeterminada, Dapr usa el valor application id
de la instancia de Dapr (basketservice
) como prefijo para la clave. Esta convención de nomenclatura permite que varias instancias de Dapr compartan el mismo almacén de datos sin que haya conflictos con los nombres de clave. Para el desarrollador, siempre es fundamental especificar el mismo valor application id
al ejecutar la aplicación con Dapr. Si se omite, Dapr generará un identificador de aplicación único. Si cambia el valor application id
, la aplicación ya no podrá acceder al estado almacenado con el prefijo de clave anterior.
Dicho esto, es posible configurar un valor constante para el prefijo de clave en el campo de metadatos keyPrefix
del archivo del componente de almacén de estado. Considere el ejemplo siguiente:
spec:
metadata:
- name: keyPrefix
- value: MyPrefix
Un prefijo de clave constante permite tener acceso al almacén de estado en varias aplicaciones de Dapr. Además, al establecer keyPrefix
en none
se omite el prefijo por completo.
Aplicación de ejemplo: Traffic Control de Dapr
En la aplicación de ejemplo Traffic Control de Dapr, el servicio TrafficControl usa el bloque de creación de administración de estado de Dapr para conservar las marcas de tiempo de entrada y salida de cada vehículo que pasa. En la figura 5-3 se muestra la arquitectura conceptual de la aplicación de ejemplo Traffic Control de Dapr. El bloque de creación de administración de estado de Dapr se usa en flujos marcados con el número 3 en el diagrama:
Figura 5-3. Arquitectura conceptual de la aplicación de ejemplo Traffic Control de Dapr.
La clase TrafficController
, un controlador de ASP.NET normal, controla la lógica de eventos de entrada y salida. El método TrafficController.VehicleEntry
acepta un mensaje entrante VehicleRegistered
y guarda el estado del vehículo indicado:
// store vehicle state
var vehicleState = new VehicleState
{
LicenseNumber = msg.LicenseNumber,
EntryTimestamp = msg.Timestamp
};
await _vehicleStateRepository.SaveVehicleStateAsync(vehicleState);
En el fragmento de código anterior, la abstracción _vehicleStateRepository
es responsable de guardar el estado en el almacén de datos. Su implementación concreta, DaprVehicleStateRepository
, se muestra a continuación:
public class DaprVehicleStateRepository : IVehicleStateRepository
{
private const string DAPR_STORE_NAME = "statestore";
private readonly DaprClient _daprClient;
public DaprVehicleStateRepository(DaprClient daprClient)
{
_daprClient = daprClient;
}
public async Task SaveVehicleStateAsync(VehicleState vehicleState)
{
await _daprClient.SaveStateAsync<VehicleState>(
DAPR_STORE_NAME, vehicleState.LicenseNumber, vehicleState);
}
public async Task<VehicleState> GetVehicleStateAsync(string licenseNumber)
{
return await _daprClient.GetStateAsync<VehicleState>(
DAPR_STORE_NAME, licenseNumber);
}
}
Como se muestra en el fragmento de código anterior, la implementación de la clase DaprVehicleStateRepository
es bastante sencilla. El método SaveVehicleStateAsync
usa el objeto DaprClient
insertado para guardar el estado en el almacén de estado de Dapr configurado. Además, utiliza el número de matrícula del vehículo como clave. La aplicación puede recuperar el estado guardado llamando al método GetVehicleStateAsync
.
El servicio TrafficControl usa Redis como almacén de datos subyacente. Mirando el código, nunca lo sabría. Un servicio que consume el bloque de creación de administración de estado de Dapr no hace referencia directamente a ningún componente de estado. En su lugar, un archivo de configuración del componente de Dapr especifica el almacén:
apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: statestore
namespace: dapr-trafficcontrol
spec:
type: state.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
secretKeyRef:
name: state.redisPassword
key: state.redisPassword
scopes:
- trafficcontrolservice
Nota
El archivo de configuración del componente incluye un elemento secretKeyRef
. La aplicación lo usa para hacer referencia al valor de contraseña de Redis desde el bloque de creación de secretos de Dapr. Consulte el capítulo 10 para más información sobre la administración de secretos con Dapr.
El elemento type
de la configuración, state.redis
, indica al bloque de creación que administre el estado con el componente Redis de Dapr.
El elemento scopes
de la configuración restringe el acceso de la aplicación al componente de almacén de estado. Solo el servicio TrafficControl puede acceder al almacén de estado.
Resumen
El bloque de creación de administración de estado de Dapr ofrece una API para almacenar datos de clave y valor en varios almacenes de datos. La API proporciona compatibilidad con:
- Operaciones masivas
- Coherencia fuerte y posible
- Control de simultaneidad optimista
- Transacciones de varios elementos
El SDK de .NET proporciona compatibilidad específica del lenguaje con .NET y ASP.NET Core. La integración del enlace de modelos simplifica el acceso y la actualización del estado desde los métodos de acción del controlador de ASP.NET Core.
En la aplicación de ejemplo Traffic Control de Dapr, las ventajas de usar la administración de estado de Dapr son claras:
- Abstrae la complejidad del uso de SDK de terceros, como
StackExchange.Redis
. - Para reemplazar la caché de Redis subyacente por otro tipo de almacén de datos solo hacen falta cambios en el archivo de configuración del componente.
Referencias
.NET feedback
The .NET documentation is open source. Provide feedback here.
Comentarios
Enviar y ver comentarios de