原创作者: robbin   阅读:25395次   评论:6条   更新时间:2011-05-26    
在Linux平台上安装和配置Ruby on Rails详解,讲述了完整的安装过程,本文提供相关的疑难解答

用Lighttpd还是Nginx/Mongrel? Top

现在Nginx/Mongrel的部署方式越来越受欢迎了,很多人置疑Lighttpd/FastCGI,并且现在也涌现了一些比Mongrel性能更好的例如Thin,ebb等ruby应用服务器,那Lighttpd/FastCGI真的过时了吗?

大家有兴趣可以看一下我这篇文章:RoR部署方案深度剖析,Lighttpd提供了很多其他Web服务器不具备的优势,可以最大化FastCGI的性能。现在Mongrel/Thin/ebb都利用了一些多线程或者事件IO机制来提供并发性能,这是FastCGI所不具备的,但遗憾的是Rails框架是单线程的,最终还是必须单进程单线程来执行Rails请求,所以这些并发优势无用武之地。但一些其他Ruby的Web框架例如camping,weavers已经开始支持ruby多线程,提供了比Rails好得多的性能,mongrel/ebb只有在这些web框架上面才能发挥其性能优势。

所以只要你还是用Rails框架,Lighttpd/FastCGI就是性能最好的部署方案。

ubuntu安装ruby缺少readline和zlib库 Top

有些人的ubuntu安装的库不全,比方说缺少readline库,缺少zlib库,可能会导致自己手工编译安装ruby的失败,那么就用apt-get先把库安装好。

编译lighttpd的时候报告缺少pcre库 Top

RHEL/CentOS用户可能要用yum安装一下pcre/pcre-devel这两个库,ubuntu用户用apt-get安装一下,Linux熟手也可以自己下载源代码编译安装,Pcre是Perl兼容的正则表达式库,Lighttpd的Rewrite功能需要它。

ubuntu操作系统执行rc.lighttpd脚本报错 Top

如果你的Linux是ubuntu,那么需要自己创建启动脚本,lighttpd官方wiki上面已经给出来一个该脚本示例,地址在:
http://redmine.lighttpd.net/wiki/1/ScriptsUbuntu

rc.lighttpd这个脚本是针对SuSE Linux写的,此外还提供了一个rc.lighttpd.redhat是针对RedHat Linux写的,你自己写一个控制脚本,也不过是举手之劳:

#!/bin/sh

case "$1" in 
  start)         
    /usr/local/lighttpd/sbin/lighttpd -f /etc/lighttpd/lighttpd.conf > /dev/null 2>&1
    ;;
  stop) 
    killall lighttpd
    ;;
  restart) 
   $0 stop
   sleep 1
   $0 start 
   ;;
  *) 
  echo "Usage: lighttpd.sh {start|stop|restart}" 
  ;; 
esac

exit 0 


创建一个shell脚本,内容如上,chmod u+x lighttpd.sh,这个脚本就可以用来启动关闭和重起lighttpd了

启动Lighttpd的时候报错,说XXX目录找不到 Top

Lighttpd启动之后默认情况下会写access log,error log,如果你启动了压缩过程,还会把文件压缩过的版本放在压缩目录下面,所以检查一下你的lighttpd.conf配置文件,是不是这些目录还没有,或者路径不对。

lighttpd可以启动,但配了Rails就无法启动 Top

如果无法启动Rails,要学会自己看Lighttpd的error log和Rails项目的log目录下面的fastcgi crash log,在这两个log文件当中可以找到出错原因。

其中一种常犯的错误是:Rails项目在Windows上面创建和开发,最后部署到Linux Server上面。这种情况下dispatch.fcgi这个脚本的ruby解析器路径是 #!c:/ruby/bin/ruby.exe 这个路径在Linux上面肯定是错误的,你可以改成#!/usr/bin/env ruby,或者干脆在Linux上面创建该Rails项目。

此外在windows上面创建的Rails项目,dispatch.fcgi没有可执行权限,这也需要你在Linux上面先赋予可执行权限才行。你可以尝试着手工运行该脚本cd public && ./dispatch.fcgi,看看是否可以运行。

启动lighttpd报错,说找不到socket路径 Top

我在前面安装文档中给出来的配置内容如下:

$HTTP["host"] == "www.xxx.com" {
 server.document-root = "/yourrails/public"
 server.error-handler-404 = "/dispatch.fcgi"
 fastcgi.server = (".fcgi" =>
    ("localhost" =>
      ("min-procs" => 10,
       "max-procs" => 10,
       "socket" => "/tmp/lighttpd/socket/rails.socket",
       "bin-path" => "/yourrails/public/dispatch.fcgi",
       "bin-environment" => ("RAILS_ENV" => "production")
      )
    )
 )
}


