4

在此处输入图像描述

作为我的 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 秒以上的时间,这是非常了不起的!

4

1 回答 1

0

如果您可以并行运行一些查询,我建议您看一下:The futoroscope gem。正如您在宣布的博客文章中看到的那样,它试图解决同时进行 API 查询的相同问题。它似乎有很好的支持和良好的测试覆盖率。

于 2013-05-28T08:43:17.407 回答