One place for hosting & domains

      Redis

      How to Set Up a Website Hit Counter With Redis and PHP on Ubuntu 20.04


      The author selected the Apache Software Foundation to receive a donation as part of the Write for DOnations program.

      Introduction

      A hit counter is an application that records and indicates the number of visits your web page has received. The counter starts from 1 and is incremented once every time a web page is visited.

      To keep track of the visits, the hit counter application requires a form of a database. While disk-based database management systems like MySQL can work, an in-memory database is better in terms of speed, performance, scalability, simplicity, and ease of use. This is where the Redis server comes into play. Redis stores data in your computer’s RAM instead of hitting the disk every time you’re performing an input/output operation. This increases the throughput significantly.

      To track your site visits, you require a Redis hash map. This is a data structure that implements a key-value pair. A hash map provides a hash table that maps keys to values. Once a user visits your web page, you create a key based on their public IP address or username (for authenticated users), and then you initialize their total visits to a value of 1. Then, every time the user revisits your web page, you check their total visits from the Redis hash map based on their IP address/username and increment the value.

      In this guide, you’ll set up a website hit counter with Redis and PHP on your Ubuntu 20.04 server. The PHP scripts in this guide use the visitors’ public IP addresses to track their visits.

      Prerequisites

      To follow along with this guide, make sure you have the following:

      Step 1 — Installing PHP Redis Extension

      In this step, you’ll install a Redis extension that allows PHP to talk to the Redis server. You’ll also create a test web page that implements the Redis hash map to track web visits.

      Before installing the Redis extension, refresh your Ubuntu package information index:

      Then, run the following command to install php-redis. The extension provides an API for communicating with the Redis server key-value store:

      • sudo apt install -y php-redis

      Restart Apache to load the new extension:

      • sudo systemctl restart apache2

      You’ve now installed a PHP extension that talks to your Redis server. Next, you’ll create a test.php web page under the root directory of the Apache webserver. This is just a sample file that visitors request when they visit your website with a browser. Under the hood, the test.php page file loads a hit_counter.php script which you’ll later create to track page visits using the Redis server.

      In a real-life scenario, your website might have tens or even hundreds of web pages. For this guide, you’ll set up a single web page for demonstration purposes.

      In your terminal window, use nano to create a new test.php file under the root directory of your web-server /var/www/html/:

      • sudo nano /var/www/html/test.php

      Then, enter the following information into the test.php file:

      /var/www/html/test.php

      <?php
        require_once 'hit_counter.php';
      ?>
      
      <!DOCTYPE html>
      <html>
      
        <head>
          <title>Sample Test Page</title>
        </head>
      
        <body>
          <h1>Sample test page</h1>
          <p>This is a sample test page.</p>
        </body>
      
      </html>
      
      

      Save and close the file when you’re through with editing. In this step, you’ve created a simple HTML web page that loads a hit_counter.php file when visited. Next, you’ll code the hit_counter.php file to track the test page visits.

      Step 2 — Creating a Redis Hit Counter Script

      When working in a production environment, it’s very conventional to separate re-usable PHP files. This allows you to implement the logic in these files on different parts of the project just by including their paths instead of copy-pasting the code. This makes maintenance easier since you only need to edit a single file in case you need to change the logic. This saves you a lot of time.

      You’re going to apply the same strategy in this guide. You’ll create a single hit_counter.php file that you can include on any web page that requires visitors’ tracking.

      In this file, you’ll use the php-redis library to connect to the Redis server from PHP. Then, you’ll create a Redis hash map to store the number of visits a visitor has made to your website. You’ll use the visitors’ unique IP addresses as Redis keys to distinguish each visitor’s hit counts in the Redis server.

      In your terminal window, open a new hit_counter.php file using nano for editing purposes:

      • sudo nano /var/www/html/hit_counter.php

      With the hit_counter.php file now created, open a new PHP tag <?php. Then, inside a try { block enter the following code to connect to your local Redis server on port 6379. Replace EXAMPLE_PASSWORD with the authentication password for the Redis server:

      /var/www/html/hit_counter.php

      
      <?php
      
          try {
      
              $redis = new Redis();
              $redis->connect('127.0.0.1', 6379);
              $redis->auth('EXAMPLE_PASSWORD');
      

      Next, give the Redis hash map($siteVisitsMap) a name of your choice. This guide uses siteStats for demonstration purposes:

      /var/www/html/hit_counter.php

      
              $siteVisitsMap = 'siteStats';
      
      

      After defining the Redis hash map, you’ll now initialize an empty Redis key ($visitorHashKey). Then, you’ll populate it with the visitors’ IP addresses. You’ll use the value of the $visitorHashKey variable to uniquely identify each visitor requesting your web page:

      /var/www/html/hit_counter.php

      
      
              $visitorHashKey = '';           
      
              if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
      
                  $visitorHashKey = $_SERVER['HTTP_CLIENT_IP'];
      
              } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
      
                  $visitorHashKey = $_SERVER['HTTP_X_FORWARDED_FOR'];
      
              } else {
      
                  $visitorHashKey = $_SERVER['REMOTE_ADDR'];
              }
      
      

      In this code, you’re using the PHP if statement to determine the visitor’s IP address by checking whether the $_SERVER['HTTP_CLIENT_IP'], $_SERVER['HTTP_X_FORWARDED_FOR'], or $_SERVER['REMOTE_ADDR'] variables are populated.

      Following this, initialize a $totalVisits variable to store the total visits for each IP address and assign it a value of 0. Then, use the PHP if (...) {...} else {...} and $redis->hExists($siteVisitsMap, $visitorHashKey) statements to check if the IP address has any entries in the Redis server.

      You’ll use the statement if ($redis->hExists($siteVisitsMap, $visitorHashKey)) {...} to check whether a $visitorHashKey exists in a map named $siteVisitsMap.

      In case the map and the key with the named IP address exist in the Redis server, retrieve it with the statement $visitorData = $redis->hMget($siteVisitsMap, array($visitorHashKey)); and use $totalVisits = $visitorData[$visitorHashKey] + 1; to increment the $totalVisits variable. You’re using the $redis->hMget statement to get hit count data associated with an IP address. The hMget function accepts the name of your map ($siteVisitsMap) and an array of the keys that you want to retrieve from the Redis server. In this case, you only have one key ($visitorHashKey), but you must convert it to an array using the statement array($visitorHashKey).

      In case your script encounters the IP address for the first time, set the $totalVisits variable to 1. Finally, use $redis->hSet($siteVisitsMap, $visitorHashKey, $totalVisits); to set the value of the $visitorHashKey according to the results of the previous if (...) {...} else {...} statement. The $redis->hSet($siteVisitsMap, $visitorHashKey, $totalVisits) statement creates a $siteVisitsMap hash map in the Redis server with a key named $visitorHashKey with a value of $totalVisits.

      Then, welcome the visitor by echoing the total visits and close the } catch (...) {...} block:

      /var/www/html/hit_counter.php

      
              $totalVisits = 0;
      
              if ($redis->hExists($siteVisitsMap, $visitorHashKey)) {
      
                  $visitorData = $redis->hMget($siteVisitsMap, array($visitorHashKey));
                  $totalVisits = $visitorData[$visitorHashKey] + 1;
      
              } else {
      
                  $totalVisits = 1;
      
              }
      
              $redis->hSet($siteVisitsMap, $visitorHashKey, $totalVisits);
      
              echo "Welcome, you've visited this page " .  $totalVisits . " timesn";
      
          } catch (Exception $e) {
              echo $e->getMessage();
          }
      
      

      Once completed, your /var/www/html/hit_counter.php file should be similar to the following code:

      /var/www/html/hit_counter.php

      
      <?php
      
          try {
      
              $redis = new Redis();
              $redis->connect('127.0.0.1', 6379);
              $redis->auth('EXAMPLE_PASSWORD');
      
              $siteVisitsMap  = 'siteStats';
              $visitorHashKey = '';           
      
              if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
      
                 $visitorHashKey = $_SERVER['HTTP_CLIENT_IP'];
      
              } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
      
                 $visitorHashKey = $_SERVER['HTTP_X_FORWARDED_FOR'];
      
              } else {
      
                 $visitorHashKey = $_SERVER['REMOTE_ADDR'];
              }
      
              $totalVisits = 0;
      
              if ($redis->hExists($siteVisitsMap, $visitorHashKey)) {
      
                  $visitorData = $redis->hMget($siteVisitsMap,  array($visitorHashKey));
                  $totalVisits = $visitorData[$visitorHashKey] + 1;
      
              } else {
      
                  $totalVisits = 1;
      
              }
      
              $redis->hSet($siteVisitsMap, $visitorHashKey, $totalVisits);
      
              echo "Welcome, you've visited this page " .  $totalVisits . " timesn";
      
          } catch (Exception $e) {
              echo $e->getMessage();
          }
      

      Save and close the file when you’re through with editing. You’ve now coded a hit_counter.php script. Next, you’ll create another PHP script that generates a report from the data gathered in the Redis hash map.

      Step 3 — Creating a Site Stats Report Script

      Once you’ve collected data in a Redis hash map, it might not make any sense if you’re not able to retrieve and represent the information in a report. In this step, you’ll create a log report to show the different site visitors and the total visits they’ve made on the test web page.

      To create the log report script, run nano on your terminal window and create a new /var/www/html/log_report.php file:

      • sudo nano /var/www/html/log_report.php

      Then, enter the information below into the file. Replace EXAMPLE_PASSWORD with the correct password for the Redis server:

      /var/www/html/log.php

      
      <!DOCTYPE html>
      <html>
      
        <head>
          <title>Site Visits Report</title>
        </head>
      
        <body>
      
            <h1>Site Visits Report</h1>
      
            <table border="1">
              <tr>
                <th>No.</th>
                <th>Visitor</th>
                <th>Total Visits</th>
              </tr>
      
              <?php
      
                  try {
      
                      $redis = new Redis();
                      $redis->connect('127.0.0.1', 6379);
                      $redis->auth('EXAMPLE_PASSWORD');
      
                      $siteVisitsMap = 'siteStats';                          
      
                      $siteStats = $redis->HGETALL($siteVisitsMap);
      
                      $i = 1; 
      
                      foreach ($siteStats as $visitor => $totalVisits) {
      
                          echo "<tr>";
                            echo "<td align = 'left'>"   . $i . "."     . "</td>";
                            echo "<td align = 'left'>"   . $visitor     . "</td>";
                            echo "<td align = 'right'>"  . $totalVisits . "</td>";
                          echo "</tr>";
      
                          $i++;
                      }
      
                  } catch (Exception $e) {
                      echo $e->getMessage();
                  }
      
              ?>
      
            </table>
        </body>
      
      </html>
      

      Save and close the file when you’re through with editing. In the above script, you’re connecting to the Redis server and you’re using the statement $redis->HGETALL($siteVisitsMap); to retrieve your web page visits’ hash map. Then, you’re using the PHP foreach ($siteStats as $visitor => $totalVisits) { statement to loop and display the visitors’ IP addresses and the number of visits they’ve made to your site. You’re using the Redis HGETALL command to retrieve all fields (IP addresses) and values (total visits per each IP address) from the siteVisitsMap map.

      You now have a test page, a hit counter script, and a report page to check your site stats. Next, you’ll test the functionalities of your hit counter and see if everything works.

      Step 4 — Testing the Redis Hit Counter

      In this step, you’ll test the whole logic for your hit counter. Navigate to the following URL on your web browser. Replace your-server-IP with your server’s public IP address or domain name.

      http://your-server-IP/test.php
      

      Refresh the page several times using different devices to generate enough stats. After each visit, you should receive the following output.

      https://www.xpresservers.com/wp-content/webpc-passthru.php?src=https://www.xpresservers.com/wp-content/uploads/2021/12/How-to-Set-Up-a-Website-Hit-Counter-With-Redis.png&nocache=1

      Next, visit the following URL to get your site visits report displayed in an HTML table

      http://your-server-IP/log_report.php
      
      

      You should now see a report similar to the following output.

      https://www.xpresservers.com/wp-content/webpc-passthru.php?src=https://www.xpresservers.com/wp-content/uploads/2021/12/1640824415_889_How-to-Set-Up-a-Website-Hit-Counter-With-Redis.png&nocache=1

      Your hit counter is now working as expected.

      Conclusion

      In this guide, you’ve set up a website hit counter with Redis and PHP on your Ubuntu 20.04 server.

      As you can see from the sample source code in this guide, Redis provides cleaner methods for creating and updating hash maps.

      As mentioned at the beginning of this guide, using a relational database management system may still work but you’ll write tons of code to insert and update data in underlying tables. In addition, disk-based databases may experience scalability issues when your site grows.

      For more information on using Redis in-memory database, follow the guides below:



      Source link

      How To Implement PHP Rate Limiting with Redis on Ubuntu 20.04


      The author selected the Apache Software Foundation to receive a donation as part of the Write for DOnations program.

      Introduction

      Redis (Remote Dictionary Server ) is an in-memory open source software. It is a data-structure store that uses a server’s RAM, which is several times faster than even the fastest Solid State Drive (SSD). This makes Redis highly responsive, and therefore, suitable for rate limiting.

      Rate limiting is a technology that puts a cap on the number of times a user can request a resource from a server. Many services implement rate limiting to prevent abuse to a service when a user may try to put too much load on a server.

      For instance, when you’re implementing a public API (Application Programming Interface) for your web application with PHP, you need some form of rate limiting. The reason is that when you release an API to the public, you’d want to put a control on the number of times an application user can repeat an action in a specific timeframe. Without any control, users may bring your system to a complete halt.

      Rejecting users’ requests that exceed a certain limit allows your application to run smoothly. If you have a lot of customers, rate limiting enforces a fair-usage policy that allows each customer to have high-speed access to your application. Rate limiting is also good for reducing bandwidth costs and minimizing congestion on your server.

      It might be practical to code a rate-limiting module by logging user activities in a database like MySQL. However, the end product may not be scalable when many users access the system since the data must be fetched from disk and compared against the set limit. This is not only slow, but relational database management systems are not designed for this purpose.

      Since Redis works as an in-memory database, it is a qualified candidate for creating a rate limiter, and it has been proven reliable for this purpose.

      In this tutorial, you’ll implement a PHP script for rate limiting with Redis on an Ubuntu 20.04 server.

      Prerequisites

      Before you begin, you’ll need the following:

      Step 1 — Installing the Redis Library for PHP

      First, you’ll begin by updating your Ubuntu server package repository index. Then, install the php-redis extension. This is a library that allows you to implement Redis in your PHP code. To do this, run the following commands:

      • sudo apt update
      • sudo apt install -y php-redis

      Next, restart the Apache server to load the php-redis library:

      • sudo systemctl restart apache2

      Once you’ve updated your software information index and installed the Redis library for PHP, you’ll now create a PHP resource that caps users’ access based on their IP address.

      Step 2 — Building a PHP Web Resource for Rate Limiting

      In this step, you’ll create a test.php file in the root directory (/var/www/html/) of your web server. This file will be accessible to the public and users can type its address in a web browser to run it. However, for the basis of this guide, you’ll later test access to the resource using the curl command.

      The sample resource file allows users to access it three times in a timeframe of 10 seconds. Users trying to exceed the limit will get an error informing them that they have been rate limited.

      The core functionality of this file relies heavily on the Redis server. When a user requests the resource for the first time, the PHP code in the file will create a key on the Redis server based on the user’s IP address.

      When the user visits the resource again, the PHP code will try to match the user’s IP address with the keys stored in the Redis server and increment the value by one if the key exists. The PHP code will keep checking if the incremented value hits the maximum limit set.

      The Redis key, which is based on the user’s IP address, will expire after 10 seconds; after this time period, logging the user’s visits to the web resource will begin again.

      To begin, open the /var/www/html/test.php file:

      • sudo nano /var/www/html/test.php

      Next, enter the following information to initialize the Redis class. Remember to enter the appropriate value for REDIS_PASSWORD:

      /var/www/html/test.php

      <?php
      
      $redis = new Redis();
      $redis->connect('127.0.0.1', 6379);
      $redis->auth('REDIS_PASSWORD');
      

      $redis->auth implements plain text authentication to the Redis server. This is OK while you’re working locally (via localhost), but if you’re using a remote Redis server, consider using SSL authentication.

      Next, in the same file, initialize the following variables:

      /var/www/html/test.php

      . . .
      $max_calls_limit  = 3;
      $time_period      = 10;
      $total_user_calls = 0;
      

      You’ve defined:

      • $max_calls_limit: is the maximum number of calls a user can access the resource.
      • $time_period: defines the timeframe in seconds within which a user is allowed to access the resource per the $max_calls_limit.
      • $total_user_calls: initializes a variable that retrieves the number of times a user has requested access to the resource in the given timeframe.

      Next, add the following code to retrieve the IP address of the user requesting the web resource:

      /var/www/html/test.php

      . . .
      if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
          $user_ip_address = $_SERVER['HTTP_CLIENT_IP'];
      } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
          $user_ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'];
      } else {
          $user_ip_address = $_SERVER['REMOTE_ADDR'];
      }
      

      While this code uses the users’ IP address for demonstration purposes, if you’ve got a protected resource on the server that requires authentication, you might log users’ activities using their usernames or access tokens.

      In such a scenario, every user authenticated into your system will have a unique identifier (for example, a customer ID, developer ID, vendor ID, or even a user ID). (If you configure this, remember to use these identifiers in place of the $user_ip_address.)

      For this guide, the user IP address is sufficient for proving the concept. So, once you’ve retrieved the user’s IP address in the previous code snippet, add the next code block to your file:

      /var/www/html/test.php

      . . .
      if (!$redis->exists($user_ip_address)) {
          $redis->set($user_ip_address, 1);
          $redis->expire($user_ip_address, $time_period);
          $total_user_calls = 1;
      } else {
          $redis->INCR($user_ip_address);
          $total_user_calls = $redis->get($user_ip_address);
          if ($total_user_calls > $max_calls_limit) {
              echo "User " . $user_ip_address . " limit exceeded.";
              exit();
          }
      }
      
      echo "Welcome " . $user_ip_address . " total calls made " . $total_user_calls . " in " . $time_period . " seconds";
      

      In this code, you use an if...else statement to check if there is a key defined with the IP address on the Redis server. If the key doesn’t exist, if (!$redis->exists($user_ip_address)) {...}, you set it and define its value to 1 using the code $redis->set($user_ip_address, 1);.

      The $redis->expire($user_ip_address, $time_period); sets the key to expire within the time period—in this case, 10 seconds.

      If the user’s IP address does not exist as a Redis key, you set the variable $total_user_calls to 1.

      In the ...else {...}... statement block, you use the $redis->INCR($user_ip_address); command to increment the value of the Redis key set for each IP address key by 1. This only happens when the key is already set in the Redis server and counts as a repeat request.

      The statement $total_user_calls = $redis->get($user_ip_address); retrieves the total requests the user makes by checking their IP address-based key on the Redis server.

      Toward the end of the file, you use the ...if ($total_user_calls > $max_calls_limit) {... }.. statement to check if the limit is exceeded; if so, you alert the user with echo "User " . $user_ip_address . " limit exceeded.";. Finally, you’re informing the user about the visits they make in the time period using the echo "Welcome " . $user_ip_address . " total calls made " . $total_user_calls . " in " . $time_period . " seconds"; statement.

      After adding all the code, your /var/www/html/test.php file will be as follows:

      /var/www/html/test.php

      <?php
      $redis = new Redis();
      $redis->connect('127.0.0.1', 6379);
      $redis->auth('REDIS_PASSWORD');
      
      $max_calls_limit  = 3;
      $time_period      = 10;
      $total_user_calls = 0;
      
      if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
          $user_ip_address = $_SERVER['HTTP_CLIENT_IP'];
      } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
          $user_ip_address = $_SERVER['HTTP_X_FORWARDED_FOR'];
      } else {
          $user_ip_address = $_SERVER['REMOTE_ADDR'];
      }
      
      if (!$redis->exists($user_ip_address)) {
          $redis->set($user_ip_address, 1);
          $redis->expire($user_ip_address, $time_period);
          $total_user_calls = 1;
      } else {
          $redis->INCR($user_ip_address);
          $total_user_calls = $redis->get($user_ip_address);
          if ($total_user_calls > $max_calls_limit) {
              echo "User " . $user_ip_address . " limit exceeded.";
              exit();
          }
      }
      
      echo "Welcome " . $user_ip_address . " total calls made " . $total_user_calls . " in " . $time_period . " seconds";
      

      When you’ve finished editing the /var/www/html/test.php file, save and close it.

      You’ve now coded the logic needed to rate limit users on the test.php web resource. In the next step, you’ll test your script.

      Step 3 — Testing Redis Rate Limiting

      In this step, you’ll use the curl command to request the web resource that you’ve coded in Step 2. To fully check the script, you’ll request the resource five times in a single command. It is possible to do this by including a placeholder URL parameter at the end of the test.php file. Here, you use the value ?[1-5] at the end of your request to execute the curl commands five times.

      Run the following command:

      • curl -H "Accept: text/plain" -H "Content-Type: text/plain" -X GET http://localhost/test.php?[1-5]

      After running the code, you will receive output similar to the following:

      Output

      [1/5]: http://localhost/test.php?1 --> <stdout> --_curl_--http://localhost/test.php?1 Welcome 127.0.0.1 total calls made 1 in 10 seconds [2/5]: http://localhost/test.php?2 --> <stdout> --_curl_--http://localhost/test.php?2 Welcome 127.0.0.1 total calls made 2 in 10 seconds [3/5]: http://localhost/test.php?3 --> <stdout> --_curl_--http://localhost/test.php?3 Welcome 127.0.0.1 total calls made 3 in 10 seconds [4/5]: http://localhost/test.php?4 --> <stdout> --_curl_--http://localhost/test.php?4 User 127.0.0.1 limit exceeded. [5/5]: http://localhost/test.php?5 --> <stdout> --_curl_--http://localhost/test.php?5 User 127.0.0.1 limit exceeded.

      As you’ll note, the first three requests ran without a problem. However, your script has rate limited the fourth and fifth requests. This confirms that the Redis server is rate limiting users’ requests.

      In this guide, you’ve set low values for the two variables following:

      /var/www/html/test.php

      ...
      $max_calls_limit  = 3;
      $time_period      = 10;
      ...
      

      When designing your application in a production environment, you could consider higher values depending on how often you expect users to hit your application.

      It is best practice to check real-time stats before setting these values. For instance, if your server logs show that an average user hits your application 1,000 times every 60 seconds, you may use those values as a benchmark for throttling users.

      To put things in a better perspective, here are some real-world examples of rate-limiting implementations (as of 2021):

      Conclusion

      This tutorial implemented a PHP script for rate limiting with Redis on an Ubuntu 20.04 server to prevent your web application from inadvertent or malicious overuse. You could extend the code to further suit your needs depending on your use case.

      You might want to secure your Apache server for production use; follow the How To Secure Apache with Let’s Encrypt on Ubuntu 20.04 tutorial.

      You might also consider reading how Redis works as a database cache. Try out our How To Set Up Redis as a Cache for MySQL with PHP on Ubuntu 20.04 tutorial.

      You can find further resources on our PHP and Redis topic pages.



      Source link

      What is Redis?


      Redis is an open-source key-value data store known for its flexibility, performance, and wide language support. An in-memory database, Redis is also known for its speed and has found wide use as a cache and message broker, as well as a database.

      A NoSQL database, Redis doesn’t use structured query language, otherwise known as SQL. Redis instead comes with its own set of commands for managing and accessing data.

      To learn more about Redis, please visit:

      A complete list of our educational resources on Redis can be found on our Redis tag page.



      Source link