就算照抄你要改改路径吧?这个demo当中的socket路径是/tmp/lighttpd/socket/rails.socket,那你要照抄,先检查一下有没有/tmp/lighttpd/sock目录总是应该的吧?其实用啥路径都无妨,关键就是别照抄,领会原理,根据自己的环境做相应的调整。

lighttpd正常启动了,但访问Rails出现404 Top

在lighttpd的虚拟域配置里面有一项
 server.error-handler-404 = "/dispatch.fcgi"

意思是当lighttpd找不到URL对应的硬盘文件,就会调用Rails的dispatch.fcgi去处理该URL请求,这也是lighttpd访问Rails的主要方式,其性能比URL转发要快。如果你在配置文件里面忽略了这一行,lighttpd就会直接返回404错误,而不是交给Rails处理。

多次重起lighttpd以后fastcgi进程越来越多 Top

正常情况下,关闭Lighttpd以后,dispatch进程就会销毁,但是在dispatch进程处理请求的时候关闭lighttpd,dispatch进程并不会马上关闭,而是处理完毕当前请求,才会关闭掉。一些极端情况下,可能会导致dispatch进程一直不关闭,dispatch进程就会越来越多。解决办法很简单 killall -9 dispatch.fcgi,只管杀进程就好了。

lighttpd和FastCGI作为独立进程部署 Top

这种情况下,Lighttpd只是连接远程服务器的TCP端口,而不负责启动dispatch.fcgi进程,因此需要自己写脚本启动关闭dispatch.fcgi进程。lighttpd提供了一个spawn-fcgi的程序,可以用来启动dispatch.fcgi进程,监听TCP端口,你可以自己写一个shell脚本来完成这个工作。另外spawn-fcgi还可以启动dispatch.fcgi进程,创建本机的unix socket端口,和本机lighttpd通讯,例如:

例如:
#!/bin/sh

DISPATCH_PATH=/yourrailsapp/public/dispatch.fcgi
SOCKET_PATH=/tmp/lighttpd/socket
RAILS_ENV=production
export RAILS_ENV

case "$1" in

  start)
    for num in 0 1 2 3 4 5 6 7 8 9
    do
     /usr/local/lighttpd/bin/spawn-fcgi -f $DISPATCH_PATH -s $SOCKET_PATH/rails.socket-$num
    done
    ;;

  stop)
    killall -9 dispatch.fcgi
    ;;

  restart)
    $0 stop
    $0 start
    ;;
  
  *) 
    echo "Usage: dispatch.sh {start|stop|restart}"
    ;;
  
esac

exit 0


执行 ./dispatch.sh start 将启动10个dispatch.fcgi进程,在/tmp/lighttpd/sock目录下面创建了10个unix socket文件,然后配置lighttpd去连接这10个socket文件:

$HTTP["host"] =~ "www.xxx.com$" {
  server.document-root = "/yourrails/public"
  server.error-handler-404 = "/dispatch.fcgi"
  fastcgi.server = (".fcgi" =>
    (
      ("socket"=>"/tmp/lighttpd/socket/rails.socket-0"),
      ("socket"=>"/tmp/lighttpd/socket/rails.socket-1"),
      ("socket"=>"/tmp/lighttpd/socket/rails.socket-2"),
      ("socket"=>"/tmp/lighttpd/socket/rails.socket-3"),
      ("socket"=>"/tmp/lighttpd/socket/rails.socket-4"),
      ("socket"=>"/tmp/lighttpd/socket/rails.socket-5"),
      ("socket"=>"/tmp/lighttpd/socket/rails.socket-6"),
      ("socket"=>"/tmp/lighttpd/socket/rails.socket-7"),
      ("socket"=>"/tmp/lighttpd/socket/rails.socket-8"),
      ("socket"=>"/tmp/lighttpd/socket/rails.socket-9")     
    )
  )
}


这样做的好处是,每次重新部署应用,就不需要重起lighttpd了,只需要执行自己的dispatch.sh来重起dispatch.fcgi进程就可以了。同时也可以很好的解决上一个fastcgi进程堆积的问题。

在多台服务器上面远程部署FCGI进程群集 Top

对于一个大型的Rails Web应用,单台服务器硬件很可能无法支撑所有的访问量,这种情况下需要我们在多台服务器上面运行很多的FCGI进程,构成一个应用服务器群集系统来处理Rails请求。在这种情况下,lighttpd和FCGI进程不在同一台服务器上面,他们之间要通过tcp端口来通讯,针对这种分布式的群集配置,我们可以这样来做:

一、lighttpd尝试连接远程服务器的FCGI监听的TCP端口

