【发布时间】:2012-07-22 06:48:59
【问题描述】:
作为我的 rails 项目的一部分,我有一个功能允许用户向他们的 FB 朋友发出邀请。我使用 fb_graph 进行 API 调用,下面是用户点击邀请页面时来自控制器的代码示例。
这个操作变得非常昂贵。我已经看到拥有超过 1000 个朋友的用户需要超过 30 秒的时间。此外,每次用户点击邀请页面时,此代码都会重新执行。虽然用户的 FB 好友列表并不完全是静态的,但不必在每次请求时都重新计算。
所以我想做的是改进这段代码,让它更有效率。我可以想出几种不同的潜在方法来做到这一点,但在这种情况下最有意义的是什么?这比我通常想在 SO 上问的要开放一些,但是由于我对编程还比较陌生,所以我对你会/不会做什么和如何做一样好奇它。
以下是我可以做出的一些优化建议:
1) 仅在会话内进行优化。该代码在页面第一次被点击时执行,并将持续到会话的其余部分。我实际上不知道该怎么做。
2) 持久化到数据库。将一列添加到将保存朋友哈希的用户表。我可以使用后台作业定期刷新这些数据(也许每周一次?)
3) 坚持缓存。我什至不确定这涉及到什么,或者这是否是一个合适的用例。我的感觉是选项 2 需要大量手动维护,并且可能有一个很好的缓存解决方案来处理过期等问题,但不确定
其他想法?感谢您对选项的想法。
# fetch full array of facebook friends
@fb_friends = current_user.facebook.fetch.friends
# strip out only id, name, and photo for each friend
@fb_friends.map! { |f| { identifier: f.identifier, name: f.name, picture: f.picture }}
# sort alphabetically by first name
@fb_friends.sort! { |a,b| a[:name].downcase <=> b[:name].downcase }
# split into two lists. those already on vs not on network
@fb_friends_on_network = Array.new
@fb_friends.each do |friend|
friend_find = Authorization.find_by_uid_and_provider(friend[:identifier], 'facebook')
if friend_find
@fb_friends_on_network << friend_find.user_id
@fb_friends.delete(friend)
end
end
更新 #1
在我进行的初始实验中添加更多内容。我在包含 @fb_friends 数组的用户表中添加了一个列(对上面显示的转换进行后处理)。基本上上面的控制器代码被简单地替换为@fb_friends = current_user.fbfriends。我认为这将大大减少负载,因为不再需要调用 Facebook,更不用说上面完成的所有处理了。这确实节省了一些时间,但没有预期的那么多。我自己的朋友列表在我的本地机器上加载大约需要 6 秒,在这些更改之后它下降到 4 秒。在负载问题上,我必须在这里遗漏一些更大的东西。
更新 #2
经过进一步调查,我了解到几乎一半的数据传输归因于我用于“邀请”按钮的表单。该表单将为每个朋友加载一次,如下所示:
<%= form_for([@group, @invitation], :remote => true, :html => { :'data-type' => 'html', :class => 'fbinvite_form', :id => friend[:identifier]}) do |f| %>
<%= f.hidden_field :recipient_email, :value => "facebook@meetcody.com" %>
<div class = "fbinvite btn_list_right" id = "<%= friend[:identifier] %>">
<%= f.submit "Invite", :class => "btn btn-medium btn-primary", :name => "fb" %>
</div>
<% end %>
我决定删除表单并在里面放置一个简单的按钮:
<div class = "fbinvite_form" id = "<%= friend[:identifier] %>" name = "fb">
<div class = "btn btn-small">
Invite
</div>
</div>
然后我使用 ajax 来检测点击并采取适当的行动。这种变化实际上将数据传输减少了一半。在加载大约 500 位朋友之前,大约需要 650kb,现在已降至约 330kb。
我想我会回去尝试我在更新 # 1 中尝试过的方法来进行预处理。结合起来,我希望我能把这个时间缩短到 2 秒左右。
更新 #3
我最终安装了Miniprofiler 以了解更多可能会减慢此操作的原因,并了解到我上面的 for 循环效率极低,因为它会让每个朋友都访问数据库。我在separate question 上发帖,并得到了帮助,将旅行减少到只有一次。然后我继续执行我在更新 #1 中提到的预处理。通过所有这些更改,我将其降至约 700 毫秒,考虑到它在走这条路之前需要 +20 秒以上的时间,这非常了不起!
【问题讨论】:
-
好问题!感谢您的更新和分享您的经验。
标签: ruby-on-rails performance caching delayed-job fb-graph