本地Rails/Puma服务器卡顿无响应问题排查求助
本地Rails/Puma+Foreman卡顿无响应排查方案
问题现象
- 本地用
foreman start -f Procfile.dev启动服务后,运行一段时间Puma服务器卡顿无响应,控制台无日志输出,只能重启解决 - 按下
control + c停止服务器时,会批量输出积压的正常日志后才关闭 - 定期用
command + k清空控制台可避免该问题,推测与日志输出缓存/控制台缓冲区有关 - 仅本地环境出现,staging和生产环境无此问题
当前本地环境配置
- macOS 13.6.1
- 使用
bash(非zsh) - Rails 7.0.8
- Ruby 2.7.8
- Puma 4.3.12
- Sidekiq 6.5.12
- foreman 0.87.2
关键配置文件
config/environments/development.rb
require "active_support/core_ext/integer/time" require "dotenv" Dotenv.load Rails.application.configure do config.cache_classes = false config.eager_load = false config.consider_all_requests_local = true config.server_timing = true if Rails.root.join("tmp/caching-dev.txt").exist? config.action_controller.perform_caching = true config.action_controller.enable_fragment_cache_logging = true config.cache_store = :memory_store config.public_file_server.headers = { "Cache-Control" => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false config.cache_store = :null_store end config.active_storage.service = :local config.action_mailer.raise_delivery_errors = ActiveModel::Type::Boolean.new.cast(ENV.fetch('RAILS_MAILER_DELIVERY_ERRORS') { true }) config.action_mailer.perform_caching = false config.active_support.deprecation = :log config.active_support.disallowed_deprecation = :raise config.active_support.disallowed_deprecation_warnings = [] config.active_record.migration_error = :page_load config.active_record.verbose_query_logs = true config.assets.quiet = true config.assets.debug = true config.assets.quiet = true config.file_watcher = ActiveSupport::EventedFileUpdateChecker config.action_cable.mount_path = ":#{ ENV.fetch('DEFAULT_PORT') { 5000 } }/cable" config.action_cable.url = "wss://#{ ENV.fetch('HOST') { 'localhost' } }:#{ ENV.fetch('DEFAULT_PORT') { 5000 } }/cable" config.web_socket_server_url = "wss://#{ ENV.fetch('HOST') { 'localhost' } }:#{ ENV.fetch('DEFAULT_PORT') { 5000 } }/cable" config.action_cable.allowed_request_origins = [/http:\/\/#{ENV['HOST']}*/, /https:\/\/#{ENV['HOST']}*/] config.action_mailer.default_url_options = { protocol: ENV.fetch('PROTOCOL') { 'https' }, host: ENV.fetch('HOST') { 'localhost' }, port: ENV.fetch('DEFAULT_PORT') { 3000 } } config.logger = ActiveSupport::Logger.new(STDOUT) config.log_level = (ENV.fetch('RAILS_LOG_LEVEL') { 'debug' }).to_sym config.sass.inline_source_maps = true config.assets.unknown_asset_fallback = true config.force_ssl = true config.assets.prefix = '/dev-assets' config.action_dispatch.cookies_same_site_protection = :strict config.action_mailer.delivery_method = ENV.fetch('MAIL_DELIVERY_METHOD') { 'letter_opener' }.to_sym config.action_mailer.perform_deliveries = true end
config/puma/development.rb
require "dotenv" Dotenv.load max_threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 } min_threads_count = ENV.fetch('RAILS_MIN_THREADS') { max_threads_count } threads min_threads_count, max_threads_count port ENV.fetch('DEFAULT_PORT') { 3000 } environment ENV.fetch('RAILS_ENV') { 'development' } workers ENV.fetch('WEB_CONCURRENCY') { 5 } preload_app! before_fork do ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) end on_worker_boot do ActiveRecord::Base.establish_connection if defined?(ActiveRecord) end if ENV.fetch('HOST') { 'localhost' } === 'localhost' ssl_key = "#{File.join('config', 'certs', 'localhost', 'localhost.key')}" ssl_crt = "#{File.join('config', 'certs', 'localhost', 'localhost.crt')}" end host = ENV.fetch('HOST') { 'localhost' } port = ENV.fetch('SSL_PORT') { ENV.fetch('DEFAULT_PORT') { 3000 } } ssl_bind host, port, { key: ssl_key, cert: ssl_crt, verify_mode: 'none' }
Procfile.dev
web: RAILS_ENV=development bundle exec puma webpacker: ./bin/webpack-dev-server redis: redis-server worker1: bundle exec sidekiq -C config/sidekiq.yml worker2: bundle exec sidekiq -C config/sidekiq.yml worker3: bundle exec sidekiq -C config/sidekiq.yml worker4: bundle exec sidekiq -C config/sidekiq.yml worker5: bundle exec sidekiq -C config/sidekiq.yml worker6: bundle exec sidekiq -C config/sidekiq.yml worker7: bundle exec sidekiq -C config/sidekiq.yml worker8: bundle exec sidekiq -C config/sidekiq.yml
排查与解决建议
1. 禁用Foreman日志缓冲
你使用的foreman 0.87.2版本较旧,macOS bash环境下默认会缓冲子进程的STDOUT输出,缓冲区满时会阻塞进程:
- 启动Foreman时添加
--no-tty参数:foreman start -f Procfile.dev --no-tty,强制子进程输出不经过终端缓冲 - 或给进程添加环境变量强制无缓冲输出,修改Procfile.dev中的启动命令:
web: RAILS_ENV=development RUBYOPT="-E utf-8 -KU" bundle exec puma worker1: RUBYOPT="-E utf-8 -KU" bundle exec sidekiq -C config/sidekiq.yml
2. 开启Rails日志同步输出
当前Rails日志默认带缓冲,修改config/environments/development.rb中的日志配置,强制同步输出:
config.logger = ActiveSupport::Logger.new(STDOUT) config.logger.formatter = config.log_formatter config.logger.sync = true # 禁用日志缓冲,实时输出
3. 减少Sidekiq进程数量
本地开发环境启动8个Sidekiq worker会大幅增加日志输出压力,建议减到2-3个,降低控制台缓冲区负载。
4. 升级Foreman版本
0.87.2是2019年的旧版本,后续版本修复了大量终端输出相关bug,执行gem update foreman升级到最新稳定版。
5. 绕过Foreman直接测试Puma
直接运行bundle exec puma启动服务器,不通过Foreman管理,观察是否还会出现卡顿。若问题消失,可确认是Foreman的终端缓冲问题;若问题依旧,再排查Puma或Rails日志配置。
6. 调整bash终端缓冲区大小
bash默认终端缓冲区可能不足,在~/.bashrc中添加以下配置增大缓冲区:
stty size 1000 200 # 调整终端行数和列数,提升缓冲区容量
重启终端后生效。
内容的提问来源于stack exchange,提问作者Eric Norcross
相关产品推荐
相关产品推荐

