システム部の菊田です。 前回発表のAIによる自動アプリ生成機能が最速でリリースされました ありがたいことに、あちこちから注目いただき大きな反響をいただいております。
近年、サービスにAIが組み込まれるようになりはじめてからというもの、処理に時間がかかるリクエストが増えてきました。 せっかくサービスに対して、可能性を感じていただき、利用を開始してくださるお客様がいるのに、サーバーがダウンしてしまっては水の泡です。 便利な機能は、いつでも快適に利用できるからこそ、価値は継続するものだと思うので、あらためてインフラの性能をあげるべく、awsの最新のOSであるAmazon Linux2023に、最新バージョンのPerlをインストールして使う方法について取り組んでみました。
📝目次 Amazon Linux 2023での環境構築 最新の安定版のPerl5.38をインストール Perlの新機能や、試してみたかった機能の検証 総評 Amazon Linux 2023での環境構築 すでにAmazon Linux 2023については、様々なサイトで特徴や構築にまつわるさまざな情報が公開されてますので、詳細は割愛しますが、今回は社内の勉強会なので、OSやディストリビューションの全体像がわかるようなイメージを作ってみました
ベースがFedoraベースなので、少々使えるコマンドが違う点がまず最初に直面する問題です ※GPTに協力してもらって、できるだけわかりやすい図を作ろうとしましたが、きっちり親子関係になってるわけでもないものも多く (この影響を受けている、、、みたいなものが親子関係かというとそういうわけでもなかったり) あくまでもイメージの共有という形で割り切りで
MySQLがない AWSの公式が紹介しているのは、MySQLと互換性のあるMariaDBをインストールして使ってねということなのですが、後述の通り、現状データベースとの接続モジュールはMySQLを前提とした各種コマンドも多数存在しているため、これらをすべてMariaDBに対応させていくのも手間だったので、自前で入れることにしました
配布されてるバイナリには、
Amazon Linux用のものがないので(Amazon Linux2の時はExtra Packageがあったのでそちらで入れられた)、一見、Amazon Linux2023の元になってるFedora用のものがいけそうでしたが、欲してるライブラリが一致せずダメなようでした。 RHEL用のものと互換性があるようなので、こちらで成功しました
(クラスメソッドさんの記事参考にさせていただきました)
MySQLのバージョンアップ 最新のPerlのバージョンに対応した、DBD::mysqlは、MySQL 8.x代のクライアントライブラリを期待して動作しているため、Amazon Linux2023でMySQLに接続する時には、データベースのバージョンもそれにあわせる必要がありました Aurora2(MySQL5.7互換性)のサポート来年の10月には切れてしまうので、覚悟決めてバージョンアップもしていきましょう
MySQLのバージョンをあげた弊害ですが、今のところ、MySQLのデフォルトのキャラセットがutf8mb4になってるせいか、データベースがutf8のままだと、DBIの接続時にMalformed packetが出てしまうようでエラーを解消できなかったので、データベースをutf8mb4に変更し対策しました
wkhtmltopdfの問題 htmlで作ったページをPDFにしてくれるライブラリのwkhtmltopdfも活用させていただいてるのですが、こちらもAmazon Linux2023に対応したバイナリが配布されてません このため、以下ビルドツールを取得してビルドしました
https://github.com/wkhtmltopdf/packaging https://github.com/wkhtmltopdf/qt
READMEの通りで、今はdockerを使ってビルドするんですよね。 ビルドに必要なもろもろのライブラリで既存環境が肥大化しないように、同じ環境のコンテナを立ち上げて、そこに大量のライブラリをインストールしてビルド終わったらコンテナ捨てるってやり方が、とっても合理的だなーってあらためて感じました。 指定するOSの指定は以下で見つけたのですが「amazonlinux2023-x86_64」とすることで期待通りにビルドできました
ちなみに実行コマンドはこんな形で、対象のOSと、最後に引数にkhtmltopdfの本体のソースコードがあるパスを指定する形
. / packaging / build package - docker amazonlinux2023 - x86_64 / root / wkhtmltopdf /
ImageMagick問題 デフォルトでインストールできるImageMagickが、ImageMagick7になるですが、どうもcpanmでインストールするPerlのImageMagickは、ImageMagic6を前提にしたビルドになってるようで、コンパイルエラーになってしまう問題があったため、そちらとの整合性をあわせるためにImageMagick6.9.12.98を入れました
その他 あとは、他のサイトで記事になってる通り、rsyslogがデフォルトで無効になってるから、いつもの見慣れたログ(/var/log/messagesとか)がないので有効にしたり、なんやかんやした話しは割愛します
最新の安定版のPerl5.38をインストール Amazon Linux2023にインストールできるPerlは、5.32.1です。 Perl7がこのバージョンと互換性があると言われているので、ここに対応しておけば後々Perl7にも対応していけるとは思うのですが、5.32ももう、2年前なんですよね。 なので、どうせやるなら、最も新しいPerlにしようってことで、5.38を入れることにしました。
Perlのバージョン切り替えについて 今後はこまめに新しいバージョンのPerlを試しやすくしていくために、バージョンを切り替えて管理できるようにしました ユーザ単位で切り替えて検証したい時にはPlenvが良さそうだったのですが、サーバ全体で切り替えたい場合は、alternativesのほうが良さそうに思えたので、そちらを採用しました
必要なバージョンのPerlのソースコードを取得し、Configureでインストール先などを設定し、make install後、alternativesにもそのperlを優先順位をつけて登録します
curl - LO https : / / www . cpan . org / src / 5.0 / perl - 5.38 .0 . tar . gz tar - xzvf perl - 5.38 .0 . tar . gz cd perl - 5.38 .0 . / Configure - des - Dprefix = / usr / local / perl - 5.38 .0 make sudo make install sudo alternatives -- install / usr / bin / perl perl / usr / local / perl - 5.38 .0 / bin / perl 300 curl - LO https : / / www . cpan . org / src / 5.0 / perl - 5.32 .0 . tar . gz tar - xzvf perl - 5.32 .0 . tar . gz cd perl - 5.32 .0 . / Configure - des - Dprefix = / usr / local / perl - 5.32 .0 make sudo make install sudo alternatives -- install / usr / bin / perl perl / usr / local / perl - 5.32 .0 / bin / perl 10 curl - LO https : / / www . cpan . org / src / 5.0 / perl - 5.38 .0 . tar . gz tar - xzvf perl - 5.38 .0 . tar . gz cd perl - 5.38 .0 . / Configure - des - Dprefix = / usr / local / perl - 5.38 .0 make sudo make install sudo alternatives -- install / usr / bin / perl perl / usr / local / perl - 5.38 .0 / bin / perl 300 curl - LO https : / / www . cpan . org / src / 5.0 / perl - 5.32 .0 . tar . gz tar - xzvf perl - 5.32 .0 . tar . gz cd perl - 5.32 .0 . / Configure - des - Dprefix = / usr / local / perl - 5.32 .0 make sudo make install sudo alternatives -- install / usr / bin / perl perl / usr / local / perl - 5.32 .0 / bin / perl 10
そうすると、以下のような形で、どっちのPerlに切り替えるか操作できるようになります
cpanのインストールも以下ような形で、バージョンごとにインストール先を変えてインストールしていきました
cpanm -- reinstall - L / usr / local / perl - 5.38 .0 / lib / site_perl / 5.38 .0 DBI cpanm -- reinstall - L / usr / local / perl - 5.38 .0 / lib / site_perl / 5.38 .0 DBI
バージョンアップに伴う弊害 細かな影響まですべて確認できていないのですが、5.26のアップデートで一番影響が大きかった、カレントディレクトリがパスから削除されてしまった問題に比べると今回は、MySQLとの通信まわり以外は、そう大きな混乱なく、表示することができました Perlは後方互換性が高いと言われてますが、正直、ここまで簡単にバージョンアップ対応できたのは今まで経験がないので、びっくりでした
Perlの新機能や、試してみたかった機能の検証 Perl5.38の新機能 まだ実験的な機能なので、使う時にはuse feature が必用になりますが、class構文が追加になりました
# ! / usr / bin / perl use feature qw ( class try ) ; no warnings qw ( experimental : : class experimental : : try ) ; # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # 従業員クラス class Employee { field $id : param ; field $name : param ; field $age : param ; # カスタムコンストラクタ # 直後に呼ばれる。引数とかのチェックとか、初期化処理とか、コンストラクタのカスタマイズしたい時はここで制御する" ; ADJUST { die "Invalid employee ID format\n" unless $id = ~ / ^[A-Z0-9]+$ / ; } method get_id ( ) { return $id ; } method get_name ( ) { return $name ; } method set_name ( $new_name ) { $name = $new_name ; } method get_age ( ) { return $age ; } method set_age ( $new_age ) { $age = $new_age ; } } # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # マネージャークラス class Manager : isa ( Employee ) { field $department : param ; # 部署名 } package main ; try { my $employee = Employee - > new ( id => "12345" , name => "Alice" , age => 30 ) ; print "-----------Employee----------------\n" ; print "ID: " , $employee - > get_id ( ) , "\n" ; print "Name: " , $employee - > get_name ( ) , "\n" ; # 従業員の名前を変更 $employee - > set_name ( "Bob" ) ; print "New Name: " , $employee - > get_name ( ) , "\n" ; my $manager = Manager - > new ( department => 'system' , id => "11111" , name => "MG" , age => 50 ) ; print "-----------Manager----------------\n" ; print "ID: " , $manager - > get_id ( ) , "\n" ; print "Name: " , $manager - > get_name ( ) , "\n" ; } catch ( $e ) { print $e . "\n" ; } # ! / usr / bin / perl use feature qw ( class try ) ; no warnings qw ( experimental : : class experimental : : try ) ; # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # 従業員クラス class Employee { field $id : param ; field $name : param ; field $age : param ; # カスタムコンストラクタ # 直後に呼ばれる。引数とかのチェックとか、初期化処理とか、コンストラクタのカスタマイズしたい時はここで制御する" ; ADJUST { die "Invalid employee ID format\n" unless $id = ~ / ^[A-Z0-9]+$ / ; } method get_id ( ) { return $id ; } method get_name ( ) { return $name ; } method set_name ( $new_name ) { $name = $new_name ; } method get_age ( ) { return $age ; } method set_age ( $new_age ) { $age = $new_age ; } } # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # マネージャークラス class Manager : isa ( Employee ) { field $department : param ; # 部署名 } package main ; try { my $employee = Employee - > new ( id => "12345" , name => "Alice" , age => 30 ) ; print "-----------Employee----------------\n" ; print "ID: " , $employee - > get_id ( ) , "\n" ; print "Name: " , $employee - > get_name ( ) , "\n" ; # 従業員の名前を変更 $employee - > set_name ( "Bob" ) ; print "New Name: " , $employee - > get_name ( ) , "\n" ; my $manager = Manager - > new ( department => 'system' , id => "11111" , name => "MG" , age => 50 ) ; print "-----------Manager----------------\n" ; print "ID: " , $manager - > get_id ( ) , "\n" ; print "Name: " , $manager - > get_name ( ) , "\n" ; } catch ( $e ) { print $e . "\n" ; }
以下のような従来の書き方よりも直感的に書けるようになれました
# ! / usr / bin / perl # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # 従業員クラス package Employee ; { sub new { my ( $ class , % args ) = @_ ; die "Invalid employee ID format\n" unless $args { id } = ~ / ^[A-Z0-9]+$ / ; my $self = { id => $args { id } , name => $args { name } , age => $args { age } , } ; return bless $self , $ class ; } sub get_id { my $self = shift ; return $self - > { id } ; } sub get_name { my $self = shift ; return $self - > { name } ; } sub set_name { my ( $self , $new_name ) = @_ ; $self - > { name } = $new_name ; } sub get_age { my $self = shift ; return $self - > { age } ; } sub set_age { my ( $self , $new_age ) = @_ ; $self - > { age } = $new_age ; } } # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # マネージャークラス package Manager ; { use base qw ( Employee ) ; sub new { my ( $ class , % args ) = @_ ; my $self = $ class - > SUPER : : new ( % args ) ; $self - > { department } = $args { department } ; return $self ; } } package main ; eval { my $employee = Employee - > new ( id => "12345" , name => "Alice" , age => 30 ) ; print "-----------Employee----------------\n" ; print "ID: " , $employee - > get_id ( ) , "\n" ; print "Name: " , $employee - > get_name ( ) , "\n" ; $employee - > set_name ( "Bob" ) ; print "New Name: " , $employee - > get_name ( ) , "\n" ; my $manager = Manager - > new ( department => 'system' , id => "11111" , name => "MG" , age => 50 ) ; print "-----------Manager----------------\n" ; print "ID: " , $manager - > get_id ( ) , "\n" ; print "Name: " , $manager - > get_name ( ) , "\n" ; } ; if ( $@ ) { print $@ . "\n" ; } # ! / usr / bin / perl # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # 従業員クラス package Employee ; { sub new { my ( $ class , % args ) = @_ ; die "Invalid employee ID format\n" unless $args { id } = ~ / ^[A-Z0-9]+$ / ; my $self = { id => $args { id } , name => $args { name } , age => $args { age } , } ; return bless $self , $ class ; } sub get_id { my $self = shift ; return $self - > { id } ; } sub get_name { my $self = shift ; return $self - > { name } ; } sub set_name { my ( $self , $new_name ) = @_ ; $self - > { name } = $new_name ; } sub get_age { my $self = shift ; return $self - > { age } ; } sub set_age { my ( $self , $new_age ) = @_ ; $self - > { age } = $new_age ; } } # -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- # マネージャークラス package Manager ; { use base qw ( Employee ) ; sub new { my ( $ class , % args ) = @_ ; my $self = $ class - > SUPER : : new ( % args ) ; $self - > { department } = $args { department } ; return $self ; } } package main ; eval { my $employee = Employee - > new ( id => "12345" , name => "Alice" , age => 30 ) ; print "-----------Employee----------------\n" ; print "ID: " , $employee - > get_id ( ) , "\n" ; print "Name: " , $employee - > get_name ( ) , "\n" ; $employee - > set_name ( "Bob" ) ; print "New Name: " , $employee - > get_name ( ) , "\n" ; my $manager = Manager - > new ( department => 'system' , id => "11111" , name => "MG" , age => 50 ) ; print "-----------Manager----------------\n" ; print "ID: " , $manager - > get_id ( ) , "\n" ; print "Name: " , $manager - > get_name ( ) , "\n" ; } ; if ( $@ ) { print $@ . "\n" ; }
Perlの初学者が苦しむ点として、こういう独自構文なクラス構文が長年あったと思うので、こういう一般的な構文が使えるようになる点はとても好ましいと思います 次に、無名サブルーチンまわりの処理速度があがったと記載があったので、10万個の無名サブルーチンを生成する簡易プログラムでベンチマークを取ってみたところ良い数値が出てました
PSGIの検証 CGIは、Common Gateway Interfaceの略称で、Webサーバが外部プログラム(PerlやシェルやPythonなどでも)を呼び出す時の標準化された規格です。 CGIモードで動作する場合、Webサーバでは各リクエスト毎に新しいプロセスが生成されるので、プロセス間のデータの競合を意識しなくても良い利点はあるのですが、リクエストごとにプロセスが立ち上がってしまうので、大量のアクセスを捌けないなど、サーバリソースが逼迫しやすい問題があります
長らくこういう、Webサーバ自身にプログラムの仕事を一緒にさせる方式が取られていて、より高速化させるために、FastCGIや、mod_perlなど、Webサーバのモジュールとして、より高速化するための手法が取られてきました。 その後、2009年くらいから、WSGIなどに触発され、PerlをWebサーバから分離させて動かす、PSGI(Perl Web Server Gateway Interface)という方法が登場してきました。 この方法により、Webサーバは、プログラムの実行から解放され、PSGIで動作するアプリケーションサーバとの仲介だけの専念できるようになれるため、マルチスレッドでリクエストを処理できるようになれます。
なかなか難しい概念なので、GPTと相談して、ランチタイムの定食屋さんに来るお客さんと受け皿となるテーブルで例えてみました
WebサーバがCGIを動かそうとすると、このような形で、混みあってる時でも、座席に1人しか通さない形になって、店内の座席を有効活用できない状態になります。 ただし、プライベートな空間を持てるようになれるので、スマホで操作してる画面の内容や、PCを開いて社外秘の情報なども気にせず過ごすことができます
次にCGIを動かさず、マルチスレッドで動かす場合です。 こちらは空いてるスペースにどんどん人を入れていくので、スペースを有効活用できます。 そのかわり、1人1人の空間は限られているので、個人情報などまわりに見られないよう、配慮が必要です。 今回の勉強会では、GPTの画像生成DALL-Eとの対話も楽しい時間でした では、早速PSGIの応答速度を検証してみます
Starman cpanでStarmanをインストールするとインストール先に実行ファイルができるので、こちらから起動する形でした 5000番のポートで立ち上げておき、apacheのリバースプロキシでcgiのリクエストの時のみプロキシされるようにしてます
cpanm -- reinstall - L / usr / local / perl - 5.38 .0 / lib / site_perl / 5.38 .0 Starman / usr / local / perl - 5.38 .0 / lib / site_perl / 5.38 .0 / bin / starman -- listen : 5000 performance_test . psgi cpanm -- reinstall - L / usr / local / perl - 5.38 .0 / lib / site_perl / 5.38 .0 Starman / usr / local / perl - 5.38 .0 / lib / site_perl / 5.38 .0 / bin / starman -- listen : 5000 performance_test . psgi
検証用に作成したプログラムは以下の通りで
performance_test.psgi
use strict ; use warnings ; use Plack : : Request ; use File : : Temp qw / tempfile / ; use Time : : HiRes qw ( gettimeofday tv_interval ) ; my $app = sub { my $env = shift ; my $request = Plack : : Request - > new ( $env ) ; my $start_time = [ gettimeofday ] ; # ループ処理のための変数を宣言 my $total = 0 ; for ( my $i = 0 ; $i < 10000 ; $i ++ ) { $total += $i ; } # ファイル書き込みと読み取り my ( $fh , $filename ) = tempfile ( ) ; print $fh "Some data" ; seek $fh , 0 , 0 ; my $file_content = < $fh > ; close $fh ; # メモリ使用量の計測 my $memory_usage = ` ps -o rss= -p $$ ` ; my $end_time = [ gettimeofday ] ; my $elapsed = tv_interval ( $start_time , $end_time ) ; return [ 200 , [ 'Content-Type' => 'text/plain' ] , [ "Loop total: $total\nFile content: $file_content\nMemory Usage: ${memory_usage}KB\nElapsed time: ${elapsed}s" ] ] ; } ; $app ; use strict ; use warnings ; use Plack : : Request ; use File : : Temp qw / tempfile / ; use Time : : HiRes qw ( gettimeofday tv_interval ) ; my $app = sub { my $env = shift ; my $request = Plack : : Request - > new ( $env ) ; my $start_time = [ gettimeofday ] ; # ループ処理のための変数を宣言 my $total = 0 ; for ( my $i = 0 ; $i < 10000 ; $i ++ ) { $total += $i ; } # ファイル書き込みと読み取り my ( $fh , $filename ) = tempfile ( ) ; print $fh "Some data" ; seek $fh , 0 , 0 ; my $file_content = < $fh > ; close $fh ; # メモリ使用量の計測 my $memory_usage = ` ps -o rss= -p $$ ` ; my $end_time = [ gettimeofday ] ; my $elapsed = tv_interval ( $start_time , $end_time ) ; return [ 200 , [ 'Content-Type' => 'text/plain' ] , [ "Loop total: $total\nFile content: $file_content\nMemory Usage: ${memory_usage}KB\nElapsed time: ${elapsed}s" ] ] ; } ; $app ;
ループ処理やファイル書き込み処理などいくつか処理に時間がかかりそうなものを書いたプログラムで検証してみます。
performance_test.cgi
# ! / usr / bin / perl use strict ; use warnings ; use CGI ; use File : : Temp qw / tempfile / ; use Time : : HiRes qw ( gettimeofday tv_interval ) ; my $q = CGI - > new ; print $q - > header ( 'text/plain' ) ; my $start_time = [ gettimeofday ] ; # ループ処理の変数を宣言 my $total = 0 ; for ( my $i = 0 ; $i < 10000 ; $i ++ ) { $total += $i ; } # ファイル書き込みと読み取り my ( $fh , $filename ) = tempfile ( ) ; print $fh "Some data" ; seek $fh , 0 , 0 ; my $file_content = < $fh > ; close $fh ; # メモリ使用量の計測 my $memory_usage = ` ps -o rss= -p $$ ` ; my $end_time = [ gettimeofday ] ; my $elapsed = tv_interval ( $start_time , $end_time ) ; print "Loop total: $total\n" ; print "File content: $file_content\n" ; print "Memory Usage: ${memory_usage}KB\n" ; print "Elapsed time: ${elapsed}s" ; # ! / usr / bin / perl use strict ; use warnings ; use CGI ; use File : : Temp qw / tempfile / ; use Time : : HiRes qw ( gettimeofday tv_interval ) ; my $q = CGI - > new ; print $q - > header ( 'text/plain' ) ; my $start_time = [ gettimeofday ] ; # ループ処理の変数を宣言 my $total = 0 ; for ( my $i = 0 ; $i < 10000 ; $i ++ ) { $total += $i ; } # ファイル書き込みと読み取り my ( $fh , $filename ) = tempfile ( ) ; print $fh "Some data" ; seek $fh , 0 , 0 ; my $file_content = < $fh > ; close $fh ; # メモリ使用量の計測 my $memory_usage = ` ps -o rss= -p $$ ` ; my $end_time = [ gettimeofday ] ; my $elapsed = tv_interval ( $start_time , $end_time ) ; print "Loop total: $total\n" ; print "File content: $file_content\n" ; print "Memory Usage: ${memory_usage}KB\n" ; print "Elapsed time: ${elapsed}s" ;
負荷テスト 本当はJmetarでcgi以外のリクエスト(画像、js、css)も含めた全リクエストを模倣して性能を検証したかったのですが、すみません、ちょっと時間切れになってしまったので、Apache Benchで簡易的に負荷かけてみます 検証用のサーバがt3.microでスペック低かったので、10人のユーザが10ページづつサイトを閲覧し、合計100リクエストになる程度の負荷をかけてみた場合の比較とします
ab - n 100 - c 10 https : / / xxxx.xxx.xxx / ab - n 100 - c 10 https : / / xxxx.xxx.xxx /
psgi環境の結果 $ ab - n 100 - c 10 https : / / xxxx . xxx . xx / index . cgi This is ApacheBench , Version 2.3 < $Revision : 1843412 $ > Copyright 1996 Adam Twiss , Zeus Technology Ltd , http : / / www.zeustech.net / Licensed to The Apache Software Foundation , http : / / www.apache.org / Benchmarking xxxx . xxx . xx ( be patient ) ... . . done Server Software : Apache / 2.4 .56 Server Hostname : xxxx . xxx . xx Server Port : 443 SSL / TLS Protocol : TLSv1 . 2 , ECDHE - RSA - AES256 - GCM - SHA384 , 2048 , 256 Server Temp Key : X25519 253 bits TLS Server Name : xxxx . xxx . xx Document Path : / index . cgi Document Length : 91 bytes Concurrency Level : 10 Time taken for tests : 3.232 seconds Complete requests : 100 Failed requests : 9 ( Connect : 0 , Receive : 0 , Length : 9 , Exceptions : 0 ) Total transferred : 24391 bytes HTML transferred : 9091 bytes Requests per second : 30.94 [ # / sec ] ( mean ) Time per request : 323.156 [ ms ] ( mean ) Time per request : 32.316 [ ms ] ( mean , across all concurrent requests ) Transfer rate : 7.37 [ Kbytes / sec ] received Connection Times ( ms ) min mean [ + / - sd ] median max Connect : 91 197 144.7 130 806 Processing : 36 60 32.5 52 287 Waiting : 36 58 23.2 51 155 Total : 133 258 142.8 195 846 Percentage of the requests served within a certain time ( ms ) 50 % 195 66 % 258 75 % 299 80 % 357 90 % 438 95 % 566 98 % 842 99 % 846 100 % 846 ( longest request ) $ ab - n 100 - c 10 https : / / xxxx . xxx . xx / index . cgi This is ApacheBench , Version 2.3 < $Revision : 1843412 $ > Copyright 1996 Adam Twiss , Zeus Technology Ltd , http : / / www.zeustech.net / Licensed to The Apache Software Foundation , http : / / www.apache.org / Benchmarking xxxx . xxx . xx ( be patient ) ... . . done Server Software : Apache / 2.4 .56 Server Hostname : xxxx . xxx . xx Server Port : 443 SSL / TLS Protocol : TLSv1 . 2 , ECDHE - RSA - AES256 - GCM - SHA384 , 2048 , 256 Server Temp Key : X25519 253 bits TLS Server Name : xxxx . xxx . xx Document Path : / index . cgi Document Length : 91 bytes Concurrency Level : 10 Time taken for tests : 3.232 seconds Complete requests : 100 Failed requests : 9 ( Connect : 0 , Receive : 0 , Length : 9 , Exceptions : 0 ) Total transferred : 24391 bytes HTML transferred : 9091 bytes Requests per second : 30.94 [ # / sec ] ( mean ) Time per request : 323.156 [ ms ] ( mean ) Time per request : 32.316 [ ms ] ( mean , across all concurrent requests ) Transfer rate : 7.37 [ Kbytes / sec ] received Connection Times ( ms ) min mean [ + / - sd ] median max Connect : 91 197 144.7 130 806 Processing : 36 60 32.5 52 287 Waiting : 36 58 23.2 51 155 Total : 133 258 142.8 195 846 Percentage of the requests served within a certain time ( ms ) 50 % 195 66 % 258 75 % 299 80 % 357 90 % 438 95 % 566 98 % 842 99 % 846 100 % 846 ( longest request )
合計テスト時間: 3.232秒 リクエストあたりの平均時間: 323.156ミリ秒 コンカレンシーあたりの平均時間: 32.316ミリ秒 1秒あたりのリクエスト数: 30.94 失敗したリクエスト: 9
cgi環境の結果 kikuta@kikuta : ~ $ ab - n 100 - c 10 https : / / xxxx . xxx . xx / performance_test . cgi This is ApacheBench , Version 2.3 < $Revision : 1843412 $ > Copyright 1996 Adam Twiss , Zeus Technology Ltd , http : / / www.zeustech.net / Licensed to The Apache Software Foundation , http : / / www.apache.org / Benchmarking xxxx . xxx . xx ( be patient ) ... . . done Server Software : Apache / 2.4 .56 Server Hostname : xxxx . xxx . xx Server Port : 443 SSL / TLS Protocol : TLSv1 . 2 , ECDHE - RSA - AES256 - GCM - SHA384 , 2048 , 256 Server Temp Key : X25519 253 bits TLS Server Name : xxxx . xxx . xx Document Path : / performance_test . cgi Document Length : 91 bytes Concurrency Level : 10 Time taken for tests : 6.886 seconds Complete requests : 100 Failed requests : 13 ( Connect : 0 , Receive : 0 , Length : 13 , Exceptions : 0 ) Total transferred : 26399 bytes HTML transferred : 9099 bytes Requests per second : 14.52 [ # / sec ] ( mean ) Time per request : 688.597 [ ms ] ( mean ) Time per request : 68.860 [ ms ] ( mean , across all concurrent requests ) Transfer rate : 3.74 [ Kbytes / sec ] received Connection Times ( ms ) min mean [ + / - sd ] median max Connect : 107 249 261.9 138 1321 Processing : 111 307 91.1 315 506 Waiting : 92 267 78.0 271 506 Total : 248 557 247.9 486 1552 Percentage of the requests served within a certain time ( ms ) 50 % 486 66 % 559 75 % 618 80 % 630 90 % 749 95 % 1291 98 % 1539 99 % 1552 100 % 1552 ( longest request ) kikuta@kikuta : ~ $ ab - n 100 - c 10 https : / / xxxx . xxx . xx / performance_test . cgi This is ApacheBench , Version 2.3 < $Revision : 1843412 $ > Copyright 1996 Adam Twiss , Zeus Technology Ltd , http : / / www.zeustech.net / Licensed to The Apache Software Foundation , http : / / www.apache.org / Benchmarking xxxx . xxx . xx ( be patient ) ... . . done Server Software : Apache / 2.4 .56 Server Hostname : xxxx . xxx . xx Server Port : 443 SSL / TLS Protocol : TLSv1 . 2 , ECDHE - RSA - AES256 - GCM - SHA384 , 2048 , 256 Server Temp Key : X25519 253 bits TLS Server Name : xxxx . xxx . xx Document Path : / performance_test . cgi Document Length : 91 bytes Concurrency Level : 10 Time taken for tests : 6.886 seconds Complete requests : 100 Failed requests : 13 ( Connect : 0 , Receive : 0 , Length : 13 , Exceptions : 0 ) Total transferred : 26399 bytes HTML transferred : 9099 bytes Requests per second : 14.52 [ # / sec ] ( mean ) Time per request : 688.597 [ ms ] ( mean ) Time per request : 68.860 [ ms ] ( mean , across all concurrent requests ) Transfer rate : 3.74 [ Kbytes / sec ] received Connection Times ( ms ) min mean [ + / - sd ] median max Connect : 107 249 261.9 138 1321 Processing : 111 307 91.1 315 506 Waiting : 92 267 78.0 271 506 Total : 248 557 247.9 486 1552 Percentage of the requests served within a certain time ( ms ) 50 % 486 66 % 559 75 % 618 80 % 630 90 % 749 95 % 1291 98 % 1539 99 % 1552 100 % 1552 ( longest request )
合計テスト時間: 6.886秒 リクエストあたりの平均時間: 688.597ミリ秒 コンカレンシーあたりの平均時間: 68.860ミリ秒 1秒あたりのリクエスト数: 14.52 失敗したリクエスト: 13
検証結果 パフォーマンス: PSGI環境はCGI環境に比べて、リクエスト処理が約2倍速いことがわかりました 安定性: 両環境ともに失敗したリクエストがありましたが、PSGI環境での失敗数が少ないことから、PSGIの方がやや安定していると言えます 負荷耐性: PSGI環境は、より多くのリクエストを短い時間で処理できるため、高負荷状態に対する耐性が高いと言えます PSGIの有用性は確認できましたが、プロセスが使いまわされる点で、グローバル変数を使ってしまっていたり、mainパッケージが明示的についてないようなコードの場合には意図した動作にならない問題もあったので、どんなコードでもそのままPSGI化して動く、、、ような単純なものではありませんでした(Plack::App::WrapCGIでそのまま動いたらいいなーくらいの安易な検証しかしてないですが) ただ、有用性はとても感じているので、引き続き活用方法を検証していきたいと思います!
総評 Perlのことを深く知ろうとすると、古い情報から追ってく必用があって、今回、故きを温ねて新しきを知るという言葉の意味を知った気がました。 作者のラリーウォールさんがどういう設計思想でこの言語を開発し、その後、さまざまな技術的な制約を乗り越えて、ユーザに利便性をもたらし 新しい言語の台頭で人気が下火になった今も、世界中には、200を超える地域グループが存在し、日本でも毎年カンファレンスが開かれていて 今もなおPerlを信仰する熱い思いを持った方々がたくさんいる現実もしりました 自分がこれまで出たってきた、Perl好きのエンジニアの方は、本当にみなさん、Perlをこよなく愛してる方ばかりそういう方々によって支えられてるのだなーと感じました 5.38のバージョンではおよそ100名のエンジニアの方が29万行の変更を1年かけて行っていただいたようで、あらためて自分たちのビジネスはこういうコミュニティの努力によって支えられてる感じ、近い将来、恩返しをしていかなければならないと思いました。 バージョンアップ対応(リファクタリングなども)は、見た目のふるまいが特にかわるわけでもないのに、大がかりな開発が必要になることも多く、なかなかコストをかけることに躊躇してしまうことも多いものですが、かけなかったコストは、技術的負債という形で借金を追うことになるので、計画的に覚悟をもってこのタスクを遂行していこうと思います