例如监听远程服务器192.168.0.3上面的5个FCGI进程
  $HTTP["host"] =~ "*.iteye.com" {
    fastcgi.server = ("/" =>
      (
        ("host" => "192.168.0.3", "port" => 3000, "check-local" => "disable", "docroot" => "/" ),
        ("host" => "192.168.0.3", "port" => 3001, "check-local" => "disable", "docroot" => "/" ),
        ("host" => "192.168.0.3", "port" => 3002, "check-local" => "disable", "docroot" => "/" ),
        ("host" => "192.168.0.3", "port" => 3003, "check-local" => "disable", "docroot" => "/" ),
        ("host" => "192.168.0.3", "port" => 3004, "check-local" => "disable", "docroot" => "/" )
      )
    )
  }

注意要指定check-local是diable,然后我们在FCGI进程所在的服务器上面写脚本启动FCGI进程的时候,应该让他监听相应的端口,例如:

for num in 0 1 2 3 4 5 6 7 8 9  
do  
  /usr/local/lighttpd/bin/spawn-fcgi -f $DISPATCH_PATH -p `expr 3000 + $num` -P $PID_PATH/javaeye.pid-$num  
done 


这样就配置好了。

rails应用不在根目录下,在子目录下怎么配置 Top

例如你的rails应用必须使用某个网站的子目录,如:http://www.xxx.com/myapp,那么lighttpd的配置要进行相应的修改:

$HTTP["host"] == "www.xxx.com" {
  $HTTP["url"] =~ "^/myapp" {
    server.document-root = "/home/webroot/demo/public"
    alias.url = ("/myapp" => "/home/webroot/demo/public")
    server.error-handler-404 = "/myapp/dispatch.fcgi"
    fastcgi.server = ("/myapp/dispatch.fcgi" =>
        ("localhost" =>
          ("min-procs" => 1,
           "max-procs" => 10,
           "socket" => "/tmp/rails.socket",
           "check-local" => "disable",
           "bin-path" => "/home/webroot/demo/public/dispatch.fcgi",
           "bin-environment" => ("RAILS_ENV" => "production")
          )
        )
    )
  }
}


1、要使用$HTTP["url"]去匹配你要访问的子目录,根据该子目录进行特定的配置。这样做的好处是,你可以在一个虚拟域下面部署多个rails应用,用子目录分开就可以了。

2、使用alias.url来指定目录别名,这可以保证你的静态资源例如js,css,images访问路径是正常的

3、server.error-handler-404 要加上前缀: "/myapp/dispatch.fcgi"

4、在fastcgi配置里面,也要匹配该URL,特别注意,应该增加一项:"check-local" => "disable"的配置项

在进行如上配置以后,lighttpd已经可以正常的处理子目录了,但是Rails的routes仍然无法正常工作,这需要我们修改Rails的配置文件config/environment.rb,在最后面添加如下代码:

class ActionController::AbstractRequest
  def relative_url_root
    '/myapp'
  end
end


修改Rails处理URL请求的默认前缀。

好了,现在你可以打开浏览器测试一下了。

我使用了Rails的页面缓存,却不生效 Top

Rails的页面缓存机制本质上是动态页面静态化技术,当你第一次访问页面的时候,在public目录下面生成静态页面,后续同样的请求,Web服务器将直接返回静态页面,不再转发给Rails处理了。那么你如何让Web服务器知道一个动态请求的URL应该去寻找相应的静态页面呢?这需要在lighttpd里面增加如下的配置:

 url.rewrite = ( "^/$" => "index.html", "^([^.]+)$" => "$1.html" )


将所有URL请求统统重写为后缀.html的静态页面请求,如果lighttpd找不到该静态页面,就会触发404,转发给Rails的dispatch.fcgi处理。但是如果你的Rails应用不使用Rails页面缓存,则不要写这个配置,以免不必要的增加lighttpd的负担和增加处理请求的步骤。
评论 共 6 条 请登录后发表评论
6 楼 cuiyunzhuangzhu 2012-01-07 10:58
      
5 楼 eltonto 2010-07-09 16:24
Server: lighttpd/1.4.20
ROB用这个,我们也用这个
4 楼 lanvige 2009-09-04 14:10
偶就是因为Robbin的这篇文章才决定要趟RoR的混水的,太有深度了。

不过,后来发现,社区其实没这么火,英文不是很好的我,熬了一周,才把这些事情给全部搞定。
不过,有这么好的文章,总会像有灯的海岸,有着强有力的信心。

计划写一点个人的学习记录,补充一下所遇到的问题。。

谢谢!
3 楼 ahuaxuan 2008-12-20 09:41
诲人不倦

=================数字补丁===========
2 楼 oztime 2008-12-19 20:36
太谢谢了!
1 楼 sheandwei 2008-12-16 19:20
很详细,幸苦了。。。

发表评论

您还没有登录,请您登录后再发表评论

文章信息

Global site tag (gtag.js) - Google Analytics