One place for hosting & domains

      SOLID: Die ersten 5 Prinzipien des objektorientierten Designs


      Einführung

      SOLID ist ein Akronym für die ersten fünf Prinzipien des objektorientierten Designs (OOD) von Robert C. Martin (auch bekannt als Onkel Bob).

      Anmerkung: Obwohl diese Prinzipien auf verschiedene Programmiersprachen angewendet werden können, wird der in diesem Artikel enthaltene Beispielcode PHP verwendet.

      Diese Prinzipien legen Praktiken fest, die sich für die Entwicklung von Software mit Überlegungen zur Aufrechterhaltung und Erweiterung eignen, wenn das Projekt wächst. Die Übernahme dieser Praktiken kann auch zur Vermeidung von Code Smells, Refactoring von Code und agiler oder adaptiver Softwareentwicklung beitragen.

      SOLID steht für:

      In diesem Artikel werden Sie jedes Prinzip einzeln kennenlernen, um zu verstehen, wie SOLID Ihnen dabei helfen kann, ein besserer Entwickler zu werden.

      Single-Responsibility-Prinzip

      Das Single-Responsibility-Prinzip (SRP) besagt:

      Eine Klasse sollte einen und nur einen Grund haben, sich zu ändern, d. h. eine Klasse sollte nur eine Aufgabe haben.

      Betrachten Sie beispielsweise eine Anwendung, die eine Sammlung von Formen – Kreise und Quadrate – nimmt und die Summe der Fläche aller Formen in der Sammlung berechnet.

      Erstellen Sie zunächst die Formklassen und lassen Sie die Konstruktoren die erforderlichen Parameter einrichten.

      Für Quadrate müssen Sie die length einer Seite kennen:

      class Square
      {
          public $length;
      
          public function construct($length)
          {
              $this->length = $length;
          }
      }
      

      Für Kreise müssen Sie den radius kennen:

      class Circle
      {
          public $radius;
      
          public function construct($radius)
          {
              $this->radius = $radius;
          }
      }
      

      Erstellen Sie anschließend die Klasse AreaCalculator und schreiben Sie dann die Logik, um die Fläche aller bereitgestellten Formen zu summieren. Der Flächeninhalt eines Quadrats wird durch die Länge zum Quadrat berechnet. Der Flächeninhalt eines Kreises wird durch Pi mal Radius zum Quadrat berechnet.

      class AreaCalculator
      {
          protected $shapes;
      
          public function __construct($shapes = [])
          {
              $this->shapes = $shapes;
          }
      
          public function sum()
          {
              foreach ($this->shapes as $shape) {
                  if (is_a($shape, 'Square')) {
                      $area[] = pow($shape->length, 2);
                  } elseif (is_a($shape, 'Circle')) {
                      $area[] = pi() * pow($shape->radius, 2);
                  }
              }
      
              return array_sum($area);
          }
      
          public function output()
          {
              return implode('', [
                '',
                    'Sum of the areas of provided shapes: ',
                    $this->sum(),
                '',
            ]);
          }
      }
      

      Um die Klasse AreaCalculator zu verwenden, müssen Sie die Klasse instanziieren und ein Array von Formen übergeben und die Ausgabe am Ende der Seite anzeigen.

      Hier ist ein Beispiel mit einer Sammlung von drei Formen:

      • Ein Kreis mit einem Radius von 2
      • Ein Quadrat mit einer Länge von 5
      • Ein zweites Quadrat mit einer Länge von 6
      $shapes = [
        new Circle(2),
        new Square(5),
        new Square(6),
      ];
      
      $areas = new AreaCalculator($shapes);
      
      echo $areas->output();
      

      Das Problem mit der Ausgabemethode ist, dass der AreaCalculator die Logik zur Ausgabe der Daten bearbeitet.

      Bedenken Sie ein Szenario, in dem die Ausgabe in ein anderes Format wie JSON konvertiert werden soll.

      Die gesamte Logik würde von der Klasse AreaCalculator bearbeitet werden. Dies würde gegen das Single-Responsibility-Prinzip verstoßen. Die Klasse AreaCalculator sollte nur mit der Summe der Flächen der bereitgestellten Formen befasst sein. Sie sollte sich nicht damit befassen, ob der Benutzer JSON oder HTML wünscht.

      Um dies zu beheben, können Sie eine separate Klasse SumCalculatorOutputter erstellen und diese neue Klasse verwenden, um die Logik zu bearbeiten, die Sie für die Ausgabe der Daten an den Benutzer benötigen:

      class SumCalculatorOutputter
      {
          protected $calculator;
      
          public function __constructor(AreaCalculator $calculator)
          {
              $this->calculator = $calculator;
          }
      
          public function JSON()
          {
              $data = [
                'sum' => $this->calculator->sum(),
            ];
      
              return json_encode($data);
          }
      
          public function HTML()
          {
              return implode('', [
                '',
                    'Sum of the areas of provided shapes: ',
                    $this->calculator->sum(),
                '',
            ]);
          }
      }
      

      Die Klasse SumCalculatorOutputter würde wie folgt funktionieren:

      $shapes = [
        new Circle(2),
        new Square(5),
        new Square(6),
      ];
      
      $areas = new AreaCalculator($shapes);
      $output = new SumCalculatorOutputter($areas);
      
      echo $output->JSON();
      echo $output->HTML();
      

      Jetzt wird die Logik, die Sie zur Ausgabe der Daten an den Benutzer benötigen, von der Klasse SumCalculatorOutputter bearbeitet.

      Das erfüllt das Single-Responsibility-Prinzip.

      Open-Closed-Prinzip

      Das Open-Closed-Prinzip (S.R.P.) besagt:

      Objekte oder Entitäten sollten offen für Erweiterungen, aber geschlossen für Änderungen sein.

      Das bedeutet, dass eine Klasse erweiterbar sein sollte, ohne die Klasse selbst zu modifizieren.

      Gehen wir noch einmal auf die Klasse AreaCalculator ein und konzentrieren uns auf die Methode sum:

      class AreaCalculator
      {
          protected $shapes;
      
          public function __construct($shapes = [])
          {
              $this->shapes = $shapes;
          }
      
          public function sum()
          {
              foreach ($this->shapes as $shape) {
                  if (is_a($shape, 'Square')) {
                      $area[] = pow($shape->length, 2);
                  } elseif (is_a($shape, 'Circle')) {
                      $area[] = pi() * pow($shape->radius, 2);
                  }
              }
      
              return array_sum($area);
          }
      }
      

      Bedenken Sie ein Szenario, in dem der Benutzer die Summe sum zusätzlicher Formen wie Dreiecke, Fünfecke, Sechsecke usw. wünscht. Sie müssten diese Datei ständig bearbeiten und weitere if/else-Blöcke hinzufügen. Das würde das Open-Closed-Prinzip verletzen.

      Eine Möglichkeit, diese Methode sum zu verbessern, besteht darin, die Logik zur Berechnung der Fläche jeder Form aus der Klassenmethode AreaCalculator zu entfernen und sie an die Klasse jeder Form anzuhängen.

      Hier ist die in Square definierte Methode area:

      class Square
      {
          public $length;
      
          public function __construct($length)
          {
              $this->length = $length;
          }
      
          public function area()
          {
              return pow($this->length, 2);
          }
      }
      

      Und hier ist die in Circle definierte Methode area:

      class Circle
      {
          public $radius;
      
          public function construct($radius)
          {
              $this->radius = $radius;
          }
      
          public function area()
          {
              return pi() * pow($shape->radius, 2);
          }
      }
      

      Die Methode sum für AreaCalculator kann dann umgeschrieben werden als:

      class AreaCalculator
      {
          // ...
      
          public function sum()
          {
              foreach ($this->shapes as $shape) {
                  $area[] = $shape->area();
              }
      
              return array_sum($area);
          }
      }
      

      Jetzt können Sie eine andere Formklasse erstellen und diese bei der Berechnung der Summe übergeben, ohne den Code zu verändern.

      Es ergibt sich jedoch ein weiteres Problem. Woher wissen Sie, dass das an den AreaCalculator übergebene Objekt tatsächlich eine Form ist oder ob die Form eine Methode namens area aufweist?

      Die Codierung auf eine Schnittstelle ist ein integraler Bestandteil von SOLID.

      Erstellen Sie ein ShapeInterface, das area unterstützt:

      interface ShapeInterface
      {
          public function area();
      }
      

      Ändern Sie Ihre Formklassen, um das ShapeInterface mit implement zu implementieren.

      Hier ist die Aktualisierung für Square:

      class Square implements ShapeInterface
      {
          // ...
      }
      

      Und hier ist die Aktualisierung für Circle:

      class Circle implements ShapeInterface
      {
          // ...
      }
      

      In der Methode sum für AreaCalculator können Sie überprüfen, ob die bereitgestellten Formen tatsächlich Instanzen des ShapeInterface sind; andernfalls verwenden Sie „throw“ für eine Ausnahme:

       class AreaCalculator
      {
          // ...
      
          public function sum()
          {
              foreach ($this->shapes as $shape) {
                  if (is_a($shape, 'ShapeInterface')) {
                      $area[] = $shape->area();
                      continue;
                  }
      
                  throw new AreaCalculatorInvalidShapeException();
              }
      
              return array_sum($area);
          }
      }
      

      Damit ist das Open-Closed-Prinzip erfüllt.

      Liskovsches Substitutionsprinzip

      Das Liskovsche Substitutionsprinzip besagt:

      Lassen Sie q(x) eine Eigenschaft sein, die für Objekte x von Typ T beweisbar ist. Dann soll q(y) für Objekte y von Typ S beweisbar sein, wobei S ein Untertyp von T ist.

      Das bedeutet, dass jede Unterklasse oder abgeleitete Klasse für ihre Basis- oder übergeordnete Klasse ersetzbar sein sollte.

      Bedenken Sie, aufbauend auf dem Beispiel der Klasse AreaCalculator, eine neue Klasse VolumeCalculator, die die Klasse AreaCalculator erweitert:

      class VolumeCalculator extends AreaCalculator
      {
          public function construct($shapes = [])
          {
              parent::construct($shapes);
          }
      
          public function sum()
          {
              // logic to calculate the volumes and then return an array of output
              return [$summedData];
          }
      }
      

      Erinnern Sie sich daran, dass die Klasse SumCalculatorOutputter dem ähnelt:

      class SumCalculatorOutputter {
          protected $calculator;
      
          public function __constructor(AreaCalculator $calculator) {
              $this->calculator = $calculator;
          }
      
          public function JSON() {
              $data = array(
                  'sum' => $this->calculator->sum();
              );
      
              return json_encode($data);
          }
      
          public function HTML() {
              return implode('', array(
                  '',
                      'Sum of the areas of provided shapes: ',
                      $this->calculator->sum(),
                  ''
              ));
          }
      }
      

      Wenn Sie versuchen würden, ein Beispiel wie dieses auszuführen:

      $areas = new AreaCalculator($shapes);
      $volumes = new VolumeCalculator($solidShapes);
      
      $output = new SumCalculatorOutputter($areas);
      $output2 = new SumCalculatorOutputter($volumes);
      

      Wenn Sie die Methode HTML auf dem Objekt $output2 aufrufen, erhalten Sie einen Fehler E_NOTICE, der Sie über eine Array-zu-String-Konvertierung informiert.

      Um dies zu beheben, geben Sie anstelle der Rückgabe eines Arrays aus der Summenmethode der Klasse VolumeCalculator $summedData zurück:

      class VolumeCalculator extends AreaCalculator
      {
          public function construct($shapes = [])
          {
              parent::construct($shapes);
          }
      
          public function sum()
          {
              // logic to calculate the volumes and then return a value of output
              return $summedData;
          }
      }
      

      Das $summedData können ein Float, Double oder Integer sein.

      Damit ist das Liskovsche Substitutionsprinzip erfüllt.

      Das Interface-Segregation-Prinzip

      Das Interface-Segregation-Prinzip besagt:

      Ein Client sollte nie gezwungen werden, eine Schnittstelle zu implementieren, die er nicht verwendet, oder Clients sollten nicht gezwungen werden, von Methoden abzuhängen, die sie nicht verwenden.

      Weiterhin aufbauend auf dem vorherigen Beispiel ShapeInterface, müssen Sie die neuen dreidimensionalen Formen Cuboid und Spheroid unterstützen, und diese Formen müssen auch das Volumen berechnen.

      Bedenken wir, was passieren würde, wenn Sie das ShapeInterface modifizieren würden, um einen weiteren Vertrag hinzuzufügen:

      interface ShapeInterface
      {
          public function area();
      
          public function volume();
      }
      

      Nun muss jede Form, die Sie erstellen, die Methode volume implementieren, aber Sie wissen, dass Quadrate flache Formen sind und kein Volumen haben, also würde diese Schnittstelle die Klasse Square zwingen, eine Methode zu implementieren, die sie nicht braucht.

      Dies würde das Interface-Segregation-Prinzip verletzen. Stattdessen könnten Sie eine andere Schnittstelle namens ThreeDimensionalShapeInterface erstellen, die den Vertrag volume hat und dreidimensionale Formen können diese Schnittstelle implementieren:

      interface ShapeInterface
      {
          public function area();
      }
      
      interface ThreeDimensionalShapeInterface
      {
          public function volume();
      }
      
      class Cuboid implements ShapeInterface, ThreeDimensionalShapeInterface
      {
          public function area()
          {
              // calculate the surface area of the cuboid
          }
      
          public function volume()
          {
              // calculate the volume of the cuboid
          }
      }
      

      Dies ist ein wesentlich besserer Ansatz, aber ein Fallstrick, auf den Sie achten müssen, wenn Sie diese Schnittstellen mit Typ-Hinweisen versehen. Anstatt ein ShapeInterface oder ein ThreeDimensionalShapeInterface zu verwenden, können Sie eine andere Schnittstelle erstellen, vielleicht ManageShapeInterface, und diese sowohl für die flachen als auch für die dreidimensionalen Formen implementieren.

      Auf diese Weise können Sie eine einzige API für die Verwaltung der Formen haben:

      interface ManageShapeInterface
      {
          public function calculate();
      }
      
      class Square implements ShapeInterface, ManageShapeInterface
      {
          public function area()
          {
              // calculate the area of the square
          }
      
          public function calculate()
          {
              return $this->area();
          }
      }
      
      class Cuboid implements ShapeInterface, ThreeDimensionalShapeInterface, ManageShapeInterface
      {
          public function area()
          {
              // calculate the surface area of the cuboid
          }
      
          public function volume()
          {
              // calculate the volume of the cuboid
          }
      
          public function calculate()
          {
              return $this->area();
          }
      }
      

      In der Klasse AreaCalculator können Sie den Aufruf für die Methode area durch calculate ersetzen und außerdem überprüfen, ob das Objekt eine Instanz des ManageShapeInterface und nicht des ShapeInterface ist.

      Das erfüllt das Interface-Segregation-Prinzip.

      Das Dependency-Inversion-Prinzip

      Das Dependency-Inversion-Prinzip besagt:

      Entitäten müssen von Abstraktionen abhängen, nicht von Konkretionen. Es besagt, dass das Modul auf hoher Ebene nicht vom Modul auf niedriger Ebene abhängen darf, sondern diese von Abstraktionen abhängen sollten.

      Dieses Prinzip ermöglicht die Entkopplung.

      Hier ist ein Beispiel für einen PasswordReminder der sich mit einer MySQL-Datenbank verbindet:

      class MySQLConnection
      {
          public function connect()
          {
              // handle the database connection
              return 'Database connection';
          }
      }
      
      class PasswordReminder
      {
          private $dbConnection;
      
          public function __construct(MySQLConnection $dbConnection)
          {
              $this->dbConnection = $dbConnection;
          }
      }
      

      Zuerst ist die MySQLConnection das Modul auf niedriger Ebene, während der PasswordReminder auf hoher Ebene angesiedelt ist, aber gemäß der Definition von D in SOLID, die besagt, von der Abstraktion abzuhängen und nicht von Konkretionen. Dieses obige Snippet verletzt dieses Prinzip, da die Klasse PasswordReminder gezwungen wird, von der Klasse MySQLConnection abzuhängen.

      Wenn Sie später die Datenbank-Engine ändern würden, müssten Sie auch die Klasse PasswordReminder bearbeiten, und das würde das Open-Close-Prinzip verletzen.

      Die Klasse PasswordReminder sollte sich nicht darum kümmern, welche Datenbank Ihre Anwendung verwendet. Um diese Probleme zu beheben, können Sie an eine Schnittstelle kodieren, da Module auf hoher Ebene und niedriger Ebene von der Abstraktion abhängen sollten:

      interface DBConnectionInterface
      {
          public function connect();
      }
      

      Die Schnittstelle hat eine Verbindungsmethode und die Klasse MySQLConnection implementiert diese Schnittstelle. Anstatt die Klasse MySQLConnection im Konstruktor von PasswordReminder, direkt zu typisieren, geben Sie stattdessen das DBConnectionInterface an, und unabhängig davon, welchen Datenbanktyp Ihre Anwendung verwendet, kann die Klasse PasswordReminder ohne Probleme eine Verbindung zur Datenbank herstellen und das Open-Close-Prinzip wird nicht verletzt.

      class MySQLConnection implements DBConnectionInterface
      {
          public function connect()
          {
              // handle the database connection
              return 'Database connection';
          }
      }
      
      class PasswordReminder
      {
          private $dbConnection;
      
          public function __construct(DBConnectionInterface $dbConnection)
          {
              $this->dbConnection = $dbConnection;
          }
      }
      

      Dieser Code verdeutlicht, dass sowohl die Module auf hoher Ebene als auch auf niedriger Ebene von der Abstraktion abhängen.

      Zusammenfassung

      In diesem Artikel wurden Ihnen die fünf Prinzipien von SOLID Code vorgestellt. Projekte, die sich an die SOLID-Prinzipien halten, können mit weniger Komplikationen mit anderen Mitarbeitern geteilt, erweitert, modifiziert, getestet und refraktorisiert werden.

      Lernen Sie weiter, indem Sie über andere Praktiken für die Agile und Adaptive Softwareentwicklung lesen.



      Source link

      Einrichten einer Node.js-Anwendung für die Produktion unter Ubuntu 20.04


      Einführung

      Node.js ist eine Open-Source-basierte JavaScript-Laufzeitumgebung für das Einrichten von Server- und Netzwerkanwendungen. Die Plattform läuft unter Linux, MacOS, FreeBSD und Windows. Sie können Node.js-Anwendungen zwar in der Befehlszeile ausführen können, doch werden Sie sie in diesem Tutorial als Dienst ausführen. Das bedeutet, dass sie bei erneutem Booten oder einem Absturz neu gestartet werden und zur Verwendung in einer Produktionsumgebung sicher sind.

      In diesem Tutorial werden Sie eine produktionsfähige Node.js-Umgebung auf einem einzelnen Ubuntu 20.04-Server einrichten. Dieser Server wird eine von PM2 verwaltete Node.js-Anwendung ausführen und Benutzern sicheren Zugriff auf die Anwendung über einen Nginx-Reverseproxy bereitstellen. Der Nginx-Server bietet HTTPS mit einem kostenlosen Zertifikat, das von Let’s Encrypt bereitgestellt wird.

      Voraussetzungen

      Dieser Leitfaden geht davon aus, dass Sie Folgendes haben:

      Wenn Sie die Voraussetzungen abgeschlossen haben, verfügen Sie über einen Server, der die Standardplatzhalterseite Ihrer Domäne unter https://example.com/ bereitstellt.

      Schritt 1 — Installieren von Node.js

      Beginnen wir zunächst mit der Installation der neuesten LTS-Version von Node.js unter Verwendung der NodeSource-Paketarchive.

      Installieren Sie zunächst das NodeSource-PPA, um Zugriff auf dessen Inhalt zu erhalten. Stellen Sie sicher, dass Sie sich in Ihrem Stammverzeichnis befinden, und verwenden Sie curl, um das Installationskript für die aktuellste LTS-Version von Node.js aus den Archiven abzurufen.

      • cd ~
      • curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh

      Sie können den Inhalt dieses Skripts mit nano oder Ihrem bevorzugten Texteditor prüfen:

      Wenn Sie das Skript fertig geprüft haben, führen Sie es unter sudo aus:

      • sudo bash nodesource_setup.sh

      Das PPA wird Ihrer Konfiguration hinzugefügt und Ihr lokaler Paket-Cache automatisch aktualisiert. Nach Ausführung des Einrichtungsskripts aus Nodesource können Sie nun das Node.js-Paket installieren:

      Um zu prüfen, welche Version von Node.js Sie nach diesen ersten Schritten installiert haben, geben Sie Folgendes ein:

      Output

      v14.4.0

      Anmerkung: Bei der Installation aus dem NodeSource-PPA wird die ausführbare Node.js-Datei nodejs und nicht node genannt.

      Das nodejs-Paket enthält das binäre nodejs sowie npm, einen Paketmanager für Node-Module. Sie müssen npm also nicht separat installieren.

      npm verwendet eine Konfigurationsdatei in Ihrem Home Verzeichnis, um Aktualisierungen zu verfolgen. Sie wird erstellt, wenn Sie npm das erste Mal ausführen. Führen Sie diesen Befehl aus, um zu verifizieren, dass npm installiert ist, und um die Konfigurationsdatei zu erstellen:

      Output

      6.14.5

      Damit bestimmte npm Pakete funktionieren (z. B. jene, deren Code aus Source erstellt werden muss), müssen Sie das build-essential Paket installieren:

      • sudo apt install build-essential

      Jetzt haben Sie die notwendigen Tools, um mit npm Paketen zu arbeiten, deren Code aus Source erstellt werden muss.

      Mit installierter Node.js-Laufzeitumgebung können wir jetzt mit dem Schreiben einer Node.js-Anwendung fortfahren.

      Schritt 2 — Erstellen einer Node.js-Anwendung

      Lassen Sie uns eine Hello World-Anwendung schreiben, die in beliebigen HTTP-Anfragen „Hello World“ zurückgibt. Diese Beispielanwendung hilft Ihnen dabei, Node.js einzurichten. Sie können sie durch eine eigene Anwendung ersetzen – stellen Sie sicher, dass Sie Ihre Anwendung so ändern, dass an den richtigen IP-Adressen und Ports gelauscht wird.

      Erstellen wir zunächst eine Beispielanwendung namens hello.js:

      Fügen Sie in der Datei den folgenden Code hinzu:

      ~/hello.js

      const http = require('http');
      
      const hostname="localhost";
      const port = 3000;
      
      const server = http.createServer((req, res) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Hello World!n');
      });
      
      server.listen(port, hostname, () => {
        console.log(`Server running at http://${hostname}:${port}/`);
      });
      

      Speichern Sie die Datei und beenden Sie den Editor.

      Diese Node.js-Anwendung lauscht an der angegebenen Adresse (localhost) und Port (3000) und gibt “Hello World!” mit dem HTTP-Erfolgscode 200 zurück. Da wir an localhost lauschen, können sich Remoteclients nicht mit unserer Anwendung verbinden.

      Geben Sie zum Testen Ihrer Anwendung Folgendes ein:

      Sie erhalten die folgende Ausgabe:

      Output

      Server running at http://localhost:3000/

      Anmerkung: Durch Ausführung einer Node.js-Anwendung auf diese Weise werden weitere Befehle blockiert, bis der Abbruch der Anwendung durch Drücken von Strg+C erzwungen wird.

      Öffnen Sie zum Testen der Anwendung auf Ihrem Server eine weitere Terminalsitzung und stellen Sie unter Verwendung von curl mit localhost eine Verbindung her:

      • curl http://localhost:3000

      Wenn Sie die folgende Ausgabe erhalten, funktioniert die Anwendung ordnungsgemäß und lauscht an der richtigen Adresse und am richtigen Port:

      Output

      Hello World!

      Wenn Sie nicht die erwartete Ausgabe erhalten, stellen Sie sicher, dass Ihre Node.js-Anwendung ausgeführt wird und so konfiguriert ist, dass sie an der richtigen Adresse und am richtigen Port lauscht.

      Sobald Sie sicher sind, dass es funktioniert, erzwingen Sie den Abbruch der Anwendung (wenn Sie es nicht bereits getan haben), indem Sie Strg+C drücken.

      Schritt 3 – Installieren von PM2

      Als Nächstes installieren wir PM2, einen Prozessmanager für Node.js-Anwendungen. PM2 ermöglicht es, Anwendungen zu daemonisieren, damit sie im Hintergrund als Dienst ausgeführt werden.

      Verwenden Sie npm zum Installieren der neuesten Version von PM2 auf Ihrem Server:

      • sudo npm install pm2@latest -g

      Die Option -g weist npm an, das Modul global zu installieren, damit es systemweit verfügbar ist.

      Verwenden wir zunächst den Befehl pm2 start zum Ausführen Ihrer Anwendung hello.js im Hintergrund:

      Dadurch wird Ihre Anwendung auch der Prozessliste von PM2 hinzugefügt, die bei jeder Ausführung einer Anwendung ausgegeben wird:

      Output

      ... [PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2 [PM2] PM2 Successfully daemonized [PM2] Starting /home/sammy/hello.js in fork_mode (1 instance) [PM2] Done. ┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐ │ id │ name │ mode │ ↺ │ status │ cpu │ memory │ ├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤ │ 0 │ hello │ fork │ 0 │ online │ 0% │ 25.2mb │ └────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

      Wie oben angegeben, weist PM2 automatisch einen App-Namen (basierend auf dem Dateinamen ohne die Erweiterung .js) und eine PM2-ID zu. Außerdem pflegt PM2 weitere Informationen, wie z. B. die PID des Prozesses, den aktuellen Status und die Speicherauslastung.

      Anwendungen, die unter PM2 ausgeführt werden, werden automatisch neu gestartet, wenn eine Anwendung abstürzt oder ihr Abbruch erzwungen wird. Wir können jedoch mit dem Unterbefehl startup einen zusätzlichen Schritt hinzufügen, damit die Anwendung beim Systemstart gestartet wird. Dieser Unterbefehl generiert und konfiguriert ein Startskript zum Starten von PM2 und den verwalteten Prozessen beim Booten von Servern:

      Die letzte Zeile der resultierenden Ausgabe enthält einen Befehl zum Ausführen mit Superuser-Berechtigungen, damit PM2 beim Booten gestartet wird:

      Output

      [PM2] Init System found: systemd sammy [PM2] To setup the Startup Script, copy/paste the following command: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Führen Sie den Befehl aus der Ausgabe aus, wobei Sie anstelle von sammy Ihren Benutzernamen verwenden:

      • sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Als weiteren Schritt können wir die PM2-Prozessliste und entsprechende Umgebungen speichern:

      Sie haben nun eine systemd-Einheit erstellt, die beim Booten pm2 für Ihren Benutzer ausführt. Diese pm2-Instanz gibt wiederum hello.js zurück.

      Starten Sie den Dienst mit systemctl:

      • sudo systemctl start pm2-sammy

      Wenn an diesem Punkt ein Fehler auftritt, müssen Sie möglicherweise neu starten, was Sie mit sudo reboot erreichen können.

      Überprüfen Sie den Status der systemd-Einheit:

      • systemctl status pm2-sammy

      Für eine ausführliche Übersicht zu systemd lesen Sie bitte Systemd Essentials: Arbeiten mit Diensten, Einheiten und dem Journal.

      Neben den von uns abgedeckten Befehlen bietet PM2 viele Unterbefehle, mit denen Sie Daten über Ihre Anwendung verwalten oder suchen können.

      Stoppen Sie eine Anwendung mit diesem Befehl (geben Sie den App-Namen oder die ID von PM2 an):

      Starten Sie eine Anwendung neu:

      • pm2 restart app_name_or_id

      Listen Sie die aktuell von PM2 verwalteten Anwendungen auf:

      Erhalten Sie Informationen zu einer bestimmten Anwendung mithilfe des App-Namens:

      Der PM2-Prozessmonitor kann mit dem Unterbefehl monit aufgerufen werden. Dadurch werden der Anwendungsstatus sowie die CPU-und Speicherauslastung angezeigt:

      Beachten Sie, dass beim Ausführen von pm2 ohne Argumente auch eine Hilfeseite mit Beispielnutzung angezeigt wird.

      Nachdem Ihre Node.js-Anwendung ausgeführt und von PM2 verwaltet wird, können wir nun den Reverseproxy einrichten.

      Schritt 4 — Einrichten von Nginx als Reverseproxy-Server

      Ihre Anwendung wird ausgeführt und lauscht an localhost. Sie müssen jedoch einen Weg finden, damit Ihre Benutzer darauf zugreifen können. Dazu werden wir den Nginx-Webserver als Reverseproxy einrichten.

      Im Tutorial zu den Voraussetzungen richten Sie Ihre Nginx-Konfiguration in der Datei /etc/nginx/sites-available/example.com ein. Öffnen Sie diese Datei zur Bearbeitung:

      • sudo nano /etc/nginx/sites-available/example.com

      Innerhalb des server-Blocks sollten Sie einen vorhandenen location /-Block haben. Ersetzen Sie den Inhalt dieses Blocks durch die folgende Konfiguration. Wenn Ihre Anwendung so konfiguriert ist, dass sie an einem anderen Port lauscht, aktualisieren Sie den markierten Teil mit der richtigen Portnummer:

      /etc/nginx/sites-available/example.com

      server {
      ...
          location / {
              proxy_pass http://localhost:3000;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Dadurch wird der Server so konfiguriert, dass er auf Anfragen an sein Stammverzeichnis antwortet. Angenommen, unser Server ist unter example.com verfügbar. Durch Aufrufen von https://example.com/ über einen Webbrowser würde die Anfrage an hello.js gesendet werden, wobei an Port 3000 bei localhost gelauscht wird.

      Sie können dem gleichen Serverblock zusätzliche location-Blöcke hinzufügen, um Zugriff auf andere Anwendungen auf dem gleichen Server zu gewähren. Wenn Sie beispielsweise eine andere Node.js-Anwendung an Port 3001 ausführen würden, könnten Sie diesen location-Block hinzufügen, um Zugriff darauf über https://example.com/app2 zu gewähren:

      /etc/nginx/sites-available/example.com — Optional

      server {
      ...
          location /app2 {
              proxy_pass http://localhost:3001;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Sobald Sie die location-Blöcke für Ihre Anwendungen hinzugefügt haben, speichern Sie die Datei und verlassen Sie den Editor.

      Stellen Sie sicher, dass Sie keine Syntaxfehler gemacht haben, indem Sie Folgendes eingeben:

      Starten Sie Nginx neu:

      • sudo systemctl restart nginx

      Wenn Ihre Node.js-Anwendung ausgeführt wird und Ihre Anwendungs- und Nginx-Konfigurationen korrekt sind, sollten Sie nun über den Nginx-Reverseproxy auf Ihre Anwendung zugreifen können. Probieren Sie es aus, indem Sie die URL Ihres Servers (seine öffentliche IP-Adresse oder seinen Domänennamen) aufrufen.

      Zusammenfassung

      Herzlichen Glückwunsch! Ihre Node.js-Anwendung wird nun hinter einem Nginx-Reverseproxy auf einem Ubuntu 20.04-Server ausgeführt. Diese Reverseproxy-Einrichtung ist flexibel genug, um Ihren Benutzern Zugriff auf andere Anwendungen oder statische Webinhalte zu bieten, die Sie freigeben möchten.



      Source link

      Verwenden von Visual Studio Code für die Remoteentwicklung über das Remote-SSH-Plugin


      Einführung

      Visual Studio Code ist eine beliebte Integrated Developer Environment (IDE) für Entwickler. Dank ihres großen Spektrums an Plugins, eines Minimaldesigns und plattformübergreifender Unterstützung eignet sie sich perfekt für Entwickler aller Kenntnisstufen. In diesem Tutorial geht es um die Verwendung des Remote-SSH-Plugins, das eine Remoteentwicklung von Software ermöglicht. Mit diesem Plugin können Sie Dateien auf Ihrer lokalen Workstation bearbeiten, Entwicklungsaufgaben wie Programmausführung, Komponententests oder statische Analysen aber auf einem Remoteserver ausführen.

      Es gibt viele Gründe, warum dies hilfreich sein kann. Möglicherweise verfügen Sie zum Beispiel über eine Windows-Workstation und möchten unter Windows entwickeln, während der Code später in Linux ausgeführt werden soll. Vielleicht benötigen Sie mehr RAM- oder Verarbeitungsleistung, als Ihr aktueller Rechner hat, oder wollen aufgrund einer Unternehmensrichtlinie Code fern von Ihrem persönlichen Rechner halten, oder Ihre Workstation unberührt lassen.

      In diesem Tutorial aktivieren Sie das Remote-SSH-Plugin, konfigurieren Visual Studio Code so, dass Code auf dem Remoteserver ausgeführt wird, und führen Code von Ihrer lokalen Visual Studio Code-Installation auf dem Remoteserver aus.

      Voraussetzungen

      Um mit diesem Leitfaden fortfahren zu können, benötigen Sie:

      • Einen lokalen Entwicklungscomputer, auf dem Windows, MacOSX oder Linux ausgeführt wird. Dieses Tutorial funktioniert nicht auf ChromeOS-Geräten.
      • Visual Studio Code, das Sie von der offiziellen Website herunterladen und installieren können.
      • Ein generiertes SSH-Schlüsselpaar:
      • Einen Ubuntu 18.04-Server, der gemäß der Anleitung zur Einrichtung des Ubuntu 18.04-Servers eingerichtet wurde, einschließlich eines sudo-fähigen non-root users und einer Firewall.

      Schritt 1 — Installieren des Remote-SSH-Plugins

      Im Extensions Marketplace können Sie für verschiedene Tools und Programmiersprachen unterstützte Erweiterungen und Erweiterungen von Drittanbietern herunterladen. Hier suchen Sie nach dem Remote-SSH-Plugin und installieren es.

      Auf der linken Seite der IDE befindet sich eine vertikale Reihe mit fünf Symbolen. Das unterste Symbol, das wie vier Quadrate in einem Kästchen aussieht, dessen oberes rechtes Quadrat nach außen ragt, ist das Symbol für den Extensions Marketplace:

      Stelle des Extensions Marketplace-Symbols

      Sie können auch auf diesen Abschnitt zugreifen, indem Sie Strg+Umschalttaste+X drücken. Wenn Sie diese Seite öffnen, sehen Sie zum Herunterladen und Installieren vorgeschlagene Plugins.

      Wenn der Extensions Marketplace geöffnet ist, geben Sie Remote-SSH in die Suchleiste Extensions in Marketplace durchsuchen ein. Wenn Sie das Plugin finden, wählen Sie es aus und klicken Sie dann auf die grüne Schaltfläche Installieren, um die Erweiterung zu installieren.

      Suchen nach dem Remote-SSH-Plugin

      Die Erweiterung ist nun installiert. Als Nächstes konfigurieren Sie die Erweiterung, damit Sie sich mit Ihrem Server verbinden können.

      Schritt 2 — Konfigurieren des Remote-SSH-Plugins und Verbinden mit Ihrem Server

      Nachdem Sie das Plugin installiert haben, können Sie es nun konfigurieren, um eine Verbindung mit einem Server herzustellen. Dazu benötigen Sie folgende Informationen:

      • Die IP-Adresse oder den Hostnamen des Servers.
      • Den Benutzernamen, mit dem Sie sich verbinden.
      • Den privaten Schlüssel, den Sie zur Authentifizierung Ihres Benutzers verwenden werden.

      Sie verwenden diese Informationen zum Erstellen einer SSH-Konfigurationsdatei, die Visual Studio Code zur Herstellung einer SSH-Verbindung mit dem Server verwenden kann, um Dateien zu synchronisieren und Code in Ihrem Namen auszuführen. Diese Konfiguration erstellen Sie mit Visual Studio Code.

      Nachdem Sie das Remote-SSH-Plugin installiert haben, sehen Sie nun ein kleines grünes Feld in der unteren linken Ecke der Benutzeroberfläche von Visual Studio Code. Wenn Sie mit dem Mauszeiger über das Feld fahren, steht im Popup Remotefenster öffnen. Die Schaltfläche sieht ungefähr wie ein Größer-als-Zeichen unter einem Kleiner-als-Zeichen >< aus, wie im folgenden Bild dargestellt:

      Grüne Schaltfläche zum Öffnen eines Remotefensters

      Klicken Sie auf die Schaltfläche, woraufhin oben in der Mitte ein Dialogfeld erscheint. Wählen Sie Remote-SSH: Konfigurationsdatei öffnen… aus der Liste:

      Auswählen von "SSH konfigurieren" in der Benutzeroberfläche

      In der nächsten Eingabeaufforderung werden Sie gefragt, welche Konfigurationsdatei Sie öffnen möchten. Wenn Sie Windows verwenden, sehen Sie zwei Speicherorte: einen in Ihrem persönlichen Benutzerverzeichnis und einen am Installationsspeicherort für SSH. Zum Konfigurieren des Servers sollten Sie die Datei in Ihrem Benutzerverzeichnis verwenden.

      Wählen Sie die Datei aus, woraufhin Ihr Editor die config-Datei öffnet. Fügen Sie der Datei folgenden Code hinzu, um die Verbindung mit Ihrem Server festzulegen, indem Sie die hervorgehobenen Abschnitte durch die Informationen für Ihren Server ersetzen:

      config

      Host my_remote_server
          HostName your_server_ip_or_hostname
          User sammy
          IdentityFile /location/of/your/private/key
      

      So funktioniert diese Konfigurationsdatei:

      • Host: Gibt einen Namen für Ihren Host an. Damit können Sie beim Herstellen einer Verbindung mit dem Server einen kurzen Namen oder eine Abkürzung anstelle der vollständigen IP-Adresse oder des Hostnamens verwenden.
      • HostName: Der tatsächliche Hostname des Servers, der entweder eine IP-Adresse oder ein vollqualifizierter Domänenname ist.
      • User: Der Benutzer, mit dem Sie eine Verbindung herstellen möchten.
      • IdentityFile: Der Pfad zu Ihrem privaten SSH-Schlüssel. In Mac- und Linux-Systemen finden Sie diesen in Ihrem Stammverzeichnis in einem versteckten .ssh-Verzeichnis, das typischerweise id_rsa heißt. Wenn Sie Windows verwenden, haben Sie einen Speicherort zur Speicherung dieser Datei angegeben, als Sie diese mit putty-gen erstellt haben.

      Geben Sie die entsprechenden Werte in Ihrer Datei an und speichern Sie die Datei.

      Visual Studio Code ist nun konfiguriert und bereit, sich mit Ihrem Server zu verbinden. Klicken Sie unten links auf die grüne Schaltfläche Remotefenster öffnen und wählen Sie Remote-SSH: Mit Host verbinden…

      Herstellen einer Verbindung mit dem Server von Visual Studio Code

      Sobald Sie damit fertig sind, werden alle verfügbaren und konfigurierten Server im Dropdown-Menü angezeigt. Wählen Sie den Server, mit dem Sie sich verbinden möchten, aus dieser Liste aus.

      Wenn Sie sich von Ihrem Rechner zum ersten Mal mit diesem Server verbinden, werden Sie wahrscheinlich eine Aufforderung mit dem SSH Fingerprint-Verifizierungsdialog erhalten, wie im folgenden Bild zu sehen:

      Bestätigen Ihres SSH Fingerprint

      Dadurch wird sichergestellt, dass Sie sich wirklich mit dem richtigen Server verbinden. Sie können dies überprüfen, indem Sie sich bei Ihrem Server manuell anmelden und ssh-keygen -l -f /etc/ssh/ssh_host_key.pub ausführen, um den Fingerabdruck des Servers anzuzeigen. Wenn dieser Fingerabdruck mit dem übereinstimmt, der in Visual Studio Code angezeigt wird, dann verbinden Sie sich tatsächlich mit dem richtigen Server, sodass Sie auf Fortfahren klicken können.

      Visual Studio Code öffnet standardmäßig ein neues Fenster, sobald eine neue Verbindung hergestellt wird. Ein neues Fenster mit dem Empfangsbildschirm wird angezeigt. Sie wissen, dass Ihre Verbindung erfolgreich hergestellt wurde, wenn unten links im grünen Feld SSH: your_ip_address_or_hostname angezeigt wird. Das bedeutet, dass Visual Studio Code verbunden ist und mit Ihrem Remoteserver kommuniziert.

      Erfolgreiche SSH-Verbindung

      Nachdem Sie verbunden sind, können Sie nun Befehle und Code aus Ihrem Editor ausführen.

      Schritt 3 — Ausführen von Code auf dem Remoteserver

      Das Remote-SSH-Plugin ist fertig konfiguriert, sodass es an der Zeit ist, Code auf Ihrem Remotecomputer auszuführen. Öffnen Sie ein Terminalfenster, indem Sie aus der Navigationsleiste oben im Fenster Visual Studio Terminal auswählen und auf Neues Terminal klicken. Sie können auch ein Terminal öffnen, indem Sie Strg+Umschalttaste+` drücken. Das Terminal, das geöffnet wird, ist ein Terminal auf Ihrem Remoteserver, nicht auf Ihrem lokalen Rechner.

      Wenn sich das Terminal öffnet, geben Sie folgenden Befehl aus, um die IP-Adresse Ihres Servers anzuzeigen und zu überprüfen, ob Sie mit Ihrem Remoteserver verbunden sind:

      Sie sehen in Ihrem Terminal folgende Ausgabe:

      Output

      1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 16:cb:05:5b:30:f1 brd ff:ff:ff:ff:ff:ff inet your_server_ip brd your_broadcast_address scope global eth0 valid_lft forever preferred_lft forever ...

      Um die Fähigkeit zur Ausführung von Remotecode zu testen, erstellen Sie in Ihrem Editor eine neue Python-Datei namens hello.py. Wenn Sie mit Ihrem Remoteserver verbunden sind, werden alle über Visual Studio Code erstellten Dateien auf diesem Server gespeichert, nicht auf Ihrem lokalen Rechner.

      Fügen Sie der Datei folgende Inhalte hinzu:

      hello.py

      print("Hello Sammy!")
      

      Um das Programm auf Ihrem Server auszuführen, öffnen Sie in Visual Studio Code ein Terminal über das Navigationsmenü oder drücken Sie die Tastenfolge Strg+Umschalt+`. Da diese Terminalsitzung mit Ihrem Remoteserver verbunden ist, führen Sie im Terminal folgenden Befehl zur Ausführung Ihres hello.py-Programms aus:

      Die Ausgabe Ihres Programms wird angezeigt.

      Ausführen Ihres Python-Skripts

      Sie können die Datei auch über das Kontextmenü Debug ausführen, indem Sie Ohne Debugging ausführen wählen.

      Anmerkung: Wenn Sie in Visual Studio Code Entwicklungserweiterungen installiert haben (wie die Python-Erweiterung), müssen Sie diese Erweiterungen auf Ihrem Server über den Extension Marketplace neu installieren. Falls Sie diese Plugins zuvor in Visual Studio Code installiert haben, zeigt der Marketplace, wenn Sie erneut nach ihnen suchen, Installieren in SSH: Hostname an. Achten Sie stets darauf, in welchem Entwicklungskontext Sie sich befinden, da Visual Studio Code dort Ihre Plugins installieren und Dateien erstellen wird. Wenn Sie versuchen, Code auszuführen, ohne diese Plugins installiert zu haben, werden in der unteren rechten Ecke des Bildschirms Fehlerdialogfelder angezeigt, in denen Sie zur Installation der Plugins auf Ihrem Remoteserver aufgefordert werden. Nachdem Sie sie installiert haben, müssen Sie Visual Studio Code wahrscheinlich neu laden. Wenn Sie es neu starten, wird es auf dem Remoteserver weiter ausgeführt, ohne dass Sie manuell eine neue Verbindung herstellen müssen.

      Zusammenfassung

      Sie haben Visual Studio Code nun zur Entwicklung auf einem Remoteserver mit SSH konfiguriert. Remoteausführung mit einer IDE bietet viele Vorteile, einschließlich der Fähigkeit, schnell zu testen, wie Ihr Code in verschiedenen Betriebssystemen und Hardwarespezifikationen ausgeführt wird. Solange Sie über eine Internetverbindung verfügen, können Sie sich mit Ihrem Server verbinden und über beliebige Computer mit Ihrem Code arbeiten. Außerdem können Sie mit einer Linux-Umgebung entwickeln, selbst wenn Sie Windows als primäres Betriebssystem verwenden.



      Source link