9

私はGoliath(eventmachineを利用)とpostgres gempgを使用していますが、現在はpg gemをブロックする方法で使用しています:(conn.exec('SELECT * FROM products')たとえば)そしてpostgresに接続するためのより良い方法があるかどうか疑問に思っていますデータベース?

4

4 に答える 4

16

このpgライブラリは、PostgreSQLの非同期APIを完全にサポートしています。それを使用する方法の例samples/をディレクトリに追加しました。

#!/usr/bin/env ruby

require 'pg'

# This is a example of how to use the asynchronous API to query the
# server without blocking other threads. It's intentionally low-level;
# if you hooked up the PGconn#socket to some kind of reactor, you
# could make this much nicer.

TIMEOUT = 5.0 # seconds to wait for an async operation to complete
CONN_OPTS = {
    :host     => 'localhost',
    :dbname   => 'test',
    :user     => 'jrandom',
    :password => 'banks!stealUR$',
}

# Print 'x' continuously to demonstrate that other threads aren't
# blocked while waiting for the connection, for the query to be sent,
# for results, etc. You might want to sleep inside the loop or 
# comment this out entirely for cleaner output.
progress_thread = Thread.new { loop { print 'x' } }

# Output progress messages
def output_progress( msg )
    puts "\n>>> #{msg}\n"
end

# Start the connection
output_progress "Starting connection..."
conn = PGconn.connect_start( CONN_OPTS ) or 
    abort "Unable to create a new connection!"
abort "Connection failed: %s" % [ conn.error_message ] if
    conn.status == PGconn::CONNECTION_BAD

# Now grab a reference to the underlying socket so we know when the
# connection is established
socket = IO.for_fd( conn.socket )

# Track the progress of the connection, waiting for the socket to 
# become readable/writable before polling it
poll_status = PGconn::PGRES_POLLING_WRITING
until poll_status == PGconn::PGRES_POLLING_OK ||
      poll_status == PGconn::PGRES_POLLING_FAILED

    # If the socket needs to read, wait 'til it becomes readable to
    # poll again
    case poll_status
    when PGconn::PGRES_POLLING_READING
        output_progress "  waiting for socket to become readable"
        select( [socket], nil, nil, TIMEOUT ) or
            raise "Asynchronous connection timed out!"

    # ...and the same for when the socket needs to write
    when PGconn::PGRES_POLLING_WRITING
        output_progress "  waiting for socket to become writable"
        select( nil, [socket], nil, TIMEOUT ) or
            raise "Asynchronous connection timed out!"
    end

    # Output a status message about the progress
    case conn.status
    when PGconn::CONNECTION_STARTED
        output_progress "  waiting for connection to be made."
    when PGconn::CONNECTION_MADE
        output_progress "  connection OK; waiting to send."
    when PGconn::CONNECTION_AWAITING_RESPONSE
        output_progress "  waiting for a response from the server."
    when PGconn::CONNECTION_AUTH_OK
        output_progress "  received authentication; waiting for " +
                        "backend start-up to finish."
    when PGconn::CONNECTION_SSL_STARTUP
        output_progress "  negotiating SSL encryption."
    when PGconn::CONNECTION_SETENV
        output_progress "  negotiating environment-driven " +
                        "parameter settings."
    end

    # Check to see if it's finished or failed yet
    poll_status = conn.connect_poll
end

abort "Connect failed: %s" % [ conn.error_message ] unless 
    conn.status == PGconn::CONNECTION_OK

output_progress "Sending query"
conn.send_query( "SELECT * FROM pg_stat_activity" )

# Fetch results until there aren't any more
loop do
    output_progress "  waiting for a response"

    # Buffer any incoming data on the socket until a full result 
    # is ready. 
    conn.consume_input
    while conn.is_busy
        select( [socket], nil, nil, TIMEOUT ) or
            raise "Timeout waiting for query response."
        conn.consume_input
    end

    # Fetch the next result. If there isn't one, the query is 
    # finished
    result = conn.get_result or break

    puts "\n\nQuery result:\n%p\n" % [ result.values ]
end

output_progress "Done."
conn.finish

if defined?( progress_thread )
    progress_thread.kill
    progress_thread.join
end

PostgreSQLマニュアルのPQconnectStart関数と非同期コマンド処理のセクションのドキュメントを読んでから、上記のサンプルと比較することをお勧めします。

私はこれまでEventMachineを使用したことがありませんが、ソケットとコールバックを登録して読み取り/書き込み可能になったときに、データベース呼び出しを統合するのはかなり簡単だと思います。

私は、ファイバーを使用してイベントコードをクリーンアップし、非同期APIを使いやすくすることに関する、 Ilya Grigorikの記事のアイデアを使用するつもりでしたが、それは道のりです。あなたが自分でそれをすることに興味がある/やる気があるなら、私はそれを追跡するために開いているチケットを持っています。

于 2011-05-19T00:18:18.697 に答える
3

はい、goliathからブロックなしの方法でpostgresにアクセスできます。私にも同じニーズがあり、この概念実証をまとめました:https ://github.com/levicook/goliath-postgres-spike

于 2012-03-15T21:38:52.240 に答える
1

私は(もう)Pgにあまり詳しくありませんが、一般的なデータベースが接続を非同期化できるとは聞いていません。したがって、クエリの間、データベースへの接続を維持する必要があります。したがって、スタックのどこかをブロックする必要があります。

アプリケーションによっては、すでに可能な限り最善の方法で実行している場合があります。

しかし、ある種のポーリングアプリ(同じクライアントが短時間で多数のリクエストを送信する)を扱っていて、応答を取得することがより重要である場合、それが空であっても、ルビーFiberまたはフルブロースレッドを書くことができますまたは、長期間有効で、クエリをDBにプロキシし、結果をキャッシュするプロセス。

例:クライアントAからリクエストが届きました。Goliathアプリは、一意のIDを使用してDBプロセスへのクエリを処理し、「まだデー​​タがありません」でクエリに応答します。DBプロセスはクエリを終了し、結果をIDとともにキャッシュに保存します。同じクライアントから次のリクエストが届くと、Goliathはクエリ結果がすでに待機していることを確認し、結果をキャッシュから削除してクライアントに応答します。同時に、DBプロセスで次のクエリをスケジュールして、より早く準備できるようにします。最後のリクエストが終了する前に次のリクエストが届いた場合、新しいクエリはスケジュールされません(クエリを乗算しません)。

このようにして、DB ASAPからの新しいデータを提供しながら、応答が高速でブロックされません。もちろん、実際のデータと少し同期していない可能性がありますが、アプリケーションによっては、これは問題にならない場合があります。

于 2011-05-05T09:17:40.743 に答える
1

アイデアは、パフォーマンスを得るために、イベントされたWebサーバー(Goliath)と組み合わせてデータベース(Postgresql)への非同期アダプターを使用することです。Mike Perhamは、昨年Rails2.3用のPGactiverecordアダプターを作成しました。多分あなたはそれを使うことができます。

別の例として、IlyaGrigorikは非同期Railsスタックのこのデモをリリースしました。この場合、イベントが発生したサーバーはThinであり、データベースはMysqlです。デモをインストールし、EM対応ドライバーの有無にかかわらずベンチマークを試してください。違いは劇的です。

于 2011-05-05T15:34:28.987 に答える