2

この scotty アプリを見てください ( 2014 年のこの古い回答から直接取得したものです):

import Web.Scotty
import Database.MongoDB
import qualified Data.Text.Lazy as T
import Control.Monad.IO.Class

runQuery :: Pipe -> Query -> IO [Document]
runQuery pipe query = access pipe master "nutrition" (find query >>= rest) 

main = do
  pipe <- connect $ host "127.0.0.1"
  scotty 3000 $ do
    get "/" $ do
      res <- liftIO $ runQuery pipe (select [] "stock_foods")
      text $ T.pack $ show res

pipeWeb アプリの起動時にデータベース接続 ( ) が 1 回だけ作成される様子がわかります。その後、数百万とは言わないまでも数千の訪問者が同時に「/」ルートにアクセスし、同じ接続を使用してデータベースから読み取ります ( pipe)。

を適切に使用する方法について質問がありますDatabase.MongoDB

  1. これは物事を設定する適切な方法ですか?「/」にアクセスするたびにデータベース接続を作成するのとは対照的です。この後者の場合、一度に数百万の接続を持つことができます。それは落胆ですか?このようなアプローチの利点と欠点は何ですか?
  2. 上記のアプリで、データベース接続が何らかの理由で失われ、再度作成する必要がある場合はどうなりますか? そこからどのように回復しますか?
  3. auth関数による認証はどうですか?authを作成した後に関数を 1 回だけ呼び出す必要がありますpipeか、それとも「/」にヒットするたびに呼び出す必要がありますか?
  4. プール ( ) を使用することになっていると言う人もいますData.Pool。同じデータベース接続を同時に使用する訪問者の数を制限するのに役立つだけのようです. しかし、なぜ私はそれをしたいのでしょうか? MongoDB 接続には、同時使用のサポートが組み込まれていませんか?
4

3 に答える 3

2
  1. クライアントごとに接続を作成しても、あまり多くの接続を作成することはできません。ulimit にヒットします。その ulimit に達すると、この ulimit に達したクライアントは実行時エラーを受け取ります。それが意味をなさない理由は、mongodb サーバーがこれらすべての接続をポーリングするのに多くの時間を費やし、db サーバーが持つ CPU の数と同じ数の有意義なワーカーしか持たないためです。mongodb は複数の要求を送信して応答を待つように設計されているため、1 つの接続は悪い考えではありません。したがって、mongodb が持つことができる最大のリソースを 1 つだけ使用します。書き込み用のパイプは 1 つしかなく、パイプが誤って閉じてしまった場合は、このパイプを自分で再作成する必要があります。したがって、接続のプールを持つ方が理にかなっています。大きくする必要はありません。ユーザーを認証してトークンを与えるアプリがありました。1 秒あたり 2500 人の同時ユーザーで、データベースへの同時接続は 3 ~ 4 しかありませんでした。

接続プールがもたらすメリットは次のとおりです。

  • プール接続の制限に達すると、次に利用可能な接続を待機することになり、実行時エラーは発生しません。そのため、アプリはクライアントを拒否するのではなく、少し待機します。

  • プールは接続を再作成します。過剰な接続を閉じて、必要に応じて特定の制限までさらに作成するようにプールを構成できます。読み取りまたは書き込み中に接続が切断された場合は、プールから別の接続を取得するだけです。切断された接続をプール プールに戻さないと、別の接続が作成されます。

    1. データベース接続が閉じられている場合: この接続の mongodb リスナーは端末にエラー メッセージを出力して終了し、アプリは IO エラーを受け取ります。このエラーを処理するには、別の接続を作成して再試行する必要があります。この状況に対処する場合、db プールを使用する方が簡単であることがわかります。最終的にこれに対する解決策は、接続プールに非常に似ているためです。

    2. 接続を開く際に一度認証を行います。後で別のユーザーを認証する必要がある場合は、いつでも行うことができます。

    3. はい、mongodb は同時使用を処理しますが、前述のように、書き込むパイプが 1 つしかなく、すぐにボトルネックになります。mongodb サーバーがそれらを処理するためのスレッド (CPU 数) を処理できる数以上の接続を作成すると、それらはフルスピードで動作します。

私が何かを逃した場合は、お気軽に説明を求めてください。ご質問ありがとうございます。

于 2016-08-17T17:29:51.717 に答える
1

本当に必要なのは、データベース接続プールです。この他の回答のコードを見てください。

の代わりに、 MongoDB サーバーがセキュア モードの場合authは を使用できます。withMongoDBPool

于 2016-08-14T15:13:35.637 に答える
1

これは物事を設定する適切な方法ですか?「/」にアクセスするたびにデータベース接続を作成するのとは対照的です。この後者の場合、一度に数百万の接続を持つことができます。それは落胆ですか?このようなアプローチの利点と欠点は何ですか?

