作为我的 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 循环效率非常低,因为它会让每个朋友都访问数据库。我发布了一个单独的问题,并得到了帮助将旅行减少到只有一次。然后我继续执行我在更新 #1 中提到的预处理。通过所有这些更改,我将它降低到 ~700 毫秒,考虑到它在走这条路之前需要 +20 秒以上的时间,这是非常了不起的!