繁体   English   中英

如何在不等待Ruby响应的情况下发出HTTP请求

[英]How to make an HTTP request without waiting for the response in Ruby

我只想从我的Rails控制器内部命中服务器,但不等待响应。 这可能吗? (没有启动其他线程我出于性能原因无法做到这一点)

可以通过打开一个插座并关闭它来实现。 这将建立连接并关闭连接,而不会在连接的上下文中传输任何数据...

...你需要等待连接打开 - 尽管可能有办法避免这种情况。

require 'socket'
# opens a connection, will wait for connection but not for data.
s = TCPSocket.new 'host.url.com', 80
# closes the connection
s.close

它可能相当于ping并且不会打开一个新线程......虽然它不是完全异步的。

使用HTTP请求,代码可能如下所示:

require 'socket'
host = 'www.google.com'
# opens a connection, will wait for connection but not for data.
s = TCPSocket.new host, 80
# send a GET request for '/' .
s.puts "GET / HTTP/1.1\r\nHost: #{host}\r\n\r\n"
# closes the connection
s.close

您可以在堆栈交换上搜索有关HTTP请求的更多信息,并获取一些想法,例如此处

只是为了澄清(由于评论):

这将引入与建立连接(和发送请求)相关的延迟,但您不必等待处理和接收回复。

删除连接(关闭套接字的一半)可能会产生以下任何影响 - 所有这些都假设一个不错的Web服务器:

  • 如果s.close完成,则在Web服务器完全发送响应之前,Web服务器将首先处理该请求,然后在尝试发送数据时将在Web服务器的套接字上引发异常。 然后,Web服务器应关闭套接字并释放所有资源。

  • 如果在Web服务器完全发送响应后s.close完成,那么服务器可能会:1。立即关闭套接字(正常HTTP 1行为)或2.保持连接活动直到发生超时(可选HTTP 1.1)行为) - 超时通常约为10秒。

以非常小的间隔重复访问Web服务器可能会导致引发DOS安全标志并阻止将来的连接(无论您如何访问Web服务器,都是如此)。

我可能会选择使用工作线程,如下所示:

我相信运行一个单独的线程可能不会像你想象的那么昂贵。 所有异步Web请求都可以有一个线程周期。

这是一个想法:

require 'socket'

REQUESTS_MUTEX = Mutex.new
REQUESTS_QUE = []
REQUESTS_THREAD = Thread.new do
   begin
      loop do
         sleep 0.5 while REQUESTS_QUE.empty?
         host, path = REQUESTS_MUTEX.synchronize {REQUESTS_QUE.shift}
         # the following will open a connection and start a request,
         # but it's easier to use the built in HTTP API...
         # although it will wait for a response. 
         s = TCPSocket.new host, 80
         s.puts "GET #{path} HTTP/1.1\r\nHost: #{host}\r\n\r\n"
         s.close
         # log here: 
         puts "requested #{path} from #{host}."
      end
   rescue Exception => e
      retry
   end
end
def asynch_request host, path = '/'
   REQUESTS_MUTEX.synchronize {REQUESTS_QUE << [host, path]}
   REQUESTS_THREAD.alive?
end

现在,对于每个请求,您可以简单地调用asynch_request ,并且循环线程应该在它唤醒后立即点击Web服务器并注意到它。

您可以通过粘贴一些请求从终端测试它:

asynch_request 'www.google.com'
asynch_request 'www.yahoo.com'
asynch_request 'I.Dont.exist.com'
asynch_request 'very bad host address...'
asynch_request 'www.github.com'

请注意,无声失败(您可以调整代码)。

从您的控制器,将request-url作业添加到队列。

然后运行从队列中读取并执行请求的后台进程。

这将从控制器操作中删除请求性能延迟。

Rails 4.2包含一种从特定后端实现中抽象出来的方法。 它叫做ActiveJob:

https://github.com/rails/rails/tree/master/activejob

以下是将其与Sidekiq服务一起使用的示例:

https://github.com/mperham/sidekiq/wiki/Active-Job

如果您使用的是旧版本的Rails,则也可以直接使用其中一个队列服务。

