Heute werde ich Ihnen sagen, warum ausgelagerte Lieferungen nicht immer gut sind, warum Sie Prozesstransparenz benötigen und wie wir in anderthalb Jahren eine Plattform erstellt haben, die unseren Kurieren hilft, Bestellungen zu liefern. Ich werde auch drei Geschichten aus der Entwicklungswelt teilen.
Abgebildet ist das Kurierplattform-Team vor zehn Monaten. Damals wurde sie in einen Raum gebracht. Jetzt sind wir fünfmal mehr.
Warum haben wir das alles gemacht?
Bei der Entwicklung der Kurierplattform wollten wir drei wichtige Dinge aktualisieren.
Das erste ist Qualität . Wenn wir mit externen Lieferservices arbeiten, kann die Qualität nicht kontrolliert werden. Das Auftragnehmerunternehmen verspricht, dass es eine solche und eine solche Zustellbarkeit geben wird, aber eine bestimmte Anzahl von Bestellungen kann möglicherweise nicht geliefert werden. Und wir wollten den Prozentsatz der Verzögerungen auf ein Minimum reduzieren, damit fast jede Bestellung pünktlich geliefert wurde.
Zweitens ist Transparenz... Wenn etwas schief geht (es gibt Überweisungen, Fristen), wissen wir nicht, warum sie passiert sind. Wir können nicht sagen: "Leute, lasst es uns so machen." Wir selbst sehen nicht und können dem Kunden keine zusätzlichen Dinge zeigen. Zum Beispiel, dass die Bestellung nicht um acht, sondern innerhalb von 15 Minuten eintrifft. Dies liegt daran, dass es im Prozess kein solches Maß an Transparenz gibt.
Der dritte ist Geld... Wenn wir mit einem Auftragnehmer zusammenarbeiten, gibt es einen Vertrag, in dem die Beträge aufgeführt sind. Und diese Zahlen können wir im Rahmen des Vertrages ändern. Und wenn wir für den gesamten Prozess von a bis z verantwortlich sind, können Sie sehen, welche Teile des Systems wirtschaftlich unrentabel ausgelegt sind. Sie können beispielsweise den SMS-Anbieter oder das Dokumentflussformat ändern. Oder Sie bemerken möglicherweise, dass Kuriere eine zu hohe Kilometerleistung haben. Und wenn Sie Routen enger bauen, können Sie am Ende mehr Aufträge liefern. Dank dieser Funktion können Sie auch Geld sparen - die Lieferung wird effizienter.
Dies waren die drei Ziele, die wir uns an der Spitze von allem gesetzt haben.
Wie die Plattform aussieht
Mal sehen, was wir haben.
Das Bild zeigt ein Diagramm des Prozesses. Wir haben große Lagerhäuser mit Hunderttausenden von Bestellungen. Bis zum Rand mit Bestellungen gefüllte Lastwagen verlassen abends jedes Lager. Es kann 5-6 Tausend Bestellungen geben. Diese Lastwagen fahren zu kleineren Gebäuden, sogenannten Sortierzentren. In ihnen verwandelt sich in wenigen Stunden ein großer Stapel von Bestellungen in kleine Stapel für Kuriere. Und wenn Kuriere morgens in Autos ankommen, weiß jeder Kurier, dass er einen Haufen mit diesem QR-Code abholen, in sein Auto laden und zur Lieferung gehen muss.
Und das Backend, über das ich in diesem Artikel sprechen möchte, befasst sich mit dem allerletzten Teil des Prozesses, wenn Bestellungen an Kunden eingehen. Alles, was vorher ist, werden wir vorerst beiseite lassen.
Wie der Kurier es sieht
Kuriere haben eine Android-App in React Native geschrieben. Und in dieser Anwendung sehen sie ihren ganzen Tag. Sie verstehen die Reihenfolge klar: Welche Adresse soll zuerst, welche später. Wann man einen Kunden anruft, wann man zum Sortierzentrum zurückkehrt, wie man den Tag beginnt, wie man ihn beendet. Sie sehen alles in der Anwendung und stellen praktisch keine unnötigen Fragen. Wir helfen ihnen sehr. Im Grunde machen sie nur Aufgaben.
Darüber hinaus gibt es Steuerelemente auf der Plattform. Dies ist ein multifunktionales Admin-Panel, das wir von einem anderen Yandex-Dienst wiederverwendet haben. In diesem Administrationsbereich können Sie den Status des Systems konfigurieren. Wir laden dort Daten über neue Kuriere hoch und ändern die Arbeitsintervalle. Wir können den Prozess der Erstellung von Aufgaben für morgen anpassen. Fast alles, was Sie brauchen, ist geregelt.
Übrigens über das Backend. Wir auf dem Markt lieben Java sehr, hauptsächlich Version 11. Und alle Backend-Services, die besprochen werden, sind in Java geschrieben.
Die Architektur
Es gibt drei Hauptknoten in der Architektur unserer Plattform. Der erste ist für die Kommunikation mit der Außenwelt verantwortlich. Die Kurieranwendung "klopft" auf den Balancer, der herausgebracht wird, und kommuniziert mit ihm über die Standard-JSON-HTTP-API. Tatsächlich ist dieser Knoten für die gesamte Logik des aktuellen Tages verantwortlich, wenn Kuriere etwas übertragen, etwas stornieren, Befehle erteilen und neue Aufgaben erhalten.
Der zweite Knoten ist ein Dienst, der mit internen Yandex-Diensten kommuniziert. Alle Dienste sind klassische RESTful-Dienste mit Standardkommunikation. Wenn Sie eine Bestellung auf den Markt bringen, wird nach einer Weile ein Dokument im JSON-Format bei Ihnen eintreffen, in dem alles geschrieben wird: wann wir liefern, an wen wir liefern, in welchem Intervall. Und wir werden diesen Status in der Datenbank speichern. Es ist einfach.
Darüber hinaus kommuniziert der zweite Knoten auch mit anderen internen Diensten, nicht mit Market, sondern mit Yandex. Um beispielsweise die Geokoordinaten zu klären, gehen wir zum Geoservice. Um eine Push-Benachrichtigung zu senden, gehen wir zu dem Dienst, der Push und SMS sendet. Wir verwenden einen anderen Dienst zur Autorisierung. Ein weiterer Service zur Berechnung des Routings für morgen. Somit erfolgt die gesamte Kommunikation mit internen Diensten.
Dieser Knoten ist auch ein Einstiegspunkt. Er verfügt über eine API, auf die unser Admin-Panel klopft. Es hat einen eigenen Endpunkt, der beispielsweise / partner heißt. Und unser Admin-Panel, der gesamte Status des Systems, wird durch die Kommunikation mit diesem Dienst konfiguriert.
Der dritte Knoten ist die Hintergrundaufgabenbasis. Hier wird Quarz 2 verwendet, es gibt Aufgaben, die auf der Krone mit unterschiedlichen Bedingungen für unterschiedliche Punkte, für unterschiedliche Sortierzentren gestartet werden. Es gibt Aufgaben zum Aktualisieren des Tages, zum Schließen des Tages und zum Starten eines neuen Tages.
Und im Zentrum von allem steht die Datenbank, in der tatsächlich der gesamte Staat gespeichert ist. Alle Dienste sind in einer Datenbank enthalten.
Fehlertoleranz
Yandex verfügt über mehrere Rechenzentren, und unser Service ist regional auf drei Rechenzentren verteilt. Wie es aussieht.
Die Datenbank besteht aus drei Hosts, die sich jeweils in einem eigenen Rechenzentrum befinden. Ein Host ist der Master, die anderen beiden sind Replikate. Wir schreiben an den Meister, wir lesen aus den Zeilen. Alle anderen Java-Dienste sind ebenfalls Java-Prozesse, die in mehreren Rechenzentren ausgeführt werden.
Einer der Knoten ist unsere API. Es wird in allen drei Rechenzentren ausgeführt, da der Nachrichtenfluss größer ist als bei internen Diensten. Darüber hinaus können Sie mit einem solchen Layout ganz einfach horizontal skalieren.
Wenn Ihr eingehender Datenverkehr beispielsweise neunmal zunimmt, können Sie ihn optimieren, aber Sie können dieses Geschäft auch mit Eisen "überfluten", indem Sie mehr Knoten öffnen, die den eingehenden Datenverkehr verarbeiten.
Der Balancer muss lediglich den Datenverkehr in eine größere Anzahl von Punkten aufteilen, die Anforderungen erfüllen. Und jetzt haben wir zum Beispiel nicht einen Knoten, sondern zwei Knoten in jedem Rechenzentrum.
Unsere Architektur ermöglicht es uns, auch Fälle wie das Herunterfahren eines der Rechenzentren zu bewältigen. Zum Beispiel haben wir uns für eine Übung entschieden und das Rechenzentrum in Vladimir ausgeschaltet - und unser Service bleibt über Wasser, nichts ändert sich. Die dort liegenden Datenbankhosts verschwinden und der Dienst bleibt in Betrieb.
Der Balancer versteht nach einer Weile: Ja, ich habe keinen einzigen Live-Host mehr in diesem Rechenzentrum und er leitet den Datenverkehr dort nicht mehr um.
Alle Dienste in Yandex sind ähnlich angeordnet. Wir alle wissen, wie wir den Ausfall eines der Rechenzentren überleben können. Wir haben bereits beschrieben, wie dies implementiert wird, was eine ordnungsgemäße Verschlechterung ist und wie Yandex-Dienste das Herunterfahren eines der Rechenzentren handhaben .
Das war also Architektur. Und jetzt beginnen die Geschichten.
Die erste Geschichte - über Yandex.Rover
Kürzlich hatten wir eine weitere Konferenz, auf der Rover viel Aufmerksamkeit geschenkt wurde. Ich werde das Thema fortsetzen.
Einmal kamen die Jungs vom Yandex.Rover-Team zu uns und boten an, die Hypothese zu testen, dass die Leute Bestellungen auf solch außergewöhnliche Weise erhalten möchten.
Yandex.Rover ist ein kleiner Roboter von der Größe eines durchschnittlichen Hundes. Sie können dort Essen abstellen, ein paar Kisten mit Bestellungen - er wird durch die Stadt gehen und Bestellungen ohne menschliche Hilfe bringen. Es gibt einen Lidar, der Roboter versteht seine Position im Raum. Er weiß, wie man kleine Bestellungen liefert.
Und wir dachten: warum nicht? Wir haben die Details des Experiments geklärt: Zu dieser Zeit war es notwendig, die Hypothese zu testen, dass die Leute es mögen würden. Und wir haben uns entschlossen, 50 Bestellungen in anderthalb Wochen in einem sehr leichten Modus zu liefern.
Wir haben den einfachsten Ablauf gefunden, wenn eine Person eine SMS mit einem Vorschlag für eine nicht standardmäßige Zustellmethode erhält - nicht der Kurier wird sie bringen, sondern der Rover. Das gesamte Experiment fand im Hof von Yandex statt. Der Mann wählte den Eingang, zu dem der Rover vorfahren würde. Als der Roboter ankam, wurde der Deckel geöffnet, der Kunde nahm die Bestellung entgegen, schloss den Deckel und Rover ging zu einer neuen Bestellung. Es ist einfach.
Dann gingen wir zu Rovers Team, um eine API auszuhandeln.
Es gibt einfache Methoden in der Rover-API: Öffnen Sie den Deckel, schließen Sie den Deckel, gehen Sie zu einem bestimmten Punkt, erhalten Sie einen Status. Klassisch. Auch JSON. Sehr einfach.
Was auch sehr wichtig ist: Sowohl solche kleinen als auch alle großen Geschichten lassen sich am besten über Feature-Flags erstellen. In der Tat haben Sie einen Schalter, mit dem Sie diese Geschichte in der Produktion aktivieren können. Wenn Sie es nicht mehr benötigen, wurde das Experiment erfolgreich oder nicht erfolgreich abgeschlossen oder Sie haben einige Fehler bemerkt. Sie haben es einfach reduziert. Und Sie müssen die neue Version des Codes für die Produktion nicht erneut bereitstellen. Dieses Ding macht das Leben viel einfacher.
Es scheint, dass alles einfach ist und alles funktionieren sollte. Es gibt dort sogar zwei Wochen lang nichts zu entwickeln, Sie können es in ein paar Tagen tun. Aber schau, wo der Hund begraben ist.
Alle Prozesse sind meist synchron. Der Mann drückt einen Knopf, der Deckel öffnet sich. Der Mann drückt den Knopf, der Deckel schließt sich. Einer dieser Prozesse ist jedoch asynchron. In dem Moment, in dem der Rover zu Ihnen fährt, benötigen Sie einen Hintergrundprozess, der nachverfolgt, ob der Roboter zum Punkt zurückgekehrt ist.
Und in diesem Moment senden wir eine SMS an die Person, zum Beispiel, dass der Rover vor Ort wartet. Dies kann nicht synchron erfolgen, und Sie müssen dieses Problem irgendwie lösen.
Es gibt viele verschiedene Ansätze. Wir haben es so einfach wie möglich gemacht.
Wir haben beschlossen, dass wir den häufigsten Java-Hintergrundthread oder die häufigste Java-Task in Executer ausführen können. Dieser Hintergrund-Thread wird sofort gestartet, um den Prozess zu verfolgen. Sobald der Vorgang abgeschlossen ist, senden wir eine Benachrichtigung.
Zum Beispiel sieht es so aus. Dies ist praktisch eine Kopie des Produktionscodes, mit Ausnahme der gestrippten Kommentare. Aber es gibt einen Haken. Auf diese Weise können Sie keine ernsthaften Systeme erstellen. Angenommen, wir führen eine neue Version für das Backend ein. Der Host startet neu, der Zustand ist verloren, und das war's, der Rover geht ins Unendliche, niemand sonst sieht ihn.
Aber warum? Wenn wir wissen, dass unser Ziel darin besteht, in anderthalb Wochen 50 Bestellungen zu liefern, wählen wir den Zeitpunkt, zu dem wir das Backend überwachen. Wenn etwas schief geht, können Sie etwas manuell ändern. Für eine solche Aufgabe ist diese Lösung mehr als ausreichend. Und das ist die Moral der ersten Geschichte.
Es gibt Situationen, für die Sie die Mindestversion der Funktionalität festlegen müssen. Es ist nicht nötig, einen Garten und einen Überingenieur einzäunen. Es ist besser, es so entfremdet wie möglich zu machen. Um die Logik interner Objekte nicht zu sehr zu verändern. Und übermäßige Komplexität, unnötige technische Schulden haben sich nicht angesammelt.
Die zweite Geschichte - über Datenbanken
Aber zuerst ein paar Worte darüber, wie die Hauptentitäten angeordnet sind. Es gibt einen Yandex.Routing-Service, der am Ende des Tages Routen für Kuriere erstellt.
Jede Route besteht aus Punkten auf der Karte. Und an jedem Punkt haben die Kuriere eine Aufgabe. Dies kann eine Aufgabe sein, einen Auftrag zu erteilen oder einen Kunden anzurufen oder am Morgen im Sortierzentrum alle Bestellungen abzuholen.
Darüber hinaus erhalten Kunden am Morgen einen Tracking-Link. Sie können die Karte öffnen und sehen, wie der Kurier zu ihnen fährt. Der Kunde kann auch die Lieferung per Rover wählen, die ich bereits erwähnt habe.
Nun wollen wir sehen, wie diese Entitäten in der Datenbank angezeigt werden können. Dies geschieht zum Beispiel so.
Dies ist ein sehr ordentliches Datenmodell. Wir haben keine einzige neue Einheit erfunden und die bestehenden nicht stark zusammengebrochen.
Das Diagramm zeigt, dass der Pfeil von unten nach oben die Route des Kuriers zeigt und den Kurier kennt, dessen Route es ist. Das Kurierroutenschild enthält den Kurier-ID-Link. Und die obere Platte weiß das nicht. Wir haben die einfachste Konnektivität, es gibt keinen großen Schnittpunkt von Entitäten. Wenn jeder über jeden Bescheid weiß, ist die Kontrolle schwieriger und es kommt höchstwahrscheinlich zu Redundanz. Daher haben wir das einfachste mögliche Schema.
Der einzige "Overkill", den wir zu Beginn der Erstellung der Plattform gemacht haben, ist dieser. Wir hatten eine Art von Lieferauftrag. Wir haben jedoch erkannt, dass es in Zukunft andere Aufgaben geben wird. Und wir setzen ein wenig architektonische Flexibilität ein: Wir haben Aufgaben, und eine der Arten von Aufgaben ist die Auftragslieferung.
Dann haben wir Tracking und Rover hinzugefügt. Nur zwei Tabletten. Bei der Verfolgung sendet der Kurier seine Koordinaten, wir zeichnen sie auf einem separaten Schild auf. Und es gibt eine Auftragsverfolgung mit einem eigenen Statusmodell, es gibt zusätzliche Dinge, wie "SMS links / nicht weg". Sie sollten dies nicht direkt zur Aufgabe hinzufügen. Es ist besser, es in eine separate Platte zu legen, da diese Verfolgung nicht für alle Arten von Aufgaben benötigt wird.
In Rover - seine Koordinaten und Lieferung. Unsere Lieferung per Rover ist wie die Verfolgung für Rover. Sie können es der Tracking-Reihenfolge hinzufügen, aber warum? Wenn wir dieses Experiment loswerden, wenn es ausgeschaltet ist, bleiben diese Optionen für immer im Kern des Trackings. Es werden Nullfelder angezeigt.
Es kann sich die Frage stellen: Warum eine Platte mit Koordinaten erstellen? Ein Rover liefert fünf Bestellungen pro Tag. Sie müssen keine Koordinaten in der Datenbank speichern, sondern können einfach zur Rover-API gehen und diese zur Laufzeit abrufen.
Das Fazit ist, dass dies ursprünglich getan wurde. Dieses Schild war nicht da, wir gingen sofort zum Gottesdienst und nahmen alles mit. Bei den Tests haben wir jedoch festgestellt, dass viele Leute eine Karte mit einem rollenden Rover öffnen und die Belastung dieses Dienstes um ein Vielfaches zunimmt. Nehmen wir an, sieben Leute haben es geöffnet. Und dort auf der Seite fragt Java Script alle zwei Sekunden nach Koordinaten. Und Kollegen haben uns im Chat geschrieben: „Woher kommt eine solche Ladung? Du hast eine Person zum Skaten da. "
Und danach haben wir ein Schild hinzugefügt. Wir fingen an, die Koordinaten dort hinzuzufügen, den Zeitpunkt, zu dem sie empfangen wurden. Und jetzt, wenn Leute zu oft zu uns kommen, um Koordinaten zu erhalten, und seit der letzten Quittung keine zwei Sekunden vergangen sind, nehmen wir sie vom Teller. Es stellt sich ein solcher Cache auf Datenbankebene heraus.
Diese Geschichte könnte mit 20 Tischen gemacht werden. Es können zwei Tische verwendet werden: Kurier und Bestellung. Im ersten Fall wäre es jedoch eine Überentwicklung, und im zweiten Fall wäre es zu schwierig, sie aufrechtzuerhalten. Komplexe Logik, schwer zu testen.
Und weiter. Die Struktur der Datenbanken, die wir vor anderthalb Jahren erstellt haben, ist bis heute unverändert geblieben. Und wir hatten großes Glück, dass wir solche Einheiten auswählen konnten, auf denen die Basis nicht erneuert werden musste. Es war nicht erforderlich, die Datenbanken signifikant neu zu zeichnen, komplexe Migrationen durchzuführen, diese Version dann zu rollen, sie sehr lange zu testen und dann die Stammstruktur zu ändern.
Der Punkt der Geschichte ist, dass es Aspekte gibt, denen es besser ist, zusätzliche Aufmerksamkeit zu schenken. Das erste ist Achten Sie besonders auf die Struktur der API und der Datenbank . Versuchen Sie zu bemerken, welche Art von Entitäten Sie im wirklichen Leben haben. Versuchen Sie, diese Struktur auf die gleiche Weise zu digitalisieren. Versuchen Sie, es nicht zu stark zu verkürzen, nicht zu stark zu erweitern.
Zweitens gibt es Fehler, deren Behebung teuer ist . Fehler auf API-Ebene sind schwieriger zu beheben als Fehler auf Datenbankebene, da normalerweise viele Clients die API verwenden. Und wenn Sie die API häufig ändern, müssen Sie:
- Erreichen Sie alle Clients und stellen Sie die Abwärtskompatibilität sicher.
- Führen Sie die neue API ein und wechseln Sie alle Clients zur neuen API.
- Schneiden Sie den alten Code über Clients aus, schneiden Sie den alten Code im Backend aus.
Das ist sehr teuer.
Fehler im Code sind im Vergleich dazu im Allgemeinen Unsinn. Sie haben gerade den Code neu geschrieben und die Tests ausgeführt. Tests sind grün - Sie haben in den Master geschoben. Achten Sie besonders auf die Datenbank-API.
Unabhängig davon, wie sehr Sie sich bemühen, die Datenbank im Auge zu behalten, wird sie im Laufe der Zeit zu etwas Unverwaltbarem. Auf dem Screenshot sehen Sie ein Zehntel unserer Datenbank, die jetzt existiert.
Es gibt einen Ratschlag. Wenn Sie etwas sehr schnell entwickeln, gibt es Ungenauigkeiten in der Datenbank, manchmal fehlt ein Fremdschlüssel oder ein doppeltes Feld wird angezeigt. Schauen Sie deshalb manchmal alle zwei oder drei Monate nur auf die Basis. Dieselbe Intellij IDEA kann kühle Schaltkreise erzeugen. Und dort können Sie alles sehen.
Ihre Datenbank muss ausreichend sein. Es ist sehr einfach, in einer Stunde eine Liste mit sechs Tickets zu erstellen: Fügen Sie hier einen Fremdschlüssel hinzu, dort einen Index. Egal wie sehr Sie es auch versuchen, es wird sich zwangsläufig etwas Schmutz ansammeln.
Die dritte Geschichte handelt von Qualität
Es gibt Dinge, die von Anfang an am besten gemacht werden. Es ist wichtig, nach dem Prinzip zu handeln: "Normalerweise tun, es wird in Ordnung sein."
Zum Beispiel haben wir einen Prozess, der für die Plattform kritisch ist. Den ganzen Tag sammeln wir Bestellungen für morgen, aber am Abend wird eine Notiz ausgelöst, dass wir nach 22:00 keine Bestellungen mehr sammeln, aber vor 01:00 bereiten wir uns auf morgen vor. Dann beginnt die Verteilung der Bestellungen an die Sortierzentren. Wir gehen zu Yandex. Routing, es baut Routen.
Und wenn diese Vorarbeiten scheitern, ist das ganze Morgen in Frage. Morgen werden die Kuriere nirgendwo hingehen können. Sie haben keinen Staat geschaffen. Dies ist der kritischste Prozess auf unserer Plattform. Und solche wichtigen Prozesse können nicht mit minimalen Ressourcen auf Restbasis durchgeführt werden.
Ich erinnere mich, dass wir eine Zeit hatten, in der dieser Prozess fehlschlug und mehrere Wochen lang fast die Hälfte des Teams im Chat diese Probleme löste, jeder den Tag rettete, etwas überarbeitete und neu startete.
Wir haben verstanden, dass, wenn der Prozess nicht von zehn Uhr abends bis ein Uhr morgens abgeschlossen ist, die Sortierzentren nicht einmal wissen, wie Bestellungen zu sortieren sind, in welche Haufen. Dort wird alles untätig sein. Kuriere werden später herauskommen und wir werden Qualitätsmängel haben.
Es ist besser, solche Prozesse so gut wie möglich sofort durchzuführen und über jeden Schritt nachzudenken, bei dem etwas schief gehen kann. Und überall die maximale Menge Stroh geben.
Ich werde Ihnen eine der Optionen erläutern, wie Sie einen solchen Prozess einrichten können.
Dieser Prozess ist mehrkomponentig. Die Berechnung von Routen und deren Veröffentlichung kann in Teile aufgeteilt und beispielsweise Warteschlangen erstellt werden. Dann haben wir mehrere Warteschlangen, die für die abgeschlossenen Arbeitsabschnitte verantwortlich sind. Und es gibt Verbraucher in diesen Warteschlangen, die sitzen und auf Nachrichten warten.
Zum Beispiel ist der Tag vorbei, wir wollen die Routen für morgen berechnen. Wir senden zunächst eine Anfrage: Erstellen Sie eine Aufgabe und starten Sie die Berechnung. Der Verbraucher nimmt die erste Nachricht entgegen und geht zum Routing-Dienst. Dies ist eine asynchrone API. Der Verbraucher erhält eine Antwort, dass die Aufgabe zur Arbeit übernommen wurde.
Er stellt diese ID in die Basis und stellt einen neuen Job mit Aufgaben in der Verarbeitung in die Warteschlange. Und alle. Die Nachricht verschwindet aus der ersten Warteschlange, der zweite Verbraucher "wacht auf". Er übernimmt die zweite Aufgabe zur Verarbeitung, und seine Aufgabe besteht darin, regelmäßig zum Routing zu gehen und zu überprüfen, ob diese Aufgabe für die Berechnung noch nicht abgeschlossen ist.
Die Aufgabe des Levels "Routen für 200 Kuriere erstellen, die mehrere tausend Bestellungen in Moskau liefern" dauert eine halbe bis eine Stunde. Dies ist in der Tat eine sehr schwierige Aufgabe. Und die Leute von diesem Service sind sehr cool, sie lösen das komplexeste algorithmische Problem, das viel Zeit in Anspruch nimmt.
Infolgedessen prüft der Verbraucher der zweiten Warteschlange einfach, prüft, prüft. Nach einiger Zeit ist die Aufgabe abgeschlossen. Wenn die Aufgabe erledigt ist, erhalten wir eine Antwort in Form der erforderlichen Struktur der Routen und Schichten von morgen für Kuriere.
Wir setzen das Berechnete in die dritte Priorität. Die Nachricht verschwindet aus der zweiten Warteschlange. Und der dritte Verbraucher "wacht auf", nimmt diesen Kontext aus dem Yandex.Routing-Dienst und schafft auf seiner Grundlage den Zustand von morgen. Er schafft Befehle für Kuriere, er schafft Befehle, schafft Schichten. Das ist auch viel Arbeit. Er verbringt einige Zeit damit. Und wenn alles erstellt ist, endet diese Transaktion und der Job wird aus der Warteschlange entfernt.
Wenn irgendwo in diesem Prozess etwas schief geht, wird der Server neu gestartet. Bei der anschließenden Restaurierung sehen wir einfach den Punkt, an dem wir geendet haben. Nehmen wir an, die erste und die zweite Phase sind vergangen. Und gehen wir weiter zum dritten.
Mit dieser Architektur lief in den letzten Monaten alles ziemlich reibungslos, es gibt keine Probleme. Aber bevor es einen soliden Versuch gab. Es ist nicht klar, wo der Prozess fehlgeschlagen ist, welche Status in der Datenbank geändert wurden usw.
Es gibt Dinge, an denen Sie nicht sparen sollten. Es gibt Dinge, mit denen Sie sich eine Menge Nervenzellen ersparen, wenn Sie alles sofort gut machen.
Was haben wir offline gemacht?
Ich habe das meiste beschrieben, was auf unserer Plattform passiert. Aber etwas blieb hinter den Kulissen.
Wir haben etwas über Software gelernt, mit der Kuriere Bestellungen innerhalb eines Tages ausliefern können. Aber jemand sollte diese Haufen für Kuriere machen. In einem Sortierzentrum müssen die Leute eine große Auftragsmaschine in kleine Stapel sortieren. Wie es gemacht wird?
Dies ist der zweite Teil der Plattform. Wir haben die gesamte Software selbst geschrieben. Wir haben jetzt Terminals, mit denen Ladenbesitzer den Code aus den Kisten lesen und in die entsprechenden Zellen legen. Es gibt eine ziemlich komplizierte Logik. Dieses Backend ist nicht viel einfacher als das, über das ich bereits gesprochen habe.
Dieses zweite Puzzleteil war notwendig, um zusammen mit dem ersten den Prozess auf andere Städte ausweiten zu können. Andernfalls müssten wir in jeder neuen Stadt einen Auftragnehmer suchen, der den Austausch einiger Excels per Post arrangieren oder in unsere API integrieren könnte. Und das wäre eine sehr lange Zeit.
Wenn wir das erste und zweite Puzzleteil haben, können wir einfach ein Gebäude mieten und Kuriere in Autos mieten. Sagen Sie ihnen, wie man pusht, was man pusht, wie man pickt, welche Box wo platziert wird und das war's. Dank dessen haben wir bereits in sieben Städten gestartet, wir haben mehr als zehn Sortierzentren.
Und die Eröffnung unserer Plattform in einer neuen Stadt nimmt sehr wenig Zeit in Anspruch. Darüber hinaus haben wir gelernt, nicht nur Aufträge an bestimmte Personen zu liefern. Wir wissen, wie man mit Hilfe von Kurieren Bestellungen an Abholpunkte liefert. Wir haben auch Software für sie geschrieben. Und an diesen Stellen erteilen wir auch Befehle an Menschen.
Ergebnisse
Am Anfang habe ich gesagt, warum wir angefangen haben, unsere eigene Kurierplattform zu erstellen. Jetzt werde ich Ihnen sagen, was wir erreicht haben. Es ist unglaublich, aber mit unserer Plattform konnten wir fast 100% des Intervalls erreichen. In der letzten Woche lag die Lieferqualität in Moskau beispielsweise bei 95–98%. Dies bedeutet, dass wir in 95–98% der Fälle nicht zu spät kommen. Wir passen in das vom Kunden gewählte Intervall. Und von einer solchen Präzision konnten wir nicht einmal träumen, wenn wir uns ausschließlich auf externe Lieferservices stützten. Daher erweitern wir unsere Plattform jetzt schrittweise auf alle Regionen. Und wir werden die Zustellbarkeit verbessern.
Wir haben unrealistische Transparenz. Wir brauchen auch diese Transparenz. Bei uns wird alles protokolliert: alle Aktionen, der gesamte Prozess der Auftragserteilung. Wir haben die Möglichkeit, fünf Monate lang in die Geschichte zurückzugehen und eine Metrik mit der aktuellen zu vergleichen.
Diese Transparenz haben wir aber auch den Kunden gegeben. Sie sehen einen Kurier zu sich kommen. Sie können mit ihm interagieren. Sie müssen den Support nicht anrufen und sagen: "Wo ist mein Kurier?"
Darüber hinaus stellte sich heraus, dass die Kosten optimiert wurden, da wir Zugriff auf alle Elemente der Kette haben. Infolgedessen kostet die Lieferung einer Bestellung jetzt ein Viertel weniger als zuvor, als wir mit externen Services zusammengearbeitet haben. Ja, die Kosten für die Lieferung der Bestellung sind um 25% gesunken.
Wenn Sie alle diskutierten Ideen zusammenfassen, können Sie Folgendes unterscheiden.
Sie müssen klar verstehen, in welchem Entwicklungsstadium sich Ihr aktueller Service befindet, Ihr aktuelles Projekt. Und wenn dies ein etabliertes Unternehmen ist, das von Millionen von Menschen genutzt wird und möglicherweise in mehreren Ländern eingesetzt wird, können Sie nicht alles auf dem gleichen Niveau wie bei Rover ausführen.
Aber wenn Sie ein Experiment haben ... Das Experiment unterscheidet sich darin, dass wir jederzeit geschlossen werden können, wenn wir die versprochenen Ergebnisse nicht zeigen. Es ist nicht gestartet. Und das ist okay.
Und wir waren ungefähr zehn Monate in diesem Regime. Wir hatten Berichtsintervalle, alle zwei Monate mussten wir das Ergebnis zeigen. Und wir haben es geschafft.
In diesem Modus scheint es mir, dass Sie kein Recht haben, etwas zu tun, indem Sie langfristig investieren und kurzfristig nichts bekommen. Es ist unmöglich, in diesem Arbeitsformat ein so starkes Fundament für die Zukunft zu legen, weil die Zukunft einfach nicht kommen kann.
Und jeder kompetente Entwickler und technische Leiter muss sich ständig zwischen Krücken oder dem Bau eines Raumschiffs entscheiden.
Kurz gesagt, versuchen Sie es so einfach wie möglich zu halten, während Sie Raum für Erweiterungen lassen.
Es gibt eine erste Ebene, in der Sie es ganz einfach tun müssen. Und es gibt die erste Ebene mit einem Sternchen, wenn Sie es einfach halten, aber mindestens ein wenig Spielraum lassen, damit es erweitert werden kann. Mit dieser Einstellung scheint es mir, dass die Ergebnisse viel besser sein werden.
Und das Letzte. Ich habe über Rover gesprochen, dass es gut ist, solche Prozesse mit Feature-Flags durchzuführen. Ich rate Ihnen, sich den Vortrag von Maria Kuznetsova vom Java-Treffen anzuhören. Sie erzählte, wie Feature-Flags in unserem System und in der Überwachung angeordnet sind.