首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Ruby EventMachine 異步 HTTP 文件伺服器實作:支援斷點續傳與大檔案流式傳輸

     編輯:Java 相關知識

專案背景

使用 Ruby EventMachine 構建高併發非阻塞 HTTP 文件伺服器,利用 em-http-server 與 EventMachine::FileStreamer 實現大檔案異步流式傳輸,避免記憶體溢出,支援 HTTP 範圍請求(斷點續傳)。

核心依賴

# Gemfile
gem 'eventmachine', '~> 1.2'
gem 'em-http-server', '~> 0.2'

完整伺服器實作

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

class FileDownloadHandler < EM::HttpServer::Server
  attr_accessor :file_path, :file_name, :file_size

  def process_http_request
    # 僅處理 GET 請求且路徑匹配目標檔案
    if @http_request_method =~ /GET/i && @http_request_uri.end_with?("/" + file_name)
      serve_file
    else
      render_usage_page
    end
  end

  private

  def serve_file
    # 發送 HTTP 標頭
    send_data "HTTP/1.1 200 OK\r\n"
    send_data "Server: Ruby EventMachine FileServer\r\n"
    send_data "Connection: keep-alive\r\n"
    send_data "Keep-Alive: timeout=15\r\n"
    send_data "Content-Type: application/octet-stream\r\n"
    send_data "Content-Disposition: attachment; filename=\"#{file_name}\"\r\n"
    send_data "Content-Length: #{file_size}\r\n"
    send_data "Accept-Ranges: bytes\r\n" # 支援斷點續傳關鍵標頭
    send_data "\r\n"

    # 異步流式傳輸檔案內容(核心優勢:不載入整個檔案到記憶體)
    streamer = EventMachine::FileStreamer.new(self, file_path)
    streamer.callback {
      # 傳輸成功完成
      close_connection_after_writing
    }
    streamer.errback { |err|
      # 傳輸錯誤處理
      puts "檔案傳輸錯誤: #{err}"
      close_connection
    }
  end

  def render_usage_page
    response = EM::DelegatedHttpResponse.new(self)
    response.status = 200
    response.content_type 'text/html; charset=utf-8'
    response.content = "<h1>檔案下載伺服器</h1><p>用法: wget https://host:port/#{file_name}</p>"
    response.send_response
  end
end

# 啟動伺服器
EM.run do
  file_path = '/tmp/large-file.tar.gz' # 替換為實際大檔案路徑
  file_name = File.basename(file_path)
  file_size = File.size(file_path)

  puts "啟動檔案伺服器於 0.0.0.0:8080"
  puts "下載連結: https://localhost:8080/#{file_name} (#{file_size} bytes)"

  EM.start_server("0.0.0.0", 8080, FileDownloadHandler) do |conn|
    conn.file_path = file_path
    conn.file_name = file_name
    conn.file_size = file_size
  end
end

關鍵技術點解析

1. EventMachine::FileStreamer 異步流式傳輸

  • 採用 零拷貝/分塊讀取 機制,逐塊從磁碟讀取並發送至網路緩衝區。
  • 不將整個檔案載入記憶體,支援 GB 級大檔案傳輸而不 OOM。
  • 內建背壓處理:網路緩衝區滿時自動暫停讀取,騰空後繼續。

2. HTTP 斷點續傳支援(Range Requests)

上述基礎版本未完整實作 Range 處理,生產級需補充:

def serve_file
  # 解析 Range 標頭
  range_header = @http_request_headers['range']
  start_pos = 0
  end_pos = file_size - 1
  status_code = 200

  if range_header && range_header =~ /bytes=(\d+)-(\d*)/
    start_pos = $1.to_i
    end_pos = $2.empty? ? file_size - 1 : $2.to_i
    status_code = 206 # Partial Content
  end

  content_length = end_pos - start_pos + 1

  send_data "HTTP/1.1 #{status_code} #{status_code == 206 ? 'Partial Content' : 'OK'}\r\n"
  send_data "Content-Range: bytes #{start_pos}-#{end_pos}/#{file_size}\r\n" if status_code == 206
  send_data "Content-Length: #{content_length}\r\n"
  send_data "Accept-Ranges: bytes\r\n"
  # ... 其他標頭

  # 使用自定義 Streamer 支援 offset/limit(或改用 EM::FileStreamer 配合 seek)
  # 此處略過完整實作,建議參考 em-http-server 原始碼擴展
end

3. 併發與效能優化

  • 連線限制:EM.set_max_timers、EM.set_max_epoll_descriptors 調整系統限制。
  • 多進程:使用 em-synchrony 或 puma 叢集模式利用多核心。
  • 緩衝區調優:EM.set_send_buffer_size、EM.set_receive_buffer_size。

測試與驗證

# 1. 建立測試大檔案 (1GB)
dd if=/dev/zero of=/tmp/large-file.tar.gz bs=1M count=1024

# 2. 啟動伺服器
ruby file_server.rb

# 3. 測試下載
wget https://localhost:8080/large-file.tar.gz

# 4. 測試斷點續傳
wget -c https://localhost:8080/large-file.tar.gz

# 5. 壓力測試
ab -n 1000 -c 50 https://localhost:8080/large-file.tar.gz

遷移建議:從 Ruby 到 JVM 生態

若團隊主力為 Java/Scala/Kotlin,建議考慮以下替代方案:

總結

EventMachine + FileStreamer 提供了極簡單的大檔案異步傳輸方案,核心優勢在於「流式」與「非阻塞」。實務部署時需補全 Range 請求、安全性(Token/簽名)、日誌監控、多進程部署等生產級特性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
框架特點
Netty高效能 NIO,手動控制細節,學習曲線陡峭
Vert.x多語言、事件驅動、內建檔案非同步傳輸
Spring WebFluxReactive Streams 標準,整合 Spring 生態
Ratpack輕量、Groovy DSL 友善、非阻塞