如何在可配置的超时时间内响应 Rails 请求
Posted
技术标签:
【中文标题】如何在可配置的超时时间内响应 Rails 请求【英文标题】:How to respond a Rails request within a configurable timeout 【发布时间】:2016-01-16 13:15:56 【问题描述】:我想在不使用超时模块的情况下在控制器的操作中实现以下行为:
class AdminController < ApplicationController
def example
Timeout.timeout(params[:timeout].to_i) do
... # try to process the request within params[:timeout] seconds
end
rescue Timeout::Error
render nothing: true, status: :gateway_timeout
end
end
我想避免使用 Timeout,因为它会在我的应用程序中导致许多错误,包括数据库连接泄漏。其他一些问题报告在: http://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/
【问题讨论】:
你在这个请求中实际做了什么?对于更大的问题,超时是一个糟糕的解决方案 - 您可能能够预处理或将其中一些代码转换为作业,甚至可以使用状态机将请求拆分为多个阶段。 @court3nay 这是一个运行可能需要 100 毫秒到 30 秒才能处理的作业的 Web 服务。每个客户端都可以为请求的作业指定自己可接受的超时时间。 @barbolo 您当前的代码存在缺陷。最好将超时包装在begin
块中,然后是 rescue => e
。你想达到什么目的?如果查询时间过长,是否停止请求?
代码被包裹在一个“try/catch”结构中,我只是没有使用begin。
如果请求时间过长,我想停止请求。是的。但是“太长”应该是可配置的。
【参考方案1】:
不要为此使用红宝石。问题是超时模块会在执行代码中的某个地方、任何地方中止代码,这将导致打开和悬挂套接字、连接和文件或更糟。 (我猜,如果您要启动外部脚本或为每项工作进行脱壳,这并不是真正的问题,但在这些情况下,只需使用 unix/linux 本机 io 超时)
这几乎就是为什么 ruby CI 服务器从未起飞的原因(Jenkins 是 java)。 我会使用一个单独的服务来设计它,该服务可以处理您可以使用 API ping 的正在运行的作业;然后,在您看来,每隔几秒 ping 一次,这样他们就可以看到持续的更新。在这种情况下,rails 应用程序中不需要任何污染超时代码。
编辑:由于您在该块中执行 HTTP 请求,因此只需使用 http 库中内置的超时设置(例如 https://github.com/lostisland/faraday )
【讨论】:
谢谢@court3nay。我希望通过纯 Ruby 的解决方案来批准答案。让我们看看我得到了什么。 它调度一个网络爬虫。 哦,这更容易了。 (你为什么不这么说!呵呵)大多数http库实际上都有超时设置。例如,github.com/lostisland/faraday 具有每个请求的连接和请求超时设置。 另见 net/http docs.ruby-lang.org/en/2.0.0/Net/HTTP.html 有 open_timeout、read_timeout、ssl_timeout 等; rubydoc.info/github/jnunemaker/httparty/HTTParty/ClassMethods 只有一个 :timeout 但类可以被覆盖。 好吧,这不是我的问题的解决方案。任务比这要复杂一些。您可以想象我可能会为收到的每个 Web 服务请求发出 3 或 4 个 HTTP 请求和数据库操作。因此,为每个 I/O 操作设置 Timeout 似乎并不适用。【参考方案2】:您可以使用gem "rack-timeout"
https://github.com/heroku/rack-timeout 来满足您的要求。
# Gemfile
gem "rack-timeout"
# config/initializers/rack_timeout.rb
Rack::Timeout.timeout = 5 # seconds
希望这会对你有所帮助。
【讨论】:
这实际上是一个很好的宝石,但不能解决我的问题。它不允许我指定每个请求的超时时间。它需要在应用程序初始化期间指定的超时固定值。以上是关于如何在可配置的超时时间内响应 Rails 请求的主要内容,如果未能解决你的问题,请参考以下文章