锁定老帖子 主题:Sinatra 中的缓存是如此的简单
该帖已经被评为精华帖
|
||||||
---|---|---|---|---|---|---|
作者 | 正文 | |||||
发表时间:2010-02-10
最后修改:2010-03-01
页面缓存
sinatra 和 rails 的一个区别是 sinatra 默认是多线程模式。(不过当用到非线程安全的库时,可能还需要打开 mutex 选项,用单线程方式执行)
# coding: utf-8 # Ruby 1.9 # super simple cache control class Cachee < Hash # 30 min default def fetch path, t=nil # page data, expire time data, et = self[path] now = Time.now if !et or et < now data = yield self[path] = [data, now + (t || 1800)] end data end alias expire delete # clear expired caches def sweep now = Time.now delete_if do |_, (_, t)| t < now end end end
$cache = Cache.new get '/' do # expire after 1 hour $cache.fetch '/', 3600 do haml :index end end
浏览器端缓存
在上面例子中添加 etag 非常的简单: $cache = Cache.new get '/' do etag 'mimi' $cache.fetch '/', 3600 do haml :index end end
邮件列表上一个 讨论 中还提到控制浏览器的缓存时间: response['Cache-Control'] = 'public, max-age=3600' 既然 sinatra 管好动态内容的 etag 了,那么静态内容的 etag 也交给相应的专家吧。譬如 nginx 的配置就像这样:
worker_processes 3; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; gzip on; # 配置 cluster # 应保证这些端口上运行 thin 或者 mongrel 服务器 upstream myclusters { server 127.0.0.1:4001; server 127.0.0.1:4002; server 127.0.0.1:4003; } server { listen 80; server_name localhost; charset utf-8; # 主要内容交给 sinatra location / { proxy_pass http://myclusters; } # 静态内容交给 nginx location ~ /css { root myapp/public; } location ~ /img { root myapp/public; } location ~ /js { root myapp/public; } error_page 500 502 503 504 /50x.html; location = /50x.html { root myapp/public; } } }
在 windows 下也能用,开发服务器也不慢呢。 开两台机器,一台开发,一台看效果,后台跑个脚本 每 5 秒点一下浏览器的刷新钮 ……
缓存策略
再回来说说页面缓存。
根据不同的资源特点,选用不同的缓存策略,可以精细的调整程序,获得最佳的性能表现。 譬如一个队列式的,限制大小的缓存:
# coding:utf-8 class QueuedCachee < Hash # sz limits the max size of cache def initialize max @max = max super end alias expire delete alias expire_if delete_if # fetch cache or (yield and add to cache) def fetch path # ruby 1.9 的 Hash 同时也是一个链表 (万能数据结构啊!) # Hash 里面的元素是按照插入顺序排列的 # 如果已存在,删除,再将其插入最尾端 # 如果不存在,且缓存数量超标,删除最前端(老)的元素,插入 yield 结果 data = expire path if !data and size() >= @max oldest, _ = first() expire oldest end self[path] = data || yield end # remove oldest n cached items def sweep n expire_if do return if n <= 0 n -= 1 end end end
还有其它的策略,如按访问量控制(实现或许需要一个优先队列,用 algorithms 能省一点点功夫)。如果需要非常复杂的缓存功能,最好还是用现成的东西 …… 如果应用程序架构设计得好,切分为很多独立的小 sinatra app 和消息服务器,这些小缓存类虽然简单,但也非常够用了。
ps: 这些缓存类还可以兼任对象缓存、数据库缓存和 fragment 缓存,在静态语言中这几乎是不可能的。
题外:多线程和单线程的组合,scaling
处理请求的时候,mongrel 是多线程的,thin 是单线程的。 thin 处理单任务的时候性能优于 mongrel,但是进程开多了内存消耗相对高一些。 可以把线程安全的组件放到 mongrel,再把 mongrel 在负载平衡中的权重设高一点,用细致的调整满足大量并发访问的需求。
sinatra REST 的优点不仅仅是 "url 比较短",它还体现了横向切分资源的通用设计方法。 每个 sinatra 程序根本复杂不到哪里去。在设计庞大的应用程序的时候,基本套路就是按资源切分成很多简单的独立 sinatra 程序。在扩展升级的时候,把 sinatra 程序放到不同的服务器中,轻松完成 scaling。从出生起就没有 A 依赖 B,B 依赖 C …… 的连锁问题(我在整 lift 的时候被弄怕了 …… play! 那 63 M 的下载包也让人很郁闷)。
声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
||||||
返回顶楼 | ||||||
发表时间:2010-02-10
有一个问题请教,Ryan Yomayko写的Rack::Cache插件是做什么的?简单看了一下,也是用于ETag,Cache-Control的,不知道和Sinatra的配合怎么样。
|
||||||
返回顶楼 | ||||||
发表时间:2010-02-11
呃,应该和 etag 方法作用相似,不过可以作为中间件使用,不用修改代码。
|
||||||
返回顶楼 | ||||||
发表时间:2010-02-11
最近正好要做一个定制服务器,lz介绍的tilt来得正好!
另: Sinatra确实非常好用,上能通天(高性能服务器),下能入地(嵌入式系统)! |
||||||
返回顶楼 | ||||||
发表时间:2010-03-28
最后修改:2010-03-28
tilt确实非常好,最新的版本则利用一种巧妙的方式提升了render速度。(确切的说,是取值的速度。)
以下来自官方README require 'tilt' template = Tilt::ERBTemplate.new('foo.erb') # Slow. Uses Object#instance_eval to process template class Scope end scope = Scope.new template.render(scope) # Fast. Uses compiled template and Object#send to process template class Scope include Tilt::CompileSite end scope = Scope.new template.render(scope) 新版的Sinatra::Base直接帮你include了Tilt::CompileSite,现在可以认为Sinatra的响应速度是所有Ruby框架中最快的。 |
||||||
返回顶楼 | ||||||
发表时间:2010-04-04
我把Sinatra纯粹当成Rails的补充来用,专门用来写一些小而简单的程序.
|
||||||
返回顶楼 | ||||||
浏览 6247 次