`
dazuiba
  • 浏览: 130905 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

深入Rails2.3 Rack

阅读更多
注:这次dig,是由于遇到了metal代码无法自动加载的问题。

Rails2.3引入了Rack, 这使得rails内部的Http处理机制发生了很大的变化。Rack是一个非常微型的action stack, 和java社区的webwork里使用的xwork非常类似。

Rack的进入,对rails带来的最大一个好处,就是引入了metal。

./script/generate metal  MetalTest  就可以产生一个metal, 位于app/metal目录下。

metal有什么特性呢? 快,它非常快。但是web应用一次平常的请求,动辄向数据库发送数十条SQL,总时间一般都在100ms的级别。而rails内置的action_pack已经是在ms级别了, 所以对于一般的web请求, 不需要用到它。

但是,对于一些只需要发送几条SQL,然后传给浏览器一些简单的json数据,对于这样的应用场景,证实metal大展身手的地方。

rails引入metal,用DHH自己的话,也是为了这样的场景。他在自己的产品campfire中,有许多定时的ajax请求,这种请求一般用action_pack做,每秒支持200次(5ms/request),而用metal,每秒可以达到3000次(0.3ms/request)

引入rack后,action_pack的地位就陡然下降了。在新的体系里面,所有的东西都是middleware,这里的action_pack,以及metal,都是作为一个middleware,集成到rails内部的。

使用rake middlewares 你可以看到:
use Rack::Lock
use ActionController::Failsafe
use ActionController::Reloader
use ActionController::Session::CookieStore, #<Proc:0xb76182a0@(eval):8>
use Rails::Rack::Metal
use ActionController::ParamsParser
use Rack::MethodOverride
use Rack::Head
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
run ActionController::Dispatcher.new


这里结构就比以前清晰多了。以上所有的middelware,除了Rack:Metal以外,都是用来实现已有的功能的。从名字上可以非常清晰地看清楚各自的用途。

到这里,就应该提提我之前说到的那个问题了。代码不能重新加载? 这个问题之前也经常遇到,但由于时间关系,没有能够深入地纠察原因。

使用到的工具:一个编辑器,用raise设置堆栈。

首先,查看了Reloader这个middelware,从名称上看,它就是干这个事情的。

def call(env)
      Dispatcher.reload_application
      status, headers, body = @app.call(env)
      # We do not want to call 'cleanup_application' in an ensure block
      # because the returned Rack response body may lazily generate its data. This
      # is for example the case if one calls
      #
      #   render :text => lambda { ... code here which refers to application models ... }
      #
      # in an ActionController.
      #
      # Instead, we will want to cleanup the application code after the request is
      # completely finished. So we wrap the body in a BodyWrapper class so that
      # when the Rack handler calls #close during the end of the request, we get to
      # run our cleanup code.
      [status, headers, BodyWrapper.new(body)]
    end


果不其然,这里有个Dispatcher.reload_application。在每次请求过后,rails都要清除action_support中Dependencies所管理的常量,然后在下一次请求的时候,重新加载整个应用。

很显然,问题应该在清除常量这里,好像Reloader没有做这个操作啊?

呵呵,看一下注释,就清楚了,这里曾经出过一个Bug,Rails开发者用BodyWrapper解决了这个bug.clear常量的操作就在close方法里:
def close
        @body.close if @body.respond_to?(:close)
      ensure
        Dispatcher.cleanup_application
      end


难道是matal没有使用Reload这个中间件?
打个断点试试,首先找到metal这个中间件的核心代码:call
    def call(env) 
        raise 
        @metals.keys.each do |app|
          result = app.call(env)
          return result unless result[0].to_i == 404
        end
        @app.call(env)
      end


出现了这样的堆栈:

    /usr/lib/ruby/gems/1.8/gems/rails-2.3.3/lib/rails/rack/metal.rb:43:in `call'

    /usr/lib/ruby/gems/1.8/gems/actionpack-2.3.3/lib/action_controller/session/c
ookie_store.rb:93:in `call'
    /usr/lib/ruby/gems/1.8/gems/actionpack-2.3.3/lib/action_controller/reloader.
rb:29:in `call'
    /usr/lib/ruby/gems/1.8/gems/actionpack-2.3.3/lib/action_controller/failsafe.