这是可能的,但你需要使用ruby eventmachine

然后你可以使用em-http-request来执行异步http请求,即:

首先安装宝石

gem install 'eventmachine'
gem install 'em-http-request'

然后尝试代码

require 'rubygems'
require 'eventmachine'
require 'em-http'

urls = %w(http://www.google.com http://www.rorra.com.ar)

pending = urls.size

EM.run do
  urls.each do |url|
    http = EM::HttpRequest.new(url).get
    http.callback {
      puts "#{url}\n#{http.response_header.status} - #{http.response.length} bytes\n"
      puts http.response

      pending -= 1
      EM.stop if pending < 1
    }
    http.errback {
      puts "#{url}\n" + http.error

      pending -= 1
      EM.stop if pending < 1
    }
  end
end

ruby有几个不同的HTTP库。 其中一些允许ruby的“异步”请求。 虽然通常它会在另一个线程中。 我认为你可能因为性能原因而不能这样做。

HTTPClient是我首选的HTTP客户端库,尽管它不是最受欢迎的。 使用HTTPClient,您可以:

conn = HTTPClient.new.get_async(“ http://example.com ”)

通常,您将使用返回的连接对象检查请求何时完成,但您也可以忽略返回的连接对象。 在任何一种情况下,实际的HTTP请求都是在一个单独的线程中进行的,因此您的主线程不会等待它并且可以继续执行其他代码。

其他ruby http客户端库也提供异步模式。 您也可以通过启动发出http请求的线程来自己完成,如果您不关心它,甚至不等待线程完成。 您可以使用concurrent-ruby中的一些工具来使用线程池和其他人已编写的代码,以最大限度地减少对性能的担忧。 也许是来自并发红宝石的未来。

如果你真的不想使用线程,那么你基本上必须像其他人建议的那样使用EventMachine。 我不认为这会在一定程度上导致更好的表现。

有一些编译过的C gems可以让你创建异步请求,而你创建线程并不明显 - 但是C代码仍然可能在C盘中创建线程。 线程基本上是你可以像你想做的那样做异步的事情。 或者像EventMachine这样复杂的基于事件/光纤的解决方案。

在Rails 4.2中,您可以使用ActiveJob将HTTP请求排队为后台进程。 这是启动HTTP请求但不让控制器等待它的另一种方法。 但是你必须为ActiveJob设置一个后端(有几种选择),后端将在一个完全不同的进程中运行(可能不止一个)....或者再次,后端将是为你创建线程。

我建议考虑放弃你对线程的抵抗,线程确实是处理这个问题的好方法,不应该是性能问题 - 我可能会使用concurrent-ruby,以及它给你带来的一些更高级别的抽象期货(仍然在引擎盖下的线程实现),以避免必须自己直接编写线程代码,并使用由其他知道他们正在做什么的人编写的库,并为任何性能问题合理地设置所有内容。 或者,如果你真的想要避免线程,我会使用ActiveJob,后端适配器不使用线程。 就个人而言,我不会采用EventMachine路线,它只是为了处理异步http请求而添加了很多东西来处理。

或者,是的,只是提出一个HEAD请求,并认为它足够快,不用担心它。

或者,当然,Myst的答案是关于直接打开套接字以便你可以立即关闭它而无需等待响应似乎很有趣。

如果创建一个新进程是正常的(并不是整个rails进程都在考虑你),你可以使用以下几点: 在ruby中使用正确的参数转义在后台执行shell命令

# Spawn a new process and run the curl command
pid = Process.spawn("curl", "http://example.com", :out =>    '/dev/null', :err => '/dev/null')

# Detach the spawned process
Process.detach pid

当我对此进行基准测试时,我得到1.999ms 与使用Process.wait pid相比,耗时248ms

在我脑海中浮现的第一件事是:或许以某种方式分离请求。 不确定这是否可以在ruby中请求...我知道你可以分离进程。

其他而非100%的解决方案是只询问标题,因此您只需传输少量数据。 这个帖子似乎有一些很好的提示: https//stackoverflow.com/a/9699874/1933185

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM