One place for hosting & domains

      Event

      How To Build A Security Information and Event Management (SIEM) System with Suricata and the Elastic Stack on Ubuntu 20.04


      Introduction

      The previous tutorials in this series guided you through installing, configuring, and running Suricata as an Intrusion Detection (IDS) and Intrusion Prevention (IPS) system. You also learned about Suricata rules and how to create your own.

      In this tutorial you will explore how to integrate Suricata with Elasticsearch, Kibana, and Filebeat to begin creating your own Security Information and Event Management (SIEM) system. SIEM tools are used to collect, aggregate, store, and analyze event data to search for security threats and suspicious activity on your networks and servers.

      The components that you will use to build your own SIEM tool are:

      • Elasticsearch to store, index, correlate, and search the security events that come from your Suricata server.
      • Kibana to display and navigate around the security event logs that are stored in Elasticsearch.
      • Filebeat to parse Suricata’s eve.json log file and send each event to Elasticsearch for processing.
      • Suricata to scan your network traffic for suspicious events, and either log or drop invalid packets.

      First you’ll install and configure Elasticsearch and Kibana with some specific authentication settings. Then you’ll add Filebeat to your Suricata system to send its eve.json logs to Elasticsearch.

      Finally, you’ll learn how to connect to Kibana using SSH and your web browser, and then load and interact with Kibana dashboards that show Suricata’s events and alerts.

      Prerequisites

      If you have been following this tutorial series then you should already have Suricata running on an Ubuntu 20.04 server. This server will be referred to as your Suricata server.

      You will also need a second server to host Elasticsearch and Kibana. This server will be referred to as your Elasticsearch server. It should be an Ubuntu 20.04 server with:

      For the purposes of this tutorial, both servers should be able to communicate using private IP addresses. You can use a VPN like WireGuard to connect your servers, or use a cloud-provider that has private networking between hosts. You can also choose to run Elasticsearch, Kibana, Filebeat, and Suricata on the same server for experimenting.

      Step 1 — Installing Elasticsearch and Kibana

      The first step in this tutorial is to install Elasticsearch and Kibana on your Elasticsearch server. To get started, add the Elastic GPG key to your server with the following command:

      • curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -

      Next, add the Elastic source list to the sources.list.d directory, where apt will search for new sources:

      • echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list

      Now update your server’s package index and install Elasticsearch and Kibana:

      • sudo apt update
      • sudo apt install elasticsearch kibana

      Once you are done installing the packages, find and record your server’s private IP address using the ip address show command:

      You will receive output like the following:

      Output

      lo UNKNOWN 127.0.0.1/8 ::1/128 eth0 UP 159.89.122.115/20 10.20.0.8/16 2604:a880:cad:d0::e56:8001/64 fe80::b832:69ff:fe46:7e5d/64 eth1 UP 10.137.0.5/16 fe80::b883:5bff:fe19:43f3/64

      The private network interface in this output is the highlighted eth1 device, with the IPv4 address 10.137.0.5/16. Your device name, and IP addresses will be different. However, the address will be from the following reserved blocks of addresses:

      • 10.0.0.0 to 10.255.255.255 (10/8 prefix)
      • 172.16.0.0 to 172.31.255.255 (172.16/12 prefix)
      • 192.168.0.0 to 192.168.255.255 (192.168/16 prefix)

      If you would like to learn more about how these blocks are allocated visit the RFC 1918 specification)

      Record the private IP address for your Elasticsearch server (in this case 10.137.0.5). This address will be referred to as your_private_ip in the remainder of this tutorial. Also note the name of the network interface, in this case eth1. In the next part of this tutorial you will configure Elasticsearch and Kibana to listen for connections on the private IP address coming from your Suricata server.

      Step 2 — Configuring Elasticsearch

      Elasticsearch is configured to only accept local connections by default. Additionally, it does not have any authentication enabled, so tools like Filebeat will not be able to send logs to it. In this section of the tutorial you will configure the network settings for Elasticsearch and then enable Elasticsearch’s built-in xpack security module.

      Configuring Elasticsearch Networking

      Since Your Elasticsearch and Suricata servers are separate, you will need to configure Elasticsearch to listen for connections on its private network interface. You will also need to configure your firewall rules to allow access to Elasticsearch on your private network interface.

      Open the /etc/elasticsearch/elasticsearch.yml file using nano or your preferred editor:

      • sudo nano /etc/elasticsearch/elasticsearch.yml

      Find the commented out #network.host: 192.168.0.1 line between lines 50–60 and add a new line after it that configures the network.bind_host setting, as highlighted below:

      /etc/elasticsearch/elasticsearch.yml

      # By default Elasticsearch is only accessible on localhost. Set a different
      # address here to expose this node on the network:
      #
      #network.host: 192.168.0.1
      network.bind_host: ["127.0.0.1", "your_private_ip"]
      #
      # By default Elasticsearch listens for HTTP traffic on the first free port it
      # finds starting at 9200. Set a specific HTTP port here:
      

      Substitute your private IP in place of the your_private_ip address. This line will ensure that Elasticsearch is still available on its local address so that Kibana can reach it, as well as on the private IP address for your server.

      Next, go to the end of the file using the nano shortcut CTRL+v until you reach the end.

      Add the following highlighted lines to the end of the file:

      /etc/elasticsearch/elasticsearch.yml

      . . .
      discovery.type: single-node
      xpack.security.enabled: true
      

      The discovery.type setting allows Elasticsearch to run as a single node, as opposed to in a cluster of other Elasticsearch servers. The xpack.security.enabled setting turns on some of the security features that are included with Elasticsearch.

      Save and close the file when you are done editing it. If you are using nano, you can do so with CTRL+X, then Y and ENTER to confirm.

      Finally, add firewall rules to ensure your Elasticsearch server is reachable on its private network interface. If you followed the prerequisite tutorials and are using the Uncomplicated Firewall (ufw), run the following commands:

      • sudo ufw allow in on eth1
      • sudo ufw allow out on eth1

      Substitute your private network interface in place of eth1 if it uses a different name.

      Next you will start the Elasticsearch daemon and then configure passwords for use with the xpack security module.

      Starting Elasticsearch

      Now that you have configured networking and the xpack security settings for Elasticsearch, you need to start it for the changes to take effect.

      Run the following systemctl command to start Elasticsearch:

      • sudo systemctl start elasticsearch.service

      Once Elasticsearch finishes starting, you can continue to the next section of this tutorial where you will generate passwords for the default users that are built-in to Elasticsearch.

      Configuring Elasticsearch Passwords

      Now that you have enabled the xpack.security.enabled setting, you need to generate passwords for the default Elasticsearch users. Elasticsearch includes a utility in the /usr/share/elasticsearch/bin directory that can automatically generate random passwords for these users.

      Run the following command to cd to the directory and then generate random passwords for all the default users:

      • cd /usr/share/elasticsearch/bin
      • sudo ./elasticsearch-setup-passwords auto

      You will receive output like the following. When prompted to continue, press y and then RETURN or ENTER:

      Initiating the setup of passwords for reserved users elastic,apm_system,kibana,kibana_system,logstash_system,beats_system,remote_monitoring_user.
      The passwords will be randomly generated and printed to the console.
      Please confirm that you would like to continue [y/N]y
      
      
      Changed password for user apm_system
      PASSWORD apm_system = eWqzd0asAmxZ0gcJpOvn
      
      Changed password for user kibana_system
      PASSWORD kibana_system = 1HLVxfqZMd7aFQS6Uabl
      
      Changed password for user kibana
      PASSWORD kibana = 1HLVxfqZMd7aFQS6Uabl
      
      Changed password for user logstash_system
      PASSWORD logstash_system = wUjY59H91WGvGaN8uFLc
      
      Changed password for user beats_system
      PASSWORD beats_system = 2p81hIdAzWKknhzA992m
      
      Changed password for user remote_monitoring_user
      PASSWORD remote_monitoring_user = 85HF85Fl6cPslJlA8wPG
      
      Changed password for user elastic
      PASSWORD elastic = 6kNbsxQGYZ2EQJiqJpgl
      

      You will not be able to run the utility again, so make sure to record these passwords somewhere secure. You will need to use the kibana_system user’s password in the next section of this tutorial, and the elastic user’s password in the Configuring Filebeat step of this tutorial.

      At this point in the tutorial you are finished configuring Elasticsearch. The next section explains how to configure Kibana’s network settings and its xpack security module.

      Step 3 — Configuring Kibana

      In the previous section of this tutorial, you configured Elasticsearch to listen for connections on your Elasticsearch server’s private IP address. You will need to do the same for Kibana so that Filebeats on your Suricata server can reach it.

      First you’ll enable Kibana’s xpack security functionality by generating some secrets that Kibana will use to store data in Elasticsearch. Then you’ll configure Kibana’s network setting and authentication details to connect to Elasticsearch.

      Enabling xpack.security in Kibana

      To get started with xpack security settings in Kibana, you need to generate some encryption keys. Kibana uses these keys to store session data (like cookies), as well as various saved dashboards and views of data in Elasticsearch.

      You can generate the required encryption keys using the kibana-encryption-keys utility that is included in the /usr/share/kibana/bin directory. Run the following to cd to the directory and then generate the keys:

      • cd /usr/share/kibana/bin/
      • sudo ./kibana-encryption-keys generate -q

      The -q flag suppresses the tool’s instructions so that you only receive output like the following:

      Output

      xpack.encryptedSavedObjects.encryptionKey: 66fbd85ceb3cba51c0e939fb2526f585 xpack.reporting.encryptionKey: 9358f4bc7189ae0ade1b8deeec7f38ef xpack.security.encryptionKey: 8f847a594e4a813c4187fa93c884e92b

      Copy your output somewhere secure. You will now add them to Kibana’s /etc/kibana/kibana.yml configuration file.

      Open the file using nano or your preferred editor:

      • sudo nano /etc/kibana/kibana.yml

      Go to the end of the file using the nano shortcut CTRL+v until you reach the end. Paste the three xpack lines that you copied to the end of the file:

      /etc/kibana/kibana.yml

      . . .
      
      # Specifies locale to be used for all localizable strings, dates and number formats.
      # Supported languages are the following: English - en , by default , Chinese - zh-CN .
      #i18n.locale: "en"
      
      xpack.encryptedSavedObjects.encryptionKey: 66fbd85ceb3cba51c0e939fb2526f585
      xpack.reporting.encryptionKey: 9358f4bc7189ae0ade1b8deeec7f38ef
      xpack.security.encryptionKey: 8f847a594e4a813c4187fa93c884e92b
      

      Keep the file open and proceed to the next section where you will configure Kibana’s network settings.

      Configuring Kibana Networking

      To configure Kibana’s networking so that it is available on your Elasticsearch server’s private IP address, find the commented out #server.host: "localhost" line in /etc/kibana/kibana.yml. The line is near the beginning of the file. Add a new line after it with your server’s private IP address, as highlighted below:

      /etc/kibana/kibana.yml

      # Kibana is served by a back end server. This setting specifies the port to use.
      #server.port: 5601
      
      # Specifies the address to which the Kibana server will bind. IP addresses and host names are both valid values.
      # The default is 'localhost', which usually means remote machines will not be able to connect.
      # To allow connections from remote users, set this parameter to a non-loopback address.
      #server.host: "localhost"
      server.host: "your_private_ip"
      

      Substitute your private IP in place of the your_private_ip address.

      Save and close the file when you are done editing it. If you are using nano, you can do so with CTRL+X, then Y and ENTER to confirm.

      Next, you’ll need to configure the username and password that Kibana uses to connect to Elasticsearch.

      Configuring Kibana Credentials

      There are two ways to set the username and password that Kibana uses to authenticate to Elasticsearch. The first is to edit the /etc/kibana/kibana.yml configuration file and add the values there. The second method is to store the values in Kibana’s keystore, which is an obfuscated file that Kibana can use to store secrets.

      We’ll use the keystore method in this tutorial since it avoids editing Kibana’s configuration file directly

      If you prefer to edit the file instead, the settings to configure in it are elasticsearch.username and elasticsearch.password.

      If you choose to edit the configuration file, skip the rest of the steps in this section.

      To add a secret to the keystore using the kibana-keystore utility, first cd to the /usr/share/kibana/bin directory. Next, run the following command to set the username for Kibana:

      • sudo ./kibana-keystore add elasticsearch.username

      You will receive a prompt like the following:

      Username Entry

      Enter value for elasticsearch.username: *************
      

      Enter kibana_system when prompted, either by copying and pasting, or typing the username carefully. Each character that you type will be masked with an * asterisk character. Press ENTER or RETURN when you are done entering the username.

      Now repeat the same command for the password. Be sure to copy the password for the kibana_system user that you generated in the previous section of this tutorial. For reference, in this tutorial the example password is 1HLVxfqZMd7aFQS6Uabl.

      Run the following command to set the password:

      • sudo ./kibana-keystore add elasticsearch.password

      When prompted, paste the password to avoid any transcription errors:

      Password Entry

      Enter value for elasticsearch.password: ********************
      

      Starting Kibana

      Now that you have configured networking and the xpack security settings for Kibana, as well as added credentials to the keystore, you need to start it for the changes to take effect.

      Run the following systemctl command to restart Kibana:

      • sudo systemctl start kibana.service

      Once Kibana starts, you can continue to the next section of this tutorial where you will configure Filebeat on your Suricata server to send its logs to Elasticsearch.

      Step 4 — Installing Filebeat

      Now that your Elasticsearch and Kibana processes are configured with the correct network and authentication settings, the next step is to install and set up Filebeat on your Suricata server.

      To get started installing Filebeat, add the Elastic GPG key to your Suricata server with the following command:

      • curl -fsSL https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -

      Next, add the Elastic source list to the sources.list.d directory, where apt will search for new sources:

      • echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list

      Now update the server’s package index and install the Filebeat package:

      • sudo apt update
      • sudo apt install filebeat

      Next you’ll need to configure Filebeat to connect to both Elasticsearch and Kibana. Open the /etc/filebeat/filebeat.yml configuration file using nano or your preferred editor:

      • sudo nano /etc/filebeat/filebeat.yml

      Find the Kibana section of the file around line 100. Add a line after the commented out #host: "localhost:5601" line that points to your Kibana instance’s private IP address and port:

      /etc/filebeat/filebeat.yml

      . . .
      # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API.
      # This requires a Kibana endpoint configuration.
      setup.kibana:
      
        # Kibana Host
        # Scheme and port can be left out and will be set to the default (http and 5601)
        # In case you specify and additional path, the scheme is required: http://localhost:5601/path
        # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601
        #host: "localhost:5601"
        host: "your_private_ip:5601"
      
      . . .
      

      This change will ensure that Filebeat can connect to Kibana in order to create the various SIEM indices, dashboards, and processing pipelines in Elasticsearch to handle your Suricata logs.

      Next, find the Elasticsearch Output section of the file around line 130 and edit the hosts, username, and password settings to match the values for your Elasticsearch server:

      output.elasticsearch:
        # Array of hosts to connect to.
        hosts: ["your_private_ip:9200"]
      
        # Protocol - either `http` (default) or `https`.
        #protocol: "https"
      
        # Authentication credentials - either API key or username/password.
        #api_key: "id:api_key"
        username: "elastic"
        password: "6kNbsxQGYZ2EQJiqJpgl"
      
      . . .
      

      Substitute in your Elasticsearch server’s private IP address on the hosts line in place of the your_private_ip value. Uncomment the username field and leave it set to the elastic user. Change the password field from changeme to the password for the elastic user that you generated in the Configuring Elasticsearch Passwords section of this tutorial.

      Save and close the file when you are done editing it. If you are using nano, you can do so with CTRL+X, then Y and ENTER to confirm.

      Next, enable Filebeats’ built-in Suricata module with the following command:

      • sudo filebeat modules enable suricata

      Now that Filebeat is configured to connect to Elasticsearch and Kibana, with the Suricata module enabled, the next step is to load the SIEM dashboards and pipelines into Elasticsearch.

      Run the filebeat setup command. It may take a few minutes to load everything:

      Once the command finishes you should receive output like the following:

      Output

      Overwriting ILM policy is disabled. Set `setup.ilm.overwrite: true` for enabling. Index setup finished. Loading dashboards (Kibana must be running and reachable) Loaded dashboards Setting up ML using setup --machine-learning is going to be removed in 8.0.0. Please use the ML app instead. See more: https://www.elastic.co/guide/en/machine-learning/current/index.html It is not possible to load ML jobs into an Elasticsearch 8.0.0 or newer using the Beat. Loaded machine learning job configurations Loaded Ingest pipelines

      If there are no errors, use the systemctl command to start Filebeat. It will begin sending events from Suricata’s eve.json log to Elasticsearch once it is running.

      • sudo systemctl start filebeat.service

      Now that you have Filebeat, Kibana, and Elasticsearch configured to process your Suricata logs, the last step in this tutorial is to connect to Kibana and explore the SIEM dashboards.

      Step 5 — Navigating Kibana’s SIEM Dashboards

      Kibana is the graphical component of the Elastic stack. You will use Kibana with your browser to explore Suricata’s event and alert data. Since you configured Kibana to only be available via your Elasticsearch server’s private IP address, you will need to use an SSH tunnel to connect to Kibana.

      Connecting to Kibana with SSH

      SSH has an option -L that lets you forward network traffic on a local port over its connection to a remote IP address and port on a server. You will use this option to forward traffic from your browser to your Kibana instance.

      On Linux, macOS, and updated versions of Windows 10 and higher, you can use the built-in SSH client to create the tunnel. You will use this command each time you want to connect to Kibana. You can close this connection at any time and then run the SSH command again to re-establish the tunnel.

      Run the following command in a terminal on your local desktop or laptop computer to create the SSH tunnel to Kibana:

      • ssh -L 5601:your_private_ip:5601 sammy@203.0.113.5 -N

      The various arguments to SSH are:

      • The -L flag forwards traffic to your local system on port 5601 to the remote server.
      • The your_private_ip:5601 portion of the command specifies the service on your Elasticsearch server where your traffic will be fowarded to. In this case that service is Kibana. Be sure to substitute your Elasticsearch server’s private IP address in place of your_private_ip
      • The 203.11.0.5 address is the public IP address that you use to connect to and administer your server. Substitute your Elasticsearch server’s public IP address in its place.
      • The -N flag instructs SSH to not run a command like an interactive /bin/bash shell, and instead just hold the connection open. It is generally used when forwarding ports like in this example.

      If you would like to close the tunnel at any time, press CTRL+C.

      On Windows your terminal should resemble the following screenshot:

      Note: You may be prompted to enter a password if you are not using an SSH key. Type or paste it into the prompt and press ENTER or RETURN.

      Screenshot of Windows Command Prompt Showing SSH Command to Port Forward to Kibana

      On macOS and Linux your terminal will be similar to the following screenshot:

      Screenshot of Windows Command Prompt Showing SSH Command to Port Forward to Kibana

      Once you have connected to your Elasticsearch server over SSH with the port forward in place, open your browser and visit http://127.0.0.1:5601. You will be redirected to Kibana’s login page:

      Screenshot of a Browser on Kibana's Login Page

      If your browser cannot connect to Kibana you will receive a message like the following in your terminal:

      Output

      channel 3: open failed: connect failed: No route to host

      This error indicates that your SSH tunnel is unable to reach the Kibana service on your server. Ensure that you have specified the correct private IP address for your Elasticsearch server and reload the page in your browser.

      Log in to your Kibana server using elastic for the Username, and the password that you copied earlier in this tutorial for the user.

      Browsing Kibana SIEM Dashboards

      Once you are logged into Kibana you can explore the Suricata dashboards that Filebeat configured for you.

      In the search field at the top of the Kibana Welcome page, input the search terms type:dashboard suricata. This search will return two results: the Suricata Events and Suricata Alerts dashboards per the following screenshot:

      Screenshot of a Browser Using Kibana's Global Search Box to Locate Suricata Dashboards

      Click the [Filebeat Suricata] Events Overview result to visit the Kibana dashboard that shows an overview of all logged Suricata events:

      Screenshot of a Browser on Kibana's Suricata Events Dashboard

      To visit the Suricata Alerts dashboard, repeat the search or click the Alerts link that is included in the Events dashboard. Your page should resemble the following screenshot:

      Screenshot of a Browser on Kibana's Suricata Alerts Dashboard

      If you would like to inspect the events and alerts that each dashboard displays, scroll to the bottom of the page where you will find a table that lists each event and alert. You can expand each entry to view the original log entry from Suricata, and examine in detail the various fields like source and destination IPs for an alert, the attack type, Suricata signature ID, and others.

      Kibana also has a built-in set of Security dashboards that you can access using the menu on the left side of the browser window. Navigate to the Network dashboard for an overview of events displayed on a map, as well as aggregate data about events on your network. Your dashboard should resemble the following screenshot:

      Screenshot of a Browser on Kibana's Security -> Network Dashboard

      You can scroll to the bottom of the Network dashboard for a table that lists all of the events that match your specified search timeframe. You can also examine each event in detail, or select an event to generate a Kibana timeline, that you can then use to investigate specific traffic flows, alerts, or community IDs.

      Conclusion

      In this tutorial you installed and configured Elasticsearch and Kibana on a standalone server. You configured both tools to be available on a private IP address. You also configured Elasticsearch and Kibana’s authentication settings using the xpack security module that is included with each tool.

      After completing the Elasticsearch and Kibana configuration steps, you also installed and configured Filebeat on your Suricata server. You used Filebeat to populate Kibana’s dashboards and start sending Suricata logs to Elasticsearch.

      Finally, you created an SSH tunnel to your Elasticsearch server and logged into Kibana. You located the new Suricata Events and Alerts dashboards, as well as the Network dashboard.

      The last tutorial in this series will guide you through using Kibana’s SIEM functionality to process your Suricata alerts. In it you will explore how to create cases to track specific alerts, timelines to correlate network flows, and rules to match specific Suricata events that you would like to track or analyze in more detail.



      Source link

      Understanding the Event Loop, Callbacks, Promises, and Async/Await in JavaScript


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

      Introduction

      In the early days of the internet, websites often consisted of static data in an HTML page. But now that web applications have become more interactive and dynamic, it has become increasingly necessary to do intensive operations like make external network requests to retrieve API data. To handle these operations in JavaScript, a developer must use asynchronous programming techniques.

      Since JavaScript is a single-threaded programming language with a synchronous execution model that processes one operation after another, it can only process one statement at a time. However, an action like requesting data from an API can take an indeterminate amount of time, depending on the size of data being requested, the speed of the network connection, and other factors. If API calls were performed in a synchronous manner, the browser would not be able to handle any user input, like scrolling or clicking a button, until that operation completes. This is known as blocking.

      In order to prevent blocking behavior, the browser environment has many Web APIs that JavaScript can access that are asynchronous, meaning they can run in parallel with other operations instead of sequentially. This is useful because it allows the user to continue using the browser normally while the asynchronous operations are being processed.

      As a JavaScript developer, you need to know how to work with asynchronous Web APIs and handle the response or error of those operations. In this article, you will learn about the event loop, the original way of dealing with asynchronous behavior through callbacks, the updated ECMAScript 2015 addition of promises, and the modern practice of using async/await.

      Note: This article is focused on client-side JavaScript in the browser environment. The same concepts are generally true in the Node.js environment, however Node.js uses its own C++ APIs as opposed to the browser’s Web APIs. For more information on asynchronous programming in Node.js, check out How To Write Asynchronous Code in Node.js.

      The Event Loop

      This section will explain how JavaScript handles asynchronous code with the event loop. It will first run through a demonstration of the event loop at work, and will then explain the two elements of the event loop: the stack and the queue.

      JavaScript code that does not use any asynchronous Web APIs will execute in a synchronous manner—one at a time, sequentially. This is demonstrated by this example code that calls three functions that each print a number to the console:

      // Define three example functions
      function first() {
        console.log(1)
      }
      
      function second() {
        console.log(2)
      }
      
      function third() {
        console.log(3)
      }
      

      In this code, you define three functions that print numbers with console.log().

      Next, write calls to the functions:

      // Execute the functions
      first()
      second()
      third()
      

      The output will be based on the order the functions were called—first(), second(), then third():

      Output

      1 2 3

      When an asynchronous Web API is used, the rules become more complicated. A built-in API that you can test this with is setTimeout, which sets a timer and performs an action after a specified amount of time. setTimeout needs to be asynchronous, otherwise the entire browser would remain frozen during the waiting, which would result in a poor user experience.

      Add setTimeout to the second function to simulate an asynchronous request:

      // Define three example functions, but one of them contains asynchronous code
      function first() {
        console.log(1)
      }
      
      function second() {
        setTimeout(() => {
          console.log(2)
        }, 0)
      }
      
      function third() {
        console.log(3)
      }
      

      setTimeout takes two arguments: the function it will run asynchronously, and the amount of time it will wait before calling that function. In this code you wrapped console.log in an anonymous function and passed it to setTimeout, then set the function to run after 0 milliseconds.

      Now call the functions, as you did before:

      // Execute the functions
      first()
      second()
      third()
      

      You might expect with a setTimeout set to 0 that running these three functions would still result in the numbers being printed in sequential order. But because it is asynchronous, the function with the timeout will be printed last:

      Output

      1 3 2

      Whether you set the timeout to zero seconds or five minutes will make no difference—the console.log called by asynchronous code will execute after the synchronous top-level functions. This happens because the JavaScript host environment, in this case the browser, uses a concept called the event loop to handle concurrency, or parallel events. Since JavaScript can only execute one statement at a time, it needs the event loop to be informed of when to execute which specific statement. The event loop handles this with the concepts of a stack and a queue.

      Stack

      The stack, or call stack, holds the state of what function is currently running. If you’re unfamiliar with the concept of a stack, you can imagine it as an array with “Last in, first out” (LIFO) properties, meaning you can only add or remove items from the end of the stack. JavaScript will run the current frame (or function call in a specific environment) in the stack, then remove it and move on to the next one.

      For the example only containing synchronous code, the browser handles the execution in the following order:

      • Add first() to the stack, run first() which logs 1 to the console, remove first() from the stack.
      • Add second() to the stack, run second() which logs 2 to the console, remove second() from the stack.
      • Add third() to the stack, run third() which logs 3 to the console, remove third() from the stack.

      The second example with setTimout looks like this:

      • Add first() to the stack, run first() which logs 1 to the console, remove first() from the stack.
      • Add second() to the stack, run second().
        • Add setTimeout() to the stack, run the setTimeout() Web API which starts a timer and adds the anonymous function to the queue, remove setTimeout() from the stack.
      • Remove second() from the stack.
      • Add third() to the stack, run third() which logs 3 to the console, remove third() from the stack.
      • The event loop checks the queue for any pending messages and finds the anonymous function from setTimeout(), adds the function to the stack which logs 2 to the console, then removes it from the stack.

      Using setTimeout, an asynchronous Web API, introduces the concept of the queue, which this tutorial will cover next.

      Queue

      The queue, also referred to as message queue or task queue, is a waiting area for functions. Whenever the call stack is empty, the event loop will check the queue for any waiting messages, starting from the oldest message. Once it finds one, it will add it to the stack, which will execute the function in the message.

      In the setTimeout example, the anonymous function runs immediately after the rest of the top-level execution, since the timer was set to 0 seconds. It’s important to remember that the timer does not mean that the code will execute in exactly 0 seconds or whatever the specified time is, but that it will add the anonymous function to the queue in that amount of time. This queue system exists because if the timer were to add the anonymous function directly to the stack when the timer finishes, it would interrupt whatever function is currently running, which could have unintended and unpredictable effects.

      Note: There is also another queue called the job queue or microtask queue that handles promises. Microtasks like promises are handled at a higher priority than macrotasks like setTimeout.

      Now you know how the event loop uses the stack and queue to handle the execution order of code. The next task is to figure out how to control the order of execution in your code. To do this, you will first learn about the original way to ensure asynchronous code is handled correctly by the event loop: callback functions.

      Callback Functions

      In the setTimeout example, the function with the timeout ran after everything in the main top-level execution context. But if you wanted to ensure one of the functions, like the third function, ran after the timeout, then you would have to use asynchronous coding methods. The timeout here can represent an asynchronous API call that contains data. You want to work with the data from the API call, but you have to make sure the data is returned first.

      The original solution to dealing with this problem is using callback functions. Callback functions do not have special syntax; they are just a function that has been passed as an argument to another function. The function that takes another function as an argument is called a higher-order function. According to this definition, any function can become a callback function if it is passed as an argument. Callbacks are not asynchronous by nature, but can be used for asynchronous purposes.

      Here is a syntactic code example of a higher-order function and a callback:

      // A function
      function fn() {
        console.log('Just a function')
      }
      
      // A function that takes another function as an argument
      function higherOrderFunction(callback) {
        // When you call a function that is passed as an argument, it is referred to as a callback
        callback()
      }
      
      // Passing a function
      higherOrderFunction(fn)
      

      In this code, you define a function fn, define a function higherOrderFunction that takes a function callback as an argument, and pass fn as a callback to higherOrderFunction.

      Running this code will give the following:

      Output

      Just a function

      Let’s go back to the first, second, and third functions with setTimeout. This is what you have so far:

      function first() {
        console.log(1)
      }
      
      function second() {
        setTimeout(() => {
          console.log(2)
        }, 0)
      }
      
      function third() {
        console.log(3)
      }
      

      The task is to get the third function to always delay execution until after the asynchronous action in the second function has completed. This is where callbacks come in. Instead of executing first, second, and third at the top-level of execution, you will pass the third function as an argument to second. The second function will execute the callback after the asynchronous action has completed.

      Here are the three functions with a callback applied:

      // Define three functions
      function first() {
        console.log(1)
      }
      
      function second(callback) {
        setTimeout(() => {
          console.log(2)
      
          // Execute the callback function
          callback()
        }, 0)
      }
      
      function third() {
        console.log(3)
      }
      

      Now, execute first and second, then pass third as an argument to second:

      first()
      second(third)
      

      After running this code block, you will receive the following output:

      Output

      1 2 3

      First 1 will print, and after the timer completes (in this case, zero seconds, but you can change it to any amount) it will print 2 then 3. By passing a function as a callback, you’ve successfully delayed execution of the function until the asynchronous Web API (setTimeout) completes.

      The key takeaway here is that callback functions are not asynchronous—setTimeout is the asynchronous Web API responsible for handling asynchronous tasks. The callback just allows you to be informed of when an asynchronous task has completed and handles the success or failure of the task.

      Now that you have learned how to use callbacks to handle asynchronous tasks, the next section explains the problems of nesting too many callbacks and creating a “pyramid of doom.”

      Nested Callbacks and the Pyramid of Doom

      Callback functions are an effective way to ensure delayed execution of a function until another one completes and returns with data. However, due to the nested nature of callbacks, code can end up getting messy if you have a lot of consecutive asynchronous requests that rely on each other. This was a big frustration for JavaScript developers early on, and as a result code containing nested callbacks is often called the “pyramid of doom” or “callback hell.”

      Here is a demonstration of nested callbacks:

      function pyramidOfDoom() {
        setTimeout(() => {
          console.log(1)
          setTimeout(() => {
            console.log(2)
            setTimeout(() => {
              console.log(3)
            }, 500)
          }, 2000)
        }, 1000)
      }
      

      In this code, each new setTimeout is nested inside a higher order function, creating a pyramid shape of deeper and deeper callbacks. Running this code would give the following:

      Output

      1 2 3

      In practice, with real world asynchronous code, this can get much more complicated. You will most likely need to do error handling in asynchronous code, and then pass some data from each response onto the next request. Doing this with callbacks will make your code difficult to read and maintain.

      Here is a runnable example of a more realistic “pyramid of doom” that you can play around with:

      // Example asynchronous function
      function asynchronousRequest(args, callback) {
        // Throw an error if no arguments are passed
        if (!args) {
          return callback(new Error('Whoa! Something went wrong.'))
        } else {
          return setTimeout(
            // Just adding in a random number so it seems like the contrived asynchronous function
            // returned different data
            () => callback(null, {body: args + ' ' + Math.floor(Math.random() * 10)}),
            500,
          )
        }
      }
      
      // Nested asynchronous requests
      function callbackHell() {
        asynchronousRequest('First', function first(error, response) {
          if (error) {
            console.log(error)
            return
          }
          console.log(response.body)
          asynchronousRequest('Second', function second(error, response) {
            if (error) {
              console.log(error)
              return
            }
            console.log(response.body)
            asynchronousRequest(null, function third(error, response) {
              if (error) {
                console.log(error)
                return
              }
              console.log(response.body)
            })
          })
        })
      }
      
      // Execute 
      callbackHell()
      

      In this code, you must make every function account for a possible response and a possible error, making the function callbackHell visually confusing.

      Running this code will give you the following:

      Output

      First 9 Second 3 Error: Whoa! Something went wrong. at asynchronousRequest (<anonymous>:4:21) at second (<anonymous>:29:7) at <anonymous>:9:13

      This way of handling asynchronous code is difficult to follow. As a result, the concept of promises was introduced in ES6. This is the focus of the next section.

      Promises

      A promise represents the completion of an asynchronous function. It is an object that might return a value in the future. It accomplishes the same basic goal as a callback function, but with many additional features and a more readable syntax. As a JavaScript developer, you will likely spend more time consuming promises than creating them, as it is usually asynchronous Web APIs that return a promise for the developer to consume. This tutorial will show you how to do both.

      Creating a Promise

      You can initialize a promise with the new Promise syntax, and you must initialize it with a function. The function that gets passed to a promise has resolve and reject parameters. The resolve and reject functions handle the success and failure of an operation, respectively.

      Write the following line to declare a promise:

      // Initialize a promise
      const promise = new Promise((resolve, reject) => {})
      

      If you inspect the initialized promise in this state with your web browser’s console, you will find it has a pending status and undefined value:

      Output

      __proto__: Promise [[PromiseStatus]]: "pending" [[PromiseValue]]: undefined

      So far, nothing has been set up for the promise, so it’s going to sit there in a pending state forever. The first thing you can do to test out a promise is fulfill the promise by resolving it with a value:

      const promise = new Promise((resolve, reject) => {
        resolve('We did it!')
      })
      

      Now, upon inspecting the promise, you’ll find that it has a status of fulfilled, and a value set to the value you passed to resolve:

      Output

      __proto__: Promise [[PromiseStatus]]: "fulfilled" [[PromiseValue]]: "We did it!"

      As stated in the beginning of this section, a promise is an object that may return a value. After being successfully fulfilled, the value goes from undefined to being populated with data.

      A promise can have three possible states: pending, fulfilled, and rejected.

      • Pending – Initial state before being resolved or rejected
      • Fulfilled – Successful operation, promise has resolved
      • Rejected – Failed operation, promise has rejected

      After being fulfilled or rejected, a promise is settled.

      Now that you have an idea of how promises are created, let’s look at how a developer may consume these promises.

      Consuming a Promise

      The promise in the last section has fulfilled with a value, but you also want to be able to access the value. Promises have a method called then that will run after a promise reaches resolve in the code. then will return the promise’s value as a parameter.

      This is how you would return and log the value of the example promise:

      promise.then((response) => {
        console.log(response)
      })
      

      The promise you created had a [[PromiseValue]] of We did it!. This value is what will be passed into the anonymous function as response:

      Output

      We did it!

      So far, the example you created did not involve an asynchronous Web API—it only explained how to create, resolve, and consume a native JavaScript promise. Using setTimeout, you can test out an asynchronous request.

      The following code simulates data returned from an asynchronous request as a promise:

      const promise = new Promise((resolve, reject) => {
        setTimeout(() => resolve('Resolving an asynchronous request!'), 2000)
      })
      
      // Log the result
      promise.then((response) => {
        console.log(response)
      })
      

      Using the then syntax ensures that the response will be logged only when the setTimeout operation is completed after 2000 milliseconds. All this is done without nesting callbacks.

      Now after two seconds, it will resolve the promise value and it will get logged in then:

      Output

      Resolving an asynchronous request!

      Promises can also be chained to pass along data to more than one asynchronous operation. If a value is returned in then, another then can be added that will fulfill with the return value of the previous then:

      // Chain a promise
      promise
        .then((firstResponse) => {
          // Return a new value for the next then
          return firstResponse + ' And chaining!'
        })
        .then((secondResponse) => {
          console.log(secondResponse)
        })
      

      The fulfilled response in the second then will log the return value:

      Output

      Resolving an asynchronous request! And chaining!

      Since then can be chained, it allows the consumption of promises to appear more synchronous than callbacks, as they do not need to be nested. This will allow for more readable code that can be maintained and verified easier.

      Error Handling

      So far, you have only handled a promise with a successful resolve, which puts the promise in a fulfilled state. But frequently with an asynchronous request you also have to handle an error—if the API is down, or a malformed or unauthorized request is sent. A promise should be able to handle both cases. In this section, you will create a function to test out both the success and error case of creating and consuming a promise.

      This getUsers function will pass a flag to a promise, and return the promise:

      function getUsers(onSuccess) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            // Handle resolve and reject in the asynchronous API
          }, 1000)
        })
      }
      

      Set up the code so that if onSuccess is true, the timeout will fulfill with some data. If false, the function will reject with an error:

      function getUsers(onSuccess) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            // Handle resolve and reject in the asynchronous API
            if (onSuccess) {
              resolve([
                {id: 1, name: 'Jerry'},
                {id: 2, name: 'Elaine'},
                {id: 3, name: 'George'},
              ])
            } else {
              reject('Failed to fetch data!')
            }
          }, 1000)
        })
      }
      

      For the successful result, you return JavaScript objects that represent sample user data.

      In order to handle the error, you will use the catch instance method. This will give you a failure callback with the error as a parameter.

      Run the getUser command with onSuccess set to false, using the then method for the success case and the catch method for the error:

      // Run the getUsers function with the false flag to trigger an error
      getUsers(false)
        .then((response) => {
          console.log(response)
        })
        .catch((error) => {
          console.error(error)
        })
      

      Since the error was triggered, the then will be skipped and the catch will handle the error:

      Output

      Failed to fetch data!

      If you switch the flag and resolve instead, the catch will be ignored, and the data will return instead:

      // Run the getUsers function with the true flag to resolve successfully
      getUsers(true)
        .then((response) => {
          console.log(response)
        })
        .catch((error) => {
          console.error(error)
        })
      

      This will yield the user data:

      Output

      (3) [{…}, {…}, {…}] 0: {id: 1, name: "Jerry"} 1: {id: 2, name: "Elaine"} 3: {id: 3, name: "George"}

      For reference, here is a table with the handler methods on Promise objects:

      MethodDescription
      then()Handles a resolve. Returns a promise, and calls onFulfilled function asynchronously
      catch()Handles a reject. Returns a promise, and calls onRejected function asynchronously
      finally()Called when a promise is settled. Returns a promise, and calls onFinally function asynchronously

      Promises can be confusing, both for new developers and experienced programmers that have never worked in an asynchronous environment before. However as mentioned, it is much more common to consume promises than create them. Usually, a browser’s Web API or third party library will be providing the promise, and you only need to consume it.

      In the final promise section, this tutorial will cite a common use case of a Web API that returns promises: the Fetch API.

      Using the Fetch API with Promises

      One of the most useful and frequently used Web APIs that returns a promise is the Fetch API, which allows you to make an asynchronous resource request over a network. fetch is a two-part process, and therefore requires chaining then. This example demonstrates hitting the GitHub API to fetch a user’s data, while also handling any potential error:

      // Fetch a user from the GitHub API
      fetch('https://api.github.com/users/octocat')
        .then((response) => {
          return response.json()
        })
        .then((data) => {
          console.log(data)
        })
        .catch((error) => {
          console.error(error)
        })
      

      The fetch request is sent to the https://api.github.com/users/octocat URL, which asynchronously waits for a response. The first then passes the response to an anonymous function that formats the response as JSON data, then passes the JSON to a second then that logs the data to the console. The catch statement logs any error to the console.

      Running this code will yield the following:

      Output

      login: "octocat", id: 583231, avatar_url: "https://avatars3.githubusercontent.com/u/583231?v=4" blog: "https://github.blog" company: "@github" followers: 3203 ...

      This is the data requested from https://api.github.com/users/octocat, rendered in JSON format.

      This section of the tutorial showed that promises incorporate a lot of improvements for dealing with asynchronous code. But, while using then to handle asynchronous actions is easier to follow than the pyramid of callbacks, some developers still prefer a synchronous format of writing asynchronous code. To address this need, ECMAScript 2016 (ES7) introduced async functions and the await keyword to make working with promises easier.

      Async Functions with async/await

      An async function allows you to handle asynchronous code in a manner that appears synchronous. async functions still use promises under the hood, but have a more traditional JavaScript syntax. In this section, you will try out examples of this syntax.

      You can create an async function by adding the async keyword before a function:

      // Create an async function
      async function getUser() {
        return {}
      }
      

      Although this function is not handling anything asynchronous yet, it behaves differently than a traditional function. If you execute the function, you’ll find that it returns a promise with a [[PromiseStatus]] and [[PromiseValue]] instead of a return value.

      Try this out by logging a call to the getUser function:

      console.log(getUser())
      

      This will give the following:

      Output

      __proto__: Promise [[PromiseStatus]]: "fulfilled" [[PromiseValue]]: Object

      This means you can handle an async function with then in the same way you could handle a promise. Try this out with the following code:

      getUser().then((response) => console.log(response))
      

      This call to getUser passes the return value to an anonymous function that logs the value to the console.

      You will receive the following when you run this program:

      Output

      {}

      An async function can handle a promise called within it using the await operator. await can be used within an async function and will wait until a promise settles before executing the designated code.

      With this knowledge, you can rewrite the Fetch request from the last section using async/await as follows:

      // Handle fetch with async/await
      async function getUser() {
        const response = await fetch('https://api.github.com/users/octocat')
        const data = await response.json()
      
        console.log(data)
      }
      
      // Execute async function
      getUser()
      

      The await operators here ensure that the data is not logged before the request has populated it with data.

      Now the final data can be handled inside the getUser function, without any need for using then. This is the output of logging data:

      Output

      login: "octocat", id: 583231, avatar_url: "https://avatars3.githubusercontent.com/u/583231?v=4" blog: "https://github.blog" company: "@github" followers: 3203 ...

      Note: In many environments, async is necessary to use await—however, some new versions of browsers and Node allow using top-level await, which allows you to bypass creating an async function to wrap the await in.

      Finally, since you are handling the fulfilled promise within the asynchronous function, you can also handle the error within the function. Instead of using the catch method with then, you will use the try/catch pattern to handle the exception.

      Add the following highlighted code:

      // Handling success and errors with async/await
      async function getUser() {
        try {
          // Handle success in try
          const response = await fetch('https://api.github.com/users/octocat')
          const data = await response.json()
      
          console.log(data)
        } catch (error) {
          // Handle error in catch
          console.error(error)
        }
      }
      

      The program will now skip to the catch block if it receives an error and log that error to the console.

      Modern asynchronous JavaScript code is most often handled with async/await syntax, but it is important to have a working knowledge of how promises work, especially as promises are capable of additional features that cannot be handled with async/await, like combining promises with Promise.all().

      Note: async/await can be reproduced by using generators combined with promises to add more flexibility to your code. To learn more, check out our Understanding Generators in JavaScript tutorial.

      Conclusion

      Because Web APIs often provide data asynchronously, learning how to handle the result of asynchronous actions is an essential part of being a JavaScript developer. In this article, you learned how the host environment uses the event loop to handle the order of execution of code with the stack and queue. You also tried out examples of three ways to handle the success or failure of an asynchronous event, with callbacks, promises, and async/await syntax. Finally, you used the Fetch Web API to handle asynchronous actions.

      For more information about how the browser handles parallel events, read Concurrency model and the event loop on the Mozilla Developer Network. If you’d like to learn more about JavaScript, return to our How To Code in JavaScript series.



      Source link

      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