世界上最大的基于Rails的网站是如何构建的

Jan 发表于 2007-12-30 00:24:23

如果我把37Signals称作世界上最大的基于Rails的网站,应该没有问题吧 :) 原谅我做一回标题党. ROR总是让人觉得性能低下,毕竟它是一个动态语言成就的框架,可以理解. 但还是让事实来说话吧.

数据来源:

Ask 37signals: Numbers?
Ask 37signals: How do you process credit cards?

平台:

  • Ruby on Rails
  • Memcached
  • Xen
  • MySQL
  • S3 for image storage

数据:

  • 从单处理器的文件服务器到8CPU的应用服务器一共30台,总共大约100枚CPU和200GB内存
  • 为增强系统管理使用Xen虚拟机
  • Basecamp(web based project management, 37signals最著名的服务)数据:
    • 2000000注册用户
    • 1340000项工程
    • 13200000项待办条目
    • 9200000条信息
    • 12200000条留言
    • 5500000条时间跟踪条目(time tracking entries)
    • 4000000个里程碑
  • Backpack(personal and small business information management)数据:
    • 低于1000000个页面
    • 6800000个待办事项
    • 1500000条记事
    • 829000张照片
    • 370000个文件
  • 总的存储数据(截止至2007年11月)
    • 5.9TB的客户上传文件
    • 888GB的普通上传文件(通过900000个请求)
    • 2TB的文件下载(通过8500000个请求)

其他:

  • 疯狂的使用Memcached缓存,而且将会更加疯狂的使用
  • 用URL helper方法替代人肉生成URL(这也算到架构里面了...)
  • 大部分情况下使用标准ActiveRecord查询,偶尔为了性能使用find_by_sql
  • 每当碰到性能问题,他们就会给Rails打补丁 :) (我想这是Rails一个最特别的地方,它是由一群真正在使用它的人维护着)
  • 使用Amazon的S3服务为用户上传提供空间

其实国内的JavaEye社区新版也是个不错的例子.
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Ruby Parser 1.0: 用纯ruby实现的ruby解析器

Jan 发表于 2007-12-30 00:16:56

ruby社区期待已久的东东,它意味着一个支持自动代码补全以及错误提示之类的不依赖Java的ruby ide将会到来, textmate怎么办? Rubinius也会获得好处, 哦也.

谢谢大牛Ryan Davis.
关键词(Tag): ruby parser
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Ruby 1.9 (2): 内核与对象

Jan 发表于 2007-12-28 16:05:23

BasicObject

1.9增加了一个新的顶级类,很纯洁

BasicObject.instance_methods # => [:==, :equal?, :"!", :"!=", "__send__]
Object.ancestors # => [Object, Kernel, BasicObject]

注意instance_methods返回的array中现在都是symbol对象,以前是string对象.

#instance_exec

为特定的instance执行block

BasicObject.instance_methods
# => ["__send__", "__id__", "==", "send", "send!", "respond_to?", "equal?", "object_id"]
Object.ancestors # => [Object, Kernel, BasicObject]

Kernel#require

通过require载入的文件现在以绝对路径的形式存放在$"变量中, require现在这样工作:

$" << File.expand_path(loaded_file)

Object#=~

匹配失败的时候返回nil而不是false

1 =~ 1 # => nil

Object#tap

把对象传给block, 返回这个对象,在链式调用的时候非常有用.

"F".tap{|x|x.upcase!}[0] # => "F"
"F".upcase![0] # error, 此时upcase!返回nil,你将会对nil调用[]方法

Kernel#define_singleton_method

a=""
a.define_singleton_method(:foo){|x|x+1}
a.foo(2) # => 3

Kernel#singleton_methods, Kernel#methods

返回一个由symbol组成数组而不是一个由字符串组成的数组

经我试验失败的features:

* send不能再调用private方法
* 没有新加入send!方法
关键词(Tag): ruby 1.9 changes
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Ruby 1.9 (1): 新语法

Jan 发表于 2007-12-28 13:21:09

(标记为Ruby2的是已经确定将在2.0中保留的feature, 标记为Experimental的尚在取舍之中)

字面量Hash (literal hash syntax) (Ruby2)

Hash有了一种新的表示方法: {a: "foo"}, 和{:a => "foo"}效果相同,活生生减去两个键的输入,hash用的太多了,这个改动影响巨大.

Block局部变量(Experimental)

在block中使用局部变量:

a = lambda{|;d| d = 1}
a.call()

注意调用proc的时候没有传参数,因为;d是局部变量的生命(d前面是分号). 如果lambda外部也有一个变量叫做d, 外部变量会被遮盖住,此时ruby会给个warning: "warning: shadowing outer local variable - d "

Block参数的作用域被限制在了局部

a=1
10.times{|a|}
a

