导读:本期聚焦于梦乃创作的《如何在Ruby中使用http-2 gem实现HTTP/2多路复用请求?》,敬请观看详情。一个TCP连接上并行发送多个请求而不必排队等待响应,是HTTP/2最实用的能力之一。Ruby项目若要直接利用这一特性,可以使用http-2 gem在帧级别控制流与数据收发。本文从客户端角度出发,先梳理HTTP/2多路复用与流控的基础概念,再结合代码演示如何建立带ALPN的加密连接、创建多个并发流并统一处理事件。同时还会讨论窗口更新、错误处理与gem本身的定位,帮助开发者理解在何种场景下适合绕开HTTP客户端库直接操作HTTP/2帧。

HTTP/2协议引入的二进制分帧层让同一个TCP连接可以同时承载多条独立的数据流,客户端在一个连接上交错发送请求,服务端也能乱序返回响应。这种多路复用能力显著缓解了HTTP/1.1中前端请求排队的问题,但Ruby标准库的Net::HTTP对HTTP/2支持有限,无法完全发挥该优势。http-2 gem是一个纯Ruby实现的HTTP/2协议库,提供帧编解码、流状态管理和事件回调,适合构建需要主动发起HTTP/2请求的客户端工具。本文将围绕这个gem,展示如何在Ruby中完成连接建立、ALPN协商、并发请求发送以及响应处理。

如何在Ruby中使用http-2 gem实现HTTP/2多路复用请求?

HTTP/2多路复用与http-2 gem的定位

HTTP/2把通信单元拆成帧,每个帧属于某个流,流由唯一的整数标识。客户端发起的流使用奇数ID,服务端推送使用偶数ID。多路复用意味着多个流的帧可以在连接上交错传输,不需要像HTTP/1.1那样为每个请求单独建立连接或串行等待。对于Ruby这类动态语言,直接解析二进制帧需要小心处理字节序和变长整数,http-2 gem正好封装了这些复杂逻辑。

需要注意的是,http-2 gem并不负责套接字管理、TLS握手或事件循环。它只维护协议状态机:输入字节流后产生事件,调用者需要把要发送的帧写回套接字。这样的设计一方面带来灵活性,可以集成进任意IO框架;另一方面也提高了使用门槛。相比之下,像Net::HTTP这样的高层库更适合普通HTTP请求,而http-2 gem适合需要多路复用、服务端推送或协议级测试的场景。

引入该库非常简单,在Gemfile中添加依赖即可。安装后通过require加载HTTP/2模块,后续代码统一使用HTTP2::Client类。

# Gemfile
source 'https://rubygems.org'
gem 'http-2'

建立连接与TLS ALPN协商

HTTP/2在互联网上几乎总与TLS一起使用,因为主流浏览器只支持基于TLS的h2。客户端需要先在TLS握手中通过ALPN扩展声明自己支持h2协议,如果服务端也支持h2,协商结果才会是h2,否则可能回退到http/1.1。Ruby的OpenSSL绑定提供了alpn_protocols属性,可以在创建SSLContext时设置。

下面代码创建一个TCP连接,将其包装为SSL连接,并检查协商后的协议。实际地址使用测试域名ipipp.com,读者可替换为自己的服务端。连接建立后,需要创建HTTP2::Client实例,并开始处理套接字读写。

require 'socket'
require 'openssl'
require 'http/2'

tcp = TCPSocket.new('ipipp.com', 443)
ctx = OpenSSL::SSL::SSLContext.new
ctx.alpn_protocols = ['h2']
ssl = OpenSSL::SSL::SSLSocket.new(tcp, ctx)
ssl.hostname = 'ipipp.com'
ssl.connect
raise 'ALPN negotiation failed' unless ssl.alpn_protocol == 'h2'

conn = HTTP2::Client.new
buffer = ''.dup
loop do
  data = ssl.read_nonblock(65536, exception: false)
  break if data == :wait_readable && buffer.empty?
  # processing happens in next step
end

