在Telegram Bot API中,机器人获取消息更新只有两种方式:一种是客户端发起的getUpdates长轮询,另一种是服务端主动推送的Webhook。两种方式在TCP连接建立、空闲等待和异常处理上的表现差异很大,直接决定机器人的响应速度和部署成本。使用Ruby开发时,借助Net::HTTP、Sinatra或telegram-bot-ruby等库可以快速实现,但想在生产环境稳定运行,还需要针对网络层做不少调优。

一、长轮询与Webhook的通信模型对比
长轮询模式下,Ruby进程会向 https://api.telegram.org/bot<token>/getUpdates 发送GET请求。如果不带timeout参数,服务器会立即返回当前积累的更新,没有更新就返回空数组。带上timeout=50后,服务器会保持连接最多50秒,期间一旦有新消息就立即返回,客户端处理完后用offset参数继续拉取。这个模型本质上是客户端在维持一个较长的HTTP请求,好处是不需要公网IP,任何能访问Telegram API的机器都能运行;坏处是每次连接结束后都要重新建立TLS握手,而且空闲时依然占有一个出站连接。
Webhook则相反,开发者先通过setWebhook接口注册一个公网HTTPS URL,Telegram收到新消息时,会向该URL发送一个POST请求,请求体为JSON格式的Update对象。Ruby服务只需要挂一个HTTP监听端口,用Sinatra、Rack或Rails接收请求即可。Webhook延迟通常更低,因为消息到达Telegram服务器后会立刻推送;但它要求服务必须公网可达、证书受信任,且服务端必须快速返回2xx状态码,否则Telegram会按固定节奏重试,造成重复推送。
从网络优化的角度看,长轮询的主要开销在TLS握手和空闲连接占用的时间,Webhook的主要开销在公网入口的TLS终止和后台处理能力。两者并没有绝对优劣,关键要看机器人运行在什么环境、需要多快的响应、以及是否方便维护HTTPS证书。下面分别展开Ruby中的实现与优化策略。
二、长轮询的Ruby实现与网络调优
使用Net::HTTP写一个长轮询循环是最直观的方式。核心参数有两个:URI中的timeout表示Telegram服务端等待时间,建议设为50秒;Ruby客户端侧的read_timeout需要略大于这个值,比如65秒,避免服务端还没返回更新,客户端就提前断开。示例代码如下:
require 'net/http'
require 'json'
require 'uri'
TOKEN = ENV.fetch('TELEGRAM_BOT_TOKEN')
API_BASE = 'https://api.telegram.org'
offset = 0
def fetch_updates(token, offset)
uri = URI("#{API_BASE}/bot#{token}/getUpdates")
uri.query = URI.encode_www_form({ offset: offset, timeout: 50 })
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.open_timeout = 10
http.read_timeout = 65
request = Net::HTTP::Get.new(uri)
response = http.request(request)
unless response.is_a?(Net::HTTPSuccess)
raise "getUpdates failed with #{response.code}"
end
JSON.parse(response.body)['result']
end
loop do
updates = fetch_updates(TOKEN, offset)
updates.each do |update|
next unless update['message']
puts "收到来自 #{update['message']['from']['username']} 的消息: #{update['message']['text']}"
offset = update['update_id'] + 1
end
rescue => e
warn "轮询异常: #{e.class} - #{e.message}"
sleep 5
end
offset的推进必须放在消息处理之后,而且只有在成功处理后才更新,否则进程崩溃后重启会重复消费,甚至可能漏掉更新。如果多个工作线程并行处理,要把offset记录在共享存储中,并保证单调递增。长轮询的异常处理也很重要:网络抖动会抛出EOFError、Timeout::Error或SocketError,429表示请求过于频繁。建议实现指数退避,比如首次等待5秒,连续失败后等待时间翻倍,最长不超过60秒。这样既能避免Telegram API限流,也能在网络恢复后自动继续拉取。
如果机器人只做简单回复,单线程轮询足够;但如果回复逻辑涉及数据库查询或外部HTTP调用,长连接会被业务逻辑阻塞。此时可以把收到的消息放入Queue,让消费者线程异步处理,主循环只负责拉取。Ruby的Thread和Queue组合可以避免引入额外依赖。要注意Telegram服务器发送的update_id是全局递增的,处理顺序不严格会影响回复先后,但通常可以接受。还可以设置allowed_updates参数只订阅message、callback_query等类型,减少无效流量。
三、Webhook服务的搭建与网络优化
Webhook模式下,Ruby进程需要常驻一个HTTP服务。Sinatra是轻量选择。一个可用的示例:
require 'sinatra'
require 'json'
set :bind, '0.0.0.0'
set :port, 8443
set :environment, :production
TOKEN = ENV.fetch('TELEGRAM_BOT_TOKEN')
SECRET = ENV.fetch('TELEGRAM_WEBHOOK_SECRET')
post '/webhook' do
request.body.rewind
body = request.body.read
provided_secret = request.env['HTTP_X_TELEGRAM_BOT_API_SECRET_TOKEN']
unless provided_secret == SECRET
status 401
return 'unauthorized'
end
update = JSON.parse(body)
Thread.new { handle_update(update) }
status 200
body 'ok'
end
def handle_update(update)
# 耗时处理逻辑
puts update['update_id']
end
上面的代码里有两个关键优化点。第一是读取请求体后立刻校验secret token,这能挡住伪造请求;第二是确认合法后马上返回200,把耗时处理放进后台线程。Telegram的Webhook重试策略要求服务端在较短时间内响应,如果在处理业务逻辑时同步响应,消息量一上来就会触发大量重试,造成重复推送和CPU浪费。对于更高并发场景,可以用Sidekiq、Sucker Punch等后台队列替换裸线程,避免线程无上限膨胀。
Webhook对网络部署的要求比长轮询高。Telegram只接受两种证书类型:受信任的CA证书,或者通过setWebhook上传的自签名证书。生产环境通常用Nginx或Caddy作为TLS终止,再反向代理到Ruby进程。Nginx配置示例如下:
server {
listen 443 ssl;
server_name bot.ipipp.com;
ssl_certificate /etc/letsencrypt/live/bot.ipipp.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/bot.ipipp.com/privkey.pem;
location /webhook {
proxy_pass http://127.0.0.1:8443;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Telegram-Bot-Api-Secret-Token $http_x_telegram_bot_api_secret_token;
}
}
本地开发没有公网域名时,可以用cloudflared、ngrok等隧道工具把本地端口暴露到HTTPS地址,再调用setWebhook。需要注意,这些隧道服务的地址稳定性不如真实域名,重新启动后URL变化,需要重新设置Webhook。注册Webhook时还可以使用secret_token参数,并在setWebhook请求中携带allowed_updates,减少不感兴趣的事件推送。删除Webhook后可恢复长轮询。
四、模式选择与切换策略
选择哪种模式,可以先回答三个问题:运行环境是否有固定公网IP或域名?是否需要低于1秒的消息响应?是否愿意维护HTTPS证书和常驻服务?如果答案多数为否,长轮询是更省心的方案;如果机器人承载业务且对延迟敏感,Webhook更合适。很多生产机器人采用Webhook,但在开发调试阶段用长轮询,两者切换只需要调用setWebhook或deleteWebhook。
从长轮询迁移到Webhook时,必须完全停止getUpdates循环,否则同一个机器人同时使用两种方式会导致409 Conflict。切换时先在setWebhook里设置drop_pending_updates为false或根据业务决定,避免历史消息突然涌入。同时检查Webhook端点返回码是否稳定为200,错误日志是否只有合法失败。稳定运行一段时间后,再逐步增大allowed_updates范围。
另外,网络优化不是一次性动作。建议记录每个轮询周期的耗时、Webhook的响应时间、Telegram重试次数以及Ruby进程内存占用。借助Ruby的Logger或监控库,可以快速发现TLS握手耗时过高、后台队列堆积等问题。对于长轮询,保持read_timeout大于Telegram服务端timeout是基础;对于Webhook,保证入口TLS握手速度够快、后台任务有界是核心。无论哪种模式,理解Update对象的更新机制和网络契约,才能让Ruby机器人稳定高效运行。
Ruby Telegram机器人Webhook长轮询修改时间:2026-10-01 09:40:20