我在Nginx / Passenger上有一个rails 3应用程序,我刚刚转移到Nginx / Thin(1.3.1)。但是,我的应用程序现在明显慢于乘客。很多请求也超时。
Thin是一个公平的网络服务器。从我所读到的关于规范的Web服务器,他们没有工人的概念。一个“工人”处理一切。因此,如果一个请求正在等待IO,则thin只会继续执行下一个请求,因此只需要一个请求。我读到的关于服务器的一个解释是,配置服务器应该比基于工作服务器的性能更好或更好,因为它们只受系统资源的约束。
但是,我的CPU使用率非常低。我的内存使用量也很少,并且发生的IO也不多。我的应用只是做了一些MySQL查询。
这里的瓶颈是什么?在CPU达到100%之前,我的瘦服务器不应该处理请求吗?我是否必须在我的应用程序中执行任何不同的操作,才能使用事件服务器更好地执行此操作?
答案 0 :(得分:13)
塞尔吉奥是对的。此时,您的应用程序可能更适合传统的Apache / Passenger模型。如果你采取规范的路线,特别是在像Ruby这样的单线程平台上,你永远不会阻止任何事情,无论是数据库,缓存服务器,你可能做的其他HTTP请求 - 没有。
这使得异步(事件)编程变得更加困难 - 它很容易阻塞,通常以同步磁盘I / O或DNS解析的形式。非阻塞(偶数)框架(如nodejs)非常谨慎,因为它们(几乎)从未向您提供阻塞的框架函数调用,而是使用回调(包括数据库查询)处理所有内容。
如果你看一下单线程非阻塞服务器的核心,这可能更容易可视化:
while( wait_on_sockets( /* list<socket> */ &$sockets, /* event */ &$what, $timeout ) ) {
foreach( $socketsThatHaveActivity as $fd in $sockets ) {
if( $what == READ ) { // There is data availabe to read from this socket
$data = readFromSocket($fd);
processDataQuicklyWithoutBlocking( $data );
}
elseif ($what == WRITE && $data = dataToWrite($fd)) { // This socket is ready to be written to (if we have any data)
writeToSocket( $fd, $data );
}
}
}
您在上面看到的内容称为事件循环。 wait_on_sockets
通常由操作系统以系统调用的形式提供,例如select,poll,epoll或kqueue。如果processDataQuicklyWithoutBlocking花费的时间太长,那么操作系统维护的应用程序的网络缓冲区(新请求,传入数据等)将最终填满,并将导致它拒绝新连接并超时现有连接,因为$ socketsThatHaveActivity的处理速度不够快。这与线程服务器(例如典型的Apache安装)的不同之处在于每个连接都使用单独的线程/进程提供服务,因此传入的数据一到达就会被读入应用程序,并且传出的数据将立即发送
当您进行(例如)数据库查询时,nodejs所执行的非阻塞框架是将数据库服务器的套接字连接添加到受监视的套接字列表($ sockets),因此即使您的查询需要而在这个套接字上没有阻止你的(唯一)线程。相反,它们提供回调:
$db.query( "...sql...", function( $result ) { ..handle result ..} );
正如您在上面所看到的,db.query立即返回,并且在数据库服务器上完全没有阻塞。这也意味着你经常需要编写这样的代码,除非编程语言本身支持异步函数(比如新的C#):
$db.query( "...sql...", function( $result ) { $httpResponse.write( $result ); $connection.close(); } );
如果有许多进程分别运行事件循环(通常是运行节点集群的方式),或者使用线程池来维护事件循环(java的码头),那么永远不会阻塞规则可以有所放松,netty等,你可以用C / C ++编写自己的。当一个线程被阻塞时,其他线程仍然可以执行事件循环。但是在负载足够大的情况下,即使这些也无法执行。所以永远不要在一个事件服务器中阻塞。
正如您所看到的,对等服务器通常会尝试解决不同的问题 - 它们可以拥有大量的开放连接。他们擅长的地方就是用光计算推送字节(例如彗星服务器,像memcached一样的缓存,清漆,代理如nginx,鱿鱼等)。值得一提的是,即使它们扩展得更好,响应时间通常也会增加(没有什么比保留连接的整个线程更好)。当然,运行与并发连接数相同的线程数可能在经济上/计算上不可行。
现在回到你的问题 - 我建议你仍然保持Nginx,因为它在连接管理(基于事件)方面非常出色 - 通常意味着处理HTTP保持活动,SSL等。然后你应该将它连接到您的Rails应用程序使用FastCGI,您仍然需要运行工作程序,但不必重写您的应用程序以完全平衡。您还应该让Nginx提供静态内容 - 没有必要让您的Rails工作人员与Nginx通常可以做得更好的事情捆绑在一起。这种方法通常比Apache / Passenger更好地扩展,特别是如果你运行一个高流量的网站。
如果你可以编写你的整个应用程序,那么很棒,但我不知道Ruby中有多么简单或困难。
答案 1 :(得分:3)
是的,Thin会对I / O进行操作,但仅适用于HTTP部分。这意味着它可以在处理请求时接收传入的HTTP数据。但是,您在处理期间执行的所有阻止I / O仍然是阻塞的。如果您的MySQL响应缓慢,那么Thin请求队列将会填满。
对于“更多”的网络服务器,您应该查看Rainbows。