1.9中最后a依然是1, block中的a是个本地变量把block外部的a遮盖了. 而在1.8中a最终被修改成了9. 这是一个不向后兼容的修改,估计会导致许多框架出问题吧.

lambda的新语法(Very Experimental)

a = ->(b,c){b+c}
a.call(1,2) # => 3

Matz保证说旧的声明lambda的语法永远不会被去除. 又是一个简化写法的改进,lambda六个字母简化成了->两个字符. 同时新语法支持默认参数, 你可以这样做:

a = ->(b,c=2){b+c}
a.call(1) # => 3

但是你不能这么做:

a = lambda{|b,c=2|b+c} #error

因为据说此写法是无法用Ruby现在使用的基于bison打造LALR(1) parser来实现的. 下面介绍更诡异点的写法,你可以把参数外的括号省略:

->{}.call # => nil
->a,b{a+b}.call(1,2) # => 3
c=1; ->a,b;c{c=a+b}.call(1,2); c # =>1 (注意c前面是分号, 分号后面都是局部变量,不是参数. 新特性,记得吗)
c=2; ->;c{c=1}.call;c # => 2
c=2; ->*d;c{d}.call(1,2,3) # => [1,2,3] (只用了一个*d,还没用上1.9中某些新特性呢...)

好了,不折磨你了,继续看新东西.

用.()来调用Proc (Experimental)

a = lambda{|b|b}
a.(1,2)

现在你不需要call这四个字母了.注意.()中的句点不要漏了,不然ruby以为你在调用一个名字叫做a的函数. 值得一提的是无论对象是什么如果你对它调用.()方法ruby实际上都会试图去调用它的call方法,通过为proc之外的类实现其call方法你可以做出有趣的事情:

"foo".(1,2) # ~> undefined method `call' for "foo":String (NoMethodError)

将block作为参数传递给一个block

class A
  define_method(:foo){|&b| b.()}
end
A.new.foo{puts "bar"}

正确处理多余的block参数

def m
  yield 1,2
end
m{|v| v} # => 1

1.8中v的值会是[1,2], 同时ruby给出warning说传入的参数比block接收的参数多. 1.9能够正确处理,把多余的参数忽略,注意在1.8中你可以通过m{|v,|v}达到相同效果.

允许多个数组展开

def foo(*a)
  a
end
foo(1, *[2,3], 4, *[5,6]) # => [1,2,3,4,5,6]

a,b = [1,2,3],[4,5,6]
[*a, *b] #=> [1,2,3,4,5,6]

必需参数可以放在可选参数后面!

def m(a,b=nil,*c,d)
  [a,b,c,d]
end
m(1) # error
m(1,2) #[1,nil,[],2]
m(1,2,3) #[1,2,[],3]
m(1,2,3,4) #[1,2,[3],4]
m(1,2,3,4,5) #[1,2,[3,4],5]

甚为诡异的分配方法,初学者估计要一头栽倒... 记住必需参数获得赋值的优先级最高,可选参数次之,变长参数最低,然后位置一卡,分配下去... 不知道我这样说是不是更混乱了...

?c语义

?a # => "a" (in 1.9, string)
?a # => 97 (in 1.8, integer)

传递Hash给[]方法

class Foo
  def[](*a)
    a
  end
end
f = Foo.new
f[1,2,:ab=>3,:bc=>4] # => [1,2,{:ab=>3,:bc=>4}]

printf风格的格式化字符串

%c: "%c" % ?a # => "a"
%u: 太诡异了,忘了它吧...

经我试验不成功的feature

* 用to_splat代替to_a
* 传递block给[]方法
* 三元运算符?中允许换行(指a:b分成三行, ?后换行是1.8就允许的)
关键词(Tag): ruby 1.9 changes
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

在Rails启动的时候自动下载Google Maps Library

Jan 发表于 2007-12-26 02:28:44

如果你用Google Maps做mashup, 不可避免的需要在网页中include google maps的javascript文件. 如果每个request都要通过访问google的服务器取得那个小小的js文件,未免太让人郁闷,尤其对于开发人员来讲. 很自然的,我们想要保存一个本地拷贝,但是依靠人肉下载的手段一来麻烦,二则容易忘记更新本地拷贝,万一google更新了api,你就挂了.

于是我让rails在启动的时候自动下载一份最新版本放到制定目录,这样每次重启rails server后你用的都是最新版本的api了.

添加一个新文件RAILS_ROOT/config/initializers/initialize.rb, 包含以下代码:

require 'open-uri'

#--------------------------------------------------
# use to download the latest google maps library when
# rails startup
#--------------------------------------------------
def download(from_link, to_dir, save_name)
  open(from_link) do |stream|
    File.open(File.join(to_dir, save_name), "wb") do |file|
      file.write(stream.read)
    end
  end
end

