PHP MongoDB ドライバーのバージョン 1.3 では、接続処理ライブラリが書き直され、以前のバージョンと比較して、永続接続と接続プールに大きな変更が加えられました。
バージョン 1.2 のドライバーでは、クエリを実行すると接続プールから接続が要求され、完了後に接続プールに返されます。ここでの完了とは、接続を保持している変数がそのスコープを離れることを意味します。次に例を示します。
最も単純なバージョン:
<?php$m = new MongoClient(); // ← 从连接池请求连接$c = $m->demo->test;$c->insert( array( 'test' => 'yes' ) );?>
← $m がスコープを離れ、接続は接続プールに返されます
関数内:
<?phpfunction doQuery(){ $m = new MongoClient(); // ← 从连接池请求连接 $c = $m->demo->test; $c->insert( array( 'test' => 'yes' ) );} // ← $m离开作用域,连接归还给连接池?>
場合によっては、次の例に示すように、システムは ORM/ODM の複雑な構造内の接続オブジェクトを参照するなど、多数の接続を生成することがあります。
<?phpfor ( $i = 0; $i < 5; $i++ ){ $conns[] = new MongoClient();}// ← 现在有5个连接?>
バージョン 1.3 では、接続管理が大幅に変更されました。各ワーカー プロセス (スレッド、PHP-FPM、または Apache ワーカー) では、ドライバーは接続管理と Mongo* オブジェクトを分離して、ドライバーの複雑さを軽減します。以下では、単一ノードの MongoDB インスタンスを使用して、ドライバーが接続を処理する方法を示しています。
ワーカー プロセスが開始されると、MongoDB ドライバーは接続を管理するために接続マネージャーを初期化し、デフォルトでは接続は存在しません。
最初のリクエストで new MongoClient(); が呼び出されるとき、ドライバーは新しい接続を作成し、ハッシュ値で接続を識別します。このハッシュ値には、ホスト名、ポート、プロセス ID、およびオプションのレプリカ セット名が含まれます。パスワード認証された接続の場合は、データベース名、ユーザー名、パスワードのハッシュ値も含まれます (パスワード認証された接続の場合)。 , これについては後ほど詳しく説明します)。 MongoClient::getConnections() メソッドを呼び出して、接続に対応するハッシュ値を表示します。
<?php$m = new MongoClient( 'mongodb://whisky:27017/' );var_dump( $m->getConnections()[0]['hash'] );?>
出力:
string(22) “whisky:27017;-; X ;22835″
出力内の「-」は、接続がレプリカ セットに属していないことを示します。「X」は、ユーザー名、データベース、パスワードがない場合のプレースホルダーです。22835現在のプロセスのプロセス ID です。
次に、接続は接続マネージャーに登録されます。
コマンドの挿入、削除、更新、検索、実行など、接続が必要になるたびに、ドライバーは適切な接続を要求します。マネージャーから実行します。 new MongoClient() のパラメーターと現在のプロセスの ID は、接続を要求するときに使用されます。各ワーカー プロセス/スレッド、接続マネージャーには接続リストがあり、各 PHP ワーカーは同時に 1 つのリクエストのみを実行するため、各 MongoDB に必要な接続は 1 つだけであり、PHP ワーカーが終了するまで継続的に再利用されます。 MongoClient::close() を明示的に呼び出して接続を閉じます。
レプリカ セットが存在する環境では、状況は少し異なります。 new MongoClient() の接続文字列では、複数のホストを指定し、レプリケーション セットが現在使用されていることを示す必要があります:
$m = new MongoClient(“mongodb://whisky:13000 ,whisky: 13001/?replicaSet=seta”);
省略できない場合、ドライバーは 3 つの異なる mongos プロセスを接続する準備ができていると認識します。
インスタンス化の際、ドライバーはレプリカ セットのトポロジをチェックします。次の例の出力は、new MongoClient() を呼び出した後、レプリカ セット内のすべての表示可能なデータ ノードがマネージャーとの接続を登録することを示しています。
<?php$m = new MongoClient( 'mongodb://whisky:13001/?replicaSet=seta' );foreach ( $m->getConnections() as $c ){ echo $c['hash'], "\n";}?>
出力:
whiskey:13001;seta;X;32315 whiskey:13000;seta;X;32315
接続文字列に whisky:13000 ノードはありませんが、マネージャーには 2 つの接続が登録されています。
マネージャーには、接続と TCP/IP ソケットのハッシュが含まれるだけでなく、どのノードがマスターであるか、および各ノードの「距離」も保存されます。以下のスクリプトは、この追加情報を表示します。
<?php$m = new MongoClient( 'mongodb://whisky:13001/?replicaSet=seta' );foreach ( $m->getConnections() as $c ){ echo $c['hash'], ":\n", " - {$c['connection']['connection_type_desc']}, ", "{$c['connection']['ping_ms']} ms\n";}?>
whiskey:13001;seta;X;5776: ? ;5776: ? PRIMARY, 0 ms
ドライバーは、挿入、更新、削除を含む書き込み操作と、find および findOne を含む読み取り操作の 2 つのタイプに操作を分割します。デフォルトでは、読み取り設定パラメータが設定されていない場合、マネージャーは常にプライマリ ノードに接続を返します。読み取り設定パラメータは setSlaveEveryone() を通じて設定することも、接続文字列で設定することもできます:
これらのパラメータを追加すると、接続文字列が特に長くなるため、PHP ドライバではオプションを次のように設定できます。配列に配置され、2 番目のパラメーターとして渡されます:
$m = new MongoClient("mongodb://whisky:13000,whisky:13001/?replicaSet=seta&readPreference=secondaryPreferred");
操作ごとに、ドライバーはマネージャーに適切な接続を取得するように要求します。書き込み操作の場合は、常にプライマリ ノードへの接続が返されます。読み取り操作の場合、セカンダリ ノードが利用可能であり、「遠く」にない場合は、セカンダリ ノードへの接続が返されます。
$options = array( 'replicaSet' => 'seta', 'readPreference' => 'secondaryPreferred',);$m = new MongoClient("mongodb://whisky:13000,whisky:13001/", $options);
MongoDB で認証が有効になっている場合、接続ハッシュには認証ハッシュが含まれます。このようにして、異なるスクリプトが異なるユーザー名とパスワードを使用して同じ MongoDB 上の異なるデータベースに接続する場合、接続を悪用することなく相互に区別できます。次の例では、管理者ユーザー名を使用して管理者データベースに接続し、ハッシュ値の変化を観察します。
出力:
<?php$m = new MongoClient( 'mongodb://admin:admin@whisky:27017/admin' );var_dump( $m->getConnections()[0]['hash'] );?>
string(64) “whisky: 27017;-;admin/ admin/bda5cc70cd5c23f7ffa1fda978ecbD30;8697''
以前示例中的”X”部分已经替换为一个包含数据库名admin、用户名admin和哈希值bda5cc70cd5c23f7ffa1fda978ecbd30,该哈希值是根据用户名、数据库名和密码哈希值计算得来。
为了验证能够正确工作,需要在连接字符串中包含数据库名,否则会默认为admin。
在建立连接后要使用数据库,需要先选择该数据库,如:
$collection = $m->demoDb->collection; $collection->findOne();
如果选择的数据库是连接字符串中指定的数据库,或者连接字符串中的数据库是admin,那么一切都会正常运行。否则,驱动会创建一个新的连接,从而防止验证被绕过,如下所示:
<?php$m = new MongoClient( 'mongodb://user:user@whisky:27017/test' );$db = $m->test2;$collection = $db->collection;var_dump( $collection->findOne() );?>
输出:
Fatal error: Uncaught exception ‘MongoCursorException’ with message ‘whisky:27017: unauthorized db:test2 ns:test2.collection lock type:0 client:127.0.0.1′ in …/mongo-connect-5.php.txt:6
因为我们的连接并没有执行test2数据库的授权验证,因而失败。如果我们执行验证,就会正常运行:
<?php$m = new MongoClient( 'mongodb://user:user@whisky:27017/test' );$db = $m->test2;$db->authenticate('user2', 'user2' );$collection = $db->collection;$collection->findOne();foreach ( $m->getConnections() as $c ){ echo $c['hash'], "\n";}?>
输出:
whisky:27017;-;test/user/602b672e2fdcda7b58a042aeeb034376;26983 whisky:27017;-;test2/user2/984b6b4fd6c33f49b73f026f8b47c0de;26983
现在管理器中有两个已验证的连接:
顺便提一句,如果你打开了E_DEPRECATED级别的错误提示,则会看到:
Deprecated: Function MongoDB::authenticate() is deprecated in …/mongo-connect-6.php.txt on line 5
驱动建议通过创建两个MongoClient对象完成该类任务:
<?php$mTest1 = new MongoClient( 'mongodb://user:user@whisky:27017/test', array( 'connect' => false ) );$mTest2 = new MongoClient( 'mongodb://user2:user2@whisky:27017/test2', array( 'connect' => false ) );$mTest1->test->test->findOne();$mTest2->test2->test->findOne();foreach ( $mTest2->getConnections() as $c ){ echo $c['hash'], "\n";}?>
单个MongoDB服务器能支持的并发连接相当有限,如果使用PHP-FPM的话,每个worker进程有自己独立的连接池,那么很容易达到连接数 的上限。因此,在生产环境中,不管有没有使用复制集,都要部署mongos,然后PHP-FPM连接mongos,这样可以减少mongod的连接数,并 且PHP-FPM和mongos之间可以使用短连接(即每个请求结束时都显式调用close函数关闭MongoDB连接)。
来源:风之缘的博客