Hallo zusammen,
ich hätte eine kurze Frage: Die Anforderung ist es, Excel-Dateien in einer Datenbank zu importieren. Das ganze muss von extern funktionieren.
Bisher wurden die Excel-Dateien nur über eine Web Api hochgeladen(In Chunks).
Mich würde interessieren, welcher der x-tausend Wege eurer Meinung nach am besten geeignet wäre:
**Nachdem die Dateien hochgeladen sind, dem Benutzer die Möglichkeit geben den Import anzustoßen (pro Datei anstoßen, oder einmal anstoßen für alle?). Daraus folgt: **
--> Request-Verbindung offen halten und dann einen Statuscode zurückgeben. Wenn einmal anstoßen für alle, müsste man noch entsprechend zurückgeben, welche Datei einen eventuellen Fehler hatte. Außerdem müsste die Timeout-Zeit wahrscheinlich relativ hoch sein.
--> Anstoßen und Ok zurückgeben für Start des Imports(Request-Ende). Intern dann vielleicht in einem Dataflow die Dateien abarbeiten. Nur wie bekommt der Benutzer eine Rückmeldung? Polling und gucken ob abgeschlossen? Wenn ja, wie sollte der Dataflow-Service aufgebaut sein? Als Singleton für alle Benutzer, oder jedes mal eine neue Instanz und entsprechend neue Threads (eher ungünstig für Web Api).
--> SingalR für "Live-Aktualisierung" des Imports. Bisher wird SignalR aber in der Web Api nicht gebraucht. Live-Aktualisierung beim Client ist kein muss.
Oder auf den Chunk-Upload verzichten?
--> Dann direkt den Import nach Upload starten.
Ich hoffe ihr könnt meinen Gedankengängen folgen und mir den richtigen Weg zeigen.
Danke
Verbindung offen halten wird in 90% der Zeit schief gehen. Wenn es länger dauert, dann ist die Gefahr groß, dass der Benutzer irgendwann einfach das Fenster (ausversehen) schließt.
Zudem soll man i.d.R. nichts im WebAPI Prozess ausführen, das länger als 10-30 Sekunden dauert. Hier währen wir beim Thema Architektur und Microservices.
Besserer Weg ist definitiv das Pipelining.
SignalR wäre dann auch nicht innerhalb der WebAPI, sondern eben im Rahmen eines Microservices eine eigenständige Instanz.
Ich nenne das gerne einfach nur PushService.
Trennung zB
host/ExcelApi/v2/Upload host/PushAPI/v2
Singeltons haben in WebAPIs nichts verloren.
Eine WebAPI sollte logisch gesehen immer stateless sein.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Da das ganze auf Asp.Net Core umziehen soll, wäre Plattformunabhängigkeit ganz schön. Daher würde der Windows-Service rausfallen (auf Mono würde ich gerne verzichten)
Oder einmal ein Windows-Service für Windows und eine Anwendung für Linux (Asp.Net Core-> Konsole?).
Wie würdest du mit dem Windows-Service kommunizieren? Wenn über SignalR Live-Aktualisieren rausgehen sollen, müsste ich diese vom Serivce bekommen. IPC --> WCF?
EDIT: Wie am besten mit der Linux-Anwendung kommunizieren?
komplett auf WebSockets setzen (Linux und Windows)?
Ablauf:
Da SignalR sonst nicht gebraucht wird, würde ich nur dafür die Verbindung öffnen und dann wieder schließen.
SignalR und WebApi in einer Anwendung, mit verschiedenen URL's, oder hier auch nochmal splitten?
(Mir persönlich wäre es ein bisschen viel 3 Anwendungen zu haben, für einen "kleinen" Import.)
Vielen Dank
Spielt keine Rolle ob Windows Service oder irgendein Äquivalent.
Lässte die WebAPI halt in nem Docker Contailer laufen. Zusätzlich haste ne .NET Core App für den Import selbst in nem anderen Docker Container.
Die Excel nimmste in der WebAPI an, legst diese in einen Ordner, auf den beide Docker Container Zugriff haben. Die Import App nimmt die Excel Datei entgegen und verarbeitet sie - fertig.
Entsprechendes Prinzip hat Rainer Stropek auf der Basta gezeigt
Docker für ASP.NET-Entwickler, bringt das was? (Position ca. Minute 55)
SignalR und WebApi in einer Anwendung, mit verschiedenen URL's, oder hier auch nochmal splitten?
(Mir persönlich wäre es ein bisschen viel 3 Anwendungen zu haben, für einen "kleinen" Import.)
Im Sinne der Microservice Architektur separierst Du Anwendungen nach ihren Aufgaben.
Es ist auch viel leichter zu konfigurieren, es zu warten und auch auszurollen. Mit Docker dann sowieso.
Kannst auch alles in eines packen - finde ich (auch aus Erfahrung) aber nicht gut.
Ich separiere es mittlerweile für die simpelsten Dinge, einfach weil auch die Wiederverwendbarkeit viel viel höher ist.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Danke erstmal. "Eine" Frage hätte ich aber noch zur Architektur:
Die Web Api übernimmt den Upload und schiebt die Datei in einen Ordner.
Gibt es hier nun 2 weitere Anwendungen oder eine? SignalR für Kommunikation zum Client und Anwendung für den Import? Oder nur die SignalR Anwendung, die auch den Import übernimmt?
Sollte es 2 Anwendungen geben, wäre meine Frage, wie die Anwendungen miteinander "sprechen". Auch wieder SignalR? Da sonst der Client keine Informationen bekommt.
Ich sag Dir mal, wie ich da eigentlich vorgehe; habe derzeit eine ähnliche Anforderung für ein Projekt für den führenden Hersteller von Kettensägen.
Täglich wird von einem externen System eine Datei mit rund 130 MB an Informationen hochgeladen.
Diese Datei muss ich also entgegennehmen, strukturell validieren, parsen, inhaltlich validieren, verarbeiten und Werte in die Datenbank eintragen oder aktualisieren.
Gleichzeitig wird über eine WebAPI REST-mäßig eben CRUD-Befehle ausgeführt.
Eine weitere Anforderung ist bei mir das administrative Verwalten der Einträge sowie Reporting.
Ich habe also eine Anwendung, die nur den Import entgegen nimmt.
Eine Anwendung für die CRUD-Ausführung.
Eine Anwendung für administrative Dinge und eine für die Reports.
Warum trenne ich das?
Aber wenn ich was am Reporting ändere, dann muss ich nicht die hoch-prio API abschalten für den Deployment Prozess.
Wenn das Reporting kracht oder mal ne Woche nicht verfügbar ist: stört hier niemanden.
Wenn das Hochladen mal eine Nacht nicht geht, oder auch hier was geändert werden muss: gut, dann ist das so. Hauptsache die CRUD-API läuft.
Kommunizieren tun Microservices entweder durch direkte Schnittstellen, oder durch einen Message Bus.
Das kommt drauf an.
Azure selbst intern nutzt zB. REST APIs untereinander.
Deswegen gibt es bei Microsoft intern auch die Regel, dass ein Breaking Change einer REST API eines Services mindestens 1 Jahr vorher schriftlich angekündigt werden muss.
In Deinem Fall würde ich wahrscheinlich ebenfalls vier Anwendungen haben:
Das hört sich alles erst mal viel an:
aber mit entsprechend guter Aufteilung der Projekte in einer Solution und einem guten Continuous Integration Design ist das alles echt ein Kinderspiel und dann nur noch eine Sache von Minuten.
Gleichzeitig bist Du aber enorm flexibel.
Die Upload API nimmt also die Excel entgegen und schiebt sie in einen Ordner.
Wenn das Verschieben schief geht, dann wird über den Push-Service der Benutzer informiert, dass es schief ging.
Hat es geklappt, dann erkennt die .NET Core Import App die neue Excel-Datei und verarbeitet diese.
Statusnachrichten werden hier ebenfalls über die PushAPI an den Benutzer verteilt.
Die CRUD-API selbst sollte keine/so wenig wie auch nur möglich Abhängigkeiten an andere Services haben (meine Ansicht/Meinung!).
Das ist alles nur eine Idee für Dich und eine Erklärung, wie ich i.d.R. vorgehe. Du kannst das auch entsprechend vereinfachen.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Vielen Dank für die ausführliche Antwort. Sehr nachvollziehbar und hilfreich. Ich werde es nun erstmal wie von die beschrieben aufziehen und gucken ob ich auf Hindernisse stoße.
Danke
EDIT: Wenn du von einem "Watcher" redest
eine .NET Core App mit einem Watcher für die Excel Datei für die Verarbeitung
meinst du den FileSystemwatcher? Ich hatte bisher nie die Anforderung diesen zu benutzen, habe allerdings gehört, dass dieser nicht unbedingt zuverlässig sein muss.
Da gibts ja viele Wege, wie Du die Verarbeitung des Imports anstößt.
FileSystemwatcher ist glaube ich bei .NET Core auch gar nicht verfügbar. Daher fällt der eh raus.
Aber Du könntest zB. in dem Import-Docker-Container nen NodeJS laufen lassen (der kann Watchen, siehe Rainers YouTube Video).
NodeJS informiert dann Deine .NET Core App oder startet diese an und fertig.
Das schöne bei solch einer Architektur ist ja, dass Du das jederzeit anpassen und Technologien einfach mixen kannst, wie eben die Anforderungen wachsen 😃
Ich hab auch nen NodeJS Container, der via chokidar auf nen Ordner hört und diese dann via HTTP Post an einen externen Service weiter reicht.
Das ist alles super flexibel; aber ich versteh, dass man sich an diese super Flexibilität erstmal gewöhnen muss 😃
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Danke nochmal, dass mit NodeJS war mir nicht bewusst. Ich werde mir das Video natürlich ansehen, werde nur leider erst abends dazu kommen.
Bezüglich dem FilesystemWatcher hatte ich kurz gesucht und folgendes gefunden:
https://stackoverflow.com/questions/33902230/cannot-use-filesystemwatcher-with-coreclr-on-ubuntu
Daher war ich davon ausgegangen, dass es geht.
Das heißt, dein Pushservice sendet die "Nachrichten" von X Programmen über Websockets raus. Die Nachrichten, die verschickt werden, kommen von diesen Programmen zum Beispiel über Rest-Calls rein? So wäre das ganze um beliebig viele Programme erweiterbar.
Clientseitig müsste ich dann nur dafür sorgen, dass die Nachrichten richtig verteilt werden, oder? (Angular-Controller X für Excel, Angular-Index-Controller für Nachrichten vom Typ Y)
Sei es nun eine Angular oder Desktop-Anwendung. (Ich gehe davon aus, dass die Verbindung einmal aufgebaut wird und dann bis zum Logout bestehen bleibt)
Spricht etwas dagegen, die Nachrichten per Broadcast in einer Angular-App an die Controller zu verteilen? Oder ist das zu ineffizient?
Ja gut, kann gut sein, dass der FSW mittlerweile unter CoreCLR verfügbar ist.
Die Nachrichten, die verschickt werden, kommen von diesen Programmen zum Beispiel über Rest-Calls rein?
Bei einer Anwendung mach ich das so. Auf Azure benutze ich dafür i.d.R. den Service Bus als Queue.
Das hilft mir noch mehr modular zu sein. Sollte der PushService kurz nicht zur Verfügung stehe geht die Nachricht nicht verloren.
Clientseitig müsste ich dann nur dafür sorgen, dass die Nachrichten richtig verteilt werden, oder?
Si.
Wie man aber das Verteilen macht (im Controller, eigener ng Service...) kann man pauschal nicht sagen.
Auch hier gibts es viele Wege.
Prinzipiell sollte man aber Toasts so designen, dass sie auch ankommen, wenn man mittlerweile in einem anderen Controller ist. Sonst macht es ja wenig sinn.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Noch ein Zusatz zur Queue für den PushService:
Wenn ein Service eine Nachricht verschicken soll, dann schreib ich nur in die Queue das Nachrichtenobjekt; der Service muss gar nicht wissen, wer die Nachricht nachher absendet
Hab das auch so (nicht bei allen Anwendungen) implementiert, dass der PushService bei manchen Nachrichten schaut, ob der Benutzer aktuell überhaupt via WebSocket/App verbunden ist.
Wenn nicht, dann geht es (zusätzlich) als E-Mail oder je nach Priorität als SMS raus und geht so nicht verloren.
Die Logik muss der sendende Service ja alles gar nicht kennen. Is dem ja egal. Der will nur senden.
Sollte es einen neuen Nachrichtenkanal (zB Rauchzeichen) geben, dann muss nur der PushService geändert werden, und nicht jede sendende Logik in anderen Services.
Das Senden der E-Mail könnte aber wieder ein anderer Service sein und muss nicht unbedingt direkt die WebApp sein, die für WebSockets zuständig ist.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Das heißt dein Pushservice hat eine Queue die abgearbeitet wird? Die Queue hält mehrere Nachrichtenobjekte und die wiederum Ziel-Client, Nachricht usw.
Sämtliche Programme die Nachrichten rausschicken möchten, befüllen die Queue über Rest-Calls mit Nachrichtenobjekten.
Enthält das Nachrichtenobjekt noch eigene Logik? Zum Beispiel:
Interface INachricht mit Methode Send();
App X hat das Nachrichtenobjekt X und eigene Logik zum Versenden. App Y genauso, Client Z reicht das allgemeine Nachrichtenobjekt mit Standard-Logik
Die Queue würde dann über INachricht laufen.
So müsste eine neue Nachricht (mit neuer Funktion) nur im Pushservice implementiert werden.
Oder verstehe ich etwas falsch?
EDIT: Ich glaube das war quatsch. Um verschiedene Nachrichtenobjekte an die RestApi zu senden, bräuchte man entweder ein Custom-Modelbinder oder verschiedene Routen.... Ich warte besser erstmal auf deine Antwort.
Mein Nachrichtenobjekt enthält i.d.R. ne ID, ne Prio, Absendersystem, Empfänger, Datum und die Nachricht selbst, wobei diese selbst wiederum ein Generic sein kann, sodass es einmal nur ein Text ist oder zB HTML.
Die Nachricht in der Datenbank hat dann noch die Info, wann die Nachricht gelesen wurde, wobei diese nur gesetzt wird, wenn der Benutzer die Nachricht in der Oberfläche (Website oder App) gelesen hat zB. der Benutzer die E-Mail Bestätigung bei sich (zB in Outlook aktiviert hat).
So verhinder ich, dass der Benutzer mehrfach die gleiche Meldung liest so gut es eben technisch geht.
Nachts gibt es nur eine Instanz des Push Service, der Nachrichten aus der Queue nimmt; tagsüber durch die Skalierung auch bis zu 5.
Nachrichten selbst sind aber nur dumme Containerobjekte.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Alles klar. Da mir leider kein Azure zur Verfügung steht: Wie würdest du die Queue implementieren? Auf Basis einer Blocking-Collection wie auf deinem Blog gezeigt? Ich setze diese Logik bereits bei einem Logging-Service ein und hatte bisher ganz gute Erfahrungen damit.
Eine BlockingQueue hilft Dir nur die Abhängigkeiten zu minimieren, was schon mal wichtig ist.
Für persistente Queues, die Nachrichen zB in eine DB speichern, damit sie bei einem Absturzt nicht verloren gehen, ist das aber nichts, da brauchst Du zB. RabbitMQ.
Kommt also drauf an, was Deine Anforderungen hier sind.
Du kannst die Persistenz aber selbst (zB. mit Hilfe von LiteDB) implementieren. Hab ich bei einem Demoprojekt gemacht. In-Process DBs sind für größere Anwendungen und Redundanz aber nichts.
Wir bewegen uns langsam in ne Richtung, bei der es nun konkret um Deine Implementierung geht - weniger um das grundsätzliche Vorgehen.
Ab nem gewissen Punkt kann ich Dir nicht mehr nicht-pauschal antworten. Wie gesagt; da gibt es dann viele Möglichkeiten.
Da musste dann einfach selbst überlegen und schauen, was in Deiner Umgebung umsetzbar ist bzw. wie die Äquivalente bei OnPremise ausschauen.
Auch hat so eine Architektur einen gewissen Overhead, den ihr eben evaluieren müsst.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Ja du hast recht. Vielen Dank nochmal für deine hervorragende Hilfe.
Ich werte mir RabbitMQ mal ansehen.
Danke nochmal für die ausführliche Hilfe.
Eine Frage ist mir mittlerweile noch gekommen. Wie würdest du unterschiedliche Sprachen handhaben?
Die Services, die die Nachrichten über den Pushservice rausschicken möchten, wissen ja so erstmal nichts von der Client-System-Sprache und immer ist das vielleicht auch nicht möglich. In der Hand des Pushservices sollte dies wohl auch nicht liegen die Nachrichten zu "übersetzen".
Sollte das Übersetzen beim Client liegen, könnten nur standardisierte Nachrichten rausgeschickt werden wie zum Beispiel: "UploadStatusOk" wird zu "Datei erfolgreich hochgeladen" oder eben das ganze auf Englisch.
Ich drehe mich momentan ein wenig im Kreis. Vielleicht hättest du ja nochmal einen Tipp.
Danke
Auch hier führen viele Wege nach Rom.
Kann man beides machen. Gibt aber genug Tutorials dazu, zB auf http://asp.net oder Blogs wie von in der ASP.NET Szene bekannten Damian Bod WEB API LOCALIZATION 😉
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo,
verstehe ich das richtig, dass nach der Aufteilung in die verschiedenen Anwendungen, jede getrennt (sei es im IIS, Self-Hosted Console oder Windows Server) irgendwie laufen muss? Das heißt bei einer umfangreicheren Anwendung da schon so einige Services zusammen kommen?
Dabei kann die Kommunikation entweder über REST oder einen ServiceBus erfolgen?
Gibt es Frameworks, welche das Hosten quasi übernehmen und man nur noch die Services "reinwirft"?
Habe ich nun eine Anwendung mit x- Services sowie einer ASP.NET MVC Seite als Frontend, bietet es sich nun an, dass die ASP.NET MVC Anwendung auf die Services zugreift oder der Client direkt auf die APIs?
Ich weiß, dass es da viele richtige Möglichkeiten gibt, wäre aber für ein bisschen input dankbar.
Das heißt bei einer umfangreicheren Anwendung da schon so einige Services zusammen kommen?
Ja, sowas nennt man im Prinzip Microservices und im Sinne eines ganzen Ökosystems dann Serverless Architecture.
Dabei kann die Kommunikation entweder über REST oder einen ServiceBus erfolgen?
Clients sollten immer via REST mit Services kommunizieren; und wenn möglich mit einem entsprechenden Hypermedia Protokoll.
Gibt es Frameworks, welche das Hosten quasi übernehmen und man nur noch die Services "reinwirft"?
... Docker?
Ansonsten weiß ich nicht, was Du genau meinst.
Habe ich nun eine Anwendung mit x- Services sowie einer ASP.NET MVC Seite als Frontend, bietet es sich nun an, dass die ASP.NET MVC Anwendung auf die Services zugreift oder der Client direkt auf die APIs?
Beides möglich.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Das heißt bei einer umfangreicheren Anwendung da schon so einige Services zusammen kommen?
Ja, sowas nennt man im Prinzip Microservices und im Sinne eines ganzen Ökosystems dann Serverless Architecture.
Konkret hätte ich also je Service ein Projekt in Visual Studio?
Hat man da bestenfalls noch ein Shared Assembly zu jedem Service um nicht in jedem anderen Projekt das den Service nutzt entsprechende Model Klassen duplikate zu haben?
Gibt es Frameworks, welche das Hosten quasi übernehmen und man nur noch die Services "reinwirft"?
... Docker?
Ansonsten weiß ich nicht, was Du genau meinst.
Docker ist natürlich eine gute Möglichkeit, zumindest für Asp.NET Core MVC. Geht das auch mit Subfolder /service1 /service2 usw. oder nur mit Proxy?
Möchte ich das nun aber im IIS beispielsweise hosten (Asp.NET MVC 5), bräuchte ich für jeden Service den ganzen Overhead einer eigenen Website (im IIS), richtig?
Wäre das dann nicht overkill für low traffic Anwendungen/Websites (sind wohl ca 98%)?
Konkret hätte ich also je Service ein Projekt in Visual Studio?
Kann, muss aber nicht.
Hat man da bestenfalls noch ein Shared Assembly zu jedem Service um nicht in jedem anderen Projekt das den Service nutzt entsprechende Model Klassen duplikate zu haben?
Wenn sich das ergibt natürlich; aber im Prinzip ist jeder Microservice seine eigene Umgebung.
Kommuniziert wird über Service Bus oder APIs.
Docker ist natürlich eine gute Möglichkeit, zumindest für Asp.NET Core MVC. Geht das auch mit Subfolder /service1 /service2 usw. oder nur mit Proxy?
Geht alles. Kann man beides machen.
Kommt aber alles auf die Gesamtumgebung an.
Möchte ich das nun aber im IIS beispielsweise hosten (Asp.NET MVC 5), bräuchte ich für jeden Service den ganzen Overhead einer eigenen Website (im IIS), richtig?
Korrekt. Kann man aber super automatisieren.
Wäre das dann nicht overkill für low traffic Anwendungen/Websites (sind wohl ca 98%)?
Nope.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code