这段代码只完成了连接前置工作,还不能收发数据。HTTP/2协议要求连接建立后双方交换SETTINGS帧,因此客户端必须不断读取SSLSocket中的字节并喂给conn对象,同时把conn产生的输出帧写回socket。如果忽略这一步,后续创建流时数据不会真正发送。

并发发起请求与事件处理

创建请求的核心是conn.new_stream。每个流对象可以注册多个事件,包括headersdatacloseheaders在收到响应头时触发,data在收到响应体分片时触发,close在流结束时触发。发送请求时调用stream.headers并设置end_stream为true,表示请求体已经结束。

多个流可以连续创建,不需要等待前一个流完成。这正是多路复用的体现:所有请求共享同一个连接,只是在协议层用不同的流ID区分。下面定义一个send_request方法,用不同路径发起三个并发请求。

def send_request(conn, path)
  stream = conn.new_stream
  stream.on(:headers) do |headers|
    puts "响应头: #{headers[':status']}"
  end
  stream.on(:data) do |chunk|
    print chunk
  end
  stream.on(:close) do
    puts "\n流 #{stream.id} 完成"
  end
  stream.headers({
    ':method' => 'GET',
    ':path'   => path,
    ':scheme' => 'https',
    ':authority' => 'ipipp.com'
  }, end_stream: true)
end

def pump(conn, ssl)
  loop do
    data = ssl.read_nonblock(65536, exception: false)
    case data
    when :wait_readable
      outgoing = conn.data_to_send
      ssl.write(outgoing) unless outgoing.empty?
      sleep 0.01
    when :wait_writable
      sleep 0.01
    when nil
      break
    else
      conn << data
      outgoing = conn.data_to_send
      ssl.write(outgoing) unless outgoing.empty?
    end
  end
end

send_request(conn, '/first')
send_request(conn, '/second')
send_request(conn, '/third')
pump(conn, ssl)

pump方法中,读取使用了nonblock模式并处理:wait_readable:wait_writablenil三种情况。当读到数据时,调用conn << data将字节送入协议解析器;解析器可能会产生新的待发送数据,通过conn.data_to_send获取并写回SSL套接字。如果暂时没有可读数据,就写入待发送数据并短暂休眠,避免忙等。

这种手动事件循环虽然比不上异步框架优雅,但足够透明。每个响应帧可以携带不同流的ID,客户端根据事件回调区分数据来源。读者会看到三个响应的输出交错打印,直观展示多路复用效果。

流控窗口与常见错误处理

HTTP/2的流控机制作用于每条流和整个连接,接收方通过窗口大小告知发送方还能发送多少数据。客户端在接收响应时,如果应用层处理速度跟不上,就需要及时发送WINDOW_UPDATE帧扩大窗口。http-2 gem会在窗口接近耗尽时触发window_update事件,调用者可以打印日志或执行增量调整。

conn.on(:window_update) do |stream_id|
  puts "窗口更新,流ID: #{stream_id}"
end

另一方面,服务端可能发送GOAWAY帧通知连接即将关闭,此时需要停止新建流。http-2 gem同样提供goaway事件。实际使用中还要注意流ID冲突、SETTINGS超时、自身心跳等细节。如果连接被关闭,客户端应重新协商并重试尚未完成的请求,这需要业务层维护幂等性。

使用该gem的常见误区是把所有请求塞进同一连接而不处理背压,导致内存中积压过多未读数据。虽然HTTP/2解决了队头阻塞,但应用层仍需要根据业务吞吐量控制并发流数量,可以在创建流之前检查conn.active_stream_count,避免超过服务端SETTINGS_MAX_CONCURRENT_STREAMS限制。

总体而言,http-2 gem适合Ruby生态中需要精细协议控制的场景,例如内部RPC网关、压力测试工具或自定义推送客户端。对于普通业务调用,使用基于该gem的高层封装会更省心,但理解底层帧流通机制依然有助于排查协议相关问题。

HTTP/2http-2 gemRuby多路复用修改时间:2026-08-26 14:18:15

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。