我正在运行带有Postgresql的Puma上的Ruby 2.3.3的Rails 3。我有一个initializer/twitter.rb文件,该文件启动了带有Twitter的流API在启动上的线程。当我使用rails server
启动应用程序时,Twitter流媒体可以正常地到达我的网站。(如果我不将流媒体放在另一个线程上,则流媒体可行,但由于线程被Twitter流阻止,因此无法在浏览器中查看我的应用程序(。但是,当我使用puma -C config/puma.rb
启动我的应用程序时,我会收到以下消息,这些消息告诉我我的线程是在启动时发现的,并且被入睡。我该如何告诉PUMA让我在启动的背景中运行此线程?
initializer/twitter.rb
### START TWITTER THREAD ### if production
if Rails.env.production?
puts 'Starting Twitter Stream...'
Thread.start {
twitter_stream.user do |object|
case object
when Twitter::Tweet
handle_tweet(object)
when Twitter::DirectMessage
handle_direct_message(object)
when Twitter::Streaming::Event
puts "Received Event: #{object.to_yaml}"
when Twitter::Streaming::FriendList
puts "Received FriendList: #{object.to_yaml}"
when Twitter::Streaming::DeletedTweet
puts "Deleted Tweet: #{object.to_yaml}"
when Twitter::Streaming::StallWarning
puts "Stall Warning: #{object.to_yaml}"
else
puts "It's something else: #{object.to_yaml}"
end
end
}
end
config/puma.rb
workers Integer(ENV['WEB_CONCURRENCY'] || 2)
threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5)
threads threads_count, threads_count
preload_app!
rackup DefaultRackup
port ENV['PORT'] || 3000
environment ENV['RACK_ENV'] || 'development'
on_worker_boot do
# Valid on Rails up to 4.1 the initializer method of setting `pool` size
ActiveSupport.on_load(:active_record) do
config = ActiveRecord::Base.configurations[Rails.env] ||
Rails.application.config.database_configuration[Rails.env]
config['pool'] = ENV['RAILS_MAX_THREADS'] || 5
ActiveRecord::Base.establish_connection(config)
end
end
启动上的消息
2017-04-19T23:52:47.076636+00:00 app[web.1]: Connecting to database specified by DATABASE_URL
2017-04-19T23:52:47.115595+00:00 app[web.1]: Starting Twitter Stream...
2017-04-19T23:52:47.229203+00:00 app[web.1]: Received FriendList: --- !ruby/array:Twitter::Streaming::FriendList []
2017-04-19T23:52:47.865735+00:00 app[web.1]: [4] * Listening on tcp://0.0.0.0:13734
2017-04-19T23:52:47.865830+00:00 app[web.1]: [4] ! WARNING: Detected 1 Thread(s) started in app boot:
2017-04-19T23:52:47.865870+00:00 app[web.1]: [4] ! #<Thread:0x007f4df8bf6240@/app/config/initializers/twitter.rb:135 sleep> - /app/vendor/ruby-2.3.3/lib/ruby/2.3.0/openssl/buffering.rb:125:in `sysread'
2017-04-19T23:52:47.875056+00:00 app[web.1]: [4] - Worker 0 (pid: 7) booted, phase: 0
2017-04-19T23:52:47.865919+00:00 app[web.1]: [4] Use Ctrl-C to stop
2017-04-19T23:52:47.882759+00:00 app[web.1]: [4] - Worker 1 (pid: 11) booted, phase: 0
2017-04-19T23:52:48.148831+00:00 heroku[web.1]: State changed from starting to up
事先感谢您的帮助。我查看了提到WARNING: Detected 1 Thread(s) started in app boot
的其他几篇文章,但是答案说明线程并不重要的情况下忽略了警告。就我的情况而而不是一个睡眠线...我猜这可能是由于某些事情被错误地命名的事实而引起
在服务器世界中,"工人"是指执行服务器相关任务的fork
ED进程,通常接受新的连接并处理Web请求。
但是 - fork
不重复线程! - 新过程(工人(仅以一个单个线程开头,该线程是该线程的副本,称为fork
。
这是因为进程不共享内存(通常(。无论您在一个过程中拥有的全局数据都是私人的(即,如果将连接的Websocket端列在数组中,则每个"工作"的数组都不同(。
这是无法帮助的,这是OS和fork
的设计的一部分。
因此,警告不是您可以规避的 - 这是应用程序中设计缺陷的指示(!(。
例如,在您当前的设计中(假设线程没有睡觉(,handle_tweet
方法仅用于原始服务器过程,并且不会为任何工作过程调用。
如果您使用的是Pub/sub,则只需要一个twitter_stream
连接整个应用程序(无论您应用多少服务器或工人有多少个( - 也许twitter_stream
流程(或背景应用程序(都比线程更好。
但是,如果您要以特定方式实现handle_tweet
- 即,通过向保存在数组中的每个连接客户端发送消息,您需要确保每个"工作者"启动twitter_stream
线程(!(。p>当我编写碘(与PUMA不同的服务器(时,我使用Iodine.run
方法处理了这些用例,该方法为稍后的任务进行了辩护。仅在初始化工人并开始运行事件循环后才执行"保存"任务,因此在每个过程中都执行(允许您在每个Proccess中启动新的胎面(。
即。
Iodine.run do
Thread.start do
twitter_stream.user do |object|
# ...
end
end
end
我假设PUMA具有类似的解决方案。据我了解PUMA群集模式文档,将以下块添加到您的config/puma.rb
可能会有所帮助:
# config/puma.rb
on_worker_boot do
Thread.start do
twitter_stream.user do |object|
# ...
end
end
end
祝你好运!
edit :使用ActivereCord
与twitter_stream
的评论有关从我收集的评论中,twitter_stream
回调将数据存储在数据库中以及处理"推"事件或通知。
尽管这两个问题已连接,但它们彼此截然不同。
例如,twitter_stream
回调仅应将数据存储在数据库曾经>中。即使您的应用程序增长到十亿用户,您也只需要将数据保存在。
这意味着twitter_stream
回调应具有仅运行一次的专用过程,可能与主应用程序分开。
首先,只要您将应用程序限制为单个(仅运行一台服务器/应用程序(,就可以将fork
与initializer/twitter.rb
脚本一起使用...即:
### START TWITTER PROCESS ### if production
if Rails.env.production?
puts 'Starting Twitter Stream...'
Process.fork do
twitter_stream.user do |object|
# ...
end
end
end
另一方面,应通过特定过程在特定连接拥有的特定连接上的特定用户。
因此,通知应与twitter_stream
数据库更新是一个单独的关注点,并且应该使用上述on_worker_boot
(或Iodine.run
(在每个过程的背景中运行。
为了实现这一目标,您可能让on_worker_boot
启动一个背景线程,该线程将收听redis等酒吧/sub服务,而twitter_stream
回调"发布"更新到Pub/sub Service。
这将允许每个过程查看更新并检查其"拥有"的任何连接是否属于应通知更新的客户端。
我正在阅读您的问题的方式,这看起来不像一个问题。A 睡觉线程与A Dead 线程不同。睡眠只是意味着线程正在等待空闲,而不是消耗任何CPU。如果其他所有内容都正确连接,那么一旦Twitter API检测到事件,它应该唤醒线程,运行您定义的任何处理程序,然后直接回到睡眠状态。睡觉不是"在后台运行",而是"在等待某些事情发生(例如,有人推文@ME。(,所以我可以在后台运行。"
一个简单的例子来证明这一点:
2.4.0 :001 > t = Thread.new { TCPServer.new(1234).accept ; puts "Got a connection! Dying..." }
=> #<Thread:0x007fa3941fed90@(irb):1 sleep>
2.4.0 :002 > t
=> #<Thread:0x007fa3941fed90@(irb):1 sleep>
2.4.0 :003 > t
=> #<Thread:0x007fa3941fed90@(irb):1 sleep>
2.4.0 :004 > TCPSocket.new 'localhost', 1234
=> #<TCPSocket:fd 35>
2.4.0 :005 > Got a connection! Dying...
t
=> #<Thread:0x007fa3941fed90@(irb):1 dead>
睡觉只是"等待行动"。
PUMA是基于线程的服务器,非常特别地是在其启动过程中旋转线程,因此在App Boot上启动有关线程的警告。
对于它的价值,让线程收听Web服务器中的API的更新是很奇怪的。也许您应该考虑让工人使用Resque之类的东西处理Twitter事件?或者也许与您的用例有关?