Webhook多次触发,导致大量API调用

时间:2015-10-10 00:30:45

标签: ruby-on-rails

当我创建新客户时,我的应用程序有一些沉重的回调验证。基本上我在创建新客户记录之前检查多个API以查看是否存在匹配。我不希望在创建之后发生这种情况,因为如果没有任何匹配,我宁愿不首先保存记录。

我有一个创建新客户的webhook设置。问题是,因为我的客户验证花了这么长时间,所以webhook继续开火,因为它没有得到立即响应。

这是我的客户模型:

  validates :shopify_id, uniqueness: true, if: 'shopify_id.present?'
  before_validation :get_external_data, :on => :create 


def get_external_data
  ## heavy API calls that I don't want to perform multiple times
end

我的钩子:

        customer = shop.customers.new(:first_name => first_name, :last_name => last_name, :email => email, :shopify_url => shopify_url, :shopify_id => id)
        customer.save

        head :ok

customer.save大约需要20秒。

澄清一下,问题在于:

  1. Webhook被解雇

  2. 进行重型API调用

  3. 第二个Webhook被触发(API调用仍然来自第一个webhook)。运行重API调用

  4. 第三个Webhook被解雇

  5. 直到最后保存第一条记录才会发生这种情况,以便我现在可以检查以确保shopify_id是唯一的

    有解决方法吗?我如何进行防御性编程以确保没有重复记录开始处理?

2 个答案:

答案 0 :(得分:2)

多么有趣的问题,谢谢。

<强>异步性

这里的主要问题是依赖于外部网络钩子。

测试这些所需的延迟不仅会影响您的保存时间,还会阻止您的服务器处理其他请求(除非您使用某种多处理)。

让您的流程依赖于多个外部资源通常不是一个好主意。在这种情况下,它是合法的。

我唯一真正的建议就是让它成为异步流程......

-

Asynchronous vs synchronous execution, what does it really mean?

  

当您同步执行某些操作时,请等待它完成   然后继续进行另一项任务。当你执行某些事情   异步地,您可以在完成之前继续执行另一项任务。

在JS中,最着名的异步制作示例是使用Ajax回调... IE通过Ajax发送请求,使用某种“等待”过程来保持用户更新,然后返回响应。

我建议为前端实现此功能。后端必须确保在处理外部API调用时服务器的手不受限制。这可能需要使用系统的其他部分(不需要使用Web服务器进程),或将功能分成其他格式。

<强>的Ajax

我绝对会在前端使用Ajax,另一种异步技术(web sockets?)。

无论哪种方式,当用户创建帐户时,我都会创建一个“待处理”屏幕。使用ajax是最简单的例子;但是,它在范围上受到很大限制(IE如果用户刷新页面,他就失去了联系)。

也许有人可以提出一种在异步系统中恢复状态的方法吗?

您可以使用Ajax callbacks处理它:

#app/views/users/new.html.erb
<%= form_for @user, remote: true do |f| %>
   <%= f.text_field ... %>
   <%= f.submit %>
<% end %>

#app/assets/javascripts/application.js
$(document).on("ajax:beforeSend", "#new_user", function(xhr, settings){
   //start "pending" screen
}).on("ajax:send", "#new_user", function(xhr){
   // keep user updated somehow
}).on("ajax:success", "#new_user", function(event, data, status, xhr){
   // Remove "pending" screen, show response
});

这将为您提供一个不会堵塞服务器的前端流程。在请求处理过程中,您仍然可以在页面上执行“填充”操作。

-

<强>队列

第二部分将与您的服务器处理请求的方式有关。

具体来说,它是如何处理API请求的,因为它们会导致延迟。

目前我能想到的唯一方法是排队请求,并有一个单独的过程通过它们。这里的主要好处是它会使你的Rails应用程序的请求异步,而不必等待响应的到来。

您可以使用Resque之类的gem对请求进行排队(它使用Redis),允许您将请求发送到Resque队列&amp;捕捉它的反应。然后,此响应将形成您对ajax请求的响应。

在执行此操作之前,您可能需要设置一个临时用户:

#app/models/user.rb
class User < ActiveRecord::Base
   after_create :check_shopify_id
   private

   def check_shopify_id
      #send to resque/redis
   end
end

当然,这是一个非常高级别的建议。希望它能为您提供更好的视角。

答案 1 :(得分:1)

这是一个棘手的问题,因为您的客户创建取决于昂贵的验证。我看到了一些可以减轻这种情况的方法,但它将是一种“较小的邪恶”类型决定:

  1. 您可以预先调用/预加载客户列表吗?如果是这样,您可以缓存客户列表并对其进行验证,而不是查询每个create。这需要一个cron作业来保持客户列表的更新。
  2. 创建客户,然后执行客户检查作为“验证”步骤。如在,在客户上设置validated标志,然后在后台任务中运行一次检查。如果客户存在,则与现有客户合并;如果没有,请将客户标记为有效。
  3. 任何一种选择都需要解决,以避免昂贵的电话。