Ruby Rack 中间件简单理解及例子

yhuiche · 发布于 2016年08月13日 · 最后由 suffering 回复于 2016年09月09日 · 1428 次阅读
5741

此博客原文链接

注: 这里是根据网上的常见的rack例子更改

rack 版本

  • Rack为使用Ruby开发web应用提供了一个最小的模块化和可修改的接口。用可能最简单的方式来包装HTTP请求和响应,它为web 服务器,web框架和中间件的API进行了统一并提纯到了单一的方法调用。

  • 测试中rack gem 版本为 1.6.4


示例代码

module Rack
  class A
    def initialize(app)
      @app = app            # @app: Rack::B
      @header_name = "X-A"
    end

    def call(env)
      start_time = Time.now
      status, headers, body = @app.call(env)  #  Rack::B 实例调用 call
      request_time = Time.now - start_time

      if !headers.has_key?(@header_name)
        headers[@header_name] = "%0.6f" % request_time
      end

      [status, headers, body]
    end
  end

  class B
    def initialize(app)
      @app = app          # @app: Rack::C
      @header_name = "X-test"
    end

    def call(env)
      status, headers, body = @app.call(env)   #  Rack::C 实例调用 call

      if !headers.has_key?(@header_name)
        headers[@header_name] = 'yyyyyy'
      end

      [status, headers, body+['aaaaaa']]
    end
  end

  class C
    def call(env)
      [200, {'Content-Type' => 'text/plain'}, ['hello world!']]
    end
  end
end

use Rack::A
use Rack::B

run Rack::C.new
  • 为了探究中间件的加载运行过程,这里用A、B、C顺序来定义class
  • 实际运行的代码存在打印语句,后面有打印结果
  • 其中Rack::C就相当于Rails App
  • A、B 则相当于中间件
  • A、B、C 都为 rack app
  • 通过实例变量@app实现rack app一种嵌套结构
  • 文件命名为 ~/test/config.ru
  • call 方法统一返回格式,每次返回结果都会在内层返回的结果上做处理后返回结果给外层的调用

运行打印结果

~/test $ rackup

new B  -------------
@app: #<Rack::C:0x007fa4b2ad4298>
------------------------------
new A-------------
@app: #<Rack::B:0x007fa4b2ad41f8>
------------------------------

Thin web server (v1.6.3 codename Protein Powder)
Maximum connections set to 1024
Listening on localhost:9292, CTRL+C to stop

A call -------------
@app: #<Rack::B:0x007fa4b2ad41f8>
------------------------------
B call ----------------
@app: #<Rack::C:0x007fa4b2ad4298>
------------------------------
C call ----------
#<Rack::C:0x007fa4b2ad4298>
------------------------------
127.0.0.1 - - [08/Aug/2016:00:57:19 +0800] "GET / HTTP/1.1" 200 - 0.0041

A call -------------
@app: #<Rack::B:0x007fa4b2ad41f8>
------------------------------
B call ----------------
@app: #<Rack::C:0x007fa4b2ad4298>
------------------------------
C call ----------
#<Rack::C:0x007fa4b2ad4298>
------------------------------
127.0.0.1 - - [08/Aug/2016:00:57:20 +0800] "GET /favicon.ico HTTP/1.1" 200 - 0.0006
  • 可以看到在class Rack::A中,@app为class Rack::B的一个实例;而在class Rack::B中,@app则为Rack::C。
  • 在接受到浏览器的访问后,调用了中间件的call方法,这里有点类似于递归的调法。从代码及返回结果可以看出,是Rack::A中的call方法先被调用,然后是Rack::B中的,最后是Rack::C中的。返回是顺序刚好相反,先是Rack::C中的call方法先执行完,然后是Rack::B,最后是Rack::A。
  • 这段日志有两个访问记录,一个是 http://localhost:9292 主体,一个是网站收藏图标favicon.ico

浏览器查看到的部分数据

HTTP/1.1 200 OK
Content-Type: text/plain
X-test: yyyyyy
X-A: 0.000032
Transfer-Encoding: chunked
Connection: close
Server: thin

浏览器页面看到的结果

hello world!aaaaaa
  • 这里看到的是浏览器中查看到了部分数据
  • 可以看出rack默认的web服务器是thin
  • X-text、X-A为call方法中返回的部分数据
  • 页面看到的东西则为call方法中返回的body部分


共收到 4 条回复
20099

这个中间件是不是相当于os的管道 前面的输出 作为后面的输入

5741

#1楼 @tablecell 之前表述不清楚。rack app之前形成一种嵌套结构。外层@app的call方法里调用内存@app的call方法,内层call方法先返回,之后外层call方法接收数据并处理后返回给更外层的call方法。

20099

#2楼 @yhuiche 这种应该是调用栈 就象函数调用一样 不是管道

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册