Rails RAILS API TO BE PART OF RAILS 5

lgn21st · June 28, 2015 · Last by helperhaps replied at June 11, 2017 · 10428 hits
Topic has been selected as the excellent topic by the admin.

Rails API 项目整合工作已经完成,具体见https://github.com/rails/rails/pull/19832,下一步就等待 Rails 5.0 发布了!

关于 Rails API 项目

Rails API 项目是由一群 Rails 社区的知名技术大牛创建的:Yehuda Katz, José Valim, Carlos Antonio da Silva 和 Santiago Pastorino 等。该项目把 Rails 做了拆解,然后重新组合成了一个剪裁过的 Rails,这个剪裁过的 Rails 特别适用于 API-Only App 的场景,即 Rails 只用来提供 API 服务,以 JSON 或者 XML 作为主要的数据交换格式,API-Only App 的主要服务 API Client 或者各种重前端应用,比如各种基于 ReactJS / AngularJS / Backbone / Whatever 等 SPA 应用。

用 Rails API 的好处

一句话总结:Memory footprint 显著降低,性能有所提升。

Rails 社区流行一种观点,如果做 API-Only 服务的话,用 Rails 太重了,Sinatra 之类的轻量级框架更适合,性能更好。我部分认同这个观点,如果直接拿 Rails 来做 API-Only 服务的话,只要你对 Rails 框架非常熟悉并有能力做适当的裁剪,那么 Rails 也能大幅削减资源消耗,且性能跟 Sinatra 没什么差别。但是如果直接使用 Rails 默认的配置的话,的确是挺浪费资源的。关于性能并不在本文讨论范围内,API 服务的性能更多取决于服务模型,以及应用架构。剪裁过的 Rails 跟 Sinatra 或者其他轻量级框架比如 Grape 的性能表现其实差不多,如果追求更高的性能,建议考虑使用基于 Fiber 的 Goliath 或者基于 Eventmachine 的 Thin 并使用异步 IO 编程模型来提升 API 服务的性能,当然架构和应用层面进行优化也对性能有至关重要的影响。

所以 Rails API 项目相当于帮你把裁剪这件事给做了,且做的非常专业,彻底。包括去掉不必要的 middleware,去掉 cookie 支持,去掉视图,assets,剪裁 Controller 层,以及各种前端相关的特性等等,再此基础之上,还可以灵活的配置和扩展。更多相关的细节请参考项目介绍页面:https://github.com/rails-api/rails-api

关于合并到 Rails 5.0

DHH 在 RailsConf 2015 Open Keynote 上宣布 Rails API 项目将会成为 Rails 5.0 的一部分,但是这并不是第一次合并,早在 Rails 4.0 发布的时候就曾经这么干过一次了,但是却在发布之前又给 revert 撤销了,然后 Rails API 项目才以独立插件的方式出现在人们面前。这段历史可能鲜为人知,基于当时的环境,Rails Core Team 发布 Rails 4.0 的决策是大幅重构和砍掉过时 (Deprecated) 的 API,抽出所有可以被抽出去作为插件存在的功能,瘦身,灵活,更好的性能和安全性,同时引入更多对前端的支持和新特性等等。所以当时集成这么个一个东西并不符合 Rails 4.0 的精神。

这次合并其实算起来是第二次了,Rails API 项目经过两年多的发展也日趋成熟,且在 Rails 社区地位越来越重要,现如今前端领域的技术和社区有了长足的发展,对 Rails 作为纯 API 后端的需求越发强烈,所以 Rails Core Team 决定把 Rails API 项目吸收进来的确是一个很 make sense 的决策。

Rails 本身是一个 Full Stack 的框架,这意味着 Rails 自身提供了从前端到后端一套解决方案,整个解决方案集成得非常紧密。而 Rails API 的思路是先解构,然后削减掉所有跟前端相关的部分,且仅仅保留后端核心功能,且有一套自己的方法论和解决方案。所以集成过程中就涉及到要对 Rails 自身架构进行前后端分离,分离后的前端组件需要考虑如何协同工作,比如 Controller 层的 flash,assets,render, 以及 assets generation 等属于前端领域,而真正为后端服务的 Controller 其实非常轻薄。后端还有一些组件的选型问题也是社区讨论的焦点,而讨论并解决这些问题都是为了让用户能直接用到最佳预设的框架,而不需要把时间精力放在技术选型和配置上。

ActiveModel::Serializer 还是 JBuilder?

JBuilder 是 DHH 大神的作品,是目前 Rails 的默认组件,用于 API 请求时生成 JSON 数据的模版。优点是 DSL 声明语法灵活强大,对 russian-doll caching 支持良好。缺点是生成 JSON 的时候依赖 view render 机制,在视图环境之外,比如你需要在 API Client 端生成一段 JSON,这个时候 JBuilder 就排不上用场。

ActiveModel::Serializer 是 Rails API 项目默认的 JSON 生成方案,特点是引入了一套类似 ActiveRecord 的方式定义 Serializer model,然后通过 model 生成 JSON,整个过程非常的 Object-Oriented。

DHH 的态度非常明确,希望 JBuilder 作为默认视图层的 JSON Generator 能够继续保持下去,当使用 API 模式的时候,AMS 可以作为默认的 JSON Generator。

除了 JBuilder 和 AMS,社区流行的 JSON 模版生成工具还有 RABL,也是可选的组件之一。但是在 API 模式下,除了 Controller 层和 Model 层之外,几乎没有 View 层,所以不依赖 View Render 机制的 AMS 是最好的选择。AMS 在性能上比 JBuilder 要好一点,但是差距不大,不到一个数量级。

$ mkdir movies && mv jbuilder.json.jbuilder movies/
$ bundle exec ruby benchmark.rb 
Calculating -------------------------------------
            jbuilder    24.000  i/100ms
     AM::Serializers    23.000  i/100ms
-------------------------------------------------
            jbuilder    240.349  (±10.4%) i/s -      1.200k
     AM::Serializers    285.601  (±14.0%) i/s -      1.403k

Comparison:
     AM::Serializers:      285.6 i/s
            jbuilder:      240.3 i/s - 1.19x slower

AMS 还有个特点是倾向在生成 JSON 的时候遵循标准,当遵循标准的时候,你只需要声明需要暴露的 attributes 即可,而不是采取 Template Base 的生成方式,需要自己定义 JSON 文件的结构,以及格式。

结论:在 API 模式下,AMS 胜出。

关于 CSRF Protection 和 Session

如果 API-Only App 作为后端,如果仅仅服务于 API Client 的话,是不需要 CSRF Protection 的,但是如果是服务于 Single Page javascript Application 的话,那么 CSRF Protection 还是有意义的。不过 Rails API 的集成做的比较彻底,没有 CSRF Protection 且不支持 Session。

如何使用 Rails 5.0 的 API 模式

如果不出意外的话,Rails 5.0 发布后,创建新项目就可以用下面的命令

rails new myapp --api

效果就像你在使用 Rails API 项目一样,生成一个裁剪过的 API-Only 的 Rails 项目,然后就可以愉快的开发了。如果不添加 --api 参数,那么生成的应用则仍然是标准的 Rails 项目。至于混合项目,即 Rails 应用即包含前端,又提供 API 功能的时候应该怎么搞?我现在也不太确定,估计 Rails 5.0 正式版发布时就应该有结论了吧。

ActiveCable, Turbolink 3, Rails API,明显 Web Application SPA 化是趋势,Rails 5 就是为 SPA 准备的。

You're gonna love it in day 5 and you're gonna hate it in year, but that's all good because the meantime you'll be blaming me and we can still work together. —— DHH,RailsConf 2015 - Opening Keynote

#2 楼 @rei

Holy shit did DHH just give us the meaning of life?

本文出现了很多让我膜拜的大神。API 快点来吧,我越来越需要你了

好厉害的感觉!期待 Rails 5 快点出来!我正好要学习用 AngularJS 把前后端彻底分离……

好东西啊

好想尝试

前后端分离是趋势,期待。。。

我就忽然好想笑,😄

顺手贴一个一年前做过的尝试:https://github.com/very-geek/dearann

说下背景:我公司里后端都是写 Java 的,我总是插不上手心里着急……自从看到 RAILS API 这个项目后,想起最初尝试 RAILS 的快乐就动了研究一下的念头。然而那时候 RAILS API 项目只是一个 gem 而已(也就是本文所说的裁剪都还没有完成),限于个人水平我没能把它单独给整起来。于是就有了这个半调子的 DEMO,它还是一个 RAILS 项目,不过用了 ActiveModel Serializer,代码的写法和组织都遵照了(当时)RAILS API 项目的约定,API 的格式也是遵照了(当时)JSON API 的约定,唯一没做的就是裁剪。可惜当时 JSON API 还未完善,有些情形到底怎么写没有定论;再加上当时的 Ember,Query Params 功能也没有完全做好,这个 Demo 就中断了。又过了大约三、四个月,我把最初手工搭建的 Ember 项目迁移到了 Ember CLI 上,现在这个 Repo 就是一个 Ember CLI + RAILS API 的前后端分离的例子。觉得这是趋势的就看一眼吧,我为了这个已经研究了两年多了(不过多数时候是 Angular + JAVA Spring 而已,Ember + Rails API 只有业余抽空玩玩),这个 Demo 的完成度相当低,主要是我比较菜 😄 各位看官看个大概就是了。

关于两端分离的事情,诸位有问题的我可以试着解答,毕竟在工作中我们已经完全践行了,四个大规模的 Angular 项目都在开发+维护中,还是有些经验心得的。

刚尝试了一下 rails 4.2.2 已经支持rails new myapp --api 而且 rails-api 的官方文档也说 IMPORTANT: Rails::API has been merged into Rails

#11 楼 @ken 我很怀疑你现在用 Rails 4.2.2 就能创建 API-Only 项目,八成是 Rails 4.2.2 压根不认识 --api 参数,所以直接忽略掉了?

#10 楼 @nightire 我也经历过好几个前后端分离的项目,以后这样的讨论一定会越来越多,可以开专题版块讨论了。

@lgn21st 你是对的。

#13 楼 @lgn21st 是啊,有感同意,既然好多人认为是趋势,社区可以带动下这个趋势了。

#15 楼 @nightire 前端的几个版块已经都整理出来了。

个人感觉纯写 api 的话还是 grape 的方式比较顺手,get 还是 post?需要对参数做怎样的要求或者转换?缺省参数是什么?这些在 grape 里面定义的时候一起就都写完了,同一个接口的东西都在一起。按照 rails controller 的写法好多东西要分开,过重了,维护不方便。

另外在支持多个 api 版本的时候,grape 组织上更灵活。

另外对于 api 来说,非阻塞的模式要好于阻塞模式,可以避免一个慢请求导致其他正常请求排队,最后一堆请求全部超时。非阻塞既可以加强服务的健壮性,出问题的时候也比较容易找到问题点。这方面 rails 明显不给力啊~

看到 OSChina 有翻译的,就弄了个传送门: Rails 5 有什么新特性?

就等 rails 5 发布,后端改到 rails --api 前端 Ember.js 已经磨刀霍霍了~

@dy1901 所以前端的 load balance 是个好东西,newrelic 也是个好东西。有数据,你才知道何时要怎么处理什么问题。

#21 楼 @wppurking load balance 是不能从跟上解决阻塞问题滴~ 遇到这种情况框架和日志都有要有,两手都要硬~

#10 楼 @nightire 决定开始 努力学习 angular

需要对参数做怎样的要求或者转换?缺省参数是什么?

确实没看到 Rails API 这方面相关的文档。

怎么感觉好久不见

@reyesyang github 上,master 分支已经拥有 rails api 了,但 4.2-stable 是木有的。

#23 楼 @pathbox angular 性能有大问题,还是学习 react 吧,我学了 angular,还没学 react

在玩 Rails API(with AMS) + ReactJS 爽到尿崩, 真爱 Rails 社区,聚合离散都是 work together, move forward.

亲,你觉得,现在能用rails 5 edge 版吗?会有很多坑吗?

很赞!期待!

很好,以后会不会有人用它来做游戏服务端

#31 楼 @jmukirin 实际上在页游时代有不少用 Rails 做游戏后端服务器的。

#32 楼 @lgn21st 我做过,那时做过一个游戏的一个模块,前端是用 flash,接口用的 xml

不喜欢 ActiveModel Serializer, 在里面注入 session 相关的内容例如 current_user 很困难 (得用莫名奇妙的 scope), 分页造假很困难 (谁知道是 expose ... metadata...), 不能缓存整个生成的 json 字符串,得 parse 一遍再给它序列化真实白白浪费 CPU ...

#34 楼 @luikore 还有什么办法吗?手动构造 json hash 太麻烦了,目前用 jbuilder, 但总感觉 json 不是试图层的东西

#35 楼 @flowerwrong 没什么办法但 jbuilder 至少还能直接访问 session...

#34 楼 @luikore 还有什么可替代的呢?首先我不会选择 as_json,其次 jBuilder 和 Rabl 都依赖 ActionView 的 View Render 机制,而纯 API 后端不一定需要 ActionView 层。

#37 楼 @lgn21st 好像没有...

rails-api + jbuilder 已经在使用了

#38 楼 @luikore #37 楼 @lgn21st 这样设计 JSON hash 是不是反模式?有很多重叠的地方,而且这样就只能as_json,不能玩ActiveModel Serializer

{
  code: logical_code,
  msg: 'success or error msg',
  status: success/fail/error,
  data: array or obj data
}

#34 楼 @luikore ActiveModel Serializer 的 master 版已经支持 cache json

