2017-04-20 27 views
0

我正在用PostgreSQL的彪馬與Ruby 2.3.3上運行Rails 3。我有一個初始化器/ twitter.rb文件,在啓動時使用流API傳輸啓動線程。當我使用rails server開始我的應用程序時,Twitter流式傳輸可以正常工作,並且可以正常訪問我的網站。 (如果我不把流傳輸到不同的線程上,流式傳輸可以工作,但由於線程被Twitter流阻止,我無法在瀏覽器中查看我的應用程序)。但是,當我使用puma -C config/puma.rb來啓動我的應用程序時,我收到以下消息,告訴我我的線程在啓動時找到並進入睡眠狀態。我如何告訴美洲獅讓我在啓動時在後臺運行這個線程?彪馬睡在鐵軌應用程序的啓動一個重要的線程

初始化/ 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 

配置/ 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:[email protected]/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的帖子,但答案是如果線程不重要,就會忽略警告。在我的情況下,線程是非常重要的,我需要這個線程不睡覺。

+0

更新:我瞭解了睡眠線程真的是什麼,這不是問題。但是,我不確定從哪裏開始我的Twitter流線程。它應該留在初始化器中嗎?我應該使用單獨的進程(後臺應用程序)嗎?我應該啓動每個美洲獅工人的Twitter流嗎? –

回答

0

從你的代碼中,我認爲你的手上比在睡覺的線程有更大的問題......我想這可能是由於某些事件被誤稱,而其他人在依靠Web時並不經常考慮框架。

在服務器領域,「工作人員」是指執行與服務器相關的任務,經常接受新連接和處理Web請求的ed進程。

- fork重複的主題! - 新過程(工作人員)只從一個單獨的線程開始,它是稱爲fork的線程的副本。

這是因爲進程不共享內存(通常)。無論您在某個進程中擁有的全局數據是否爲該進程的私有數據(即,如果將連接的websocket客戶端保存在數組中,則該數組對於每個「工作人員」都是不同的)。

這不能被幫助,這是如何設計操作系統和fork的一部分。

所以,警告不​​是你可以繞過的東西 - 這是一個應用程序(!)的設計缺陷的跡象。

例如,在您當前的設計中(假設線程未處於睡眠狀態),handle_tweet方法將只針對原始服務器進程調用,並且不會爲任何工作進程調用。

如果您使用pub/sub,則整個應用程序只需一個twitter_stream連接(無論您的應用程序有多少個服務器或工作人員) - 也許twitter_stream進程(或後臺應用程序)會比線。

但是,如果您正在以特定的過程實施handle_tweet - 即通過向保存在數組中的每個連接的客戶端發送一條消息 - 您需要確保每個「工作人員」啓動一個線程(!)twitter_stream

當我編寫Iodine(與Puma不同的服務器)時,我使用Iodine.run method處理這些用例,這些用例將稍後推遲任務。只有在工作人員初始化並且事件循環開始運行後才能執行「已保存」任務,因此它在每個進程中都執行(允許您在每個進程中開始一個新步驟)。

Iodine.run do 
    Thread.start do 
    twitter_stream.user do |object| 
    # ... 
    end 
    end 
end 

我假定彪馬也有類似的解決方案。從我瞭解的Puma Clustered-Mode Documentation的,把下面的模塊到你的config/puma.rb可能會幫助:

# config/puma.rb 
on_worker_boot do 
    Thread.start do 
    twitter_stream.user do |object| 
    # ... 
    end 
    end 
end 

祝你好運!


編輯:有關評論有關twitter_stream使用ActiveRecord

從我收集的意見,在數據庫中的twitter_stream回調存儲數據以及處理「推」事件或通知。

儘管這兩個問題是相關的,但它們彼此之間卻有很大的不同。

例如,twitter_stream回調應該只將數據存儲在數據庫中一次。即使您的應用程序增長到十億用戶,您只需要將數據保存在數據庫中一次

這意味着twitter_stream回調應該有自己的專用進程,它只運行一次,可能與主應用程序分離。

首先,只要你限制你的應用程序的單一(只有一臺服務器/應用程序運行),可以用該initializer/twitter.rb腳本一起使用fork ...即:

### 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(或​​)如上所述。

要實現此目的,您可能需要on_worker_boot啓動一個後臺線程,該線程將偵聽發佈/訂閱服務(如Redis),而twitter_stream回調將發佈更新發布到發佈/訂閱服務。

這將允許每個進程檢查更新並檢查它「擁有」的任何連接是否屬於應該被通知更新的客戶端。

+0

感謝您的幫助。由於我有兩個工作人員,當我將我的線程放入on_worker_boot塊時,Twitter流開始兩次。可以嗎?在這種情況下,一切都會重複嗎? handle_tweet方法只能被調用一次,因爲它將信息存儲在每條推文的活動記錄中,並可能會將直接消息發回給用戶。如果我需要爲Twitter流使用整個其他流程(後臺應用程序),那麼您是否有關於如何開始使用它的指示? 感謝所有的幫助和抱歉,我對這個問題的無知。 –

+0

Hi @ToddSutter,感謝您的評論。我更新了我的答案,以反映'twitter_stream'回調在DataBase中存儲數據的胖點。 – Myst

+0

額外的信息是非常有幫助的。我的twitter流基本上是一個數據庫接口,所以我將嘗試運行一個單獨的進程,如上面描述的Process.fork。我不知道如何使用,甚至是一個單獨的過程,所以我很高興我接觸了!我會盡快測試並回復你。 –

0

我正在閱讀你的問題的方式,這看起來不是一個問題。A 睡覺線程不同於死了線程。睡眠只是意味着線程正在等待空閒,不消耗任何CPU。如果所有其他東西都正確連接起來,那麼只要twitter api檢測到事件,它應該喚醒線程,運行您定義的任何處理程序,然後馬上回到睡眠狀態。睡覺並不是「在後臺運行」,而是「等待發生某些事情(例如有人發推文@me),所以我可以在後臺運行。」

一個簡單的例子來說明這一點:

2.4.0 :001 > t = Thread.new { TCPServer.new(1234).accept ; puts "Got a connection! Dying..." } 
=> #<Thread:[email protected](irb):1 sleep> 
2.4.0 :002 > t 
=> #<Thread:[email protected](irb):1 sleep> 
2.4.0 :003 > t 
=> #<Thread:[email protected](irb):1 sleep> 
2.4.0 :004 > TCPSocket.new 'localhost', 1234 
=> #<TCPSocket:fd 35> 
2.4.0 :005 > Got a connection! Dying... 
t 
=> #<Thread:[email protected](irb):1 dead> 

睡覺只是意味着 「伺機而動」。


Puma是一個基於線程的服務器,在啓動過程中對線程進行了非常細緻的處理,因此警告有關在應用程序啓動時啓動的線程。

對於它的價值來說,有一個線程監聽web服務器中的api更新的線程有點奇怪。也許你應該考慮讓工作人員使用像Resque這樣的東西來處理twitter事件?或者,也許ActionCable與您的用例有關?

+0

所以,你會建議我有另一臺服務器運行,只需要照顧這個Twitter的API,從我的Web服務器分開?我對這一切都很陌生(如果那還不是很明顯)。還是這些模塊Resque和ActionCable插件到我的Web服務器? –

+0

這確實取決於你的回調在做什麼。他們是否在向用戶反饋信息?他們是否將記錄保存到數據庫?他們是否完全在做其他事情?同樣重要的是您願意花多少時間投資您的應用。如果不是第二臺服務器,那麼至少有一個單獨的進程處理這些請求,這是更爲標準和嚴謹的做事方式,因爲這些回調不用於響應即時請求。從架構的角度來看,它有點更多的工作,但它通過分離服務器/工作者的關注來搶佔這些問題 – Glyoko

+0

**旁註**:Puma是**不是基於線程的服務器,而是基於反應器服務器。 Puma使用事件循環和有限的線程池,而不是每個連接的線程基於線程的服務器設計。 Puma還支持集羣模式(使用進程進行併發)。 – Myst

相關問題