Datenpersistenz und -serialisierung in Durable Functions (Azure Functions)

Die Durable Functions-Runtime behält Funktionsparameter, Rückgabewerte und andere Zustände automatisch in einem Aufgabenhub bei, um eine zuverlässige Ausführung sicherzustellen. Umfang und Häufigkeit der im dauerhaftem Speicher aufbewahrten Daten können sich jedoch auf die Leistung der Anwendung und die Kosten für Speichertransaktion auswirken. Abhängig von dem Datentyp, der von Ihrer Anwendung gespeichert wird, müssen auch die Richtlinien für die Datenaufbewahrung und den Datenschutz berücksichtigt werden.

Inhalt des Aufgabenhubs

Aufgabenhubs speichern den aktuellen Status von Instanzen und alle ausstehenden Nachrichten:

  • Instanzzustände speichern den aktuellen Status und den Verlauf einer Instanz. Für Orchestrierungsinstanzen umfasst dieser Status den Runtimezustand, den Orchestrierungsverlauf, Eingaben, Ausgaben und den benutzerdefinierten Status. Für Entitätsinstanzen enthält sie den Entitätsstatus.
  • Nachrichten speichern Funktionseingaben oder -ausgaben, Ereignisnutzdaten und Metadaten, die für interne Zwecke verwendet werden, z. B. für Routing und End-to-End-Korrelation.

Nachrichten werden nach der Verarbeitung gelöscht, aber Instanzenzustände bleiben erhalten, es sei denn, sie werden explizit von der Anwendung oder einem Operator gelöscht. Insbesondere bleibt ein Orchestrierungsverlauf auch nach Abschluss der Orchestrierung im Speicher.

Ein Beispiel dafür, wie Zustände und Nachrichten den Fortschritt einer Orchestrierung darstellen, finden Sie im Ausführungsbeispiel des Aufgabenhubs.

Wo und wie Zustände und Nachrichten im Speicher dargestellt werden, hängt vom Speicheranbieter ab. Der Durable Functions-Standardanbieter ist Azure Storage, der Daten in Warteschlangen, Tabellen und Blobs in einem von Ihnen angegebenen Azure Storage-Konto speichert.

Typen von Daten, die serialisiert und persistent gespeichert werden

Die folgende Liste nennt die unterschiedlichen Typen von Daten, die serialisiert und persistent gespeichert werden, wenn Sie Features von Durable Functions nutzen:

  • Alle Ein- und Ausgaben von Orchestrator-, Aktivitäts- und Entitätsfunktionen, einschließlich IDs und Ausnahmefehler
  • Namen der Orchestrator-, Aktivitäts- und Entitätsfunktionen
  • Namen und Nutzdaten externer Ereignisse
  • Statusnutzdaten der benutzerdefinierten Orchestrierung
  • Nachrichten zur Beendigung der Orchestrierung
  • Dauerhafte Timernutzdaten
  • Dauerhafte HTTP-Anforderungen und Antwort-URLs, Header und Nutzdaten
  • Nutzdaten zu Entitätsaufrufen und Signalen
  • Nutzdaten zum Entitätszustand

Arbeiten mit vertraulichen Daten

Wenn Sie den Azure Storage-Anbieter verwenden, werden alle Daten automatisch im Ruhezustand verschlüsselt. Jeder Benutzer, der Zugriff auf das Speicherkonto hat, kann die Daten jedoch in unverschlüsselter Form lesen. Wenn Sie vertrauliche Daten stärker schützen möchten, sollten Sie in Erwägung ziehen, die Daten zunächst mit Ihren eigenen Verschlüsselungsschlüsseln zu verschlüsseln, damit die Daten in einer vorverschlüsselten Form gespeichert werden.

Alternativ können .NET-Benutzer benutzerdefinierte Serialisierungsanbieter implementieren, die eine automatische Verschlüsselung bereitstellen. Ein Beispiel für die benutzerdefinierte Serialisierung mit Verschlüsselung finden Sie in diesem GitHub-Beispiel.

Hinweis