#40 楼 @flowerwrong active model serializer 和写个 view model 然后 as_json 一样,都是围绕 model 去做序列化,就是依赖 active model, 轻量级的服务可以完全抛弃 active model 的... 重复的地方可以加 helper, 也是只用写一次的

#37 楼 @lgn21st

为了封装这个方法凭空多了多少代码。

def as_json(options = nil)
  hash = serializable_hash(options)
  include_meta(hash) unless self.class == FlattenJson
  hash
end

https://github.com/rails-api/active_model_serializers/blob/6266b6a0021cb96d6f3cc92c1a2b03ad08506c1e/lib/active_model/serializer/adapter.rb#L23-L27

我认为 json 生成属于 view 层,简单的对象序列化可以用 as_json 完成,复杂的 json 生成应该写到 view。能利用 Rails 现有的 helper 是好事而不是坏事,不然就要一个个搬运,单 cache 就有 cache_digests,russian-doll caching……

#43 楼 @rei 这三行代码我左看右看,都觉得是有需求且合理的呀。

#44 楼 @rei 关于 json 生成是否属于 view 层,应该是一个代码组织风格的问题。我的看法是目前 Rails 的 Action View 设计本身并非针对 API Only 的场景充分考虑,所以使用 view 层可能带来一部分额外开销。

我同意 @luikore 的看法,围绕 model 去序列化,而不依赖 Active View 层以及相关的 view helper,最近接触到的案例是用比 ActiveRecord 更轻量的 ORM 方案,比如直接用 ohm + Redis,让 Redis 直接担当 Database 的职责,这样单机处理能力上升一个台阶,在一个相对复杂压力比较大的场景下,单台服务器就撑得住。

#45 楼 @lgn21st 我意思是整个 gem 就是为了隐藏这个 API,创建另一个 DSL。

#46 楼 @rei 哦,明白了。

#45 楼 @lgn21st 很多人都把 redis 当数据库用,不是一个好的方案

#48 楼 @sharpx 同意,特别是对那些抱残守缺,观念僵化的人来说,把 Redis 直接当数据库用,是很难想象的,更不要说接受了。

#4 楼 @gazeldx 看来是需要我这样对 Api 情有独钟的人

#49 楼 @lgn21st 借题请教一下。之前遇到一个问题,业务数据量突然暴增,Redis 一下子用掉大量内存,同时持久化变慢,导致磁盘 IO 飙升,影响了前面的 web 应用。现在用 redis 之前都小心评估一下数据量。

你们是怎么用 redis 的呢?

#51 楼 @qhwa 我们其实还好,业务量没有那么大,还未碰到过 Redis 因为内存不足导致问题。┐(─__─)┌

#51 楼 @qhwa

Reids 有备份到磁盘的机制,当频繁触发这个这个的时候,会连续写到硬盘,这个时候磁盘的 IO 会暴增,而且内存也会用到很多。把这个触发机制修改下就好了。比如原来的 1 秒写入 60000 次会备份到硬盘改为 3 秒 600000 次。这个问题我们以前遇到过,当时就是这样解决的。

还有就是不知道appendonly yes不知道有没有影响。

#52 楼 @lgn21st Redis 目前能做到掉电或宕机时完全不丢数据了吗,还是仍然会丢失最后 1 秒的数据?

#54 楼 @kgen 理论上仍然有可能会丢最后一秒的数据。

理论上会丢。

@qhwa 目前阿里云已经出了 键值存储 相关的服务,兼容 redis,如果对稳定性和可靠性要求高,可以考虑使用。 http://docs.aliyun.com/#/pub/kvstore/key-value-store/kvstore-introduction

#55 楼 @lgn21st Redis 的性能真的非常好,我们曾经想用它来做一个重要数据的存储的,但是因为它不保证最后一秒的数据不丢或可恢复,所以最后还是当作临时中间数据存储的数据库了。

rails 发布周期 一般是 怎么样的 5.0 预计何时 发布

大爱 API 模式,Rails 的 erb、sprockets 对于复杂前端项目越来越鸡肋,有了 API 模块再也不用一个个把它们禁掉了。

#60 楼 @camel 那你们现在对于打包前端文件是通过什么来完成的?webpack ?

#62 楼 @_kaichen 打包完全依靠 webpack,然后部署到 CDN。使用 babel-loader 来支持 ES6 语法。ESLint 来做语法检查。

楼主猜得好对,--api 果然来了

@lgn21st 👍 上周五用 rails5 建新项目,然后就看到了这里。恰巧要建的就是个混合项目😂 😂

至于混合项目,即 Rails 应用即包含前端,又提供 API 功能的时候应该怎么搞?我现在也不太确定

过去两年了,不知道大大有什么思路吗

You need to Sign in before reply, if you don't have an account, please Sign up first.