Kamal部署Sinatra应用遭遇Grover::JavaScript::TimeoutError求助
解决Sinatra+Grover调用自身路由超时的问题
你的问题核心是Sinatra默认使用的WEBrick服务器是单线程单进程模式,同一时间只能处理一个请求。当/pdf3的请求触发Grover去请求自身的/up路由时,主进程正被/pdf3占用,无法响应这个内部请求,导致Chrome等待超时,最终抛出Grover::JavaScript::TimeoutError。
下面是具体的解决办法:
方案1:替换为多线程/多进程服务器(最直接有效)
Sinatra默认的WEBrick不适合生产环境并发场景,换成Puma服务器,它支持多线程+多进程,能同时处理多个请求。
操作步骤:
- 在Gemfile中添加Puma依赖:
gem 'puma'
- 执行
bundle install更新依赖包。 - 修改Dockerfile的启动命令,用Puma替代默认的rackup:
CMD ["bundle", "exec", "puma", "--bind", "0.0.0.0:4567", "--threads", "2:16", "--workers", "2"]
说明:
--threads 2:16表示每个进程的线程数范围为2-16,--workers 2表示启动2个工作进程,可根据服务器CPU核心数调整(比如4核CPU可设为4个workers)。
- 修正Kamal的健康检查配置,匹配实际的路由和端口:
healthcheck: path: /up port: 4567
方案2:直接渲染内容,避免内部请求(更高效)
如果/up路由仅返回简单文本,没必要通过Grover发起HTTP请求,直接把内容传给Grover,跳过请求环节,既避免死锁又提升性能。
修改/pdf3路由代码:
get '/pdf3' do # 直接传入HTML内容,无需请求外部URL grover = Grover.new("<html><body>UP</body></html>", base_url: "https://myserver.com") content_type 'application/pdf' attachment 'output.pdf' grover.to_pdf end
说明:
base_url用于处理内容中的相对资源路径,若内容无相对路径可省略。
方案3:调整Nginx和Kamal代理配置(辅助优化)
- 移除Nginx的
limit_rate 50k限制,该限速会拖慢内部请求,加剧超时风险,同时补充必要的代理头:
server { server_name myserver.com; access_log off; location / { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } }
- 确认Kamal的Traefik配置中,端口转发规则匹配应用实际端口(你的应用容器端口为4567,Traefik转发到8081的配置是正确的)。
验证方法
部署修改后的代码后,访问/pdf3测试是否正常生成PDF。可通过curl -v https://myserver.com/pdf3查看请求过程,或查看应用日志确认并发请求是否被正常处理。
内容的提问来源于stack exchange,提问作者map7
相关产品推荐
相关产品推荐

