Ich möchte Ihnen sagen, wie wir eine Hintergrunddatenaktualisierung während einer Anfrage an einen REST-Service organisiert haben.
Die Aufgabe lautet wie folgt: Das System speichert Benutzerdaten. Der Dienst arbeitet isoliert und hat mit diesen Daten keinen direkten Zugriff auf die Datenbanken. Damit der Dienst funktioniert, müssen die Vor- und Nachnamen der Benutzer in der internen Datenbank enthalten sein. Sie können aus der Identität des aktuellen Benutzers zum Zeitpunkt der Anforderung abgerufen werden. Erforderlich, um Namen während jeder Anforderung hinzuzufügen oder zu aktualisieren. Es ist ratsam, dies in einem separaten Thread zu implementieren, damit diese Arbeit die Ausführungszeit der Hauptanforderung nicht beeinflusst.
Aufgabenverfeinerung
In der Servicedatenbank speichern wir die Vor- und Nachnamen der Benutzer. Kunden benötigen sie, um Informationen darüber zu erhalten, wer die Ressource erstellt oder geändert hat.
Diese Daten sind nicht systemrelevant: Wenn plötzlich in der Datenbank keine erforderlichen Datensätze vorhanden sind, passiert nichts Schlimmes. Daher möchten wir unsere Hintergrundarbeit nicht mit QueueBackgroundWorkItem in ASP registrieren, um die Überlastung der Anwendungsdomäne zu erschweren.
Es ist ratsam, das Problem so einfach wie möglich zu lösen.
Für diejenigen, die mehr über Hintergrundaufgaben in ASP.NET erfahren möchten, empfehle ich Ihnen, einen
guten Artikel darüber zu lesen.
Lösung
Wir haben eine DbRefresher-Klasse, die Benutzerdaten in der RefreshAsync-Methode hinzufügt oder ändert.
Unsere Controller verwenden das Authorize-Attribut. Fügen Sie unseren Nachkommen zu dieser Klasse hinzu und überschreiben Sie die OnAuthorization-Methode:
public override void OnAuthorization(HttpActionContext actionContext) { base.OnAuthorization(actionContext); if (IsAuthorized(actionContext)) DbRefresher.RefreshAsync(actionContext.RequestContext.Principal) .ContinueWith(t => { LogFactory.For<AuthorizeAndRefreshUserAttribute>() .ErrorException("Error occured", t.Exception); }, TaskContinuationOptions.OnlyOnFaulted); }
DbRefresher.RefreshAsync ist eine asynchrone Methode, die ein Task-Objekt zurückgibt, das weiterhin in einem anderen Thread ausgeführt wird. Die OnAuthorize-Methode wird sofort beendet, ohne auf den Abschluss der Aufgabe zu warten. Im Falle eines Absturzes wird dem Protokoll eine Fehlermeldung hinzugefügt.
Das ist alles: Alles, was bleibt, ist, das Authorize-Attribut in den Controllern durch den Namen unseres neuen Attributs zu ersetzen. Das neue Attribut gibt die Kontrolle sofort zurück, nachdem die Rechte des aktuellen Benutzers überprüft wurden. Danach beginnt der Controller seine Arbeit. Die Datenbank wird parallel zur Vorbereitung der Antwort durch den Controller aktualisiert.
Testen
Ein Test, bei dem mehrere Serviceanfragen gleichzeitig ausgeführt werden, hilft dabei, mögliche Probleme zu identifizieren:
[TestMethod] public void ConcurrentTest() { const int threadCount = 10; var tasks = new Task[threadCount]; for (int i = 0; i < threadCount; i++) {
Die Probleme
Wenn für den Betrieb einiger Aktionsmethoden des Controllers erforderlich ist, dass die Datenbank offensichtlich die Daten des aktuellen Benutzers enthält, ist dieser Ansatz nicht geeignet. Sie müssen einen expliziten Aufruf von DbRefresher.RefreshAsync im Methodenkörper verwenden.
Beim Hinzufügen eines neuen Benutzers zur Datenbank mit mehreren gleichzeitigen Anforderungen können Probleme auftreten. Wenn versucht wird, einen Benutzer mit einem vorhandenen Schlüssel zu einer Tabelle hinzuzufügen, sollten Sie die Ausnahme für die Verletzung des Primärschlüssels abfangen und die Arbeit einstellen. Dann werden alle Arbeiten zur Aktualisierung der Benutzerdaten von nur einem Thread ausgeführt.
Fazit
Dieser Ansatz arbeitet seit über einem Jahr erfolgreich in einem unserer Dienste in Konfermit.
Es scheint mir einfach und elegant. Es wäre interessant, von der Community zu hören.