Referenzdienst für mobile Anwendungen

Ruslan Aromatov, Chefentwickler, ICD



Guten Tag, Chabrowiten! Ich arbeite als Backend-Entwickler bei der Moscow Credit Bank und möchte dieses Mal darüber sprechen, wie wir die Bereitstellung von Laufzeitinhalten für unsere mobile MKB Online-Anwendung organisiert haben. Dieser Artikel kann für diejenigen nützlich sein, die mit dem Entwurf und der Entwicklung von Frontservern für mobile Anwendungen befasst sind, bei denen ständig verschiedene Aktualisierungen bereitgestellt werden müssen, ob Bankdokumente, Geolokalisierungspunkte, aktualisierte Symbole usw., ohne die Anwendung selbst in Geschäften zu aktualisieren. Wer mobile Anwendungen entwickelt, wird auch nicht schaden. Der Artikel enthält keine Codebeispiele, nur einige Diskussionen zum Thema.

Hintergrund


Ich denke, dass jeder Entwickler von mobilen Anwendungen auf das Problem gestoßen ist, einen Teil des Inhalts seiner Anwendung zu aktualisieren. Ändern Sie beispielsweise die Benutzervereinbarungsklausel, das Symbol oder die Koordinaten des Geschäfts eines Kunden, der plötzlich umgezogen ist. Es scheint, dass es einfacher sein könnte? Wir erstellen die Anwendung neu und stellen sie in den Store. Kunden werden aktualisiert, alle sind glücklich.

Dieses einfache Schema funktioniert jedoch aus einem einfachen Grund nicht - nicht alle Clients werden aktualisiert. Und nach den Statistiken zu urteilen, gibt es viele solcher Kunden.

Im Falle einer Bankanwendung kann die Nichtlieferung relevanter Informationen sowohl Geld als auch Unzufriedenheit der Kunden kosten. Beispielsweise werden am ersten Tag des nächsten Monats die Kartentarife geändert, neue Regeln für das Bonusprogramm aufgenommen oder neue Arten von Zahlungsempfängern hinzugefügt. Und wenn der Client die Anwendung genau um 0 Stunden 01 Minuten startet, sollte er den aktualisierten Inhalt sehen.

"Grundstufe!" - du sagst. - "Laden Sie diese Daten vom Server herunter und Sie werden glücklich sein."

Und du wirst recht haben. Machen wir so. Das ist es, wir sind nicht einverstanden .

Allerdings nicht alles so einfach. Wir haben Anwendungen für iOS und Android. Jede Plattform verfügt über mehrere verschiedene Versionen mit unterschiedlichen Funktionen und APIs.
Infolgedessen kann es vorkommen, dass wir die Datei für eine Android-Anwendung mit einer API-Version über 27 aktualisieren müssen, iOS und frühere Versionen jedoch nicht berühren müssen.

Noch interessanter wird es, wenn wir beispielsweise die Symbole der Zahlungsempfänger aktualisieren oder neue Artikel mit neuen Symbolen hinzufügen müssen. Wir zeichnen jede Instanz des Symbols in sieben verschiedenen Auflösungen für jeden bestimmten Bildschirmtyp: Für Android haben wir 4 davon (hdpi, xhdpi, xxhdpi, xxxhdpi) und 3 für iOS (1x, 2x, 3x). Welches soll ich an eine bestimmte Bewerbung senden?

"Dann senden Sie die Dateiparameter, die von einer bestimmten Anwendung benötigt werden."

Korrekt! Niemand weiß, welche Datei die Anwendung benötigt, außer der Anwendung.
Dies ist jedoch nicht alles. In Anwendungen gibt es einige Dateien, die miteinander verbunden sind. Beispielsweise werden Zahlungsempfängerlisten (eine JSON-Datei) mit Details zu Zahlungsempfängern (eine andere JSON-Datei) verknüpft. Und wenn wir die erste Datei erhalten und aus irgendeinem Grund die zweite nicht erhalten können, können die Kunden den Service nicht bezahlen. Und das ist ehrlich gesagt nicht sehr gut.

Der zweite Fall: Wir aktualisieren den gesamten Satz von Symbolen der Zahlungsempfänger (und es gibt mehr als hundert davon), wenn wir die Zahlungsseite aufrufen. Abhängig von der Geschwindigkeit des Internets kann dies zwischen 10 Sekunden und mehreren Minuten dauern. Was sollte das richtige Seitenverhalten sein? Sie können beispielsweise einfach die vorherige Version der Symbole anzeigen und neue im Hintergrund herunterladen, dann zwischenspeichern und neue nur dann anzeigen, wenn der Client das nächste Mal die Seite besucht. Irgendwie nicht wirklich, oder?

Eine andere Möglichkeit besteht darin, bereits heruntergeladene Symbole dynamisch durch neue zu ersetzen. Nicht zu hübsch, oder? Und wenn ein Symbol überhaupt nicht heruntergeladen wird? Dann sehen wir eine schöne Reihe neuer Symbole mit einem Stück des alten Designs in der Mitte.