Wenn Sie sich für die Implementierung der Verschlüsselung auf Anwendungsebene entscheiden, beachten Sie, dass Orchestrierungen und Entitäten für eine unbegrenzte Zeit vorhanden sein können. Dies ist wichtig, wenn Sie Ihre Verschlüsselungsschlüssel rotieren müssen, da eine Orchestrierung oder Entitäten länger ausgeführt werden können, als Ihre Richtlinie für die Schlüsselrotation gilt. Bei einer Schlüsselrotation ist der Schlüssel zum Verschlüsseln der Daten möglicherweise nicht mehr für die Entschlüsselung verfügbar, wenn die Orchestrierung oder Entität das nächste Mal ausgeführt wird. Daher wird die kundenseitige Verschlüsselung nur empfohlen, wenn davon ausgegangen werden kann, dass Orchestrierungen und Entitäten nur für relativ kurze Zeitabschnitte ausgeführt werden.

Anpassen von Serialisierung und Deserialisierung

Standardserialisierungslogik

Durable Functions für .NET verwendet im Prozess Json.NET zum Serialisieren von Orchestrierungs- und Entitätsdaten in JSON. Standardmäßig werden die folgenden Json.NET-Einstellungen verwendet:

Eingaben, Ausgaben und Zustand:

JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.None,
    DateParseHandling = DateParseHandling.None,
}

Ausnahmen:

JsonSerializerSettings
{
    ContractResolver = new ExceptionResolver(),
    TypeNameHandling = TypeNameHandling.Objects,
    ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
}

Eine ausführlichere Dokumentation zu JsonSerializerSettings finden Sie hier.

Anpassen der Serialisierung mit .NET-Attributen

Während der Serialisierung von Daten sucht Json.NET nach verschiedenen Attributen für Klassen und Eigenschaften, mit denen gesteuert wird, wie die Daten aus JSON serialisiert und deserialisiert werden. Wenn Sie den Quellcode für den an die Durable Functions-APIs übergebenen Datentyp besitzen, sollten Sie dem Typ diese Attribute hinzufügen, um Serialisierung und Deserialisierung anzupassen.

Anpassen der Serialisierung per Abhängigkeitsinjektion

Bei Funktions-Apps, die auf .NET abzielen und in der Functions V3-Runtime ausgeführt werden, können Sie per Abhängigkeitsinjektion anpassen, wie Daten und Ausnahmen serialisiert werden. Der folgende Beispielcode veranschaulicht die Verwendung von Abhängigkeitsinjektion zum Überschreiben der Standardeinstellungen von Json.NET für die Serialisierung mithilfe von benutzerdefinierten Implementierungen der Dienstschnittstellen IMessageSerializerSettingsFactory und IErrorSerializerSettingsFactory.

using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;
using System.Collections.Generic;

[assembly: FunctionsStartup(typeof(MyApplication.Startup))]
namespace MyApplication
{
    public class Startup : FunctionsStartup
    {
        public override void Configure(IFunctionsHostBuilder builder)
        {
            builder.Services.AddSingleton<IMessageSerializerSettingsFactory, CustomMessageSerializerSettingsFactory>();
            builder.Services.AddSingleton<IErrorSerializerSettingsFactory, CustomErrorSerializerSettingsFactory>();
        }

        /// <summary>
        /// A factory that provides the serialization for all inputs and outputs for activities and
        /// orchestrations, as well as entity state.
        /// </summary>
        internal class CustomMessageSerializerSettingsFactory : IMessageSerializerSettingsFactory
        {
            public JsonSerializerSettings CreateJsonSerializerSettings()
            {
                // Return your custom JsonSerializerSettings here
            }
        }

        /// <summary>
        /// A factory that provides the serialization for all exceptions thrown by activities
        /// and orchestrations
        /// </summary>
        internal class CustomErrorSerializerSettingsFactory : IErrorSerializerSettingsFactory
        {
            public JsonSerializerSettings CreateJsonSerializerSettings()
            {
                // Return your custom JsonSerializerSettings here
            }
        }
    }
}