Nginxツールを使用して一意のユーザーIDを生成する

ようこそ、ハブラチタテリ!



私が直面した問題とその解決方法についてお話しします。



私はすぐに予約します-Gで1時間の検索を行い、満足のいく結果をもたらしませんでしたが、次の1時間で独自のソリューションが実装されました。



これはすべて実験に過ぎません。アイデアと実装の両方に白い点があります。この段階では、生きるかどうかを理解する必要があります。





タスクの本質は、システムコンポーネント(Webプロジェクト)の性質と宗教に関係なく、訪問者を一意に識別する必要があるということでした。 そして、これを可能な限り簡単に、迅速に、そして速度の面で大きなオーバーヘッドなしに行うために。

ログイン/パスワードによるユーザー認証またはまだ実行されていないことに注意することが重要です。



Webサーバーおよびプライマリロードバランサーとして、Nginxがあります。



私のシステムのphpでは、fastcgi経由でphp-fpmを使用し、c ++ビジネスロジックサーバーはfastcgiを介して動作します。







以下に図の例を示します。

画像



C ++サーバーがシステムに追加された後、グローバル識別の問題に遭遇しました。これは、それ自体に対するいくつかの要求をキャッチします。



肯定的なサーバーへのリクエストはphpバックエンドへのリクエストよりも早いかもしれないので、PHPSESSIDによるユーザー識別の便利で、高速で、最も重要な美しい実装を見つけることができませんでした。



私はこれをすべてNginx、またはむしろperlモジュールを使用して実装することにしました。

これは、これまでのソリューションの唯一の重要なマイナスです-このモジュールはデフォルトではコンパイルされていません。つまり、nginxを再構築する必要があります。



$ ./configure --with-http_perl_module







ここで、タスクはいくつかの段階に分けられます。



ステップ1.識別子を生成して確認する真珠モジュールを書く


1. Cookieなどでセット識別子を確認します。

識別子は、クッキーで送信できるだけでなく、私の場合はそこにあります。



2a。 有効性識別子を確認してください

識別子の有効性、つまり モジュールによって生成されたのか、エラーが入り込んだのかをチェックします。

これは、コンポーネントのさらなる作業に必要です。

この識別子を使用してクライアント側の負荷を分散する方法もいくつかあります。



識別子が有効な場合、生成手順を完了します。

そうでない場合は、新しいものを生成します(p。2b)-おそらくここで何らかの形で異なる反応をする必要があります。それについて考える必要があります。



2b。 IDを生成

これは、ランダムシーケンス+ユーザーからのデータを使用した識別子の生成です。

現在、識別子は32バイト(hexstr)のランダムシーケンスと32バイト(hexstr)のダイジェストで構成されています(以下を参照)。

もちろん、これは削減される可能性があります。



 package session; use strict; use Digest::MD5 qw(md5_hex); my $secret_key = '__TOP_SECRET__KEEP_IT_IN_BANK__'; # ,     my $cookie_name = 'SID'; #      my $rand_len = 16; #    my $hex_length = $rand_len * 2; my $hex_mask = "H".$hex_length; my $digest_length = 32; #    hexstr - 32 . #    sub hash { # data -  , ng - nginx . my ($data,$ng) = @_; #      ip  return md5_hex($data."_".$secret_key."_".$ng->header_in("User-Agent")."_".$ng->remote_addr); } #    .  MAN, , /dev/random  . #           nginx. #     . open(my $rand, '<', "/dev/random"); sub gen { # ng - nginx  my $ng = shift; #       #  32  (hexstr)   #  32  (hexstr)  (. sub hash) if ($ng->header_in("Cookie")=~/$cookie_name=(\w{$hex_length})(\w{$digest_length});?/) { if ($2 eq hash($1, $ng)) { return "$1$2"; } } #    read($rand, my $data, $rand_len); #    hexstr my $h = unpack($hex_mask, $data); #     (. sub hash) my $id = $h.hash($h, $ng); #   $ng->header_out("Set-Cookie","$cookie_name=$id;"); #   nginx return $id; } 1; __END__
      
      







ステージ2.識別子を使用してフレンドシステムコンポーネントを作成する


このモジュールを接続するには、nginx.confを編集します

     http {
         ...
         perl_modules conf / perl;  #モジュールが保存されているディレクトリ
         perl_require session.pm;  #モジュールファイル
         perl_set $ sid session :: gen;  #識別子が保存される変数
         ...
 
        サーバー{ 
             ..
            場所〜* \。php $ {
                ルートhtml / www;
                 fastcgi_pass http:// backend_upstreams;
                 fastcgi_index index.php;
                 fastcgi_param SCRIPT_FILENAME $ document_root / $ fastcgi_script_name;
                 fastcgi_paramsを含めます。
                 fastcgi_param SID $ sid;  #バックエンドへのFastCGI ID転送
             }
            
            場所〜* \。tst $ {            
                 fastcgi_pass unix:/ tmp / cpp_server;
                 fastcgi_paramsを含めます。
                 fastcgi_param SID $ sid;  #FastCGIを介してバックエンドに識別子を転送
             }
         }
         ...
     }




ステージ3.すべてのnafigに失敗しないことの検証


小さな負荷テストが作成されました。 標準の真珠ベンチマークベンチマークを使用しました。

 #!/usr/bin/perl use strict; use Benchmark; use Digest::MD5 qw(md5_hex); my $secret_key = '__TOP_SECRET__KEEP_IT_IN_BANK__'; my $cookie_name = 'SID'; my $rand_length = 16; my $hex_mask = "H".($rand_length * 2); open(my $rand, '<', "/dev/random"); sub hash { my ($data) = @_; my $hash = md5_hex($data."_".$secret_key."_Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.63 Safari/535.7_127.0.0.1"); return $hash; } sub gen { read($rand, my $data, $rand_length); my $h = unpack($hex_mask,$data); my $id = $h.hash($h); my $ng = "$cookie_name=$id;"; return $ng; } my $t0 = new Benchmark; for (my $i =0; $i < 1000000;++$i) { gen(); } my $t1 = new Benchmark; my $td = timediff($t1, $t0); print "Total:".timestr($td)."\n";
      
      







600Mhz VDSkeでの作業の結果:

合計:6ウォールクロック秒(5.75 usr + 0.30 sys = 6.05 CPU)

つまり 1つの識別子を生成するのに約6 * 10 -6秒かかります。

最悪のオプションは、検証+リクエストごとの生成= 12 * 10 -6秒であるとします。

これまでのところ、私はテストしませんでしたが、もちろんどこにプルするかがあります。



Php



PHPバックエンドから識別子へのアクセス-$ _SERVER ['SID'];

この識別子をsession_idとして設定することもできます

 <? session_id($_SERVER['SID']); session_name('SID'); //         PHPSESSID, SID session_start(); ?>
      
      







この場合、たとえば、セッションがデータベースまたはMemcacheに保存されている場合、システムのすべてのコンポーネントがセッションデータにアクセスできます(ただし、セッションレコードのロックに問題があります)。



UPD:

なぜngx_http_userid_moduleではない



正しい質問をしてくれたDemetrosに感謝します。



このモジュールが適合しない理由は2つあります( 詳細 ):

  1. ユーザーID検証制御なし
  2. 最初のリクエストでは、uidをバックエンドに渡す方法はありません



All Articles