Betriebssymbole

"Laden Sie dann beim Start der Anwendung den gesamten Satz von Symbolen in einem Archiv herunter."

Schöner Gedanke. Nicht wirklich. Aber es gibt eine Nuance.

Es kommt häufig vor, dass ein Designer nur ein paar Hundert Symbole neu zeichnet und Sie sie nur ersetzen müssen. Sie wiegen 200 Bytes und das gesamte Archiv hat 200 Kilobyte. Muss der Kunde das, was er bereits hat, erneut pumpen?

Und wir haben die Kosten für solche Arbeiten auf dem Server noch nicht berechnet. Nehmen wir an, 10.000 Kunden pro Stunde kommen zu uns (dies ist der Durchschnittswert, es passiert mehr). Der Anwendungsstart initiiert die Hintergrundaktualisierung von Verzeichnissen(Ja, Sie wissen jetzt, wie wir es nennen). Wenn ein Client 1 Kilobyte aktualisieren muss, gibt der Server in einer Stunde mehr als 10 Megabyte aus. Pennies, richtig? Und wenn die Updates 1 Megabyte wiegen? In diesem Fall müssen wir bereits 10 Gigabyte angeben. Irgendwann kommen wir zu dem Schluss, dass der Verkehr berücksichtigt werden sollte.

Dann müssen Sie lernen, zu verstehen, welche Dateien sich geändert haben und welche nicht, und nur die erforderlichen herunterladen.

Recht. Aber wie kann man verstehen, welche Dateien sich geändert haben und welche nicht? Wir betrachten einen Hash dafür. Daher wird in der Anwendung ein bestimmter Dateicache angezeigt, der eine Reihe von Referenzdateien enthält. Diese Dateien werden nach Bedarf als Ressourcen verwendet. Und auf der Serverseite wurden wir schließlich geboren ...

Verzeichnisdienst


Im Allgemeinen ist dies ein regulärer Webdienst, der Dateien über http unter Berücksichtigung aller Anforderungen der Anwendung sendet. Es besteht aus einer Reihe von Docker-Containern, in denen eine Java-Anwendung mit dem an Bord befindlichen Jetty-Webserver zusammenarbeitet. Das Backend ist die Tarantool-Datenbank auf der Vinyl-Engine (es gab keine schmerzhafte Wahl - es gab nur die gesamte Bindung für diese Datenbank; Sie können dies in meinem vorherigen Artikel Smart Cache Service basierend auf ZeroMQ und Tarantool lesen ) mit Master-Slave-Replikation. Zum Verwalten von Dateien gibt es eine Service-Weboberfläche, die ebenfalls vollständig selbst geschrieben ist.



Die technischen Implementierungsdetails im Thema dieses Artikels sind nicht besonders wichtig. Es kann sich um PHP + Apache + MySQL, C # + IIS + MSSQL oder ein anderes Bundle handeln, auch ohne Datenbank.

Das folgende Diagramm zeigt, wie der von uns angerufene Service Woodside funktioniert. Mobile Clients gehen über den Balancer zu Instanzen von Webdiensten, die wiederum die erforderlichen Dateien aus der Datenbank abrufen.

Arbeitsschema

In diesem Artikel werde ich jedoch nur auf die Struktur des Referenzsystems eingehen und darauf, wie wir sie in Anwendungen verwenden.

Dateien, die für Anwendungen erforderlich sind, werden in drei verschiedene Typen unterteilt.

  1. Dateien, die sich immer in der Anwendung befinden müssen und unabhängig vom Betriebssystemtyp sind. Dies ist beispielsweise eine PDF-Datei mit einem Bankdienstleistungsvertrag.
  2. -, , ( ) . , .
  3. , , . - , , . , .

Partnerprogramm

Die ersten beiden Dateitypen in Form von Archiven werden sofort in die Anwendungsassembly eingefügt. Eine neue Version enthält standardmäßig die neuesten Verzeichnisse. Sie fallen in das automatische Update-System, das beim Start der Anwendung im Hintergrund ausgeführt wird und wie folgt funktioniert.

1. Der Verzeichnisdienst empfängt automatisch einen Teil der Daten von verschiedenen Orten: Datenbanken, zugehörige Dienste, Netzwerkbälle - dies sind einige wichtige allgemeine Bankinformationen, die von anderen Abteilungen aktualisiert werden. Der andere Teil sind Verzeichnisse, die in unserem Team über die Weboberfläche erstellt wurden und Dateien enthalten, die nur für mobile Anwendungen bestimmt sind.

2. Gemäß dem Zeitplan (oder über die Schaltfläche) durchläuft der Dienst alle Dateien aller Verzeichnisse und bildet auf ihrer Basis eine Reihe von Indexdateien (innerhalb von json) sowohl für Dateien des ersten Typs (2 Versionen für iOS und Android) als auch für Ressourcendateien des zweiten Typ (7 Versionen für jeden Bildschirmtyp).
Es sieht ungefähr so ​​aus:

{
  "version": "43",
  "date": "04 Apr 2020 12:31:59",
  "os": "android",
  "screen": "any",
  "hashType": "md5",
  "ts": 1585992719,
  "files": [
    {
      "id": "WBRbDUlWhhhj",
      "name": "action-in-rhythm-of-life.json",
      "dir": "actions",
      "ts": 1544607853,
      "hash": "68c589c4fa8a44ded4d897c3d8b24e5c"
    },
    {
      "id": "o3K4mmPOOnxu",
      "name": "banks.json",
      "dir": "banks",
      "ts": 1583524710,
      "hash": "c136d7be420b31f65627f4200c646e0b"
    }
  ]
}

Die Indizes enthalten Informationen zu allen Dateien eines bestimmten Typs, auf deren Grundlage der Mechanismus zum Aktualisieren von Verzeichnissen für Anwendungen erstellt wird.

3. Anwendungen werden beim Start zunächst als Indexdateien im Verzeichnis / new in ihrem Dateicache heruntergeladen . Und im Verzeichnis / current haben sie Indizes für den aktuellen Satz von Dateien zusammen mit den Dateien selbst.

4. Basierend auf den neuen und alten Indexdateien (unter Beteiligung aller aktuellen Dateien, aus denen der Hash berücksichtigt wird) werden Listen von Dateien erstellt, die aktualisiert oder gelöscht werden müssen, und die Notwendigkeit einer Aktualisierung wird im Allgemeinen festgestellt.

5. Danach in das Verzeichnis / newAnwendungen laden die erforderlichen Dateien über einen direkten Link vom Server herunter (die Datei-ID im Index ist dafür verantwortlich). In diesem Fall werden das Vorhandensein und die Hashes von Dateien berücksichtigt , die sich bereits im Verzeichnis / new befinden , da dies ein Lebenslauf sein kann.

6. Sobald der gesamte Satz von Dateien im Verzeichnis / new empfangen wurde , werden sie mit der Indexdatei verglichen (manchmal kam es vor, dass die Dateien nicht vollständig heruntergeladen wurden).

7. Wenn die Prüfung erfolgreich war, wird der gesamte Dateibaum mit der Ersetzung in das Verzeichnis / current verschoben . Neue Indexdatei wird aktuell.

8. Wenn die Überprüfung nicht erfolgreich ist, werden keine Dateiübertragungen durchgeführt, und die Anwendung verwendet weiterhin die aktuellen Verzeichnisse. Beim nächsten Start der Anwendung versucht der Aktualisierungsmechanismus, das Problem zu beheben. Wenn beim Verschieben von Dateien ein globaler Absturz auftritt, müssen wir auf die allererste Version der Verzeichnisse zurücksetzen, die mit der Assembly geliefert wurden. Bisher gab es keine Präzedenzfälle.

Aber warum ist es so schwierig?

In Wirklichkeit nicht sehr schwierig. Tatsache ist jedoch, dass wir ständig experimentieren und Kompromisse zwischen der Anzahl der ständig aktualisierten Dateien und den Ladezeiten, zwischen der Einsparung von Datenverkehr und der Geschwindigkeit finden müssen. Eine wichtige Rolle bei der Auswahl eines Dateityps spielt, wenn genau dies in der Anwendung benötigt wird. Angenommen, wenn das Symbol unmittelbar nach der Anmeldung auf der Hauptseite angezeigt werden soll, kann die Anwendung eine solche Datei sofort zur Laufzeit laden und nicht in einen langen Aktualisierungsmechanismus einfügen. Jetzt beträgt die Gesamtgröße des Archivs mit nur den Hauptdateien 12 Megabyte, ohne bildschirmabhängige Ressourcen. Und da das Update im Wesentlichen eine atomare Operation ist, müssen wir warten, bis es beendet ist. Dies kann bis zu einigen Minuten dauern, wenn die Verbindung schlecht ist und viele neue Dateien vorhanden sind.

Ein wichtiger Punkt ist die Einsparung von Verkehr. Es gab Zeiten, in denen wir nach umfangreichen Updates einen 100-Megabit-Kanal vollständig genutzt haben. Ich musste auf 300 expandieren. Bisher genug. Im Durchschnitt zeigen Metriken, dass Clients normalerweise tagsüber 25 bis 50 Gigabyte pro Stunde herunterladen (dies liegt daran, dass wir ziemlich große Dateien haben, die täglich aktualisiert werden). In wirtschaftlicher Hinsicht gibt es noch Raum für weitere Entwicklungen, aber auch das Geschäft ist in Alarmbereitschaft - ständig fügen sie eine Vielzahl neuer Schönheiten hinzu.

Abschließend kann ich hinzufügen, dass die Frontserver selbst auch den Dienst verwenden, der beim Start die für die Verarbeitung von Clientanforderungen erforderlichen Daten herunterlädt.

Und wie liefern Sie Inhaltsaktualisierungen für Anwendungen?

All Articles