gmap_include_js = "INSERT_YOUR_GOOGLE_MAPS_KEY_HERE"
download(gmap_include_js, "#{RAILS_ROOT}/public/javascripts", "google_maps_library.js")

然后在你的layout里面直接javascript_include_tag "google_map_library"就行了. 如果你有svn, 还能保存所有google maps js的历史版本, 就不怕google又发布新版本导致网站一团糟了 :)
关键词(Tag): google maps rails initializer
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Ruby 1.9.0 is released

Jan 发表于 2007-12-26 00:34:14

Posted by Yukihiro Matsumoto (Guest)
on 25.12.2007 16:05
Hi,
We are happy to announce of the release of the 1.9.0 the development
release. You can fetch it from:

ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.0-0.tar.bz2
407cc7d0032e19eb12216c0ebc7f17b3

ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.0-0.tar.gz
b20cce98b284f7f75939c09d5c8e846d

ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.0-0.zip
78b2a5f9a81c5f6775002c4fb24d2d75

We hope this helps you to enjoy hacking. Happy Holidays.

matz.
关键词(Tag): ruby 1.9
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

使用firefox 3.0 beta2 无限爽中...

Jan 发表于 2007-12-25 23:58:23

把ff升级到3.0beta2,发现自带的书签功能和del.icio.us插件功能几乎一样强大,速度却比插件快了许多.正好我又想乘机把del.icio.us清理一下,收藏的东西太多也不是件好事,于是干脆手工把常用链接转移到了自带书签中, 然后... 我就很无耻的把del.icio.us插件给卸载了... 曾经号称没有这个插件就没法活的我真是罪过啊,哦米托佛

现在浏览器只装了一个插件,firebug,只对localhost启用.浏览速度快啊,爽死我了.
关键词(Tag): firefox delicious 3.0 beta2
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Rails Test Coverage

Jan 发表于 2007-12-21 20:20:52

Rails是一个很强调测试的框架,就不用阐述了,写测试最需要的就是评估测试覆盖率的工具了,Ruby/Rails怎么可能没有?rcovrails_rcov插件就是完美的解决方案,比用ant+junit+emma舒服多了,泪奔。。。

很痛苦的是,rails的贡献者基本上用的都是linux,于是乎基本上无视所编写程序在windows平台上的稳定性。。。rails_cov的作者直接告诉我们说,他没有windows的机器,windows上碰到的问题他没有办法解决,你可以解决了把patch发给他云云,我晕。

言归正传,rails_rcov装了以后用不了,如果老是出这个错误:

rake aborted!
You have a nil object when you didn't expect it!
The error occured while evaluating nil.exitstatus
(See full trace by running task with --trace)
rake aborted!
Command failed with status (1): [c:/ruby/bin/ruby ./vendor/plugins/rails_rc...]
c:/ruby/lib/ruby/gems/1.8/gemsbr />

打开Rails目录中vendor/plugin/rails_rcov里面的rails_rcov.rake文件,把134行的rcov.cmd改成rcov.bat就行了,这是一个和ruby system调用相关的错误。搞了我两个小时。。。我恨windows。。。要不是笔记本挂了我也不会用windows来做开发了。。。
关键词(Tag): rails rcov rails_rcov rake
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

Ruby/Rails: too good to be true? Options

Jan 发表于 2007-12-20 23:33:53

Rails新闻组里面的一个thread,楼主是个喜欢抛开框架用PHP的小公司老板,由于对Ruby/Rails产生了兴趣来请人比较PHP和Ruby(某种程度上),对PHP和Ruby的区别的切入点很有意思:他说用PHP做网站的不喜欢用框架,用Ruby做网站的几乎都用框架,为什么?

其实原因还是蛮简单的,呵呵,不过仍然值得一看。
关键词(Tag): ruby php rails
收藏: QQ书签 del.icio.us 订阅: Google 抓虾

JS template in Rails 2.0

Jan 发表于 2007-12-18 17:19:40

Rails 2.0的一个新feature是把文件类型和渲染引擎分开,比如以前写作index.rhtml的view,现在可以写成index.html.erb

这个特性带来了一个1.2中没有的用法,你可以用erb来写js!

1. 我在index.html.erb里面写一个AJAX call,link_to_remote "xxx", :url => {:action => :ajaxaction}
2. 跑到controller里面,在ajaxaction中加入respond_to do |format| format.js end
3. 在app/views/:controller里面建立一个叫做ajaxaction.js.erb的新文件,在这里面你可以非常方便的把erb和js结合起来用:

<% for item in @items %>
    alert(item.id);
<% end %>

注意,这不是RJS - RJS让你写的是ruby code,这里让你写JS code,这就是比RJS方便的地方。

爽歪歪。
关键词(Tag): js 2.0 rails rjs erb
收藏: QQ书签 del.icio.us 订阅: Google 抓虾