rb:26:in `call'
  .....................

明明在用,并且和action_controller的堆栈一摸一样。

好,看一下action_support中Dependencies中的代码,发现有log信息,打开日志看看。
ActiveSupport::Dependencies.log_activity=true

发现日志也非常正常!

实在没有办法了,问题只能处在metal自己身上。
陡然间,就在刚才设置raise的地方,发现了:
@metals.keys....................

原来在使用一个内部变量,先试试让每次call都重新刷新@metal变量:问题居然解决了!

哦,原来这个变量中直接将每个metal加载后产生的常量放了进来,虽然reloader有清洗,但是他肯定不会清洗这个变量里的metal类。

到这里,问题就基本解决了,下一步看看怎么切换metal这个中间件。

呵呵,对了,rack中内置了丰富的插入删除更改中间件的方法。

附赠代码:
lib/smart_metal.rb
#
# <<代码重构>>中指出用方法代替变量,是多么的闪闪发光啊。
# 你看,在这里,由于Metal用的是变量,我只能copy&plast
class SmartMetal  < Rails::Rack::Metal
	
	def get_metals				
    metals = ActiveSupport::OrderedHash.new
    self.class.metals.each { |app| metals[app] = true }
    metals
	end
	
  def call(env)
    get_metals.keys.each do |app|
      result = app.call(env)
      return result unless result[0].to_i == 404
    end
    @app.call(env)
  end
end		


config/initializers/my_init.rb
  Rails::configuration.middleware.swap 'Rails::Rack::Metal', SmartMetal

然后,在运行
$ rake middleware
(in /cygdrive/d/dev/twork)
use Rack::Lock
use ActionController::Failsafe
use ActionController::Reloader
use ActionController::Session::CookieStore, #<Proc:0x7fc2552c@(eval):8>
use SmartMetal############ 看到了吧 #################################
use ActionController::ParamsParser
use Rack::MethodOverride
use Rack::Head
use ActiveRecord::ConnectionAdapters::ConnectionManagement
use ActiveRecord::QueryCache
run ActionController::Dispatcher.new
分享到:
评论
6 楼 dazuiba 2009-09-05  
to 花花公子

我上面说的有问题,需要更正一下。

我这个middleware,是一个extjs的direct的路由。需要再将请求转发给其他的Controller。而这个ParamsParser负责的是解析action和controller相关的参数。

我需要重新设置这些参数,然后转发给rails 的action_pack处理。

另外,也需要支持批量请求。
说以这个情况下,就需要此ParamsParser了。

5 楼 花花公子 2009-09-03  
dazuiba 写道


从下面这个stack可以看出,Metal在ParamsParser的上面,它只支持session,连参数解析都不支持。

在我的应用中,我把ParamParser交换到Metal的上面,之后metal就支持这一特性了。

难道你不是用sinatra作metal中间件吗?如果是自己解析的话,要用到Rack::Request
  req = Rack::Request.new(env)
  req.post?
  req.params["data"]

4 楼 dazuiba 2009-09-03  
花花公子 写道
根据 http://blog.codefront.net/2009/06/15/activerecord-rails-metal-too-many-connections/
如果在Metal里面用ActiveRecord,要手动释放,感觉很tricky。




从下面这个stack可以看出,Metal在ParamsParser的上面,它只支持session,连参数解析都不支持。


   1. use Rack::Lock  
   2. use ActionController::Failsafe  
   3. use ActionController::Reloader  
   4. use ActionController::Session::CookieStore, #<Proc:0xb76182a0@(eval):8>  
   5. use Rails::Rack::Metal  
   6. use ActionController::ParamsParser  
   7. use Rack::MethodOverride  
   8. use Rack::Head  
   9. use ActiveRecord::ConnectionAdapters::ConnectionManagement  


在我的应用中,我把ParamParser交换到Metal的上面,之后metal就支持这一特性了。

	config.middleware.delete Rails::Rack::Metal
		config.middleware.insert_after ActionController::ParamsParser, SmartMetal



    最后这个ConnectionManagement  应该就是管理连接的。

    如果你非得用active_recod,可以试试把这个中间件放在metal的上面,但是非常不建议。这样以来,估计metal的速度会有所下降。优势就不在了。

      10. use ActiveRecord::QueryCache 
      11. run ActionController::Dispatcher.new 

3 楼 wosmvp 2009-09-02  
花花公子 写道
根据 http://blog.codefront.net/2009/06/15/activerecord-rails-metal-too-many-connections/
如果在Metal里面用ActiveRecord,要手动释放,感觉很tricky。


呃,很dirty
2 楼 花花公子 2009-09-02  
根据 http://blog.codefront.net/2009/06/15/activerecord-rails-metal-too-many-connections/
如果在Metal里面用ActiveRecord,要手动释放,感觉很tricky。
1 楼 night_stalker 2009-09-02  
以前据说 metal 的速度还是比不上 sinatra,不知道现在效率怎么样。

相关推荐

    rails 2.3 chm文档 ,官方最新版

    rails 2.3 chm文档 官方最新版

    railsbrain网站的rails2.3文档(bug修复版)

    Railsbrain是一个专注于Rails框架的在线资源平台,而这个“railsbrain网站的rails2.3文档(bug修复版)”显然是一份针对Rails 2.3版本的更新文档,旨在修复用户在浏览和交互过程中遇到的问题。Rails是Ruby编程语言的...

    Ruby on Rails 2.3 Guide.chm

    Ruby on Rails Guide:是rails官方教程,本人为了大家学习查阅的方便,制成chm格式。就如同java doc的chm格式一样方便。

    i18n_routing:用于Rails 2.3.x和Rails的I18n路由模块。轻松转换您的路由!

    所有必需的信息都可以在Wiki上找到: 如有疑问,请使用i18_routing谷歌论坛: 适用于Rails 2.3、3.0、3.1和3.2下一个版本的TODO(写于2010年6月9日) 处理同一资源名称的多个翻译(例如:嵌套和非嵌套资源) 处理...

    ruby on rails 2.3.5 api html版

    Rails是一个基于Ruby语言的开源Web应用程序框架,它遵循模型-视图-控制器(MVC)架构模式,旨在提高开发效率和代码的可读性。 在这个API文档中,你可以找到关于以下关键知识点的详细信息: 1. **Ruby语法基础**:...

    rails 3.2 API

    Rails 3.2 API 是一个重要的开发资源,主要用于Ruby on Rails框架的开发。Rails是基于Ruby语言的一个开源Web应用程序框架,遵循MVC(Model-View-Controller)架构模式,广泛应用于构建动态网站和Web应用程序。Rails ...

    rails2.3.2

    描述中的 "ruby and rails 的框架rails-2.3.3.zip" 提到的是 Rails 的另一个版本 2.3.3,尽管与标题中的版本号不完全匹配,但我们可以推断这是关于 Rails 2.3.x 系列的讨论。这个压缩包很可能包含了 Rails 框架的源...

    pgq:基于 PgQ Skytools for PostgreSQL 的 ARRails 队列系统,如 Resque on Redis。 Rails 2.3 和 3 兼容

    Rails 2.3 和 3 兼容。 关于 PgQ 安装 安装天空工具:Ubuntu 11.10: # apt-get install postgresql-server postgresql-client # apt-get install skytools 宝石档案: gem 'pgq' 从 database.yml 创建股票...

    bugsnag-ruby, Rails Sinatra rack 和 ruby的Bugsnag错误监视.zip

    bugsnag-ruby, Rails Sinatra rack 和 ruby的Bugsnag错误监视 ruby的 Bugsnag异常报告器 ruby 异常报告器提供了从你的 Rails Sinatra/英镑/或者英镑的普通 ruby 应用程序中抛出的异常通知。 任何未捕获的异常都会...

    rails guides 2.3 CHM版

    rails guides的CHM版本,这个向导的版本是2.3

    Ruby on Rails中Rack中间件的基础学习教程

    Rails本身就是在Rack的基础上构建起来的,这意味着了解Rack对于深入理解Rails的工作原理非常关键。 #### 二、Rack的工作原理 Rack的核心概念是将Web应用抽象成一个简单的接口,这个接口能够响应`call`方法,并且...

    HeadFirst系列之:深入浅出Rails(中文版)高清完整PDF

    《深入浅出Rails》将使你的编程和生产力达到最大值。你将学习一切Rails scaffolding的基本原理,以创建自定义的交互式网络应用程序,全部使用Rails的一套丰富的工具和MVC框架。 你将掌握数据库交互、Ajax和XML的集成...

    深入浅出Rails(中文版)

    《深入浅出Rails(影印版)》内容简介:通过此书,你将学习:希望你的网络应用超越平庸进入Web 2.0时代?《深入浅出Rails》将使你的编程和生产力达到最大值。你将学习一切Rails scaffolding的基本原理,以创建自定义的...

    easy_config:易于配置,Rails和Rack应用程序的配置变得简单

    简单的配置 Rails和Rack应用程序的配置变得容易。安装gem install easy_config 或在您的Gemfile中gem "easy_config" 用法Rails / Sinatra 在配置目录中,创建文件redis.yml: development: host: 127.0.0.1 port: ...

    vitals:适用于Rails,Rack,Grape等的灵活StatsD仪表

    目前,它支持Rails,Rack(Sinatra和任何Rack支持的框架)和Grape。安装将此行添加到您的应用程序的Gemfile中: gem 'vitals' 然后执行: $ bundle 或将其自己安装为: $ gem install vitals用法滑轨或葡萄创建一个...

    mailgun_webhooks:Mailgun Webhooks 的 Rails 和 Rack 集成

    Mailgun 网络钩子 为您的 Rack 或 Rails 应用程序添加对 Mailgun webhook 的支持。导轨安装将gem 'mailgun_webhooks'添加到您的 Gemfile。 设置config/initializers/mailgun.rb MailgunWebhooks . api_key = ...

    The Rails 4 Way

    本书深入浅出地介绍了Rails 4的各种组件和功能,适合已经熟悉Ruby编程并希望深入了解Rails框架的开发人员。 #### 描述:学习ruby! 这句简短的描述暗示了本书的一个重要目标:帮助读者通过学习Rails来提高他们的...

Global site tag (gtag.js) - Google Analytics