1 つの接続を開いてから使用することは望ましくありません。あなたが使用している、Scotty を支える HTTP サーバーは、Warp と呼ばれます。Warp はマルチコア、マルチグリーン スレッド設計です。すべてのスレッドで同じ接続を共有することが許可Database.MongoDBされています。接続はスレッドセーフであると明確に述べているためですが、1 つのスレッドが応答を待ってブロックされると ( MongoDB プロトコルは単純な要求応答設計に従います) 、すべてのWeb サービスのスレッドがブロックされます。これは残念です。

代わりに、リクエストごとに接続を作成できます。これにより、あるスレッドが別のスレッドをブロックするという問題は簡単に解決されますが、それ自体が問題の共有につながります。TCP 接続をセットアップするオーバーヘッドも、実質的ではありませんが、ゼロではありません。ソケットを開いたり閉じたりするたびに、ユーザーからカーネルにジャンプし、カーネルが内部データ構造を更新するのを待ってから戻る必要があることを思い出してください (コンテキスト スイッチ)。また、TCP ハンドシェイクとさよならも処理する必要があります。また、負荷が高いと、ファイル記述子またはメモリが不足します。

その間のどこかに解決策があればいいのですが。解決策は

  • スレッドセーフ
  • オペレーティング システムの有限のリソースを使い果たしてしまわないように、接続数を最大に制限しましょう。
  • 素早い
  • 通常の負荷でスレッド間で接続を共有する
  • 負荷の増加に伴い、新しい接続を作成します
  • 負荷が軽減された状態で接続が削除されるため、リソースをクリーンアップできます (ハンドルを閉じるなど)。
  • 願わくば、他の本番システムによってすでに作成され、実戦でテストされていることを願っています

resource-pool が取り組むのはまさにこの問題です。

プール (Data.Pool) を使用することになっていると言う人もいます。同じデータベース接続を同時に使用する訪問者の数を制限するのに役立つだけのようです. しかし、なぜ私はそれをしたいのでしょうか? MongoDB 接続には、同時使用のサポートが組み込まれていませんか?

同時使用の意味が不明です。私が推測できる解釈が 1 つあります。それは、プロトコルにパイプラインが組み込まれている HTTP/2 のようなものを意味します。

パイプラインの標準的な図 http://research.worksap.com/wp-content/uploads/2015/08/pipeline.png

上記では、クライアントが応答を待たずにサーバーに複数の要求を行っていることがわかります。その後、クライアントはある順序で応答を受け取ることができます。(時間は上から下に流れます。) この MongoDB にはありません。これはかなり複雑なプロトコル設計であり、クライアントに接続プールを使用するように要求するよりも優れているとは言えません。ここでは MongoDB だけではありません。単純な要求と応答の設計は、Postgres、MySQL、SQL Server、および他のほとんどのデータベースが採用したものです。

そして、接続プールは、すべてのスレッドがブロックされ、ユーザーに読み込みバーが表示される前に、Web サービスとして実行できる負荷を制限することは事実です。しかし、この問題は 3 つのシナリオ (接続プーリング、1 つの共有接続、要求ごとに 1 つの接続) のいずれにも存在します。コンピュータのリソースには限りがあり、十分な負荷がかかると、ある時点で何かが崩壊します。接続プーリングの利点は、拡張できないところまで適切に拡張できることです。より多くのトラフィックを処理するための正しい解決策は、コンピューターの数を増やすことです。この問題だけを理由にプーリングを避けるべきではありません。 

上記のアプリで、データベース接続が何らかの理由で失われ、再度作成する必要がある場合はどうなりますか? そこからどのように回復しますか?

この種の what-if は Stack Overflow の範囲外であり、「試してみてください」以上の答えはないと思います。サーバーが接続を終了することを考えると、何が起こるかを突き止めることができます: Warp が要求ごとに緑色のスレッドをフォークすると仮定すると (そうすると思います)、各スレッドはIOException閉じられたスレッドに書き込もうとするときにunchecked を経験します。 TCP 接続。Warp はこの例外をキャッチし、HTTP 500 として提供し、ログにも役立つ情報を書き込みます。現在のような単一接続モデルを想定すると、「再起動」する賢い(ただしコード行が多い)ことを行うことができますmain機能し、2 番目の接続をセットアップします。私が趣味のプロジェクトで行っていること: 接続が切断されたなどの異常が発生した場合、スーパーバイザー プロセス (systemd など) にログを監視して Web サービスを再起動するように依頼します。金儲けをしている実動 Web サイトにとっては明らかに優れたソリューションではありませんが、小規模なアプリには十分に機能します。

auth関数による認証はどうですか?auth関数は、パイプの作成後に 1 回だけ呼び出す必要がありますか?それとも、"/" にヒットするたびに呼び出す必要がありますか?

接続の作成後に一度呼び出す必要があります。MongoDB 認証は接続ごとです。コマンドが現在のクライアント接続に対応する MongoDB サーバーのデータ構造を変更する方法の例をここでdb.auth()見ることができます。

于 2016-08-16T18:22:18.867 に答える