Sinatra 令人苦恼的性能问题

zangcw · 2014年01月15日 · 最后由 mimosa 回复于 2014年02月02日 · 10805 次阅读

最近很长一段时间,一直在查性能问题,几个月过去了,ab 并发量从原来的 4 台机器 20 多并发,到现在 100 多,还是非常低。 服务器的环境从原来的 Nginx + Unicorn + Padrino + Mongoid改成了Nginx + Rainbows(EventMachine) + Padrino + Moped + Sinatra::Synchrony性能提升的也一般 业务逻辑上,主要是论坛的那种逻辑,分为 客户端 - 群组 - 讨论 - 回复等几个层次,主要使用 reference 进行查询,用于测试并发的接口的逻辑主要就是查询一个客户端下所有的群组信息,以及某一个用户在这些群组的权限,查询的次数已经尽量减少了。 执行 perf 输出,发现 garbage_collector 占比很高,不知道什么原因 大家都帮忙想想办法吧

mongo 的数据设计上还是尽量查什么就存什么吧。

页面还是尽量做成容易缓存的 (例如一个帖子页面带了用户信息的话,就很有点难缓存,但如果用户信息用 ajax 加载的话,帖子和回复就可以缓存了).

升 2.1 估计可以减少 40% GC 时间。

#1 楼 @luikore 2.1 我试试去 那个是个 API 服务,页面缓存不大容易了,关联的数据比较多,缓存比较困难

这图不错啊,怎么生成的呢?

#1 楼 @luikore 2.1 上次我那个大姨妈的毛病还在……不知道 sinatra 会不会…………

#2 楼 @zangcw API 服务就不容易缓存?关联数据多就不容易缓存?

要做详细分析啊,看看瓶颈是在 rails 还是在数据库 i/o 上,rails 本身可以加缓存,数据库操作也是可以缓存的,不要直接读写数据库,先在内存操作,然后定时同步到数据库中。

其实问题不在 Padrino,不在 Mongodb,也不在 Unicorn,,,在于如何使用。

好像 Rails 网站的单机用 ab 测试,100 并发都很容易呀,API 应该更快才是.........

#4 楼 @cassiuschen 大姨妈的毛病具体是什么?知道症状可能可以找出线索... 如果用 puma 开多线程部署呢?sinatra::synchrony 貌似问题也不少,而且依赖的 em::synchrony 已经不维护了...

另外 ab 是本地测试么?最好用网络测试。本地测如果要减少 ab 本身的各种问题的影响,用 wrk 测会更准一些。

#8 楼 @luikore 每隔一段时间宕机几分钟……怀疑 RUBY_GC 的问题……另外支持 Puma……我一直都是 Puma 多线程跑应用的……

#5 楼 @bugmenot 有个需求,对每一条返回的数据都要针对 api 的 current_user 进行权限的判断,返回不同的数据。for example 取 topic,当前用户对每一个 topic 的权限(是否可以称赞,取消称赞,是否可以回复,是否可以设置置顶)都要列在 topic 中。 所以,如果缓存,就相当于有多少用户,创建多少缓存

#3 楼 @sevk rack-perf-tools

#11 楼 @zangcw 这部分逻辑可以放前端去了吧,另外有多少用户访问创建多少缓存会有啥问题,反正只会留下活跃用户的……

#11 楼 @zangcw 这是简单的缓存设计粒度问题啊

#11 楼 @zangcw Basecamp 的方案,存一份有所有权限按钮的缓存,默认不显示,用 js 处理有权限的用户显示。

#10 楼 @cassiuschen 是用所有的部署方式都会 down 几分钟?前面加不加 nginx 都会?GC 早就变成增量式的,不会停掉全世界的了... 如果万一真是什么地方产生了海量对象,就只好 dtrace 找了...

#13 楼 @aptx4869 缓存是肯定可以解决问题的,但是我比较奇怪的就是,现在的 CPU 占用总是 50% 左右,mongodb 也没有大于 10ms 的查询,但是性能就是上不去。我还是怀疑是什么地方配置不正确,想问问大家有没有类似的经历。 我做过去除 reference 和权限的 API 并发测试,单台 rainbows 也只能跑到 110 并发左右,CPU 占用 33%,明显不对嘛

GC 有压力,可以试下减少对象的分配 参考:https://github.com/blog/1489-hey-judy-don-t-make-it-bad

不止一次看到些“优雅”的代码,不仅执行效率低,对对象的分配也毫无节制。仅仅因为看起来比较魔法或者短小,就被看成优雅,实在不可取。

改用 Erlang,妥妥的

这可以通过设计缓存的粒度来解决,我们的应用在 api 上也有类似你说的权限判断问题,比如用户访问一个地点,需要返回该用户是否收藏过这个地点以及该地点的其他信息,可以将这部分拆封开来,我们用的是 rabl 做模板输出,代码类似如下:

object @attraction
node :current_user_favorited do |a|
  user_signed_in? && PoiFavorite.where(:poi_type => 'Attraction', :poi_id => a.id, :user_id => current_user.id).present?
end
extends "api/attractions/show_cache"

然后在 show_cache 这个文件里面,再可以用 Preload 减少 N+1 的查询:

object @attraction
ActiveRecord::Associations::Preloader.new(@attraction, :xxx => xxx).run
attributes :id, :name, :description, :lat, :lng
...

供参考

#20 楼 @quakewang 感谢,我来看看 rabl

#16 楼 @luikore 不加 nginx 不知道……但是 nginx 上挂的静态站点没事。所有 rails 的和 sinatra 的同时挂…

EM 是采用 select 的 IO 模式,如果不阻塞,理论上单机 C10k,检测下你的一个页面不配置页面缓存时,需要阻塞多少 ms,瓶颈在那里,就优化那里,@zangcw,总觉得数据是这样出来的: 100 并发=20 个默认的 rack(如 thin) 服务器的线程数(每个都阻塞执行)*4 台单机

我觉得应该是你们代码的问题...

我们也使用 Padrino ,同样是 garbage_collector

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