使用postgresql gem async

我正在使用Goliath (由eventmachine驱动)和postgres gem pg ,目前我正以阻塞的方式使用pg gem: conn.exec('SELECT * FROM products') (例如)和我想知道是否有更好的方式连接到postgres数据库?

pg库为PostgreSQL的异步API提供全面支持。 我添加了一个如何将它用于samples/目录的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,但是如果它允许你注册套接字和回调它何时变得可读/可写,我认为将数据库调用集成到它中相当容易。

我一直想使用Ilya Grigorik关于使用Fibers来清理偶数代码以使异步API更容易使用的文章中的想法,但这是一个方法。 如果您对自己感兴趣/有动力,我确实有一张可以跟踪它的门票 。

是的,您可以从goliath以非阻塞的方式访问postgres。 我有同样的需求,并将这个概念certificate放在一起: https : //github.com/levicook/goliath-postgres-spike

我(不再)非常熟悉Pg,但我没有听说任何流行的数据库都可以异步连接。 因此,您仍需要在查询期间保持与数据库的连接。 因此,您仍然需要阻止堆栈中的某些位置。

根据您的应用程序,您可能已经采用了最佳方式。

但是当你正在处理某种轮询应用程序时(同一个客户端在短时间内发送大量请求)并且将响应输出更为重要,即使它是空的,那么你可以写一个ruby Fiber或flull blown长期存在的线程或进程,代理查询数据库并缓存结果。

例如:来自客户端A的请求.Goliath应用程序使用一些唯一ID处理对DB进程的查询,并使用“还没有数据”响应查询。 数据库进程完成查询并将结果保存到具有ID的缓存中。 当下一个请求来自同一个客户端时,Goliath发现它已经有等待的查询结果,从缓存中删除结果并响应客户端。 同时,它使用数据库进程安排下一个查询,以便更快地准备好。 如果下一个请求在最后一个请求完成之前进入,则不会安排新查询(不会将查询相乘)。

这样,您的响应快速且无阻塞,同时仍然可以从DB ASAP提供新数据。 当然,它们可能与实际数据有点不同步,但同样,根据应用程序的不同,这可能不是问题。

我们的想法是使用与数据库(Postgresql)的异步适配器和一个事件Web服务器(Goliath)来获得性能。 Mike Perham去年为Rails 2.3编写了一个PG activerecord适配器 。 也许你可以使用它。

另一个例子是,Ilya Grigorik发布了这个异步Rails堆栈的演示 。 在这种情况下,事件服务器是Thin,数据库是Mysql。 安装演示并使用和不使用EM感知驱动程序尝试基准测试。 差别很大。