One place for hosting & domains

      So konfigurieren Sie Apache HTTP mit MPM Event und PHP-FPM unter Ubuntu 18.04


      Der Autor hat den COVID-19 Relief Fund dazu ausgewählt, eine Spende im Rahmen des Programms Write for DOnations zu erhalten.

      Einführung

      Der Apache-HTTP-Webserver wurde im Laufe der Jahre weiterentwickelt, damit er in verschiedenen Umgebungen arbeitet und verschiedene Anforderungen erfüllt. Ein wichtiges Problem, das Apache HTTP wie jeder andere Webserver auch lösen muss, ist die Handhabung verschiedener Prozesse bei der Bearbeitung von http-basierten Anfragen. Dazu zählt das Öffnen eines Sockets, das die Anforderung verarbeitet, das Offenhalten der Verbindung für eine bestimmte Zeit, die Handhabung neuer Ereignisse, die während dieser Verbindung eintreten und die Rückgabe des produzierten Contents durch ein Programm, dass in einer bestimmten Sprache geschrieben wurde (wie PHP, Perl oder Python). Diese Aufgaben werden von einem Multi-Processing-Module (MPM) ausgeführt und gesteuert.

      Apache HTTP ist mit drei verschiedenen MPM ausgestattet:

      • Prefork: Für jede eingehende Verbindung, die den Server erreicht, wird ein neuer Vorgang erstellt. Jeder Vorgang ist isoliert von den anderen und es wird kein Speicher zwischen ihnen geteilt, selbst dann, wenn sie in der Ausführung identische Anrufe an einem bestimmten Punkt ausführen. Auf diese Weise können Sie mit Bibliotheken verknüpfte Anwendungen, die Thread-Ausführungen nicht unterstützen, sicher ausführen – meist ältere Anwendungen oder Bibliotheken.
      • Worker: Ein Elternprozess ist für das Starten eines Bündels von Kindprozessen verantwortlich, von denen einige neu eingehende Verbindungen erfassen und andere den angeforderten Content bereitstellen. Für jeden Prozess gibt es einen dazugehörigen Thread (ein einzelner Thread kann jeweils eine Verbindung verwalten), sodass ein Prozess mit mehreren Anfragen gleichzeitig umgehen kann. Diese Methode für die Handhabung von Verbindungen fördert eine bessere Ressourcennutzung und gewährleistet die Aufrechterhaltung der Stabilität. Das ist auf das Bündel von verfügbaren Prozessen zurückzuführen, bei denen oft frei verfügbare Threads bereitstehen, die neue Verbindungen sofort bedienen können.
      • Event: Basierend auf Worker geht dieses MPM noch einen Schritt weiter, indem es die Art und Weise optimiert, wie der Elternprozess Aufgaben für die Kindprozesse und für die Threads, die damit verknüpft sind, vorgibt. Eine Verbindung bleibt für 5 Sekunden standardmäßig geöffnet und schließt sich bei jedem neuen Ereignis, das eintritt; das ist der Standardwert für die Keep-Alive-Anweisung, der den mit ihm verknüpften Thread beibehält. Das Event MPM ermöglicht dem Prozess das Verwalten von Threads, damit einige Threads für die Verwaltung neuer eingehender Verbindungen bereitstehen, während andere weiterhin mit den Live-Verbindungen verknüpft sind. Die Ressourcennutzung und Leistungsfähigkeit wird dadurch verbessert, dass die den Threads zugewiesenen Aufgaben neu verteilt werden können.

      Mit dem MPM Event-Modul ist ein schnelles Multi-Processing-Modul auf dem Apache-HTTP-Webserver verfügbar.

      PHP-FPM ist der FastCGI-Prozessmanager für PHP. Das FastCGI-Protokoll basiert auf dem Common Gateway Interface (CGI), einem Protokoll, das zwischen Anwendungen und Webservern wie Apache HTTP steht. Dadurch können Entwickler Anwendungen schreiben, ohne das Verhalten der Webserver berücksichtigen zu müssen. Die Programme führen ihre Prozesse unabhängig aus und übergeben ihr Produkt über dieses Protokoll an den Webserver. Jede neue Verbindung, die von einer Anwendung verarbeitet werden muss, erstellt einen neuen Prozess.

      Durch die Kombination von MPM Event in Apache HTTP mit dem PHP FastCGI-Prozessmanager (PHP-FPM) kann eine Website schneller laden und mehr gleichzeitige Verbindungen mit weniger Ressourcen verarbeiten.

      In diesem Tutorial verbessern Sie die Leistung des LAMP-Stacks, indem Sie das standardmäßige Multi-Processing-Module von Prefork auf Event umstellen und den PHP-FPM-Prozessmanager für die Handhabung des PHP-Codes nutzen anstelle des klassischen mod_php in Apache HTTP.

      Voraussetzungen

      Bevor Sie diese Anleitung beginnen, benötigen Sie Folgendes:

      Schritt 1 — Umstellen des Multi-Processing-Module

      Ubuntu übernimmt Skripte, um Apache-HTTP-Module über die eigene übergeordnete Distribution Debian zu aktivieren oder zu deaktivieren. Sie werden dieses Toolset in diesem Schritt verwenden, um das Prefork-Modul zu deaktivieren und das Event-Modul zu aktivieren.

      In diesem Schritt halten Sie Apache HTTP an, deaktivieren das Modul PHP 7.2, das mit dem Prefork-Modul verknüpft ist, und deaktivieren anschließend Prefork, um das Event-Modul unmittelbar aktivieren zu können.

      Zuerst halten Sie den Apache-HTTP-Dienst an:

      • sudo systemctl stop apache2

      Nun können Sie das Modul PHP 7.2 deaktivieren, das mit dem Prefork-Modul in Verbindung steht:

      Deaktivieren Sie dann das Prefork MPM-Modul:

      • sudo a2dismod mpm_prefork

      Nun Aktivieren Sie das Event MPM-Modul:

      Sie haben das MPM von Prefork auf Event umgestellt und die Modulverbindung PHP 7.2 zwischen PHP und Apache HTTP entfernt. Im nächsten Schritt installieren Sie das php-fpm-Modul sowie die verwandten Bibliotheken und Proxy-Module. Sie konfigurieren Apache HTTP so, dass es auch mit PHP kommunizieren kann.

      Schritt 2 — Konfigurieren von Apache HTTP für die Nutzung des FastCGI-Prozesses

      In dieser Phase haben Sie die Verarbeitung von Verbindungen durch Apache HTTP umgestellt, indem Sie sie von dem Prefork-MPM auf Event verlagert haben. Im Zuge dessen haben Sie jedoch das PHP-Modul deaktiviert, das Apache HTTP mit jedem Programm verbunden hatte, das mit PHP ausgeführt wird.

      In diesem Schritt installieren Sie den PHP-FPM-Prozessor, damit Apache HTTP wieder PHP-Programme verarbeiten kann. Außerdem installieren Sie die Abhängigkeitsbibliotheken und aktivieren die Module, damit beide reibungslos und schneller zusammenarbeiten können als zuvor.

      Installieren Sie zuerst php-fpm. Der folgende Befehl installiert das PHP-FPM und aktiviert automatisch den Dienst php7.2-fpm, der in systemd integriert ist, sodass der Dienst beim Booten gestartet wird:

      Apache HTTP und PHP benötigen für die Kommunikation eine Bibliothek, die diese Funktion ermöglicht. Nun installieren Sie libapache2-mod-fcgid, das als Schnittstelle zwischen Programmen mit Webservern dient und Apache-HTTP-spezifisch ist. Diese Kommunikation erfolgt über ein UNIX-Socket.

      Installieren Sie diese Bibliothek:

      • sudo apt install libapache2-mod-fcgid

      Sie haben php-fpm und das libapache2-mod-fcgid installiert, aber noch keines davon aktiviert.

      Aktivieren Sie zuerst das php-fpm-Modul mit folgendem Befehl:

      Aktivieren Sie in einem zweiten Schritt das Apache HTTP-Proxy-Modul:

      Aktivieren Sie in einem dritten Schritt das FastCGI-Proxy-Modul auf Apache HTTP:

      Hinweis: Sie können die Konfiguration dieser Interaktion zwischen PHP-Programmen und Apache HTTP über einen UNIX-Socket mit Folgendem lesen:

      • cat /etc/apache2/conf-enabled/php7.2-fpm.conf

      Nun wurden alle Vorkehrungen getroffen, damit Sie Apache HTTP starten können. Führen Sie eine Konfigurationsüberprüfung durch:

      • sudo apachectl configtest

      Output

      Syntax OK

      Danach können Sie mit dem Neustart von Apache HTTP fortfahren, da es beim Installieren der FastCGI-Bibliothek libapache2-mod-fcgid automatisch gestartet wurde:

      • sudo systemctl restart apache2

      Sie haben das php-fpm-Modul installiert und Apache HTTP so konfiguriert, dass es damit funktioniert. Zudem haben Sie ermöglicht, dass die erforderlichen Module für das FastCGI-Protokoll funktionieren, und die entsprechenden Dienste gestartet.

      Nachdem Apache das Event MPM-Modul aktiviert hat und PHP-FPM verfügbar ist und ausgeführt wird, ist es an der Zeit sicherzustellen, das alles wie geplant funktioniert.

      Schritt 3 — Testen Ihrer Konfiguration

      Führen Sie einige Tests aus, um zu prüfen, ob die Konfigurationsänderungen angewendet wurden. Beim ersten Test wird geprüft, welches Multi-Processing-Modul Apache HTTP verwendet. Beim zweiten Test wird sichergestellt, dass PHP den FPM-Manager verwendet.

      Überprüfen Sie den Apache-HTTP-Server, indem Sie den folgenden Befehl ausführen:

      • sudo apachectl -M | grep 'mpm'

      Sie erhalten folgende Ausgabe:

      Output

      mpm_event_module (shared)

      Für das Proxy-Modul und FastCGI können Sie diese Prozedur wiederholen:

      • sudo apachectl -M | grep 'proxy'

      Die Ausgabe zeigt Folgendes:

      Output

      proxy_module (shared) proxy_fcgi_module (shared)

      Wenn Sie die gesamte Liste der Module sehen möchten, können Sie den zweiten Teil des Befehls nach -M entfernen.

      Nun ist es Zeit zu prüfen, ob PHP den FastCGI-Prozessmanager verwendet. Dazu schreiben Sie ein kleines PHP-Skript, das Ihnen alle Informationen zeigt, die mit PHP in Verbindung stehen.

      Führen Sie den folgenden Befehl aus, um eine Datei zu schreiben, deren Name wie folgt lautet:

      • sudo nano /var/www/your_domain/info.php

      Fügen Sie den folgenden Inhalt in die Datei info.php ein:

      info.php

      <?php phpinfo(); ?>
      

      Rufen Sie nun die URL Ihres Servers auf und fügen Sie info.php am Ende hinzu: http://your_domain/info.php.

      Der Server-API-Eintrag lautet FPM/FastCGI.

      PHP Screen the Server API entry FPM/FastCGI

      Löschen Sie die Datei info.php nach diesem Test, damit keine Informationen über den Server veröffentlicht werden:

      • sudo rm /var/www/yourdomain.com/info.php

      Sie haben den Betriebszustand des MPM-Moduls und der Module, die für die Handhabung von FastCGI zuständig sind, sowie die Handhabung des PHP-Codes überprüft.

      Zusammenfassung

      Sie haben Ihren ursprünglichen LAMP-Stack optimiert, sodass sich die Anzahl der Verbindungen zur Erstellung neuer Apache HTTP-Prozesse erhöht hat, PHP-FPM den PHP-Code effizienter verwaltet und sich die Ressourcennutzung insgesamt verbessert.

      Weitere Informationen zu den verschiedenen Modulen und verwandten Projekten finden Sie in der Projekt-Dokumentation zum Apache HTTP-Server.



      Source link

      Using Event Emitters in Node.js


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      Event emitters are objects in Node.js that trigger an event by sending a message to signal that an action was completed. JavaScript developers can write code that listens to events from an event emitter, allowing them to execute functions every time those events are triggered. In this context, events are composed of an identifying string and any data that needs to be passed to the listeners.

      Typically in Node.js, when we want to have an action occur upon completion of another action, we use asynchronous programming techniques like nesting callbacks or chaining promises. However, these techniques tightly couple the triggering action and the resulting action, making it difficult to modify the resulting action in the future. Event emitters provide a different way to structure this relationship: the publish-subscribe pattern. In this software architecture pattern, a publisher (the event emitter) sends a message (an event), and a subscriber receives the event and performs an action. The power of this pattern is that the publisher does not need to know about the subscribers. A publisher publishes a message, and it’s up to the subscribers to react to it in their respective ways. If we wanted to change the behavior of our application, we could adjust how the subscribers react to the events without having to change the publisher.

      In this article, we will create an event listener for a TicketManager JavaScript class that allows a user to buy tickets. We will set up listeners for the buy event, which will trigger every time a ticket is bought. This process will also show how to manage erroneous events from the emitter and how to manage event subscribers.

      Prerequisites

      Step 1 — Emitting Events

      In this step, we’ll explore the two most common ways to create an event emitter in Node.js. The first is to use an event emitter object directly, and the second is to create an object that extends the event emitter object.

      Deciding which one to use depends on how coupled your events are to the actions of your objects. If the events you want to emit are an effect of an object’s actions, you would likely extend from the event emitter object to have access to its functions for convenience. If the events you want to emit are independent of your business objects or are a result of actions from many business objects, you would instead create an independent event emitter object that’s referenced by your objects.

      Let’s begin by creating a standalone, event-emitting object. We’ll begin by creating a folder to store all of our code. In your terminal, make a new folder called event-emitters:

      Then enter that folder:

      Open the first event emitter, firstEventEmitter.js, in a text editor. We will use nano as it’s available in the terminal:

      • nano firstEventEmitter.js

      In Node.js, we emit events via the EventEmitter class. This class is part of the events module. Let’s begin by first loading the events module in our file by adding the following line:

      event-emitters/firstEventEmitter.js

      const { EventEmitter } = require("events");
      

      With the class imported, we can use it to create a new object instance from it:

      event-emitters/firstEventEmitter.js

      const { EventEmitter } = require("events");
      
      const firstEmitter = new EventEmitter();
      

      Let’s emit an event by adding the following highlighted line at the end of firstEventEmitter.js:

      event-emitters/firstEventEmitter.js

      const { EventEmitter } = require("events");
      
      const firstEmitter = new EventEmitter();
      
      
      firstEmitter.emit("My first event");
      

      The emit() function is used to fire events. We need to pass the name of the event to it as a string. We can add any number of arguments after the event name. Events with just a name are fairly limited; the other arguments allow us to send data to our listeners. When we set up our ticket manager, our events will pass data about the purchase when it happens. Keep the name of the event in mind, because event listeners will identify it by this name.

      Note: While we don’t capture it in this example, the emit() function returns true if there are listeners for the event. If there are no listeners for an event, it returns false.

      Let’s run this file to see what happens. Save and exit nano, then execute the file with the node command:

      • node firstEventEmitter.js

      When the script finishes its execution, you will see no output in the terminal. That’s because we do not log any messages in firstEventEmitter.js and there’s nothing that listens to the event that was sent. The event is emitted, but nothing acts on these events.

      Let’s work toward seeing a more complete example of publishing, listening to, and acting upon events. We’ll do this by creating a ticket manager example application. The ticket manager will expose a function to buy tickets. When a ticket is bought, an event will be sent with details of the purchaser. Later, we’ll create another Node.js module to simulate an email being sent to the purchaser’s email confirming the purchase.

      Let’s begin by creating our ticket manager. It will extend the EventEmitter class so that we don’t have to create a separate event emitter object to emit events.

      In the same working directory, create and open a new file called ticketManager.js:

      As with the first event emitter, we need to import the EventEmitter class from the events module. Put the following code at the beginning of the file:

      event-emitters/ticketManager.js

      const EventEmitter = require("events");
      

      Now, make a new TicketManager class that will soon define the method for ticket purchases:

      event-emitters/ticketManager.js

      const EventEmitter = require("events");
      
      class TicketManager extends EventEmitter {}
      

      In this case, the TicketManager class extends the EventEmitter class. This means that the TicketManager class inherits the methods and properties of the EventEmitter class. This is how it gets access to the emit() method.

      In our ticket manager, we want to provide the initial supply of tickets that can be purchased. We’ll do this by accepting the initial supply in our constructor(), a special function that’s called when a new object of a class is made. Add the following constructor to the TicketManager class:

      event-emitters/ticketManager.js

      const EventEmitter = require("events");
      
      class TicketManager extends EventEmitter {
          constructor(supply) {
              super();
              this.supply = supply;
          }
      }
      

      The constructor has one supply argument. This is a number detailing the initial supply of tickets we can sell. Even though we declared that TicketManager is a child class of EventEmitter, we still need to call super() to get access to the methods and properties of EventEmitter. The super() function calls the constructor of the parent function, which in this case is EventEmitter.

      Finally, we create a supply property for the object with this.supply and give it the value passed in by the constructor.

      Now, let’s add a buy() method that will be called when a ticket is purchased. This method will decrease the supply by one and emit an event with the purchase data.

      Add the buy() method as follows:

      event-emitters/ticketManager.js

      const EventEmitter = require("events");
      
      class TicketManager extends EventEmitter {
          constructor(supply) {
              super();
              this.supply = supply;
          }
      
          buy(email, price) {
              this.supply--;
              this.emit("buy", email, price, Date.now());
          }
      }
      

      In the buy() function, we take the purchaser’s email address and the price they paid for the ticket. We then decrease the supply of tickets by one. We end by emitting a buy event. This time, we emit an event with extra data: the email and price that were passed in the function as well as a timestamp of when the purchase was made.

      So that our other Node.js modules can use this class, we need to export it. Add this line at the end of the file:

      event-emitters/ticketManager.js

      ...
      
      module.exports = TicketManager
      

      Save and exit the file.

      We’ve finished our setup for the event emitter TicketManager. Now that we’ve put things in place to push events, we can move on to reading and processing those events. To do that, we will create event listeners in the next step.

      Step 2 — Listening for Events

      Node.js allows us to add a listener for an event with the on() function of an event emitter object. This listens for a particular event name and fires a callback when the event is triggered. Adding a listener typically looks like this:

      eventEmitter.on(event_name, callback_function) {
          action
      }
      

      Note:: Node.js aliases the on() method with addListener(). They perform the same task. In this tutorial, we will continue to use on().

      Let’s get some first-hand experience with listening to our first event. Create a new file called firstListener.js:

      As a demonstration of how the on() function works, let’s log a simple message upon receiving our first event.

      First, let’s import TicketManager into our new Node.js module. Add the following code into firstListener.js:

      event-emitters/firstListener.js

      const TicketManager = require("./ticketManager");
      
      const ticketManager = new TicketManager(10);
      

      Recall that TicketManager objects need their initial supply of tickets when created. This is why we pass the 10 argument.

      Now let’s add our first Node.js event listener. It will listen to the buy event. Add the following highlighted code:

      event-emitters/firstListener.js

      const TicketManager = require("./ticketManager");
      
      const ticketManager = new TicketManager(10);
      
      ticketManager.on("buy", () => {
          console.log("Someone bought a ticket!");
      });
      

      To add a new listener, we used the on() function that’s a part of the ticketManager object. The on() method is available to all event emitter objects, and since TicketManager inherits from the EventEmitter class, this method is available on all of the TicketManager instance objects.

      The second argument to the on() method is a callback function, written as an arrow function. The code in this function is run after the event is emitted. In this case, we log "Someone bought a ticket!" to the console if a buy event is emitted.

      Now that we set up a listener, let’s use the buy() function so that the event will be emitted. At the end of your file add this:

      event-emitters/firstListener.js

      ...
      
      ticketManager.buy("test@email.com", 20);
      

      This performs the buy method with a user email of test@email.com and a ticket price of 20.

      Save and exit the file.

      Now run this script with node:

      Your console will display this:

      Output

      Someone bought a ticket!

      Your first event listener worked. Let’s see what happens if we buy multiple tickets. Re-open your firstListener.js in your text editor:

      At the end of the file, make another call to the buy() function:

      event-emitters/firstListener.js

      ...
      
      ticketManager.buy("test@email.com", 20);
      ticketManager.buy("test@email.com", 20);
      

      Save and exit the file. Let’s run the script with Node.js to see what happens:

      Your console will display this:

      Output

      Someone bought a ticket! Someone bought a ticket!

      Since the buy() function was called twice, two buy events were emitted. Our listener picked up both.

      Sometimes we’re only interested in listening to the first time an event was fired, as opposed to all the times it’s emitted. Node.js provides an alternative to on() for this case with the once() function.

      Like on(), the once() function accepts the event name as its first argument, and a callback function that’s called when the event is fired as its second argument. Under the hood, when the event is emitted and received by a listener that uses once(), Node.js automatically removes the listener and then executes the code in the callback function.

      Let’s see once() in action by editing firstListener.js:

      At the end of the file, add a new event listener using once() like the following highlighted lines:

      event-emitters/firstListener.js

      const TicketManager = require("./ticketManager");
      
      const ticketManager = new TicketManager(10);
      
      ticketManager.on("buy", () => {
              console.log("Someone bought a ticket!");
      });
      
      ticketManager.buy("test@email.com", 20);
      ticketManager.buy("test@email.com", 20);
      
      ticketManager.once("buy", () => {
          console.log("This is only called once");
      });
      

      Save and exit the file and run this program with node:

      The output is the same as the last time:

      Output

      Someone bought a ticket! Someone bought a ticket!

      While we added a new event listener with once(), it was added after the buy events were emitted. Because of this, the listener didn’t detect these two events. You can’t listen for events that already happened in the past. When you add a listener you can only capture events that come after.

      Let’s add a couple more buy() function calls so we can confirm that the once() listener only reacts one time. Open firstListener.js in your text editor like before:

      Add the following calls at the end of the file:

      event-emitters/firstListener.js

      ...
      
      ticketManager.once("buy", () => {
          console.log("This is only called once");
      });
      
      ticketManager.buy("test@email.com", 20);
      ticketManager.buy("test@email.com", 20);
      

      Save and exit, then execute this program:

      Your output will be:

      Output

      Someone bought a ticket! Someone bought a ticket! Someone bought a ticket! This is only called once Someone bought a ticket!

      The first two lines were from the first two buy() calls before the once() listener was added. Adding a new event listener does not remove previous ones, so the first event listener we added is still active and logs messages.

      Since the event listener with on() was declared before the event listener with once(), we see Someone bought a ticket! before This is only called once. These two lines are both responding to the second-to-last buy event.

      Finally, when the last call to buy() was made, the event emitter only had the first listener that was created with on(). As mentioned earlier, when an event listener created with once() receives an event, it is automatically removed.

      Now that we have added event listeners to detect our emitters, we will see how to capture data with those listeners.

      Step 3 — Capturing Event Data

      So far, you’ve set up event listeners to react to emitted events. The emitted events also pass along data. Let’s see how we can capture the data that accompanies an event.

      We’ll begin by creating some new Node.js modules: an email service and a database service. They’ll be used to simulate sending an email and saving to a database respectively. We’ll then tie them all together with our main Node.js script—index.js.

      Let’s begin by editing our email service module. Open the file in your text editor:

      Our email service consists of a class that contains one method—send(). This method expects the email that’s emitted along with buy events. Add the following code to your file:

      event-emitters/emailService.js

      class EmailService {
          send(email) {
              console.log(`Sending email to ${email}`);
          }
      }
      
      module.exports = EmailService
      

      This code creates an EmailService class that contains a send() function. In lieu of sending an actual email, it uses template literals to log a message to the console that would contain the email address of someone buying a ticket. Save and exit before moving on.

      Let’s set up the database service. Open databaseService.js with your text editor:

      The database service saves our purchase data to a database via its save() method. Add the following code to databaseService.js:

      event-emitters/databaseService.js

      class DatabaseService {
          save(email, price, timestamp) {
              console.log(`Running query: INSERT INTO orders VALUES (email, price, created) VALUES (${email}, ${price}, ${timestamp})`);
          }
      }
      
      module.exports = DatabaseService
      

      This creates a new DatabaseService class that contains a single save() method. Similar to the email service’s send() method, the save() function uses the data that accompanies a buy event, logging it to the console instead of actually inserting it into a database. This method needs the email of the purchaser, price of the ticket, and the time the ticket was purchased to function. Save and exit the file.

      We will use our last file to bring the TicketManager, EmailService, and DatabaseService together. It will set up a listener for the buy event and will call the email service’s send() function and the database service’s save() function.

      Open the index.js file in your text editor:

      The first thing to do is import the modules we are using:

      event-emitters/index.js

      const TicketManager = require("./ticketManager");
      const EmailService = require("./emailService");
      const DatabaseService = require("./databaseService");
      

      Next, let’s create objects for the classes we imported. We’ll set a low ticket supply of three for this demonstration:

      event-emitters/index.js

      const TicketManager = require("./ticketManager");
      const EmailService = require("./emailService");
      const DatabaseService = require("./databaseService");
      
      const ticketManager = new TicketManager(3);
      const emailService = new EmailService();
      const databaseService = new DatabaseService();
      

      We can now set up our listener with the instantiated objects. Whenever someone buys a ticket, we want to send them an email as well as saving the data to a database. Add the following listener to your code:

      event-emitters/index.js

      const TicketManager = require("./ticketManager");
      const EmailService = require("./emailService");
      const DatabaseService = require("./databaseService");
      
      const ticketManager = new TicketManager(3);
      const emailService = new EmailService();
      const databaseService = new DatabaseService();
      
      
      ticketManager.on("buy", (email, price, timestamp) => {
          emailService.send(email);
          databaseService.save(email, price, timestamp);
      });
      

      Like before, we add a listener with the on() method. The difference this time is that we have three arguments in our callback function. Each argument corresponds to the data that the event emits. As a reminder, this is the emitter code in the buy() function:

      event-emitters/ticketManager.js

      this.emit("buy", email, price, Date.now());
      

      In our callback, we first capture the email from the emitter, then the price, and finally the Date.now() data, which we capture as timestamp.

      When our listener detects a buy event, it will call the send() function from the emailService object as well as the save() function from databaseService. To test that this setup works, let’s make a call to the buy() function at the end of the file:

      event-emitters/index.js

      ...
      
      ticketManager.buy("test@email.com", 10);
      

      Save and exit the editor. Now let’s run this script with node and observe what comes next. In your terminal enter:

      You will see the following output:

      Output

      Sending email to test@email.com Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 10, 1588720081832)

      The data was successfully captured and returned in our callback function. With this knowledge, you can set up listeners for a variety of emitters with different event names and data. However, there are certain nuances to handling error events with event emitters.

      Next, let’s look at how to handle error events and what standards we should follow in doing so.

      Step 4 — Handling Error Events

      If an event emitter cannot perform its action, it should emit an event to signal that the action failed. In Node.js, the standard way for an event emitter to signal failure is by emitting an error event.

      An error event must have its name set to error. It must also be accompanied by an Error object. Let’s see a practical example of emitting an error event.

      Our ticket manager decreases the supply by one every time the buy() function is called. Right now there’s nothing stopping it from selling more tickets than it has available. Let’s modify the buy() function so that if the ticket supply reaches 0 and someone wants to buy a ticket, we emit an error indicating that we’re out of stock.

      Open ticketManager.js in your text editor once more:

      Now edit the buy() function as follows:

      event-emitters/ticketManager.js

      ...
      
      buy(email, price) {
          if (this.supply > 0) {
              this.supply—;
              this.emit("buy", email, price, Date.now());
              return;
          }
      
          this.emit("error", new Error("There are no more tickets left to purchase"));
      }
      ...
      

      We’ve added an if statement that allows a ticket purchase if our supply is greater than zero. If we don’t have any other tickets, we’ll emit an error event. The error event is emitted with a new Error object that contains a description of why we’re throwing this error.

      Save and exit the file. Let’s try to throw this error in our index.js file. Right now, we only buy one ticket. We instantiated the ticketManager object with three tickets, so we should get an error if we try to buy four tickets.

      Edit index.js with your text editor:

      Now add the following lines at the end of the file so we can buy four tickets in total:

      event-emitters/index.js

      ...
      
      ticketManager.buy("test@email.com", 10);
      ticketManager.buy("test@email.com", 10);
      ticketManager.buy("test@email.com", 10);
      ticketManager.buy("test@email.com", 10);
      

      Save and exit the editor.

      Let’s execute this file to see what happens:

      Your output will be:

      Output

      Sending email to test@email.com Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 10, 1588724932796) Sending email to test@email.com Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 10, 1588724932812) Sending email to test@email.com Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 10, 1588724932812) events.js:196 throw er; // Unhandled 'error' event ^ Error: There are no more tickets left to purchase at TicketManager.buy (/home/sammy/event-emitters/ticketManager.js:16:28) at Object.<anonymous> (/home/sammy/event-emitters/index.js:17:15) at Module._compile (internal/modules/cjs/loader.js:1128:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:10) at Module.load (internal/modules/cjs/loader.js:983:32) at Function.Module._load (internal/modules/cjs/loader.js:891:14) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:71:12) at internal/main/run_main_module.js:17:47 Emitted 'error' event on TicketManager instance at: at TicketManager.buy (/home/sammy/event-emitters/ticketManager.js:16:14) at Object.<anonymous> (/home/sammy/event-emitters/index.js:17:15) [... lines matching original stack trace ...] at internal/main/run_main_module.js:17:47

      The first three buy events were processed correctly, but on the fourth buy event our program crashed. Let’s examine the beginning of the error message:

      Output

      ... events.js:196 throw er; // Unhandled 'error' event ^ Error: There are no more tickets left to purchase at TicketManager.buy (/home/sammy/event-emitters/ticketManager.js:16:28) ...

      The first two lines highlight that an error was thrown. The comment says "Unhandled 'error' event". If an event emitter emits an error and we did not attach a listener for error events, Node.js throws the error and crashes the program.

      It’s considered best practice to always listen for error events if you’re listening to an event emitter. If you do not set up a listener for errors, your entire application will crash if one is emitted. With an error listener, you can gracefully handle it.

      To follow best practices, let’s set up a listener for errors. Re-open the index.js file:

      Add a listener before we start buying tickets. Remember, a listener can only respond to events that are emitted after it was added. Insert an error listener like this:

      event-emitters/index.js

      ...
      
      ticketManager.on("error", (error) => {
          console.error(`Gracefully handling our error: ${error}`);
      });
      
      ticketManager.buy("test@email.com", 10);
      ticketManager.buy("test@email.com", 10);
      ticketManager.buy("test@email.com", 10);
      ticketManager.buy("test@email.com", 10);
      

      When we receive an error event, we will log it to the console with console.error().

      Save and leave nano. Re-run the script to see our error event handled correctly:

      This time the following output will be displayed:

      Output

      Sending email to test@email.com Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 10, 1588726293332) Sending email to test@email.com Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 10, 1588726293348) Sending email to test@email.com Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email.com, 10, 1588726293348) Gracefully handling our error: Error: There are no more tickets left to purchase

      From the last line, we confirm that our error event is being handled by our second listener, and the Node.js process did not crash.

      Now that we’ve covered the concepts of sending and listening to events, let’s look at some additional functionality that can be used to manage event listeners.

      Step 5 — Managing Events Listeners

      Event emitters come with some mechanisms to monitor and control how many listeners are subscribed to an event. To get an overview of how many listeners are processing an event, we can use the listenerCount() method that’s included in every object.

      The listenerCount() method accepts one argument: the event name you want the count for. Let’s see it in action in index.js.

      Open the file with nano or your text editor of choice:

      You currently call the buy() function four times. Remove those four lines. When you do, add these two new lines so that your entire index.js looks like this:

      event-emitters/index.js

      const TicketManager = require("./ticketManager");
      const EmailService = require("./emailService");
      const DatabaseService = require("./databaseService");
      
      const ticketManager = new TicketManager(3);
      const emailService = new EmailService();
      const databaseService = new DatabaseService();
      
      ticketManager.on("buy", (email, price, timestamp) => {
          emailService.send(email);
          databaseService.save(email, price, timestamp);
      });
      
      ticketManager.on("error", (error) => {
          console.error(`Gracefully handling our error: ${error}`);
      });
      
      console.log(`We have ${ticketManager.listenerCount("buy")} listener(s) for the buy event`);
      console.log(`We have ${ticketManager.listenerCount("error")} listener(s) for the error event`);
      

      We’ve removed the calls to buy() from the previous section and instead logged two lines to the console. The first log statement uses the listenerCount() function to display the number of listeners for the buy() event. The second log statement shows how many listeners we have for the error event.

      Save and exit. Now run your script with the node command:

      You’ll get this output:

      Output

      We have 1 listener(s) for the buy event We have 1 listener(s) for the error event

      We only used the on() function once for the buy event and once for the error event, so this output matches our expectations.

      Next, we’ll use the listenerCount() as we remove listeners from an event emitter. We may want to remove event listeners when the period of an event no longer applies. For example, if our ticket manager was being used for a specific concert, as the concert comes to an end you would remove the event listeners.

      In Node.js we use the off() function to remove event listeners from an event emitter. The off() method accepts two arguments: the event name and the function that’s listening to it.

      Note: Similar to the on() function, Node.js aliases the off() method with removeListener(). They both do the same thing, with the same arguments. In this tutorial, we will continue to use off().

      For the second argument of the off() function, we need a reference to the callback that’s listening to an event. Therefore, to remove an event listener, its callback must be saved to some variable or constant. As it stands, we cannot remove the current event listeners for buy or error with the off() function.

      To see off() in action, let’s add a new event listener that we will remove in subsequent calls. First, let’s define the callback in a variable so that we can reference it in off() later. Open index.js with nano:

      At the end of index.js add this:

      event-emitters/index.js

      ...
      
      const onBuy = () => {
          console.log("I will be removed soon");
      };
      

      Now add another event listener for the buy event:

      event-emitters/index.js

      ...
      
      ticketManager.on("buy", onBuy);
      

      To be sure that we successfully added that event listener, let’s print the listener count for buy and call the buy() function.

      event-emitters/index.js

      ...
      
      console.log(`We added a new event listener bringing our total count for the buy event to: ${ticketManager.listenerCount("buy")}`);
      ticketManager.buy("test@email", 20);
      

      Save and exit the file, then run the program:

      The following message will be displayed in the terminal:

      Output

      We have 1 listener(s) for the buy event We have 1 listener(s) for the error event We added a new event listener bringing our total count for the buy event to: 2 Sending email to test@email Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588814306693) I will be removed soon

      From the output, we see our log statement from when we added the new event listener. We then call the buy() function, and both listeners react to it. The first listener sent the email and saved data to the database, and then our second listener printed its message I will be removed soon to the screen.

      Let’s now use the off() function to remove the second event listener. Re-open the file in nano:

      Now add the following off() call to the end of the file. You will also add a log statement to confirm the number of listeners, and make another call to buy():

      event-emitters/index.js

      ...
      
      ticketManager.off("buy", onBuy);
      
      console.log(`We now have: ${ticketManager.listenerCount("buy")} listener(s) for the buy event`);
      ticketManager.buy("test@email", 20);
      

      Note how the onBuy variable was used as the second argument of off(). Save and exit the file.

      Now run this with node:

      The previous output will remain unchanged, but this time we will find the new log line we added confirming we have one listener once more. When buy() is called again, we will only see the output of the callback used by the first listener:

      Output

      We have 1 listener(s) for the buy event We have 1 listener(s) for the error event We added a new event listener bringing our total count for the buy event to: 2 Sending email to test@email Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588816352178) I will be removed soon We now have: 1 listener(s) for the buy event Sending email to test@email Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588816352178)

      If we wanted to remove all events with off(), we could use the removeAllListeners() function. This function accepts one argument: the name of the event we want to remove listeners for.

      Let’s use this function at the end of the file to take off the first event listener we added for the buy event. Open the index.js file once more:

      You’ll first remove all the listeners with removeAllListeners(). You’ll then log a statement with the listener count using the listenerCount() function. To confirm it’s no longer listening, you’ll buy another ticket. When the event is emitted, nothing will react to it.

      Enter the following code at the end of the file:

      event-emitters/index.js

      ...
      
      ticketManager.removeAllListeners("buy");
      console.log(`We have ${ticketManager.listenerCount("buy")} listeners for the buy event`);
      ticketManager.buy("test@email", 20);
      console.log("The last ticket was bought");
      

      Save and exit the file.

      Now let’s execute our code with the node command:

      Our final output is:

      We have 1 listener(s) for the buy event
      We have 1 listener(s) for the error event
      We added a new event listener bringing our total count for the buy event to: 2
      Sending email to test@email
      Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588817058088)
      I will be removed soon
      We now have: 1 listener(s) for the buy event
      Sending email to test@email
      Running query: INSERT INTO orders VALUES (email, price, created) VALUES (test@email, 20, 1588817058088)
      We have 0 listeners for the buy event
      The last ticket was bought
      

      After removing all listeners, we no longer send emails or save to the database for ticket purchases.

      Conclusion

      In this tutorial, you learned how to use Node.js event emitters to trigger events. You emitted events with the emit() function of an EventEmitter object, then listened to events with the on() and once() functions to execute code every time the event is triggered. You also added a listener for an error event and monitored and managed listeners with the listenerCount() function.

      With callbacks and promises, our ticket manager system would need to be integrated with the email and database service modules to get the same functionality. Since we used event emitters, the event was decoupled from the implementations. Furthermore, any module with access to the ticket manager can observe its event and react to it. If you want Node.js modules, internal or external, to observe what’s happening with your object, consider making it an event emitter for scalability.

      To learn more about events in Node.js, you can read the Node.js documentation. If you’d like to continue learning Node.js, you can return to the How To Code in Node.js series, or browse programming projects and setups on our Node topic page.



      Source link

      Comment configurer Apache HTTP avec MPM Event et PHP-FPM sur Ubuntu 18.04


      L’auteur a choisi le COVID-19 Relief Fund pour recevoir un don dans le cadre du programme Write for DOnations.

      Introduction

      Au fil des années, le serveur Web Apache HTTP a évolué. Il fonctionne aujourd’hui dans différents environnements et répond à différents besoins. Comme tout serveur Web, un des problèmes importants qu’Apache HTTP doit résoudre est de savoir de quelle manière gérer différents processus pour servir une requête de protocole http. Cela implique d’ouvrir une socket, de traiter la requête, de maintenir la connexion ouverte pendant une certaine période, de gérer les nouveaux événements se produisant via cette connexion et de renvoyer le contenu produit par un programme créé dans un langage particulier (comme PHP, Perl ou Python). Ces tâches sont effectuées et contrôlées par un Multi-Processing Module (MPM).

      Apache HTTP est livré avec trois MPM différents :

      • Pre-fork : un nouveau processus est créé pour chaque connexion entrante atteignant le serveur. Chaque processus est isolé des autres. Donc, même s’ils effectuent des appels identiques à un certain moment, ils ne partagent aucune mémoire. Il s’agit d’un moyen sûr d’exécuter des applications liées à des bibliothèques qui ne prennent pas en charge le threading, généralement des applications ou des bibliothèques plus anciennes.
      • Worker : un processus parent a pour responsabilité de lancer un pool de processus enfants, certains écoutant les nouvelles connexions entrantes et d’autres servant le contenu demandé. Chaque processus est threadé (un seul thread pour gérer une connexion), donc un processus peut gérer plusieurs requêtes simultanément. Cette méthode de traitement des connexions permet une meilleure utilisation des ressources, tout en maintenant la stabilité. Cela est permis grâce au pool de processus disponibles, qui dispose souvent de threads gratuits prêts à servir de nouvelles connexions immédiatement.
      • Event : en fonction du travailleur, ce MPM va plus loin en optimisant la façon dont le processus parent planifie les tâches pour les processus enfants et les threads qui y sont associés. Par défaut, une connexion reste ouverte pendant 5 secondes. Si aucun nouvel événement ne se produit, elle se ferme. Il s’agit de la valeur par défaut de la directive keep-alive, qui conserve le thread qui lui est associé. Le MPM Event permet au processus de gérer les threads afin que certains soient libres de gérer les nouvelles connexions entrantes tandis que d’autres sont liés aux connexions actives. En autorisant ainsi la redistribution des tâches affectées aux threads, l’utilisation des ressources et les performances sera optimisée.

      Le module MPM Event est un module de multi-traitement rapide disponible sur le serveur Web Apache HTTP.

      PHP-FPM est le Gestionnaire de processus FastCGI pour PHP. Le protocole FastCGI est basé sur la Common Gateway Interface (CGI), un protocole qui se situe entre les applications et les serveurs Web comme Apache HTTP. Cela permet aux développeurs d’écrire des applications indépendamment du comportement des serveurs Web. Les programmes exécutent leurs processus de manière indépendante et transmettent leur produit au serveur Web via ce protocole. Chaque nouvelle connexion qui devra être traitée par une application qui créera un nouveau processus.

      En combinant MPM Event dans Apache HTTP et le Gestionnaire de processus PHP FastCGI (PHP-FPM), le chargement d’un site Web se fait plus rapidement et il pourra gérer un plus grand nombre de connexions simultanées tout en utilisant moins de ressources.

      Dans ce tutoriel, vous améliorerez les performances de la pile LAMP en remplaçant le module de multi-traitement par défaut de pré-fork par un événement et en utilisant le gestionnaire de processus PHP-FPM pour gérer le code PHP au lieu du mod_php traditionnel d’Apache HTTP.

      Conditions préalables

      Avant de commencer ce guide, vous aurez besoin des éléments suivants :

      Étape 1 – Changement du module multi-traitement

      Ubuntu hérite de scripts pour activer ou désactiver les modules HTTP Apache de sa distribution parente, Debian. Vous utiliserez cet ensemble d’outils dans cette étape pour désactiver le module Pre-fork et activer le module Event.

      Au cours cette étape, vous allez arrêter Apache HTTP, désactiver le module PHP 7.2 lié au module Pre-fork, puis désactiver Pre-fork pour activer immédiatement le module Event.

      Vous allez tout d’abord arrêter le service HTTP Apache :

      • sudo systemctl stop apache2

      Vous pouvez maintenant désactiver le module PHP 7.2, qui est lié au module Pre-fork :

      Ensuite, désactivez le module Pre-fork MPM :

      • sudo a2dismod mpm_prefork

      Maintenant, activez le module Event MPM :

      Vous avez changé le MPM de pre-fork en event et supprimé la connexion du module PHP 7.2 entre PHP et Apache HTTP. À l’étape suivante, vous installerez le module php-fpm, ainsi que les bibliothèques et modules proxy associés. Vous allez configurer Apache HTTP pour qu’il puisse également communiquer avec PHP.

      Étape 2 – Configuration d’Apache HTTP pour utiliser le Gestionnaire de processus FastCGI

      À ce stade, vous avez changé la méthode de traitement des connexions d’Apache HTTP en déplaçant de Pre-fork MPM à Event. Cependant, au cours du processus, vous avez désactivé le module PHP qui connectait Apache HTTP à tout programme fonctionnant sur PHP.

      Au cours de cette étape, vous allez installer le processeur PHP-FPM pour permettre à Apache HTTP de traiter à nouveau les programmes PHP. Vous installerez également les bibliothèques de dépendances et activerez les modules afin que les deux puissent coopérer avec fluidité et plus rapidement qu’auparavant.

      Tout d’abord, installez php-fpm. La commande suivante installera le package PHP-FPM et activera automatiquement le service php7.2-fpm intégré à systemd, de sorte que le service se lance au démarrage :

      Pour communiquer, Apache HTTP et PHP ont besoin d’une bibliothèque permettant cette capacité. Vous allez maintenant installer libapache2-mod-fcgid. Il peut servir d’interface entre des programmes avec des serveurs Web et il est spécifique à Apache HTTP. Cette communication se produira via une socket UNIX.

      Installez cette bibliothèque :

      • sudo apt install libapache2-mod-fcgid

      Vous avez installé php-fpm et la libapache2-mod-fcgid, mais aucune des deux n’est encore activée.

      Tout d’abord, activez le module php-fpm avec la commande suivante :

      Ensuite, activez le module proxy d’Apache HTTP :

      Pour finir, activez le module proxy FastCGI dans Apache HTTP :

      Note : vous pouvez lire la configuration de cette interaction entre les programmes PHP et Apache HTTP via une socket UNIX avec ce qui suit :

      • cat /etc/apache2/conf-enabled/php7.2-fpm.conf

      Tout est maintenant en place pour démarrer Apache HTTP. Vous allez tout d’abord procéder à un contrôle de configuration :

      • sudo apachectl configtest

      Output

      Syntax OK

      Après cela, vous pouvez redémarrer Apache HTTP, puisqu’il a été démarré automatiquement lors de l’installation de la bibliothèque FastCGI libapache2-mod-fcgid :

      • sudo systemctl restart apache2

      Vous avez installé le module php-fpm, configuré Apache HTTP pour fonctionner avec lui, activé les modules nécessaires pour que le protocole FastCGI fonctionne et démarre les services correspondants.

      Maintenant qu’Apache a activé le module Event MPM et que PHP-FPM est présent et en cours d’exécution, il est temps de vérifier si tout fonctionne comme prévu.

      Étape 3 – Vérification de votre configuration

      Vous allez exécuter ces tests afin de vérifier que les modifications de configuration ont bien été appliquées. Le premier test consiste à vérifier quel module multi-traitement Apache HTTP utilise. Le second à vérifier que PHP utilise le gestionnaire FPM.

      Vérifiez le serveur Apache HTTP en exécutant la commande suivante :

      • sudo apachectl -M | grep 'mpm'

      Votre sortie sera la suivante :

      Output

      mpm_event_module (shared)

      Vous pouvez refaire la même chose pour le module proxy et FastCGI :

      • sudo apachectl -M | grep 'proxy'

      Le résultat sera :

      Output

      proxy_module (shared) proxy_fcgi_module (shared)

      Pour voir la liste complète des modules, vous pouvez supprimer la deuxième partie de la commande après -M.

      Il est maintenant temps de vérifier si PHP utilise le Gestionnaire de processus FastCGI. Pour cela, vous allez écrire un petit script PHP qui vous donnera toutes les informations liées à PHP.

      Exécutez la commande suivante pour écrire un fichier nommé de la manière suivante :

      • sudo nano /var/www/your_domain/info.php

      Ajoutez le contenu suivant dans le fichier info.php :

      info.php

      <?php phpinfo(); ?>
      

      Maintenant, naviguez vers l’URL de votre serveur et ajoutez info.php à la fin comme ceci : http://your_domain/info.php.

      L’entrée API du serveur sera FPM/FastCGI.

      PHP balaye l'entrée de l'API du serveur FPM/FastCGI

      Supprimez le fichier info.php après cette vérification afin qu’aucune information ne puisse être divulguée publiquement sur le serveur :

      • sudo rm /var/www/yourdomain.com/info.php

      Vous avez vérifié l’état de fonctionnement du module MPM, les modules gérant le FastCGI et le traitement du code PHP.

      Conclusion

      Vous avez optimisé votre pile LAMP d’origine,en augmentant ainsi le nombre de connexions pour créer de nouveaux processus HTTP Apache. PHP-FPM permettra de traiter le code PHP plus efficacement et l’utilisation globale des ressources est meilleure.

      Pour plus d’informations sur les différents modules et les projets associés, consultez la documentation sur le projet de serveur Apache HTTP.



      Source link