Verwendung von Consul zur Skalierung Stateful-Services

    Am 22. September fand unsere erste nicht standardisierte mitap für Entwickler von Hochlastsystemen statt. Es war sehr cool, viele positive Rückmeldungen zu den Berichten und beschlossen, sie nicht nur zu stellen, sondern auch für Habr zu entziffern. Heute veröffentlichen wir die Rede von Ivan Bubnov, DevOps von der Firma BIT.GAMES. Er sprach über die Implementierung des Consul Discovery Service in einem bereits laufenden Hochlastprojekt, um schnelles Skalieren und Failover von Stateful Services zu ermöglichen. Und auch über die Organisation eines flexiblen Namensraums für Backend-Anwendungen und Fallstricke. Nun ein Wort zu Ivan.


    Ich verwalte die Produktionsinfrastruktur im Studio BIT.GAMES und erzähle die Geschichte der Einführung des Konsuls von Hashicorp in unser Projekt „Guild of Heroes“ - ein Fantasy-Rollenspiel mit asynchronem pvp für mobile Geräte. Wir sind auf Google Play, App Store, Samsung und Amazon veröffentlicht. DAU etwa 100.000, online von 10 bis 13 tausend. Wir machen das Spiel auf Unity, also schreiben wir den Client in C # und verwenden unsere eigene Skriptsprache BHL für die Spielelogik. Wir schreiben den Server-Teil auf Golang (von PHP an ihn übergeben). Als nächstes ist die schematische Architektur unseres Projekts.


    In der Tat gibt es viel mehr Dienste, hier nur die Grundlagen der Spielelogik.

    Also, was haben wir? Von staatenlosen Diensten ist es:

    • nginx, das wir als Frontend- und Load-Balancer einsetzen, und verteilt die Kunden nach Gewichtungsfaktoren an unsere Backends;
    • gamed - backend'y, kompilierte Anwendungen von Go. Dies ist die zentrale Achse unserer Architektur. Sie leisten den Löwenanteil der Arbeit und sind mit allen anderen Backend-Services verbunden.

    Von Stateful Services sind die wichtigsten hier:

    • Redis, die wir verwenden, um "heiße" Informationen zwischenzuspeichern (wir verwenden sie auch, um im Spiel Chat zu organisieren und Benachrichtigungen für unsere Spieler zu speichern);
    • Percona Server für MySQL ist ein Repository persistenter Informationen (wahrscheinlich die größten und unhandlichsten in jeder Architektur). Wir verwenden MySQL Fork und hier werden wir heute ausführlicher darüber sprechen.

    Im Entwurfsprozess haben wir (wie alle) gehofft, dass das Projekt erfolgreich sein wird und einen Splittermechanismus vorsieht. Es besteht aus zwei Entitäten der MAINDB-Datenbanken und Shards selbst.



    MAINDB ist eine Art Inhaltsverzeichnis - es speichert Informationen zu dem spezifischen Shard, in dem die Fortschrittsdaten des Spielers gespeichert sind. So sieht die vollständige Kette des Abrufs von Informationen folgendermaßen aus: Der Client wendet sich an das Frontend, der ihn wiederum gemäß dem Gewichtungskoeffizienten an eines der Backends umverteilt, das Backend an MAINDB weitergibt, den Shard des Spielers lokalisiert und die Daten des Shards selbst abtastet.

    Aber als wir entworfen haben, waren wir kein großes Projekt, also entschieden wir uns, Shards Shards nur nominell herzustellen. Sie befanden sich alle auf demselben physischen Server, und höchstwahrscheinlich handelt es sich dabei um die Datenbankpartitionierung innerhalb eines einzelnen Servers.

    Für die Sicherung haben wir die klassische Master-Slave-Replikation verwendet. Es war keine sehr gute Lösung (ich werde später sagen, warum), aber der Hauptnachteil dieser Architektur bestand darin, dass alle unsere Backends andere Backend-Dienste nur über IP-Adressen kannten. Und bei einem anderen lächerlichen Unfall in einem Rechenzentrum wie: " Es tut uns leid, unser Techniker hat das Kabel Ihres Servers berührt, während er den anderen gewartet hat. Wir haben es lange herausgefunden, warum sich Ihr Server nicht in Verbindung gesetzt hat„Wir brauchten viele Gesten. Erstens ist es ein Backend'ov mit einem IP-Backup-Server für den Ort des fehlgeschlagenen. Zweitens ist es nach dem Vorfall notwendig, unseren Master aus dem Backup'a aus der Reserve wiederherzustellen, da er sich in einem inkonsistenten Zustand befand, und ihn mittels derselben Replikation in einen konsistenten Zustand brachte. Danach bauten wir die Backends wieder auf und perezaliv wieder. All dies verursachte natürlich Ausfallzeiten.

    Der Moment kam, als unser technischer Direktor (für den er sich sehr bedankte) sagte: "Jungs, hör auf zu leiden, wir müssen etwas ändern, lass uns nach Wegen Ausschau halten." Zuallererst wollten wir einen einfachen, verständlichen und vor allem leicht zu verwaltenden Prozess der Skalierung und Migration von Datenbanken zu unseren Datenbanken. Darüber hinaus wollten wir durch die Automatisierung des Failovers eine hohe Verfügbarkeit erreichen.



    Die zentrale Achse unserer Forschung war Konsul aus Hashicorp. Erstens wurde uns empfohlen, dies zu tun, und zweitens waren wir sehr angetan von Einfachheit, Freundlichkeit und hervorragender Technologie in einem Paket: Discovery-Service mit Gesundheitschecks, Schlüsselwertspeicherung und das Wichtigste, was wir verwenden wollten DNS, das uns Adressen aus der Domäne service.consul rezolvilieren würde.

    Consul bietet auch hervorragende Web-UI- und REST-APIs, um all dies zu verwalten.

    Bei der Hochverfügbarkeit haben wir zwei Dienstprogramme für das automatische Failover ausgewählt:

    • MHA für MySQL
    • Redis-Sentinel



    Im Fall von MHA for MySQL haben wir Agenten mit Datenbanken auf Knoten verteilt, und sie haben ihren Status überwacht. Es gab ein gewisses Timeout für den Master Fail, wonach ein Stop-Slave zur Konsistenz gemacht wurde und unser Backup-Master vom erschienenen Master in einem nicht konsistenten Zustand die Daten nicht übernommen hat. Und wir haben diesen Agenten einen Web-Hook hinzugefügt, der dort einen neuen IP-Sicherungsmaster in Consul registriert hat, woraufhin er in das DNS-Problem geriet.

    Mit Redis-Sentinel ist alles noch einfacher. Da er selbst den Löwenanteil der Arbeit leistet, bleibt uns nur noch zu berücksichtigen, dass der Redis-Sentinel ausschließlich am Master-Knoten stattfindet.

    Zuerst hat alles gut funktioniert, wie eine Uhr. Wir hatten keine Probleme auf dem Prüfstand. Es hat sich jedoch gelohnt, in die natürliche Datenübertragungsumgebung des geladenen Rechenzentrums zu wechseln, an einige OOM-Kill'ahs (dies ist nicht genügend Speicherplatz, in dem der Prozess vom Systemkern abgebrochen wird) zu erinnern und den Dienst oder komplexere Dinge wiederherzustellen, die die Verfügbarkeit des Dienstes beeinträchtigen. Da wir sofort ein ernstes Risiko für Fehlalarme oder das Fehlen einer garantierten Antwort erhalten haben (wenn wir versuchen, vor Fehlalarmen zu fliehen, verdrehen wir einige Checks).



    Zunächst hängt alles von der Schwierigkeit ab, korrekte Gesundheitschecks zu schreiben. Es scheint, dass die Aufgabe eher trivial ist - überprüfen Sie, ob der Dienst auf Ihrem Server und dem Pingani-Port ausgeführt wird. Wie eine weitere Praxis gezeigt hat, ist das Schreiben eines Healthchecks bei der Implementierung von Consul ein äußerst komplexer und zeitlich verteilter Prozess. Da so viele Faktoren die Verfügbarkeit Ihres Dienstes im Rechenzentrum beeinflussen, ist es nicht vorhersehbar - sie werden erst nach einer bestimmten Zeit erkannt.

    Außerdem ist das Rechenzentrum keine statische Struktur, in die Sie eingegeben haben, und es funktioniert wie beabsichtigt. Aber leider (oder zum Glück) erfuhren wir erst später davon, aber wir waren jetzt begeistert und voller Zuversicht, dass wir alles in der Produktion umsetzen würden.



    Was die Skalierung angeht, möchte ich kurz sagen: Wir haben versucht, ein fertiges Fahrrad zu finden, aber alle sind für bestimmte Architekturen konzipiert. Und wie bei Jetpants konnten wir die Bedingungen, die er für die Architektur einer dauerhaften Speicherung von Informationen auferlegte, nicht erfüllen.

    Deshalb haben wir über unser eigenes Skript nachgedacht und diese Frage verschoben. Wir haben uns entschlossen, konsequent zu handeln und mit der Umsetzung von Consul zu beginnen.



    Consul ist ein dezentraler, verteilter Cluster, der auf dem Klatschprotokoll und dem Raft-Konsensus-Algorithmus basiert.

    Wir haben ein unabhängiges Equorum von fünf Servern (fünf, um eine Split-Brain-Situation zu vermeiden). Für jeden Knoten geben wir den Consul-Agenten im Agent-Modus ein und schütten alle Healthchecks aus (d. H., Es gab nicht die Möglichkeit, einige Healthchecks mit einem bestimmten Server und andere mit bestimmten Servern auszufüllen). Healthcheck'i wurden so geschrieben, dass sie nur dort vorbeigehen, wo es einen Dienst gibt.

    Wir haben auch ein anderes Dienstprogramm verwendet, um nicht von Ihrem Backend lernen zu müssen, Adressen von einer bestimmten Domäne an einem nicht standardmäßigen Port zu rezolvit zu machen. Wir haben Dnsmasq verwendet - es bietet die Möglichkeit, die Adressen, die wir brauchen (die in der realen Welt sozusagen nicht existieren, sondern nur innerhalb des Clusters existieren) vollständig transparent auf die Cluster-Knoten aufzulösen. Wir haben ein automatisches Skript für das Hochladen in Ansible vorbereitet, alles in die Produktion aufgenommen, den Namespace umgedreht und sichergestellt, dass alles abgeschlossen ist. Und drücken Sie die Daumen, perezalili unser Backend'y, die nicht mehr von IP-Adressen angesprochen werden, sondern von diesen Namen aus der Domäne server.consul.

    Es begann alles beim ersten Mal, unsere Freude kannte keine Grenzen. Es war jedoch noch zu früh, um uns zu freuen, denn innerhalb einer Stunde bemerkten wir, dass auf allen Knoten, an denen sich unsere Backends befinden, die durchschnittliche Lastrate von 0,7 auf 1,0 stieg, was eine ziemlich kühne Zahl ist.



    Ich kletterte auf den Server, um zu sehen, was los war, und es wurde offensichtlich, dass die CPU Consul aß. Hier begannen wir zu verstehen, begannen mit strace zu schamanieren (ein Dienstprogramm für Unix-Systeme, mit dem Sie verfolgen können, welche Systemaufrufe der Prozess ausführt), die Dnsmasq-Statistiken zurücksetzen, um zu verstehen, was auf diesem Knoten passiert, und es stellte sich heraus, dass wir einen sehr wichtigen Punkt übersehen haben. Bei der Planung der Integration haben wir die Zwischenspeicherung von DNS-Einträgen verpasst, und es stellte sich heraus, dass unser Backend bei jeder seiner Körperbewegungen an Dnsmasq zerrte, und dass dies wiederum den Konsul ansprach und alles in ungesunde 940 DNS-Anfragen pro Sekunde übergoss.

    Die Lösung schien naheliegend - einfach Twist ttl und alles wird gut. Es war jedoch unmöglich, hier fanatisch zu sein, weil wir diese Struktur einführen wollten, um einen dynamischen, leicht verwaltbaren und sich schnell ändernden Namespace zu erhalten (so konnten wir zum Beispiel keine 20 Minuten setzen). Wir haben den TTL bis zum Limit für unsere optimalen Werte herausgeschraubt und die Anzahl der Anfragen pro Sekunde auf 540 gesenkt. Dies hatte jedoch keinen Einfluss auf den CPU-Verbrauch.

    Dann haben wir uns entschlossen, mit der benutzerdefinierten hosts-Datei geschickt auszugehen.



    Es ist gut, dass wir alles dafür hatten: ein schönes Consul-Vorlagensystem, das basierend auf dem Clusterstatus und dem Vorlagenskript eine beliebige Datei generiert, jede Konfigurationsdatei - was immer Sie möchten. Darüber hinaus verfügt Dnsmasq über den Konfigurationsparameter addn-hosts, mit dem Sie eine Nicht-System-Hosts-Datei als dieselbe zusätzliche Hosts-Datei verwenden können.

    Was wir gemacht haben, hat das Drehbuch in Ansible erneut vorbereitet, es in die Produktion gegossen und es sah ungefähr so ​​aus:



    Es gab ein zusätzliches Element und eine statische Datei auf der Platte, die ziemlich schnell regeneriert wird. Jetzt sah die Kette recht einfach aus: gaming bezieht sich auf Dnsmasq, und das wiederum (anstatt den Konsul-Agenten zu ziehen, der die Server fragt, wo dieser oder jener Knoten ist), hat die Datei gerade angesehen. Dies löste das Problem mit dem Verbrauch von CPU Consul.

    Jetzt sah alles wie geplant aus - absolut transparent für unsere Produktion, praktisch ohne Ressourcenverbrauch.

    Wir wurden an diesem Tag ziemlich gefoltert und gingen in großer Angst nach Hause. Sie hatten keine Angst umsonst, denn nachts wurde ich durch einen Alarm aus dem Monitoring geweckt und informiert, dass wir einen ziemlich großen (wenn auch nur kurzlebigen) Anstieg von Fehlern hatten.



    Da ich am Morgen Protokolle verstand, sah ich, dass alle Fehler der gleichen Art unbekannter Host sind. Es war nicht klar, warum Dnsmasq den einen oder anderen Dienst nicht aus einer Datei entfernen konnte - das Gefühl, dass es überhaupt nicht existiert. Um zu verstehen, was passiert, habe ich eine benutzerdefinierte Metrik für die erneute Migration von Dateien hinzugefügt - jetzt wusste ich genau, wann sie neu erstellt werden würde. Darüber hinaus gibt es in der Consul-Vorlage selbst eine hervorragende Sicherungsoption, d. H. Sie können den vorherigen Status der regenerierten Datei sehen.

    Während des Tages wiederholte sich der Vorfall mehrmals, und es wurde klar, dass die Hosts-Datei zu einem bestimmten Zeitpunkt (obwohl sie sporadisch und unsystematisch war) neu erstellt wurde. Es stellte sich heraus, dass in einem bestimmten Rechenzentrum (ich werde keine Anti-Werbung betreiben) eher instabile Netzwerke - aufgrund des Netzwerk-Flops haben wir völlig unvorhersehbar aufgehört, den Healthcheck zu durchlaufen, oder sogar Knoten fielen aus dem Cluster. Es sah so aus:



    Der Knoten fiel aus dem Cluster heraus, der Consul-Agent wurde sofort darüber informiert und die Consul-Vorlage hat die hosts-Datei sofort ohne den erforderlichen Dienst neu erstellt. Dies war im Allgemeinen inakzeptabel, da das Problem lächerlich ist: Wenn der Dienst für einige Sekunden nicht verfügbar ist, gibt es Zeitüberschreitungen und Einbrüche (sie haben sich nicht einmal verbunden und beim zweiten Mal passiert). Wir haben jedoch eine Situation in der Verkaufsabteilung provoziert, als der Service einfach aus dem Blickfeld verschwindet und es keine Verbindung zu ihm gab.

    Wir überlegten, was wir damit tun sollten, und verwandelten den Timeout-Parameter in Consul, woraufhin festgestellt wird, wie lange der Knoten ausgefallen ist. Wir haben es geschafft, dieses Problem mit einem eher kleinen Indikator zu lösen, die Knoten fielen nicht mehr heraus, aber mit Healthcheck half das nicht.

    Wir begannen darüber nachzudenken, verschiedene Parameter für Gesundheitschecks auszuwählen und zu verstehen, wann und wie dies geschieht. Aber weil alles sporadisch und unvorhersehbar geschah, konnten wir es nicht tun.

    Dann gingen wir zur Consul-Vorlage und beschlossen, eine Zeitüberschreitung für sie vorzunehmen, woraufhin sie auf eine Änderung des Clusterzustands reagiert. Auch hier war es unmöglich, fanatisch zu sein, denn wir könnten zu einer Situation kommen, in der das Ergebnis nicht besser wäre als die des klassischen DNS, als wir nach einem völlig anderen strebten.

    Und hier kam unser technischer Direktor erneut zur Rettung und sagte: „Jungs, lasst uns versuchen, all diese Interaktivität aufzugeben, wir sind alle in der Produktion und wir haben keine Zeit für Forschung, wir müssen dieses Problem lösen. Lasst uns einfache und verständliche Dinge verwenden. “ Daher kamen wir zu dem Konzept, das Schlüsselwert-Repository als Quelle zum Generieren der hosts-Datei zu verwenden.



    Wie es aussieht: Wir verzichten auf alle dynamischen Healthchecks und schreiben unser Vorlagenskript um, um eine Datei auf der Grundlage der im Schlüsselwertspeicher gespeicherten Daten zu generieren. Im Schlüsselwertspeicher beschreiben wir unsere gesamte Infrastruktur als Schlüsselnamen (dies ist der Name des Dienstes, den wir benötigen) und Schlüsselwerte (Dies ist der Name des Knotens im Cluster). Ie Wenn der Knoten im Cluster vorhanden ist, können wir sehr einfach seine IP-Adresse abrufen und in die hosts-Datei schreiben.

    Wir haben alle getestet, in die Produktion gegossen und in einer bestimmten Situation wurde sie zu einer Silberkugel. Wieder waren wir ziemlich den ganzen Tag lang gefoltert, und wir gingen nach Hause, kamen aber bereits ausgeruht und inspiriert zurück, weil diese Probleme nicht mehr auftraten und ein Jahr lang nicht mehr auftraten. Daraus folge ich persönlich, dass dies die richtige Entscheidung war (speziell für uns).

    So Wir haben endlich erreicht, was wir wollten, und haben einen dynamischen Namensraum für unser Backend eingerichtet. Dann machten wir uns auf Hochverfügbarkeit.



    Tatsache ist jedoch, dass die Integration von Consul und die Probleme, mit denen wir konfrontiert sind, ziemlich beunruhigt ist. Wir dachten und entschieden, dass die Implementierung von Auto-Failover keine so gute Lösung ist, weil wir erneut Fehlalarme riskieren Misserfolge Dieser Prozess ist undurchsichtig und unkontrollierbar.

    Daher haben wir uns für einen einfacheren (oder komplizierteren) Weg entschieden: Wir haben beschlossen, das Failover beim Gewissen des Pflichtverwalters zu lassen, haben ihm aber ein weiteres zusätzliches Werkzeug gegeben. Wir haben die Master-Slave-Replikation durch die Master-Replikation im schreibgeschützten Modus ersetzt. Dadurch entfällt eine Menge Kopfschmerzen beim Failover-Prozess. Wenn Sie den Assistenten beenden, müssen Sie lediglich den Wert im k / v-Speicher mithilfe der Web-Benutzeroberfläche oder des Befehls in der API ändern und Read only to entfernen Sicherungsassistent.

    Nachdem der Vorfall erledigt ist, kommt der Assistent in Kontakt und kommt automatisch in einen konsistenten Zustand, ohne dass unnötige Aktionen erforderlich sind. Wir haben bei dieser Option aufgehört und sie wie zuvor verwendet - für uns ist dies so bequem wie möglich und vor allem so einfach wie möglich, verständlich und kontrollierbar.


    Die Consul-Weboberfläche

    Auf der rechten Seite wird k / v-Speicher angezeigt und unsere Services sind sichtbar, die wir im Spielbetrieb verwenden. value ist der Name des Knotens.

    Was die Skalierung angeht, haben wir mit der Implementierung begonnen, als die Shards auf einem Server überfüllt waren, die Basen wuchsen, langsam wurden, die Anzahl der Spieler zunahm, wir tauschten und wir standen vor der Aufgabe, alle Shards auf unseren eigenen separaten Servern aufzulösen.



    Wie es aussah: Mit dem Dienstprogramm XtraBackup haben wir unser Backup auf einem neuen Serverpaar wiederhergestellt, wonach der neue Master als Sklave des alten Servers aufgehängt wurde. Es kam in einem konsistenten Zustand, wir haben den Schlüsselwert im k / v-Speicher vom Namen des Knotens des alten Masters in den Namen des Knotens des neuen Masters geändert. Dann (als wir dachten, dass alles richtig lief und alles mit seinen selects übereinstimmt, Updates, Einfügungen an den neuen Master gingen), mussten wir nur die Replikation beenden und die begehrte Drop-Datenbank-Produktion durchführen, wie wir alle mit unnötigen Datenbanken tun.



    Auf diese Weise wurden wir geschärft. Der gesamte Umzugsprozess dauerte von 40 Minuten bis zu einer Stunde und verursachte keine Ausfallzeiten, war für unsere Backends völlig transparent und für die Spieler selbst völlig transparent (abgesehen davon, dass es für sie einfacher und angenehmer wurde, sobald sie sich bewegten).



    Bei den Failover-Prozessen beträgt die Umschaltzeit 20 bis 40 Sekunden plus die Reaktionszeit des diensthabenden Systemadministrators. Das ist es jetzt, wie es bei uns aussieht.

    Um das abschließend zu sagen: Unsere Hoffnungen auf eine absolute, umfassende Automatisierung haben die strenge Realität der Datenübertragungsumgebung im beladenen Rechenzentrum und zufällige Faktoren, die wir nicht vorhersehen konnten, gebrochen.

    Zweitens hat es uns wieder gelehrt, dass ein einfacher und bewährter Vogel in den Händen Ihres Sysadmin besser ist als ein neu gerollter, selbstreagierender, selbstskalierender Kran irgendwo hinter den Wolken, den Sie nicht einmal verstehen, wenn er auseinander fällt oder wirklich begann zu skalieren.

    Die Einführung einer beliebigen Infrastruktur und Automatisierung in Ihrer Produktion sollte den Mitarbeitern, die sie bedienen, keine zusätzlichen Kopfschmerzen bereiten. Die Kosten für die Aufrechterhaltung der Infrastrukturproduktion sollten dadurch nicht wesentlich erhöht werden. Die Lösung sollte einfach, klar, transparent für Ihre Kunden, bequem und kontrollierbar sein.

    Fragen aus dem Publikum


    Wie schreibt man k / v mit Servern? Ein Skript oder Patches?

    Wir haben K / v-Speicher auf unseren Consul-Servern und entfernen entweder etwas von dort oder füllen es mit Hilfe der RESTful-API oder der Web-Benutzeroberfläche von http-request auf.

    Ich wollte mit diesem Bericht vermitteln, dass die Ideologie, mit heftigem Fanatismus alles auf der Welt zu automatisieren, aus irgendeinem Grund sehr oft verfolgt, ohne zu verstehen, dass dies manchmal das Leben kompliziert und nicht vereinfacht.

    Warum balancieren Sie zwischen den Shards durch Datenbanken, warum nicht die gleichen Redis?

    Dies ist eine historisch etablierte Entscheidung, und ich habe vergessen zu sagen, dass sich das Konzept nun ein wenig geändert hat.

    Zuerst haben wir die Configs rausgenommen und müssen das Backend nicht neu füllen. Zweitens haben wir den Parameter in die Backends selbst eingegeben, das ist die Hauptverknüpfung - das heißt, sie verteilen die Spieler. Ie Wenn wir eine neue Spielereinheit haben, nimmt er einen Eintrag in MAINDB vor und speichert die Daten auf dem von ihm ausgewählten Shard. Und jetzt wählt er es mit Gewichten. Und es ist sehr schnell und einfach zu kontrollieren, und es ist im Moment nicht nötig, eine andere Technologie zu verwenden, da alles jetzt schnell funktioniert.

    Wenn wir jedoch auf ein Problem stoßen, ist es vielleicht besser, ein schnelles Speichermedium oder einen anderen Schlüsselspeicher zu verwenden.

    Was ist deine Basis?

    Wir verwenden MySQL Fork - Percona Server.

    Und Sie haben nicht versucht, es zu einem Cluster zu kombinieren und aufgrund dieses Gleichgewichts? Wenn Sie Maria hatten, was für MySQL dieselbe MHA ist, dann hat er Galera.

    Wir waren bei Galera im Einsatz. Es gab ein weiteres Rechenzentrum für das Projekt "Guild of Heroes" für Asien, und dort haben wir Galera verwendet, und es ist oft ein sehr unangenehmes Versagen. Es muss von Zeit zu Zeit von Hand angehoben werden. Aufgrund dieser Erfahrung mit der Verwendung dieser Technologie möchten wir sie immer noch nicht wirklich nutzen.

    Auch hier sollte verstanden werden, dass die Einführung einer beliebigen Technologie nicht nur darum geht, dass Sie es besser machen wollen, sondern weil Sie dies brauchen, Sie müssen aus einer bestehenden Situation herauskommen, oder Sie haben bereits vorausgesagt, dass die Situation bald eintreten wird Sie wählen eine bestimmte Technologie zur Implementierung aus.

    Weitere Berichte aus den Pixonic DevGAMM Talks



    Jetzt auch beliebt: