2017-04-20 1 views
0

postgresql을 사용하여 puma에서 Ruby 2.3.3으로 Rails 3를 실행 중입니다. 트위터에 대한 스트리밍 API로 부팅시 스레드를 시작하는 initializer/twitter.rb 파일이 있습니다. rails server을 사용하여 응용 프로그램을 시작하면 트위터 스트리밍이 작동하고 정상적인 방식으로 웹 사이트에 연결할 수 있습니다. (스트리밍을 다른 스레드에 배치하지 않으면 스트리밍은 작동하지만 스레드는 트위터 스트림에 의해 차단되므로 브라우저에서 응용 프로그램을 볼 수 없습니다. 그러나 puma -C config/puma.rb을 사용하여 응용 프로그램을 시작하면 다음과 같은 메시지가 표시됩니다.이 메시지는 내 스레드가 시작시 발견되어 잠자기 상태가되었음을 알려줍니다. 시작할 때이 스레드를 백그라운드에서 실행할 수 있도록 퓨마에게 어떻게 알릴 수 있습니까?/twitter.rbPuma는 레일 응용 프로그램을 부팅 할 때 중요한 스레드를 잠입니다.

### 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

업데이트 : 수면 스레드가 실제로 무엇인지를 배웠으며 문제가되지 않습니다. 그러나 나는 내 트위터 스트림 스레드를 시작할 위치가 확실하지 않습니다. 이니셜 라이저에 남아 있어야합니까? 별도의 프로세스 (백그라운드 앱)를 사용해야합니까? 모든 퓨마 작업자에게 트위터 스트림을 시작해야합니까? –

답변

0

코드에서 나는 수면 스레드보다 손에 더 큰 문제가 있다고 생각합니다 ... 어떤 것들은 잘못된 이름이고 다른 것들은 웹에 의존 할 때 종종 고려되지 않습니다. 뼈대.

서버의 세계에서 "작업자"는 서버 관련 작업을 수행하고 종종 새로운 연결을 수락하고 웹 요청을 처리하는 fork 프로세스를 말합니다.

하지만-fork하지 중복 스레드를한다! - 새 프로세스 (작업자)는 하나의 단일 스레드로 시작합니다.이 스레드는 fork이라는 스레드의 복사본입니다.

프로세스는 메모리 (일반적으로)을 공유하지 않기 때문입니다. 프로세스에서 보유하고있는 모든 글로벌 데이터는 해당 프로세스에만 적용됩니다 (예 : 연결된 웹 소켓 클라이언트를 배열에 저장하면 해당 배열은 각 "작업자"마다 다릅니다).

이것은 도움이 될 수 없으며 OS 및 fork 설계 방법의 일부입니다.

경고는 사용자가 우회 할 수있는 것이 아니며, 앱의 디자인 결함 (!)을 나타냅니다.

예를 들어, 현재 디자인 (스레드가 절전 모드가 아닌 것으로 가정)에서 handle_tweet 메서드는 원래 서버 프로세스에만 호출되며 작업자 프로세스에서는 호출되지 않습니다.

펍/서브를 사용하는 경우 애플리케이션 수에 관계없이 전체 앱에 대해 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 내가 수집 한 의견에서

액티브

을 사용하여 해당 데이터베이스에 twitter_stream 콜백 데이터를 저장뿐만 아니라 핸들 "푸시"이벤트 또는 통지.

이 두 가지 관심사가 연결되어 있지만 서로 매우 다릅니다.

예를 들어, twitter_stream 콜백은 데이터베이스 번만 데이터를 저장해야합니다. 애플리케이션이 10 억 명의 사용자로 성장하더라도 데이터베이스에 데이터를 저장하면됩니다. .

즉, 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 DataBase 업데이트와 별개의 문제 여야하며 위에 설명 된 on_worker_boot (또는 Iodine.run)을 사용하여 모든 프로세스의 백그라운드에서 실행되어야합니다.

이렇게하려면 twitter_stream 콜백이 pub/sub 서비스에 대한 업데이트를 "게시"하는 동안은 Redis와 같은 pub/sub 서비스를 수신하는 백그라운드 스레드를 시작할 수 있습니다.

이렇게하면 각 프로세스가 업데이트를 검토하고 "소유"한 연결이 업데이트를 알리는 클라이언트에 속하는지 확인합니다.

+0

도움에 감사드립니다. 두 명의 작업자가 있으므로 on_worker_boot 블록에 스레드를 넣으면 트위터 스트림이 두 번 시작됩니다. 괜찮습니까? 이 경우 모든 것이 복제 될 것입니까? handle_tweet 메서드는 짹짹 당 Active Record에 정보를 저장하고 사용자에게 직접 메시지를 보낼 수 있기 때문에 한 번만 호출해야합니다. 트위터 스트림에 대해 다른 모든 프로세스 (백그라운드 응용 프로그램)를 사용해야하는 경우, 시작하는 방법에 대한 지침이 있습니까? 모든 도움을 주셔서 감사 드리며 그 주제에 대해 알지 못해 죄송합니다. –

+0

안녕하세요 @ToddSutter, 의견 주셔서 감사합니다. 'twitter_stream' 콜백이 데이터베이스에 데이터를 저장하는 팻 (fat)을 반영하여 답변을 업데이트했습니다. – Myst

+0

추가 정보는 매우 유용합니다. 내 트위터 스트림은 기본적으로 데이터베이스 인터페이스이므로 위에서 설명한 Process.fork와 같은 별도의 프로세스를 실행하려고합니다. 나는 사용법을 알지 못했거나 별도의 과정이 무엇인지 알지 못해서 다행했습니다. 나는 곧 이것을 시험 할 것이고 당신에게 돌아갈 것이다. –

0

귀하의 질문을 읽는 방식으로는 문제가되지 않습니다.수면 실은 실종 실과 다릅니다. 수면이란 스레드가 유휴 상태를 기다리고있어 CPU를 소비하지 않는다는 것을 의미합니다. 다른 모든 것이 제대로 연결되면 트위터 API가 이벤트를 감지하자마자 스레드를 깨우고 정의한 처리기를 실행 한 다음 다시 잠자기 상태로 돌아가야합니다. 잠자는 것은 "백그라운드에서 뛰는"것이 아니라 "(예를 들어, 누군가 짹짹 짹짹이 있습니다.) 그래서 나는 백그라운드에서 달릴 수 있습니다."

빠른 예는이 방법을 설명하기 위해 : 잠자는

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> 

단지 의미 "행동을 기다리고."


푸마는 스레드 기반 서버이며, 부팅 과정에서 스레드를 회전시키는 것이 매우 중요하므로 응용 프로그램 부팅시 스레드에 대한 경고가 발생합니다.

웹 서버의 API와 같은 업데이트를 수신하는 스레드가있는 것은 이상한 일입니다. 어쩌면 당신은 Resque 같은 것을 사용하여 작업자가 트위터 이벤트를 처리하도록해야합니까? 또는 ActionCable은 (는) 귀하의 유스 케이스와 관련이 있습니까?

+0

내 웹 서버와 별개로이 트위터 API 만 처리하는 다른 서버를 실행하도록 제안 하시겠습니까? 나는이 모든 것들에 대해 아주 새롭다. 아니면 Resque 및 ActionCable 모듈이 내 웹 서버에 플러그인되어 있습니까? –

+0

정말 콜백이하는 일에 달려 있습니다. 그들은 뭔가를 사용자에게 전달하고 있습니까? 레코드를 DB에 저장하고 있습니까? 그들은 완전히 다른 것을하고 있습니까? 또한 앱에 얼마 동안 투자 할 의향이 있느냐가 중요합니다. 두 번째 서버가 아니라면 이러한 요청을 처리하는 데 최소한 하나의 별도 프로세스가 필요하므로 일을 더 표준적이고 엄격하게 수행 할 수 있습니다. 즉, 이러한 콜백은 즉각적인 요청에 응답하기 위해 사용되지 않기 때문입니다. 아키텍처 관점에서 볼 때 좀 더 많은 작업이 있지만 서버/작업자의 관심사를 분리하여 이와 같은 질문을 선점합니다. – Glyoko

+0

** 사이드 노트 ** : 푸마는 ** 스레드 기반 서버가 아닌 ** 기반의 원자로입니다. 섬기는 사람. Puma는 연결 당 스레드 기반 스레드 디자인 대신 이벤트 루프와 제한된 스레드 풀을 사용합니다. Puma는 클러스터 모드 (동시성 프로세스 사용)도 지원합니다. – Myst

관련 문제