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

Moh*_*fez 2 ruby net-http

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

Mys*_*yst 6

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

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

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
Run Code Online (Sandbox Code Playgroud)

它可能相当于一个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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

  • 如果s.close在Web服务器完全发送响应之后完成,则服务器可能: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
Run Code Online (Sandbox Code Playgroud)

现在,对于每个请求,您都可以简单地调用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'
Run Code Online (Sandbox Code Playgroud)

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