2017-12-17

Distributed Parallel Fault Tolerant File System with GlusterFS

> to Japanese Pages

1. Summary

In this post, I explained “GlusterFS” as one of the source code synchronization solutions between web servers in a clustered environment. If we use this solution, the difference in source code due to deployment time lag will not occur between web servers. In addition, since “GlusterFS” is a distributed parallel fault tolerant file system, the effective range of this solution is not limited to Web servers. Depending on how you use it, you can build any fault tolerant file system on a large system.

2. GlusgerFS Introduction

Are you using “rsync” or “lsyncd” for synchronzing the file system between each node in a business cluster environment? To make the story clearer, I will explain web servers as an example, but this issue is not limited to web servers. There are several ways to synchronize project source code between web servers in a cluster environment. First, let's give some Bad know-how. For example, I often hear how to synchronize to each node using a shell script with “rsync” implemented. Even if you manually deploy to each node, the problem will be few if the system is small. However, even if synchronization is automated using “cron” with the shortest period, source code differences will occur for up to one minute. In addition, I sometimes hear how to automatically detect source code changes using “lsyncd” and synchronize incrementally to each node. However, this synchronization method may take several tens of seconds at the shortest before synchronization is completed. Furthermore, these synchronization methods are unidirectional synchronization, so there are no guarantee of data consistency. I also hear pretty much how to automatically deploy to each node using ci tools. However, these synchronization methods only fill the time difference between manual and automatic, which is not a fundamental solution. If these synchronization processes are performed to each node by serial processing, there will be a time difference of “number of nodes x time difference” until synchronization is completed. It would be better to do it at least by parallel processing. If these statuses are not a problem in UX, data management and other aspects, this post will be useless. If there is a problem, there are a number of these solutions. As one of its solutions, you have a way to use “GlusterFS.” GlusterFS is a distributed parallel fault tolerant file system. One of the advantages of using GlusterFS is that fault-tolerant design can be realized, such as file system distribution, synchronization, capacity increase/decrease can be realized with no system stop. Naturally, synchronization is bidirectional and there is no concept of master and slave. However, you should not include files in this volume what will continue to be locked by the daemon. If you do not make a mistake in usage, GlusterFS will do a great deal of power. In this post, I will explain how to implement GlusterFS. In this post, I will not announce actual measurements on sync speed, so you should implement and judge.

3. GlusterFS Architecture

The following figure is a rough concept of GlusterFS.
In addition, the following figure is a structure example of this post.
It does not prepare volume server cluster, it is a simple self-contained structure. The Web server itself is a volume server and a client, and it is a mechanism that mounts from the client and connects to its own volume. Naturally, it is possible to change the system configuration by increasing/decreasing the brick.

4. GlusterFS Environment

CentOS-7 GlusterFS 3.12

5. GlusterFS Servers Configuration

5-1. Install GlusterFS servers

# Both Web Server 1 and 2
$ sudo yum -y install centos-release-gluster
$ sudo yum -y install glusterfs-server

5-2. Startup GlusterFS servers

# Both Web Server 1 and 2
$ sudo systemctl start glusterd
$ sudo systemctl enable glusterd
$ sudo systemctl status glusterd

5-3. Set GlusgerFS hosts name

# Both Web Server 1 and 2
$ sudo vim /etc/hosts
  1. 10.0.0.1 web1.example.com
  2. 10.0.0.2 web2.example.com

5-4. Create GlusgerFS storage pool

# Only Web Server 1
$ sudo gluster peer probe web2.example.com

5-5. Confirm GlusgerFS storage pool

# Both Web Server 1 and 2
$ gluster peer status

5-6. Create GlusterFS volume

# Only Web Server 1
$ sudo gluster volume create server replica 2 web1.example.com:/server/ web2.example.com:/server/ force

5-7. Confirm GlusgerFS volume information

# Both Web Server 1 and 2
$ sudo gluster volume info

5-8. Start GlusgerFS volume

# Only Web Server 1
$ sudo gluster volume start server

5-9. Conform GlusgerFS volume status

# Both Web Server 1 and 2
$ sudo gluster volume status

6. GlusterFS Clients Configuration

6-1. Install GlusgerFS Clients

# Both Web Server 1 and 2
$ sudo yum -y install glusterfs glusterfs-fuse glusterfs-rdma

6-2. Mount Client to Server

# Web Server 1
$ sudo mkdir /client
$ sudo mount -t glusterfs web1.example.com:/server /client
$ sudo df -Th
# Web Server 2
$ sudo mkdir /client
$ sudo mount -t glusterfs web2.example.com:/server /client
$ sudo df -Th

6-3. Auto mount GlusgerFS Server

# Web Server 1
$ sudo vim /etc/fstab
  1. web1.example.com:/server /client glusterfs defaults,_netdev 0 0
# Web Server 2
$ sudo vim /etc/fstab
  1. web2.example.com:/server /client glusterfs defaults,_netdev 0 0
o

6-4. Test GlusgerFS replication

# Web Server 1
$ sudo cd /client
$ sudo touch test.txt
$ sudo ls
# Web Server 2
$ sudo cd /client
$ sudo ls
$ sudo rm text.txt
# Web Server 1
$ sudo ls

7. GlusgerFS Conclusion

In this post, I explained “GlusterFS” as one of the source code synchronization solutions between web servers in a clustered environment. If you use this solution, the difference in source code due to deployment time lag will not occur between web servers. In this way, once we have the foundation of the system, we will not have to use the CI tools desperately. In addition, since GlusterFS is a distributed parallel fault tolerant file system, the effective range of this solution is not limited to Web servers. Depending on how you use it, you can build any fault tolerant file system on a large system.

2017-12-13

Seconds Access Limiter for Web API with Python, PHP, Ruby, and Perl

> to Japanese Pages

1. Summary

In this article, I will describe the access limitation solution that is often required in Web APIs. In addition, I will exemplify “One-Second Access Limiter” which is one of access limit solutions using sample codes of Python, PHP, Ruby and Perl interpreter languages.

2. Introduction

In the Web API service development project, we may be presented with requirements such as “access limitation within a certain period.” For example, the requirement is such that the Web API returns the HTTP status code of “429 Too Many Requests” when the number of accesses is exceeded. These designers and developers will be forced to improve the speed and reducing the load of this process. This is because if the resource load reduction is the purpose of access limitation, it is meaningless if the logic is increasing the load. In addition, when the reference time is short and the accuracy of the result is required, the accuracy of the algorithm is required. If you are an engineer with the experience of developing Web Application Firewall (WAF), you should already know these things. In the world, there are many access limitation solutions, but in this post I will provide a sample of “One-Second Access Limiter” as one of its solutions.

3. Requirements

"Access limitation up to N times per second" 1. If the access exceeds N times per second, return the HTTP status code of "429 Too Many Requests" and block accesses. 2. However, the numerical value assigned to “N” depends on the specification of the project. 3. Because of the nature of access control for 1 second, this processing should not be a bottleneck of access processing capability.

4. Key Points of Architectures

Even from the above requirements, it must be processed as fast and light as possible.

# Prohibition of Use of Web Application Framework

Even if you are using a lightweight framework, loading a framework takes a lot of load. Therefore, this process should be implemented “before processing into the framework.”

# Libraries Loading

In order to minimize the load due to library loading, it should focus on built-in processing.

# Exception/Error Handling

Increasing the load by relying on the framework for exceptions and error handling makes no sense. These should be implemented simply in low-level code.

# Data Resource Selection

It is better to avoid heavyweight data resources like RDBMS, but in this requirement "Eventual Consistency" is not a good idea. Realizing with Loadbalancer or Reverse Proxy is also one solution, but the more the application layer is handled, the more the processing cost of the whole communication is incurred. Semi-synchronization such as memory cache and lightweight NoSQL is one option, but in this paper I use file system as data resource. In order to prevent wait processing such as file locking, it is controlled by the file name and the number of files. However, in the case of a cluster environment, a data synchronization solution is necessary.

5. Environments

The OS of sample codes is Linux. I prepared Python, PHP, Ruby, Perl as sample code languages. # "Python-3" Sample Code # "PHP-5" Sample Code # "Ruby-2" Sample Code # "Perl-5" Sample Code

6. "Python" Sample Code

Seconds Access Limiter with Python. Version: Python-3
  1. #!/usr/bin/python
  2. # coding:utf-8
  3.  
  4. import time
  5. import datetime
  6. import cgi
  7. import os
  8. from pathlib import Path
  9. import re
  10. import sys
  11. import inspect
  12. import traceback
  13. import json
  14.  
  15. # Definition
  16. def limitSecondsAccess():
  17. try:
  18. # Init
  19. ## Access Timestamp Build
  20. sec_usec_timestamp = time.time()
  21. sec_timestamp = int(sec_usec_timestamp)
  22.  
  23. ## Access Limit Default Value
  24. ### Depends on Specifications: For Example 10
  25. access_limit = 10
  26.  
  27. ## Roots Build
  28. ### Depends on Environment: For Example '/tmp'
  29. tmp_root = '/tmp'
  30. access_root = os.path.join(tmp_root, 'access')
  31.  
  32. ## Auth Key
  33. ### Depends on Specifications: For Example 'app_id'
  34. auth_key = 'app_id'
  35.  
  36. ## Response Content-Type
  37. ### Depends on Specifications: For Example JSON and UTF-8
  38. response_content_type = 'Content-Type: application/json; charset=utf-8'
  39.  
  40. ### Response Bodies Build
  41. ### Depends on Design
  42. response_bodies = {}
  43.  
  44. # Authorized Key Check
  45. query = cgi.FieldStorage()
  46. auth_id = query.getvalue(auth_key)
  47. if not auth_id:
  48. raise Exception('Unauthorized', 401)
  49. # The Auth Root Build
  50. auth_root = os.path.join(access_root, auth_id)
  51.  
  52. # The Auth Root Check
  53. if not os.path.isdir(auth_root):
  54. # The Auth Root Creation
  55. os.makedirs(auth_root, exist_ok=True)
  56.  
  57. # A Access File Creation Using Micro Timestamp
  58. ## For example, other data resources such as memory cache or RDB transaction.
  59. ## In the case of this sample code, it is lightweight because it does not require file locking and transaction processing.
  60. ## However, in the case of a cluster configuration, file system synchronization is required.
  61. access_file_path = os.path.join(auth_root, str(sec_usec_timestamp))
  62. path = Path(access_file_path)
  63. path.touch()
  64.  
  65. # The Access Counts Check
  66. access_counts = 0
  67. for base_name in os.listdir(auth_root):
  68. ## A Access File Path Build
  69. file_path = os.path.join(auth_root, base_name)
  70.  
  71. ## Not File Type
  72. if not os.path.isfile(file_path):
  73. continue
  74.  
  75. ## The Base Name Data Type Casting
  76. base_name_sec_usec_timestamp = float(base_name)
  77. base_name_sec_timestamp = int(base_name_sec_usec_timestamp)
  78.  
  79. ## Same Seconds Stampstamp
  80. if sec_timestamp == base_name_sec_timestamp:
  81.  
  82. ### A Overtaken Processing
  83. if sec_usec_timestamp < base_name_sec_usec_timestamp:
  84. continue
  85.  
  86. ### Access Counts Increment
  87. access_counts += 1
  88.  
  89. ### Too Many Requests
  90. if access_counts > access_limit:
  91. raise Exception('Too Many Requests', 429)
  92.  
  93. continue
  94.  
  95. ## Past Access Files Garbage Collection
  96. if sec_timestamp > base_name_sec_timestamp:
  97. os.remove(file_path)
  98.  
  99. except Exception as e:
  100. # Exception Tuple to HTTP Status Code
  101. http_status = e.args[0]
  102. http_code = e.args[1]
  103.  
  104. # 4xx
  105. if http_code >= 400 and http_code <= 499:
  106. # logging
  107. ## snip...
  108. # 5xx
  109. elif http_code >= 500:
  110. # logging
  111. # snip...
  112.  
  113. ## The Exception Message to HTTP Status
  114. http_status = 'foo'
  115. else:
  116. # Logging
  117. ## snip...
  118.  
  119. # HTTP Status Code for The Response
  120. http_status = 'Internal Server Error'
  121. http_code = 500
  122.  
  123. # Response Headers Feed
  124. print('Status: ' + str(http_code) + ' ' + http_status)
  125. print(response_content_type + "\n\n")
  126.  
  127. # A Response Body Build
  128. response_bodies['message'] = http_status
  129. response_body = json.dumps(response_bodies)
  130.  
  131. # The Response Body Feed
  132. print(response_body)
  133.  
  134. # Excecution
  135. limitSecondsAccess()

7. "PHP" Sample Code

Seconds Access Limiter with PHP Version: PHP-5
  1. <?php
  2. # Definition
  3. function limitSecondsAccess()
  4. {
  5. try {
  6. # Init
  7. ## Access Timestamp Build
  8. $sec_usec_timestamp = microtime(true);
  9. list($sec_timestamp, $usec_timestamp) = explode('.', $sec_usec_timestamp);
  10.  
  11. ## Access Limit Default Value
  12. ### Depends on Specifications: For Example 10
  13. $access_limit = 10;
  14.  
  15. ## Roots Build
  16. ### Depends on Environment: For Example '/tmp'
  17. $tmp_root = '/tmp';
  18. $access_root = $tmp_root . '/access';
  19.  
  20. ## Auth Key
  21. ### Depends on Specifications: For Example 'app_id'
  22. $auth_key = 'app_id';
  23.  
  24. ## Response Content-Type
  25. ## Depends on Specifications: For Example JSON and UTF-8
  26. $response_content_type = 'Content-Type: application/json; charset=utf-8';
  27.  
  28. ## Response Bodies Build
  29. ### Depends on Design
  30. $response_bodies = array();
  31.  
  32. # Authorized Key Check
  33. if (empty($_REQUEST[$auth_key])) {
  34. throw new Exception('Unauthorized', 401);
  35. }
  36. $auth_id = $_REQUEST[$auth_key];
  37.  
  38. # The Auth Root Build
  39. $auth_root = $access_root . '/' . $auth_id;
  40.  
  41. # The Auth Root Check
  42. if (! is_dir($auth_root)) {
  43. ## The Auth Root Creation
  44. if (! mkdir($auth_root, 0775, true)) {
  45. throw new Exception('Could not create the auth root. ' . $auth_root, 500);
  46. }
  47. }
  48.  
  49. # A Access File Creation Using Micro Timestamp
  50. /* For example, other data resources such as memory cache or RDB transaction.
  51. * In the case of this sample code, it is lightweight because it does not require file locking and transaction processing.
  52. * However, in the case of a cluster configuration, file system synchronization is required.
  53. */
  54. $access_file_path = $auth_root . '/' . strval($sec_usec_timestamp);
  55. if (! touch($access_file_path)) {
  56. throw new Exception('Could not create the access file. ' . $access_file_path, 500);
  57. }
  58.  
  59. # The Auth Root Scanning
  60. if (! $base_names = scandir($auth_root)) {
  61. throw new Exception('Could not scan the auth root. ' . $auth_root, 500);
  62. }
  63.  
  64. # The Access Counts Check
  65. $access_counts = 0;
  66. foreach ($base_names as $base_name) {
  67. ## A current or parent dir
  68. if ($base_name === '.' || $base_name === '..') {
  69. continue;
  70. }
  71.  
  72. ## A Access File Path Build
  73. $file_path = $auth_root . '/' . $base_name;
  74.  
  75. ## Not File Type
  76. if (! is_file($file_path)) {
  77. continue;
  78. }
  79.  
  80. ## The Base Name to Integer Data Type
  81. $base_name_sec_timestamp = intval($base_name);
  82.  
  83. ## Same Seconds Timestamp
  84. if ($sec_timestamp === $base_name_sec_timestamp) {
  85. ## The Base Name to Float Data Type
  86. $base_name_sec_usec_timestamp = floatval($base_name);
  87.  
  88. ### A Overtaken Processing
  89. if ($sec_usec_timestamp < $base_name_sec_usec_timestamp) {
  90. continue;
  91. }
  92.  
  93. ### Access Counts Increment
  94. $access_counts++;
  95.  
  96. ### Too Many Requests
  97. if ($access_counts > $access_limit) {
  98. throw new Exception('Too Many Requests', 429);
  99. }
  100.  
  101. continue;
  102. }
  103.  
  104. ## Past Access Files Garbage Collection
  105. if ($sec_timestamp > $base_name_sec_timestamp) {
  106. @unlink($file_path);
  107. }
  108. }
  109. } catch (Exception $e) {
  110. # The Exception to HTTP Status Code
  111. $http_code = $e->getCode();
  112. $http_status = $e->getMessage();
  113.  
  114. # 4xx
  115. if ($http_code >= 400 && $http_code <= 499) {
  116. # logging
  117. ## snip...
  118. # 5xx
  119. } else if ($http_code >= 500) {
  120. # logging
  121. ## snip...
  122.  
  123. # The Exception Message to HTTP Status
  124. $http_status = 'foo';
  125. # Others
  126. } else {
  127. # Logging
  128. ## snip...
  129.  
  130. # HTTP Status Code for The Response
  131. $http_status = 'Internal Server Error';
  132. $http_code = 500;
  133. }
  134.  
  135. # Response Headers Feed
  136. header('HTTP/1.1 ' . $http_code . ' ' . $http_status);
  137. header($response_content_type);
  138.  
  139. # A Response Body Build
  140. $response_bodies['message'] = $http_status;
  141. $response_body = json_encode($response_bodies);
  142. # The Response Body Feed
  143. exit($response_body);
  144. }
  145. }
  146.  
  147. # Execution
  148. limitSecondsAccess();
  149. ?>

8. "Ruby" Sample Code

Seconds Access Limiter with Ruby Version: Ruby-2
  1. # Definition#!/usr/bin/ruby
  2. # -*- coding: utf-8 -*-
  3.  
  4. require 'time'
  5. require 'fileutils'
  6. require 'cgi'
  7. require 'json'
  8.  
  9. def limitScondsAccess
  10.  
  11. begin
  12. # Init
  13. ## Access Timestamp Build
  14. time = Time.now
  15. sec_timestamp = time.to_i
  16. sec_usec_timestamp_string = "%10.6f" % time.to_f
  17. sec_usec_timestamp = sec_usec_timestamp_string.to_f
  18.  
  19. ## Access Limit Default Value
  20. ### Depends on Specifications: For Example 10
  21. access_limit = 10
  22.  
  23. ## Roots Build
  24. ### Depends on Environment: For Example '/tmp'
  25. tmp_root = '/tmp'
  26. access_root = tmp_root + '/access'
  27.  
  28. ## Auth Key
  29. ### Depends on Specifications: For Example 'app_id'
  30. auth_key = 'app_id'
  31.  
  32. ## Response Content-Type
  33. ### Depends on Specifications: For Example JSON and UTF-8
  34. response_content_type = 'application/json'
  35. response_charset = 'utf-8'
  36.  
  37. ## Response Bodies Build
  38. ### Depends on Design
  39. response_bodies = {}
  40.  
  41. # Authorized Key Check
  42. cgi = CGI.new
  43. if ! cgi.has_key?(auth_key) then
  44. raise 'Unauthorized:401'
  45. end
  46. auth_id = cgi[auth_key]
  47.  
  48. # The Auth Root Build
  49. auth_root = access_root + '/' + auth_id
  50.  
  51. # The Auth Root Check
  52. if ! FileTest::directory?(auth_root) then
  53. # The Auth Root Creation
  54. if ! FileUtils.mkdir_p(auth_root, :mode => 0775) then
  55. raise 'Could not create the auth root. ' + auth_root + ':500'
  56. end
  57. end
  58.  
  59. # A Access File Creation Using Micro Timestamp
  60. ## For example, other data resources such as memory cache or RDB transaction.
  61. ## In the case of this sample code, it is lightweight because it does not require file locking and transaction processing.
  62. ## However, in the case of a cluster configuration, file system synchronization is required.
  63. access_file_path = auth_root + '/' + sec_usec_timestamp.to_s
  64. if ! FileUtils::touch(access_file_path) then
  65. raise 'Could not create the access file. ' + access_file_path + ':500'
  66. end
  67.  
  68. # The Access Counts Check
  69. access_counts = 0
  70. Dir.glob(auth_root + '/*') do |access_file_path|
  71.  
  72. # Not File Type
  73. if ! FileTest::file?(access_file_path) then
  74. next
  75. end
  76.  
  77. # The File Path to The Base Name
  78. base_name = File.basename(access_file_path)
  79.  
  80. # The Base Name to Integer Data Type
  81. base_name_sec_timestamp = base_name.to_i
  82.  
  83. # Same Seconds Timestamp
  84. if sec_timestamp == base_name_sec_timestamp then
  85.  
  86. ### The Base Name to Float Data Type
  87. base_name_sec_usec_timestamp = base_name.to_f
  88.  
  89. ### A Overtaken Processing
  90. if sec_usec_timestamp < base_name_sec_usec_timestamp then
  91. next
  92. end
  93.  
  94. ### Access Counts Increment
  95. access_counts += 1
  96.  
  97. ### Too Many Requests
  98. if access_counts > access_limit then
  99. raise 'Too Many Requests:429'
  100. end
  101.  
  102. next
  103. end
  104.  
  105. # Past Access Files Garbage Collection
  106. if sec_timestamp > base_name_sec_timestamp then
  107. File.unlink access_file_path
  108. end
  109. end
  110.  
  111. # The Response Feed
  112. cgi.out({
  113. ## Response Headers Feed
  114. 'type' => 'text/html',
  115. 'charset' => response_charset,
  116. }) {
  117. ## The Response Body Feed
  118. ''
  119. }
  120.  
  121. rescue => e
  122. # Exception to HTTP Status Code
  123. messages = e.message.split(':')
  124. http_status = messages[0]
  125. http_code = messages[1]
  126.  
  127. # 4xx
  128. if http_code >= '400' && http_code <= '499' then
  129. # logging
  130. ## snip...
  131. # 5xx
  132. elsif http_code >= '500' then
  133. # logging
  134. ## snip...
  135.  
  136. # The Exception Message to HTTP Status
  137. http_status = 'foo'
  138. else
  139. # Logging
  140. ## snip...
  141.  
  142. # HTTP Status Code for The Response
  143. http_status = 'Internal Server Error'
  144. http_code = '500'
  145. end
  146.  
  147. # The Response Body Build
  148. response_bodies['message'] = http_status
  149. response_body = JSON.generate(response_bodies)
  150.  
  151. # The Response Feed
  152. cgi.out({
  153. ## Response Headers Feed
  154. 'status' => http_code + ' ' + http_status,
  155. 'type' => response_content_type,
  156. 'charset' => response_charset,
  157. }) {
  158. ## The Response Body Feed
  159. response_body
  160. }
  161. end
  162. end
  163.  
  164. limitScondsAccess

9. "Perl" Sample Code

Seconds Access Limiter with Perl Version: Perl-5
  1. #!/usr/bin/perl
  2.  
  3. use strict;
  4. use warnings;
  5. use utf8;
  6. use Time::HiRes qw(gettimeofday);
  7. use CGI;
  8. use File::Basename;
  9. use JSON;
  10.  
  11. # Definition
  12. sub limitSecondsAccess {
  13.  
  14. eval {
  15. # Init
  16. ## Access Timestamp Build
  17. my ($sec_timestamp, $usec_timestamp) = gettimeofday();
  18. my $sec_usec_timestamp = ($sec_timestamp . '.' . $usec_timestamp) + 0;
  19.  
  20. ## Access Limit Default Value
  21. ### Depends on Specifications: For Example 10
  22. my $access_limit = 10;
  23.  
  24. ## Roots Build
  25. ### Depends on Environment: For Example '/tmp'
  26. my $tmp_root = '/tmp';
  27. my $access_root = $tmp_root . '/access';
  28.  
  29. ## Auth Key
  30. ### Depends on Specifications: For Example 'app_id'
  31. my $auth_key = 'app_id';
  32.  
  33. ## Response Content-Type
  34. ## Depends on Specifications: For Example JSON and UTF-8
  35.  
  36. ## Response Bodies Build
  37. ### Depends on Design
  38. my %response_bodies;
  39.  
  40. # Authorized Key Check
  41. my $CGI = new CGI;
  42. if (! defined($CGI->param($auth_key))) {
  43. die('Unauthorized`401`');
  44. }
  45. my $auth_id = $CGI->param($auth_key);
  46.  
  47. # The Auth Root Build
  48. my $auth_root = $access_root . '/' . $auth_id;
  49.  
  50. # The Access Root Check
  51. if (! -d $access_root) {
  52. ## The Access Root Creation
  53. if (! mkdir($access_root)) {
  54. die('Could not create the access root. ' . $access_root . '`500`');
  55. }
  56. }
  57.  
  58. # The Auth Root Check
  59. if (! -d $auth_root) {
  60. ## The Auth Root Creation
  61. if (! mkdir($auth_root)) {
  62. die('Could not create the auth root. ' . $auth_root . '`500`');
  63. }
  64. }
  65.  
  66. # A Access File Creation Using Micro Timestamp
  67. ## For example, other data resources such as memory cache or RDB transaction.
  68. ## In the case of this sample code, it is lightweight because it does not require file locking and transaction processing.
  69. ## However, in the case of a cluster configuration, file system synchronization is required.
  70. my $access_file_path = $auth_root . '/' . $sec_usec_timestamp;
  71. if (! open(FH, '>', $access_file_path)) {
  72. close FH;
  73. die('Could not create the access file. ' . $access_file_path . '`500`');
  74. }
  75. close FH;
  76.  
  77. # The Auth Root Scanning
  78. my @file_pathes = glob($auth_root . "/*");
  79. if (! @file_pathes) {
  80. die('Could not scan the auth root. ' . $auth_root . '`500`');
  81. }
  82.  
  83. # The Access Counts Check
  84. my $access_counts = 0;
  85. foreach my $file_path (@file_pathes) {
  86.  
  87. ## Not File Type
  88. if (! -f $file_path) {
  89. next;
  90. }
  91.  
  92. ## The Base Name Extract
  93. my $base_name = basename($file_path);
  94.  
  95. ## The Base Name to Integer Data Type
  96. my $base_name_sec_timestamp = int($base_name);
  97.  
  98. ## Same Seconds Timestamp
  99. if ($sec_timestamp eq $base_name_sec_timestamp) {
  100. ## The Base Name to Float Data Type
  101. my $base_name_sec_usec_timestamp = $base_name;
  102.  
  103. ### A Overtaken Processing
  104. if ($sec_usec_timestamp lt $base_name_sec_usec_timestamp) {
  105. next;
  106. }
  107.  
  108. ### Access Counts Increment
  109. $access_counts++;
  110.  
  111. ### Too Many Requests
  112. if ($access_counts > $access_limit) {
  113. die("Too Many Requests`429`");
  114. }
  115.  
  116. next;
  117. }
  118.  
  119. ## Past Access Files Garbage Collection
  120. if ($sec_timestamp gt $base_name_sec_timestamp) {
  121. unlink($file_path);
  122. }
  123. }
  124. };
  125.  
  126. if ($@) {
  127. # Error Elements Extract
  128. my @e = split(/`/, $@);
  129.  
  130. # Exception to HTTP Status Code
  131. my $http_status = $e[0];
  132. my $http_code = '0';
  133. if (defined($e[1])) {
  134. $http_code = $e[1];
  135. }
  136.  
  137. # 4xx
  138. if ($http_code ge '400' && $http_code le '499') {
  139. # logging
  140. ## snip...
  141. # 5xx
  142. } elsif ($http_code ge '500') {
  143. # logging
  144. ## snip...
  145.  
  146. ## The Exception Message to HTTP Status
  147. $http_status = 'foo';
  148. # Others
  149. } else {
  150. # logging
  151. ## snip...
  152.  
  153. $http_status = 'Internal Server Error';
  154. $http_code = '500';
  155. }
  156.  
  157. # Response Headers Feed
  158. print("Status: " . $http_code . " " . $http_status . "\n");
  159. print('Content-Type: application/json; charset=utf-8' . "\n\n");
  160.  
  161. # A Response Body Build
  162. my %response_bodies;
  163. $response_bodies{'message'} = $http_status;
  164. $a = \%response_bodies;
  165. my $response_body = encode_json($a);
  166.  
  167. # The Response Body Feed
  168. print($response_body);
  169. }
  170.  
  171. }
  172.  
  173. # #Excecution
  174. &limitSecondsAccess();

10. Conclusion

In this post, I exemplified a sample of “One-Second Access limiter” solution using Python, PHP, Ruby and Perl interpreter languages. Because of the nature of “access control for one second”, it will be understood that low load, high speed processing and data consistency are required. Therefore, although there are some important points, they are as described in the architecture section. In this post, I showed a solution using file name and file number of file system. However, in a clustered environment, it is unsuitable for this architecture if the selected data synchronization solution is slow. In such cases, the asynchronous data architecture may be one of the options rather. In such a case, control is made on a per-node basis. Furthermore, the importance of the load balancing threshold is increased, and the precision of the access limitation and consistency of the result must be abandoned. However, unless precision of access limitation and consistency of results are required, it is also one.

2017-11-19

Load Balancer with “LVS + Keepalived + DSR”

> to Japanese Pages

1. Summary

In this post, I will explain the effectiveness of the load balancer solution by “LVS + Keepalived + DSR” design technology and explain how to build it.

2. Introduction

The load balancer solution by “LVS + Keepalived + DSR” is a mature technology but I have posted this solution because I was asked by my friends. For highly scalable projects, the topic of the load balancer is an agenda at least once in the system performance meeting. I have done a lot of such experiences. And we will have the opportunity to hear negative opinions about the performance of the software load balancer. In such a case, the name of a hardware load balancer like BIG-IP sometimes comes up to the topic of that agenda. However, we can not miss the fact that a load balancer using “LVS + Keepalived + DSR” design technology runs at 100% SLA and 10% load factor in our project receiving 1 million accesses per day. This demonstrates that this design technology is one of the effective load balancer solutions in cloud hosting without load balancer PaaS or on premises. Such a result is brought about by using the communication method called Direct Server Return (DSR). The dramatic load reduction of the load balancer is realized by the feature of “returning it directly to the client without going through communication from the lower node” of the DSR. In addition, this solution is not affected by various hardware related problems (failure, deterioration, support contract, support quality, end of product support, etc.). In this post, I will explain how to build “LVS + Keepalived + DSR” design. In addition, in this post, I will not specifically conduct benchmarks such as “DSR VS. Not DSR”.

3. Environment

In this post, I will explain the solution based on the following assumptions.
  1. CentOS 7
  2. Keepalived
  3. ipvsadm
  4. Firewalld
In this post, I will explain the solution based on the following system configuration diagram.

4. Install

First, we install the “Keeplived” on the Load Balancer 1.
$ sudo yum -y install keepalived
Next, we install the “Keeplived” on the Load Balancer 2.
$ sudo yum -y install keepalived
Next, we install the “ipvsadm” on the Load Balancer 1.
$ sudo yum -y install ipvsadm
Next, we install the “ipvsadm” on the Load Balancer 2.
$ sudo yum -y install ipvsadm

5. Configuration

Next, we configure the “firewalld” on the Web Server 1. We startup the “firewalld” and enable it.
$ sudo systemctl start firewalld
$ sudo systemctl enable firewalld
$ sudo systemctl status firewalld
We configure the “firewalld.”
$ sudo firewall-cmd --set-default-zone=internal
$ sudo firewall-cmd --add-port=22/tcp --zone=internal
$ sudo firewall-cmd --add-port=22/tcp --zone=internal --permanent
$ sudo firewall-cmd --add-port=80/tcp --zone=internal
$ sudo firewall-cmd --add-port=80/tcp --zone=internal --permanent
$ sudo firewall-cmd --add-port=443/tcp --zone=internal
$ sudo firewall-cmd --add-port=443/tcp --zone=internal --permanent
$ sudo firewall-cmd --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.3 -j REDIRECT
$ sudo firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.3 -j REDIRECT
$ sudo firewall-cmd --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.5 -j REDIRECT
$ sudo firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.5 -j REDIRECT
We reload the “firewalld” and confirm the configuration.
$ sudo firewall-cmd --reload
$ sudo firewall-cmd --list-all-zone
$ sudo firewall-cmd --direct --get-rule ipv4 nat PREROUTING
We use the “telnet” command to verify the communication of the Web Server 1.
$ sudo telnet 10.0.0.3 80
Next, we configure the “firewalld” on the Web Server 2. We startup the “firewalld” and enable it.
$ sudo systemctl start firewalld
$ sudo systemctl enable firewalld
$ sudo systemctl status firewalld
We configure the “firewalld.”
$ sudo firewall-cmd --set-default-zone=internal
$ sudo firewall-cmd --add-port=22/tcp --zone=internal
$ sudo firewall-cmd --add-port=22/tcp --zone=internal --permanent
$ sudo firewall-cmd --add-port=80/tcp --zone=internal
$ sudo firewall-cmd --add-port=80/tcp --zone=internal --permanent
$ sudo firewall-cmd --add-port=443/tcp --zone=internal
$ sudo firewall-cmd --add-port=443/tcp --zone=internal --permanent
$ sudo firewall-cmd --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.4 -j REDIRECT
$ sudo firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.4 -j REDIRECT
$ sudo firewall-cmd --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.5 -j REDIRECT
$ sudo firewall-cmd --permanent --direct --add-rule ipv4 nat PREROUTING 0 -d 10.0.0.5 -j REDIRECT
We reload the “firewalld” and confirm the configuration.
$ sudo firewall-cmd --reload
$ sudo firewall-cmd --list-all-zone
$ sudo firewall-cmd --direct --get-rule ipv4 nat PREROUTING
We use the “telnet” command to verify the communication of the Web Server 2.
$ sudo telnet 10.0.0.4 80
Next, we configure the “Keepalived” on the Load Balancer 1.
$ sudo cp -a /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.org
$ sudo vim /etc/keepalived/keepalived.conf
  1. ; Common Configuration Block
  2. global_defs {
  3. notification_email {
  4. alert@example.com
  5. }
  6. notification_email_from lb1@example.com
  7. smtp_server mail.example.com
  8. smtp_connect_timeout 30
  9. router_id lb1.example.com
  10. }
  11.  
  12. ; Master Configureation Block
  13. vrrp_instance VI_1 {
  14. state MASTER
  15. interface eth0
  16. virtual_router_id 1
  17. priority 101
  18. nopreempt
  19. advert_int 1
  20. authentication {
  21. auth_type PASS
  22. auth_pass foo
  23. }
  24. virtual_ipaddress {
  25. 10.0.0.5/24 dev eth0
  26. }
  27. }
  28.  
  29. ; Virtual Server Configureation Block
  30. virtusl_server 10.0.0.5 80 {
  31. delay_loop 6
  32. lvs_sched rr
  33. lvs_method DR
  34. persistence_timeout 50
  35. protocol TCP
  36. sorry_server 10.0.0.254 80
  37. real_server 10.0.0.3 80 {
  38. weight 1
  39. inhibit_on_failure
  40. HTTP_GET {
  41. url {
  42. path /
  43. status_code 200
  44. }
  45. connect_timeout 3
  46. nb_get_retry 3
  47. delay_before_retry 3
  48. }
  49. }
  50. real_server 10.0.0.4 80 {
  51. weight 1
  52. inhibit_on_failure
  53. HTTP_GET {
  54. url {
  55. path /
  56. status_code 200
  57. }
  58. connect_timeout 3
  59. nb_get_retry 3
  60. delay_before_retry 3
  61. }
  62. }
  63. }
$ sudo systemctl start keepalived
In case of failback prohibition, you should disable automatic startup of “Keepalived”.
$ :sudo systemctl enable keepalived
$ sudo systemctl status keepalived
$ sudo ip addr
Next, we configure the “Keepalived” on the Load Balancer 2.
$ sudo cp -a /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.org
$ sudo vim /etc/keepalived/keepalived.conf
  1. ; Common Configuration Block
  2. global_defs {
  3. notification_email {
  4. admin@example.com
  5. }
  6. notification_email_from lb2@example.com
  7. smtp_server mail.example.com
  8. smtp_connect_timeout 30
  9. router_id lb2.example.com
  10. }
  11.  
  12. ; Backup Configureation Block
  13. vrrp_instance VI_1 {
  14. state BACKUP
  15. interface eth0
  16. virtual_router_id 1
  17. priority 100
  18. nopreempt
  19. advert_int 1
  20. authentication {
  21. auth_type PASS
  22. auth_pass foo
  23. }
  24. virtual_ipaddress {
  25. 10.0.0.5/24 dev eth0
  26. }
  27. }
  28.  
  29. ; Virtual Server Configureation Block
  30. virtusl_server 10.0.0.5 80 {
  31. delay_loop 6
  32. lvs_sched rr
  33. lvs_method DR
  34. persistence_timeout 50
  35. protocol TCP
  36. sorry_server 10.0.0.254 80
  37. real_server 10.0.0.3 80 {
  38. weight 1
  39. inhibit_on_failure
  40. HTTP_GET {
  41. url {
  42. path /
  43. status_code 200
  44. }
  45. connect_timeout 3
  46. nb_get_retry 3
  47. delay_before_retry 3
  48. }
  49. }
  50. real_server 10.0.0.4 80 {
  51. weight 1
  52. inhibit_on_failure
  53. HTTP_GET {
  54. url {
  55. path /
  56. status_code 200
  57. }
  58. connect_timeout 3
  59. nb_get_retry 3
  60. delay_before_retry 3
  61. }
  62. }
  63. }
$ sudo systemctl start keepalived
In case of failback prohibition, you should disable automatic startup of “Keepalived”.
$ :sudo systemctl enable keepalived
$ sudo systemctl status keepalived
$ sudo ip addr
Next, we change the kernel parameters on the Load Balancer 1.
$ sudo vim /etc/sysctl.conf
  1. # Enable Packet Transfer between Interfaces
  2. net.ipv4.ip_forward = 1
  3.  
  4. # Do not discard packets from networks that do not belong to the interface.
  5. net.ipv4.conf.all.rp_filter = 0
We reflect the setting of the kernel parameters.
$ sudo sysctl -p
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 0
We startup the “ipvsadm.”
$ sudo touch /etc/sysconfig/ipvsadm
$ sudo systemctl start ipvsadm
In case of failback prohibition, you should disable automatic startup of “ipvsadm”.
$ :sudo systemctl enable ipvsadm
$ sudo systemctl status ipvsadm
Next, we change the kernel parameters on the Load Balancer 2.
$ sudo vim /etc/sysctl.conf
  1. # Enable Packet Transfer between Interfaces
  2. net.ipv4.ip_forward = 1
  3.  
  4. # Do not discard packets from networks that do not belong to the interface.
  5. net.ipv4.conf.all.rp_filter = 0
We reflect the setting of the kernel parameters.
$ sudo sysctl -p
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 0
We startup the “ipvsadm.”
$ sudo touch /etc/sysconfig/ipvsadm
$ sudo systemctl start ipvsadm
In case of failback prohibition, you should disable automatic startup of “ipvsadm”.
$ :sudo systemctl enable ipvsadm
$ sudo systemctl status ipvsadm
We will use the “ipvsadm” command to check the LVS communication settings on the Load Balancer 1.
$ sudo ipvsadm -Ln
We will use the “ipvsadm” command to check the LVS communication settings on the Load Balancer 2.
$ sudo ipvsadm -Ln

6. Conclusion

In this way, we can improve performance degradation against high load, which is a weak point of software load balancer, with the DSR technology.

2017-11-04

Surrogate Key VS. Natural Key

Surrogate Key VS. Natural Key

The other day, I discussed a "Surrogate Key VS. Natural Key" in a development project.

I sometimes come across such discussions.

This will be a brush up of my post in the past but I will post the best solution for this problem.

Furthermore, this is not only the case of the title of this post but also the basic way of thinking and solution of problems for such type of discussion.

If you are suffering about this matter in the design of the RDBMS. For your information.


If we want to solve the problem of this discussion, we must first change the recognition of the surrogate key to a artificial key before you get into the main theme.

First of all, we have to solve from the misunderstanding of "Surrogate Key VS. Natural Key" controversy contagious in the world.

The true meaning of this discussion should be "Artificial Key VS. Natural Key".

A natural key is a primary key designed by a single entity attribute or a combination of a plurality of entity attributes as you know.

A surrogate key is a primary key designed as a substitute for a natural key when it is difficult to design a natural key.

An artificial key is a primary key designed to increment an integer value mechanically, irrespective of the natural key design.

Therefore, even natural key believers, if it is difficult to design a natural key, they use the surrogate key as a matter of course.

However, it can be said that the artificial key faction does not use the natural key almost.

From the above, the misunderstanding of the "Surrogate Key VS. Natural Key" controversy would have been solved.

If you try to advance the discussion while misunderstanding this, there is a possibility that the argument may go off, so it would be better to first be aware of the misunderstanding.

Therefore, hereinafter, I will name the title "Artificial Key VS. Natural Key".


Natural key believers like natural keys in terms of the beauty of relational models and the pursuit of data design.

This trend is common among engineers who grew up with DBA and good old design method.

Meanwhile, the artificial key faction tends to favor artificial keys from aspects such as framework regulation, reduction of SQL bugs and simplicity of relations.

This trend is common among programmers and engineers who grew up with recent speed design.

There are reasons why I chose the words "believer" and "faction" in the above, but I will explain in detail later.

In the RDBMS design, "Artificial Key VS. Natural Key" has both merits and demerits in both cases.

If you are a top engineer, you must clearly understand that the criteria for choosing designs must be based on the objectives and priorities of the project.

If you are suffering from the problem of this discussion, the solution is simple.

The only thing we should do is to investigate the merits and demerits and judge it according to the situation of the project.

That's it.

We should seek both opinions and know the experience for the purpose of the project.

Therefore, in all situations, there is never a fact that either one is absolutely correct.

If we misunderstand that just the correctness of both opinions is the purpose, the problem of this discussion of the project will probably not be solved forever.

If we discuss at a level other than the purpose of the project, this sort of discussion will quickly evolve into a controversy due to the personal aspect.

If we do not have the purpose consciousness of the project, we will judge with a more subjective impression.

Why is this because, in each premise, each is correct.

For this reason, I used the words "believer" and "faction" as above.

Therefore, the only solution to this discussion is to match the members' sense of purpose in the project.

In other words, matching a purpose consciousness means that we need "ability to see the essence" and "organization development capability".

2017-10-07

Integration of PHP Error Handling


Integration of PHP Error Handling

Last week, on how to integrate PHP 5 error handling, I was asked to explain at multiple development projects.

As used herein, "Integration of PHP Error Handling" means, for example, the following development requirements.
  1. # Normal Error
  2. # Exception
  3. # PHP Core Error
  4. We want to integrate and manage error handling such as logs and mails when these occur.
In order to realize the above, you need to understand the habit and mechanism of PHP error.
Here, we describe these realization methods.


Notice

  1. # In this article, we will focus on these implementation methods.
  2. # In this article, we do not mention general concepts such as "difference between error and exception".
  3. # In this article, we describe it only for suspend processing.
  4. # In this article, we do not mention restoration processing, trigger, processing level etc.
  5. # This article applies only to PHP 5. It does not apply to PHP 7.


Flow

One way to realize this "PHP error integration processing" is the flow of error processing as follows.
  1. Normal Error Processing Normal Error
  2. Exception Processing Exception
  3. Shutdown Processing PHP Core Error


Normal Error Handling

First, take handling authority of PHP Normal Error or User-Defined Error from PHP.
In PHP, the following functions are prepared.
mixed set_error_handler(callable $error_handler [, int $error_types = E_ALL | E_STRICT])
In order to take processing authority from PHP, create a callback function and register it in this function.
(Be sure to register it as early as possible of a series of request processing.)
Within the callback function, take the normal error and rethrow as an exception.
In short, the goal is to take the normal error handling authority from PHP, and pass it to the exception.
However, in this callback function, it is one point that PHP core error can not be taken.
  1. public function handleError()
  2. {
  3. //* Error Handler Definition
  4. function handleError($_number, $_message, $_file, $_line, $_contexts)
  5. {
  6. //* Not Includ Error Reporting
  7. if (! (error_reporting() & $_number)) {
  8. return;
  9. }
  10. //* to ErrorException
  11. throw new ErrorException($_message, 500, $_number, $_file, $_line);
  12. }
  13.  
  14. //* Error Handler Set
  15. set_error_handler('handleError');
  16. }


Exception Handling

Next, take exception processing authority which was not caught from PHP.
In PHP, the following functions are prepared.
callable set_exception_handler(callable $exception_handler)
In order to take processing authority from PHP, create a callback function and register it in this function.
(Be sure to register it as early as possible of a series of request processing.)
As a result, all normal errors and all uncaught exceptions are aggregated in one place.
But this is not enough.
We have not taken PHP Core Error yet.
Therefore, processing logic is not placed here.
  1. public function handleException()
  2. {
  3. //* Exception Handler Definition
  4. function handleException($_e)
  5. {
  6. //* Exception Context
  7. $_SERVER['X_EXCEPTION_HANDLER_CONTEXT'] = $_e;
  8.  
  9. //* Error Processing to Shutdown Logic
  10. exit;
  11. }
  12.  
  13. //* Exception Handler Set
  14. set_exception_handler('handleException');
  15. }


PHP Core Error Handling

In PHP 5, set_error_handler () can not take the processing authority of core error issued by PHP.
PHP 5 does not throw an exception of core error.
Therefore, in order to capture the PHP core error, the following function is used.
void register_shutdown_function(callable $callback [, mixed $parameter [, mixed $... ]])
This function makes it possible to register a callback function to be executed when script processing is completed or when exit () is called.
Utilizing this property, it is possible to integrate all processing such as error, exception, PHP core error, etc. as a result.
  1. public function handleShutdown($_error_mails = array())
  2. {
  3. //* Shutdown Function Definition
  4. function handleShutdown($_error_numbers = array(), $_error_mails = array(), $_http_status_codes = array())
  5. {
  6. //* Exception or Error
  7. if (! empty($_SERVER['X_EXCEPTION_HANDLER_CONTEXT'])) {
  8. $e = $_SERVER['X_EXCEPTION_HANDLER_CONTEXT'];
  9. unset($_SERVER['X_EXCEPTION_HANDLER_CONTEXT']);
  10. $message = $e->__toString();
  11. $code = $e->getCode();
  12. } else {
  13. $e = error_get_last();
  14. //* Normal Exit
  15. if (empty($e)) {
  16. return;
  17. }
  18.  
  19. //* Core Error
  20. $message = $_error_numbers[$e['type']] . ': ' . $e['message'] . ' in ' . $e['file'] . ' on line ' . $e['line'];
  21. $code = 500;
  22. }
  23.  
  24. //* Error Logging
  25. error_log($message, 4);
  26.  
  27. //* Error Mail
  28. $cmd = 'echo "' . $message . '" | mail -S "smtp=smtp://' . $_error_mails['host'] . '" -r "' . $_error_mails['from'] . '" -s "' . $_error_mails['subject'] . '" ' . $_error_mails['to'];
  29. $outputs = array();
  30. $status = null;
  31. $last_line = exec($cmd, $outputs, $status);
  32.  
  33. //* HTTP Status Code
  34. header('HTTP/1.1 ' . $code . ' ' . $_http_status_codes[$code]);
  35.  
  36. //* Shutdown
  37. exit($code . ' ' . $_http_status_codes[$code]);
  38. }
  39.  
  40. //* Shutdown Function Registration
  41. $error_numbers = self::$error_numbers;
  42. $http_status_codes = self::$http_status_codes;
  43. register_shutdown_function('handleShutdown', $error_numbers, $_error_mails, $http_status_codes);
  44. }


to Class Library

When these are made into a general purpose class library, it becomes as follows.
Logging, e-mail, exception context delivery, etc. should be changed according to circumstances.
  1. class AppE
  2. {
  3.  
  4. public static $error_numbers = array(
  5. 1 => 'Fatal',
  6. 2 => 'Warning',
  7. 4 => 'Parse Error',
  8. 8 => 'Notice',
  9. 16 => 'Core Fatal',
  10. 32 => 'Core Warning',
  11. 64 => 'Compile Error',
  12. 128 => 'Compile Warning',
  13. 256 => 'Ex Error',
  14. 512 => 'Ex Warning',
  15. 1024 => 'Ex Notice',
  16. 2048 => 'Strict Error',
  17. 4096 => 'Recoverable Error',
  18. 8192 => 'Deprecated',
  19. 16384 => 'Ex Deprecated',
  20. 32767 => 'All',
  21. );
  22.  
  23. //* HTTP Status Code
  24. public static $http_status_codes = array(
  25. 'default' => 200,
  26. 100 => 'Continue',
  27. 101 => 'Switching Protocols',
  28. 102 => 'Processing',
  29. 200 => 'OK',
  30. 201 => 'Created',
  31. 202 => 'Accepted',
  32. 203 => 'Non-Authoritative Information',
  33. 204 => 'No Content',
  34. 205 => 'Reset Content',
  35. 206 => 'Partial Content',
  36. 207 => 'Multi-Status',
  37. 226 => 'IM Used',
  38. 300 => 'Multiple Choices',
  39. 301 => 'Moved Permanently',
  40. 302 => 'Found',
  41. 303 => 'See Other',
  42. 304 => 'Not Modified',
  43. 305 => 'Use Proxy',
  44. 307 => 'Temporary Redirect',
  45. 400 => 'Bad Request',
  46. 401 => 'Unauthorized',
  47. 402 => 'Payment Required',
  48. 403 => 'Forbidden',
  49. 404 => 'Not Found',
  50. 405 => 'Method Not Allowed',
  51. 406 => 'Not Acceptable',
  52. 407 => 'Proxy Authentication Required',
  53. 408 => 'Request Timeout',
  54. 409 => 'Conflict',
  55. 410 => 'Gone',
  56. 411 => 'Length Required',
  57. 412 => 'Precondition Failed',
  58. 413 => 'Request Entity Too Large',
  59. 414 => 'Request-URI Too Long',
  60. 415 => 'Unsupported Media Type',
  61. 416 => 'Requested Range Not Satisfiable',
  62. 417 => 'Expectation Failed',
  63. 418 => "I'm a teapot",
  64. 422 => 'Unprocessable Entity',
  65. 423 => 'Locked',
  66. 424 => 'Failed Dependency',
  67. 426 => 'Upgrade Required',
  68. 500 => 'Internal Server Error',
  69. 501 => 'Not Implemented',
  70. 502 => 'Bad Gateway',
  71. 503 => 'Service Unavailable',
  72. 504 => 'Gateway Timeout',
  73. 505 => 'HTTP Version Not Supported',
  74. 506 => 'Variant Also Negotiates',
  75. 507 => 'Insufficient Storage',
  76. 509 => 'Bandwidth Limit Exceeded',
  77. 510 => 'Not Extended',
  78. );
  79.  
  80.  
  81. public function __construct()
  82. {}
  83.  
  84.  
  85. public function handleError()
  86. {
  87. //* Error Handler Definition
  88. function handleError($_number, $_message, $_file, $_line, $_contexts)
  89. {
  90. //* Not Includ Error Reporting
  91. if (! (error_reporting() & $_number)) {
  92. return;
  93. }
  94. //* to ErrorException
  95. throw new ErrorException($_message, 500, $_number, $_file, $_line);
  96. }
  97.  
  98. //* Error Handler Set
  99. set_error_handler('handleError');
  100. }
  101.  
  102.  
  103. public function handleException()
  104. {
  105. //* Exception Handler Definition
  106. function handleException($_e)
  107. {
  108. //* Exception Context
  109. $_SERVER['X_EXCEPTION_HANDLER_CONTEXT'] = $_e;
  110.  
  111. //* Error Processing to Shutdown Logic
  112. exit;
  113. }
  114.  
  115. //* Exception Handler Set
  116. set_exception_handler('handleException');
  117. }
  118.  
  119.  
  120. public function handleShutdown($_error_mails = array())
  121. {
  122. //* Shutdown Function Definition
  123. function handleShutdown($_error_numbers = array(), $_error_mails = array(), $_http_status_codes = array())
  124. {
  125. //* Exception or Error
  126. if (! empty($_SERVER['X_EXCEPTION_HANDLER_CONTEXT'])) {
  127. $e = $_SERVER['X_EXCEPTION_HANDLER_CONTEXT'];
  128. unset($_SERVER['X_EXCEPTION_HANDLER_CONTEXT']);
  129. $message = $e->__toString();
  130. $code = $e->getCode();
  131. } else {
  132. $e = error_get_last();
  133. //* Normal Exit
  134. if (empty($e)) {
  135. return;
  136. }
  137.  
  138. //* Core Error
  139. $message = $_error_numbers[$e['type']] . ': ' . $e['message'] . ' in ' . $e['file'] . ' on line ' . $e['line'];
  140. $code = 500;
  141. }
  142.  
  143. //* Error Logging
  144. error_log($message, 4);
  145.  
  146. //* Error Mail
  147. $cmd = 'echo "' . $message . '" | mail -S "smtp=smtp://' . $_error_mails['host'] . '" -r "' . $_error_mails['from'] . '" -s "' . $_error_mails['subject'] . '" ' . $_error_mails['to'];
  148. $outputs = array();
  149. $status = null;
  150. $last_line = exec($cmd, $outputs, $status);
  151.  
  152. //* HTTP Status Code
  153. header('HTTP/1.1 ' . $code . ' ' . $_http_status_codes[$code]);
  154.  
  155. //* Shutdown
  156. exit($code . ' ' . $_http_status_codes[$code]);
  157. }
  158.  
  159. //* Shutdown Function Registration
  160. $error_numbers = self::$error_numbers;
  161. $http_status_codes = self::$http_status_codes;
  162. register_shutdown_function('handleShutdown', $error_numbers, $_error_mails, $http_status_codes);
  163. }
  164.  
  165. }


Afterword

Many PHP frameworks provide interfaces for extended error handlers and extended exception handlers, but their contents merely implement similar things.
Recently, when the story of this layer came up, the number of engineers who understood these technologies has become really few.
This is what has been said from around the time the non-Java web application framework came out.
I realize that it has become a reality at many development sites.
A lot of recent engineers use the framework, that is not a means, it has become a purpose.
It can be seen from the fact that the number of engineers who do not know the basis of Web application development has increased.
In this way, the factor that the number of tool engineers has increased.
One of them is the increase in speed development method like Silicon Valley.
It is by no means negative.
In short, if we categorize the category of development engineers more subdivided, the misrecognition of mutual recognition will probably decrease.

2017-06-25

Distributed Model Semantics

I sometimes receive questions from engineers at lectures on Distributed and Cooperative Autonomous Network Systems, ML, working groups and others.

"Chief, I think that model is not a Distributed Model, but a Replicated Model."

Many engineers misunderstand how to catch this semantics.
In the case of this semantics, the Distributed Model is as follows.

∀x ( x ∈ Replicated → x ∈ Deistributed ) Replicated ⊆ Distributed ∀x ( x ∈ Segmented → x ∈ Deistributed ) Segmented ⊆ Distributed Distributed = { Replicated, Segmented, ... } Replicated != Segmented

Why does such a misunderstanding happen?
What can be considered as the cause is ambiguous explanation of RAID level or ambiguous naming of distributed system middleware.

For example, although not strictly a mistake,

ex.) Striping of raid 0 is Distributed Model.
ex.) Mirroring of raid 1 is Replicated Model.

As it explains, many engineers misunderstand.
Both RAID 0 and RAID 1 are Distributed Model.

The RAID 0 Model distributes the data after spliting the data.
The RAID 1 Model distributes the data after replicating the data.

If I write so far, you will understand anymore.
Both are Distributed Model.

Of course, it is a Distributed Model because it distributes data.
If you do not distribute and arrange the data, it is not a Distributed Model.

This semantics also does not depend on distributing units.
Whether it is a bit unit, a block unit, a chunk unit, a record unit, a table unit, a file unit, a data resource unit, a node unit, or a cluster It might be a unit.

These are all Distributed Models for the purpose such as HA.

Hot Standby HA Architecture Pattern on AWS EC2


Hot Standby HA of No E/ALB by Unicast VRRP

This Hot Standby HA Architecture Pattern realizes VRRP monitor by Unicast in the AWS network that Multicast can not use.
In particular, this design is a useful HA architecture pattern in staging environments of small projects and so on which costs such as E/ALB SaaS need not be paid.


EC2 + RHEL7 + Unicast VRRP + Failure Scripts

  • IaaS: AWS EC2
  • OS: RHEL 7 or CentOS 7
  • Unicast VRRP: keepalived
  • Failover & Failback Scripts: Bash + AWS CLI


keepalived Install & Configuration

$ : Node 1
$ sudo yum -y install keepalived
$ sudo cp -a /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.org
$ sudo vim /etc/keepalived/keepalived.conf
  1. ! Configuration File for keepalived
  2.  
  3. global_defs {
  4. notification_email {
  5. admin@example.com
  6. }
  7. notification_email_from node1@example.com
  8. smtp_server mail.example.com
  9. smtp_connect_timeout 30
  10. router_id node1.example.com
  11. }
  12.  
  13. vrrp_instance VI_0 {
  14. state MASTER
  15. interface eth0
  16. virtual_router_id 10
  17. priority 101
  18. nopreempt
  19. advert_int 1
  20. authentication {
  21. auth_type PASS
  22. auth_pass foo
  23. }
  24.  
  25. ! VIP
  26. virtual_ipaddress {
  27. 10.10.10.10 dev eth0
  28. }
  29.  
  30. ! Node 1
  31. unicast_src_ip 10.10.10.11
  32.  
  33. ! Node2
  34. unicast_peer {
  35. 10.10.10.12
  36. }
  37. }
$ : Node 2
$ sudo yum -y install keepalived
$ sudo cp -a /etc/keepalived/keepalived.conf /etc/keepalived/keepalived.conf.org
$ sudo vim /etc/keepalived/keepalived.conf
  1. ! Configuration File for keepalived
  2.  
  3. global_defs {
  4. notification_email {
  5. admin@example.com
  6. }
  7. notification_email_from node2@example.com
  8. smtp_server mail.example.com
  9. smtp_connect_timeout 30
  10. router_id node2.example.com
  11. }
  12.  
  13. vrrp_instance VI_0 {
  14. state BACKUP
  15. interface eth0
  16. virtual_router_id 10
  17. priority 100
  18. nopreempt
  19. advert_int 1
  20. authentication {
  21. auth_type PASS
  22. auth_pass foo
  23. }
  24.  
  25. ! VIP
  26. virtual_ipaddress {
  27. 10.10.10.10 dev eth0
  28. }
  29.  
  30. ! Node 2
  31. unicast_src_ip 10.10.10.12
  32.  
  33. ! Node 1
  34. unicast_peer {
  35. 10.10.10.11
  36. }
  37.  
  38. ! Failover Script
  39. notify_master "/etc/keepalived/failover.sh"
  40. }

Failback Script (Bash + AWS CLI)

$ : Node 1
$ : for manual failback
$ sudo touch /etc/keepalived/failback.sh
$ sudo vim /etc/keepalived/failback.sh
  1. #!/bin/bash
  2. # failback.sh
  3.  
  4. # LAN VIP
  5. VIP=10.10.10.10
  6.  
  7. # WAN VIP
  8. ALLOCATION_ID=eipalloc-xxxxxxx0
  9.  
  10. # Instance 1 eth0 IF
  11. INTERFACE_ID_1=eni-xxxxxxx1
  12.  
  13. # Instance 2 eth0 IF
  14. INTERFACE_ID_2=eni-xxxxxxx2
  15.  
  16. # Instance ID
  17. INSTANCE_ID=`curl -s http://169.254.169.254/latest/meta-data/instance-id`
  18.  
  19. # Auth
  20. export AWS_DEFAULT_REGION=`curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | rev | cut -c 2- | rev`
  21.  
  22. # LAN VIP Unassitnment
  23. aws ec2 unassign-private-ip-addresses --private-ip-addresses $VIP --network-interface-id $INTERFACE_ID_2
  24.  
  25. # LAN VIP Assignment
  26. aws ec2 assign-private-ip-addresses --private-ip-addresses $VIP --network-interface-id $INTERFACE_ID_1 --allow-reassignment
  27.  
  28. # WAN VIP Asoociation
  29. aws ec2 associate-address --allocation-id $ALLOCATION_ID --network-interface-id $INTERFACE_ID_1 --private-ip-address $VIP

Failover Script (Bash + AWS CLI)

$ : Node 2
$ : for auto failover
$ sudo touch /etc/keepalived/faiover.sh
$ sudo vim /etc/keepalived/faiover.sh
  1. #!/bin/bash
  2. # failover.sh
  3.  
  4. # LAN VIP
  5. VIP=10.10.10.10
  6.  
  7. # WAN VIP
  8. ALLOCATION_ID=eipalloc-xxxxxxx0
  9.  
  10. # Instance 1 eth0 IF
  11. INTERFACE_ID_1=eni-xxxxxxx1
  12.  
  13. # Instance 2 eth0 IF
  14. INTERFACE_ID_2=eni-xxxxxxx2
  15.  
  16. # Instance ID
  17. INSTANCE_ID=`curl -s http://169.254.169.254/latest/meta-data/instance-id`
  18.  
  19. # Auth
  20. export AWS_DEFAULT_REGION=`curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | rev | cut -c 2- | rev`
  21.  
  22. # LAN VIP Unassitnment
  23. aws ec2 unassign-private-ip-addresses --private-ip-addresses $VIP --network-interface-id $INTERFACE_ID_1
  24.  
  25. # LAN VIP Assignment
  26. aws ec2 assign-private-ip-addresses --private-ip-addresses $VIP --network-interface-id $INTERFACE_ID_2 --allow-reassignment
  27.  
  28. # WAN VIP Asoociation
  29. aws ec2 associate-address --allocation-id $ALLOCATION_ID --network-interface-id $INTERFACE_ID_2 --private-ip-address $VIP

keepalived Daemon Start

$ : Node 1
$ sudo systemctl start keepalived
$ sudo systemctl enable keepalived
$ sudo systemctl status keepalived
$ sudo ip addr
$ : Node 2
$ sudo systemctl start keepalived
$ sudo systemctl enable keepalived
$ sudo systemctl status keepalived
$ sudo ip addr

Auto Failover Test

$ : Node 1
$ sudo systemctl stop keepalived
$ sudo systemctl status keepalived
$ sudo ip addr
$ : Node 2
$ sudo ip addr

Manual Failback Test

$ : Node 1
$ sudo systemctl start keepalived
$ sudo systemctl status keepalived
$ : Node 2
$ sudo /etc/keepalived/failback.sh
$ sudo ip addr
$ : Node 1
$ sudo ip addr