接口性能优化实践"/>
Ruby on Rails接口性能优化实践
存在的问题
最近一段时间在做性能优化的工作,项目是Rails单体,使用的RESTful API和MongoDB数据库,按照二八定律,先用20%的精力去达到80%的效果,主要是优化了首页相关接口,之前N+1查询相关问题已经解决,通过对ELK APM性能监控分析发现,影响列表接口性能的地方,主要集中在3个方面:
- 查询当前用户加入资源的数量
- 查询当前用户是否加入列表的资源
- 无用数据返回过多
针对存在的问题,采取了下面的解决方案。
解决方案
查询当前用户加入资源的数量
- 之前项目数据量比较小,对于用户相关的数量进行实时查询,随着数据和用户量的增加,访问速度变慢需要进行重构,Rails提供的counter_cache的方法已经不太够用。这部分的优化参考了Ruby China homeland的做法,主要的思路是将数量相关的数据进行持久化,不再单个进行查询,创建一个Counter类,存储数量相关的数据,主要代码实现如下:
class Counter < ApplicationRecordbelongs_to :countable, polymorphic: true...
end
具体实现可参考Ruby China homeland项目
查询当前用户是否加入列表的资源
- 目前采取的方式是借助Ruby on Rails ActiveSupport::CurrentAttributes,它提供了线程隔离属性单例,可以在每个请求,将当前用户传到系统中直接进行查询。
- 之前同事专门写了一个gem mongoid-association_scope,将Active Support::CurrentAttributes应用到mongoid当中,目前仅支持到mongoid 6,后面有时间和精力支持到mongoid最新版本。
- 以课程为例,判断当前登录用户加入哪些列表的资源,参考代码如下:
class Coursehas_one :current_member, -> { Current.user ? where(user: Current.user) : none }, class_name: "CourseUser", inverse_of: :courseend
ActiveSupport::CurrentAttributes使用注意事项:
A word of caution: It’s easy to overdo a global singleton like Current and tangle your model as a result. Current should only be used for a few, top-level globals, like account, user, and request details. The attributes stuck in Current should be used by more or less all actions on all requests. If you start sticking controller-specific attributes in there, you’re going to create a mess.
关键点是不过过度使用,主要使用在顶级全局变量,如账户、用户和请求详细信息等。
无用数据返回过多
- 使用grape entity定义接口返回的数据,项目体量小时为快速开发,列表和详情的entities会使用同一个,随着功能增多,详情接口需要的数据会变多,又未将列表与详情中数据分开,导致列表接口返回的数据也变多了,时间久了会出现性能问题。
- 当列表接口变慢时,需要进行重构和优化,通过查看ELK APM性能检测,哪些字段影响了性能,再与前端进行沟通确认,将不需要的字段进行删除,这时需要考虑版本兼容的问题。
- 这部分的难点主要在于,如何将这部分工作融入日常的开发迭代中,在每个迭代周期需要加入相关的任务,这部分的工作在开发阶段体现不出价值,但从软件整个生命周期来看,能够大大降低项目的维护成本。
更多推荐
Ruby on Rails接口性能优化实践
发布评论