2

win32-servicegemを使用してサービスとして実行するように設定された sinatra アプリがあります。アプリは正常に起動しますが、しばらくすると壊れます。ただし、サービスのステータスによると、サービスはまだ実行されていますが、外部またはコンピューター自体からアクセスできないようです。修正する唯一の方法は、サービスを停止して削除し、サービスを再インストールして起動することです.

アプリは通常、自己展開 (ビルド パイプラインのためですが、更新をプッシュした場合のみ) であり、chef によりインストールおよび実行されます。そのセットアップ方法は、chef サーバーがすべてのクックブックを 5 分ごとに実行することであり、それが壊れると思います何とか私のアプリですが、方法がわかりません。

どんな洞察も大歓迎です。

psコードを投稿していないことに気づきましたが、それは何が役立つかわからないためです。何を見る必要があるか教えてください。投稿します。

編集:シェフとは何の関係もないことがわかりました:

いくつかのロギングのおかげで、アプリが 5 秒間のヘルス ping (ワニスによって行われる) への応答を停止する前に約 57 分間実行されていたことがわかり、シェフの実行は 30 分ごとに発生します。 -無関係。

ただし、ステータスはまだ「実行中」であるため、停止しておらず、ログにはデーモンの実行を停止させるエラーが報告されていません。

編集:

ここに私のデーモンコードがあります:

  class DemoDaemon < Daemon
    def service_main
      Object.const_get("GG_Web_#{C_NAME}").run! port: PORT, server: 'thin'
      while running?
        sleep 100
        File.open(Logfile, "a"){ |f| f.puts "#{Time.now} - Service is running" }
      end
    end

    def service_stop
      File.open(Logfile, "a"){ |f| f.puts "#{Time.now} - Service stopped" }
      exit!
    end
  end

  DemoDaemon.mainloop

編集:

アプリが 50 分前後で応答を停止し、ログ出力なし (例: 例外、サービスの停止など) をギブまたはテイクし、アプリのステータスが として応答することに気付きましたrunning。念のため、ボックスでシェフクライアントを無効にしましたが、アプリはまだ応答を停止しているため、100%シェフではありません.

編集:

また、実際にはログファイルに出力さService is runningれません。Service stopped

編集:私はそれをかなり絞り込みましたが、そうではないことを知っています:

  1. シェフ
  2. ソースコードの読み込みメカニズム
  3. 主な処理、つまりリクエストの処理、データベースからのデータのロードなどを行うアプリの部分 (同じ問題を抱えた 2 つのアプリがあり、原因は両方で同じものと関係があります)

この問題が発生せずに動作していた以前のバージョンの gem にロールバックしましたが、まだ動作しています。

アプリケーションのメインループを作成するために使用しているwin32-systemgemと、sinatraとの相互作用です。

アプリを登録するために使用しているコードは次のとおりです。

binary_path = "#{self.rubypath} #{ROOT.gsub('/','\\')}boot_as_service.rb"
Service.create({
                       service_name: NAME,
                       service_type: Service::WIN32_OWN_PROCESS,
                       description: "#{DESCRIPTION}, running on: #{HOST}:#{PORT}",
                       start_type: Service::AUTO_START,
                       error_control: Service::ERROR_NORMAL,
                       binary_path_name: binary_path,
                       load_order_group: 'Network',
                       dependencies: ['W32Time', 'Schedule'],
                       display_name: NAME
                   })

主なアプリケーション クラス:

class GG_Web_My_APP < Sinatra::Base
  set :show_exceptions, true
    set :root, ROOT + NAME
  set :server, 'thin'
  set :scss, {:style => :compact, :debug_info => false}
  Compass.add_project_configuration(File.join(settings.root, 'config', 'compass.rb'))
  Tilt.register Tilt::ERBTemplate, 'html.erb'

  enable :logging
  logging_file = File.open((Object.const_defined?('Logfile') ? Logfile : 'C:\\app.log'), "a")
  logging_file.sync = true
  use Rack::CommonLogger, logging_file

  if ENV['APPLICATION_NAME']
    set :environment, :production
    set :bind, '0.0.0.0'
  end

  get '/css/:name.css' do
    content_type 'text/css', :charset => 'utf-8'
    scss :"/assets/css/#{params[:name]}", Compass.sass_engine_options
  end

  helpers Sinatra::FormHelpers
  helpers Sinatra.const_get("GG_Web_#{C_NAME}")::Helpers
  register Sinatra.const_get("GG_Web_#{C_NAME}")::Api
  register Sinatra.const_get("GG_Web_#{C_NAME}")::Actions

end

アプリをデーモンとして実行するために使用しているコードを見てきました。

これを修正するには本当に助けが必要です。これを引き起こしている可能性のあるものと修正方法を確認するためのアイデアがまったくありません。

編集:まあ、もう1つ確認できました。それは私のコードでそれを引き起こしているものではありません。それでも停止しましたが、ローカル マシンで実行したままにしていたバージョンは停止しませんでした。サーバーが応答しなくなる原因は、サーバー上で実行されている何かです。

さらに編集:

それはまったく問題ではないことが判明しました、私はまだ何も得ていません

編集:

わかりました、嘘をつくことはできませんでしたので、戻って、system32デーモンクラスを使用せずにアプリをデーモンとして実行することができました. time) 、まあ、サービス gem なしで開始したので、まだハングしているので、他に何ができるのだろうかと思っています。シナトラがハングアップする原因は何ですか?

また、ping localhost返信があれば(0%ロス)でもcurlだと繋がらない。

編集:

Hello World アプリケーションになるまで体系的にすべてを取り除いた後、私の ruby​​ コードとはまったく関係がないと言っても過言ではありません。この時点で、私は:

  1. デーモンでコードをラップして実行するのではなく、ラックアップを使用してアプリをプロセスとして開始します (ただし、メイン スレッドを手動で強制終了する必要がありますが、アプリは引き続き実行されます)。
  2. ロギングをオフにしました
  3. コンパス/サスを取り除きました(他の誰かに明らかにそうしていたので、ぶら下がっているかもしれないと思っていました。それに、付属の機能を使用していないことに気付いたので、それを維持しても意味がありません)
  4. すべてのモジュールをコメントアウトし、次のようにしました。 get '/' do 'Hello World' end

結局、それはまだ死んでいて、この時点では純粋なシナトラの「ハローワールド」アプリでした。したがって、sinatraに何か問題があるか(他の人がこの問題を抱えていたのではないかと思います)、おそらくサーバーに何かが原因である可能性があります。

それを引き起こしている可能性のあるサーバー上のものはわかりません。しかし、少なくとも私はその解決に一歩近づきました。

4

2 に答える 2

2

2 つの問題があるようです。

  1. アプリケーションが応答しなくなる
  2. サービスの制御の欠如

あなたが示唆するように、2つは直接関連している可能性があります。以下は、問題がどこにあるかを特定できるように、各懸念事項を少しずつ分離できるようにするための手順です。正確な答えではありませんが、答えが得られるかもしれません...


アプリケーションが応答しなくなる

Process Explorerを入手して、プロセスが何をしているかを調べますruby.exe。右クリックruby.exeして を選択しますProperties...。100% の CPU を使用していますか? 奇妙に感じる特定のカウンターはありますか?

Process ExplorerThreadsのプロセスのタブを見てください。リストされている TID の中に、100% の実行で止まっているものはありますか? 各スレッドについて、(右下のボタン) で、それらが win32 デーモン スレッドであると言及されている場合。ご想像のとおり、eventmachine スレッドです。StackInit_ffirubyeventmachine.so

win32/daemonシンプルな app.rb または rackup ファイルを作成して、アプリを実行します。同じ問題が発生しますか?

Thin の代わりにWebrick、Puma、または Mongrel でアプリを実行します。同じ問題が発生しますか?

を実行することで、開発キットにあるスタックプロセスをさらにgdb調べることができます。msys.bat

$ gdb -p <PID>
(gdb) thread 1
(gdb) where
#0  0x62e905f9 in setproctitle () from c:\dply\lang\ruby-1.9.3-p551-i386-mingw32\bin\msvcrt-ruby191.dll
#1  0x62e936d1 in select@20 () from c:\dply\lang\ruby-1.9.3-p551-i386-mingw32\bin\msvcrt-ruby191.dll
#2  0x673c8524 in _SelectDataSelect (v=0x22fa38) at ../../../../ext/em.cpp:810
#3  0x62e846b8 in rb_thread_blocking_region () from c:\dply\lang\ruby-1.9.3-p551-i386-mingw32\bin\msvcrt-ruby191.dll
#4  0x673c9307 in _Select (this=0x28d2bc8) at ../../../../ext/em.cpp:822
#5  EventMachine_t::_RunSelectOnce (this=0x28d2bc8) at ../../../../ext/em.cpp:898
#6  0x673ca6c4 in _RunOnce (this=<optimized out>) at ../../../../ext/em.cpp:503
#7  EventMachine_t::Run (this=0x28d2bc8) at ../../../../ext/em.cpp:485
#8  0x673cf4f0 in evma_run_machine () at ../../../../ext/cmain.cpp:88
#9  0x673d3297 in t_run_machine_without_threads (self=40004496) at ../../../../ext/rubymain.cpp:223
#10 0x62e7874a in rb_vm_call () from c:\dply\lang\ruby-1.9.3-p551-i386-mingw32\bin\msvcrt-ruby191.dll
#11 0x62e6e7d8 in rb_vm_localjump_error () from c:\dply\lang\ruby-1.9.3-p551-i386-mingw32\bin\msvcrt-ruby191.dll
#12 0x62e72eb1 in rb_vm_localjump_error () from c:\dply\lang\ruby-1.9.3-p551-i386-mingw32\bin\msvcrt-ruby191.dll
#13 0x62e798ab in rb_iseq_eval_main () from c:\dply\lang\ruby-1.9.3-p551-i386-mingw32\bin\msvcrt-ruby191.dll
#14 0x62d47919 in rb_check_frozen () from c:\dply\lang\ruby-1.9.3-p551-i386-mingw32\bin\msvcrt-ruby191.dll
#15 0x62d49e50 in ruby_run_node () from c:\dply\lang\ruby-1.9.3-p551-i386-mingw32\bin\msvcrt-ruby191.dll
#16 0x0040136f in main (argc=4, argv=0x572f80) at ../ruby_1_9/main.c:38
(gdb) thread 2 
(gdb) where...

数回実行すると、同じ場所でスタックしているスレッド (スリープしているだけではない) を特定できるはずです。CPU 使用率が 100% のスレッドがある場合は、検索する必要はなく、スレッドが何をしているかを特定するだけです。


サービスの制御

アプリケーションは、状態に関係なく、Windows が行うサービス要求に常に応答できる必要があります。応答できない場合、サービス マネージャーは、経験したような状態になります。これは、Windows サービスやその他のサービスを管理する際によくある落とし穴です。

  • マルチスレッド プログラム これは、通常のワーカー スレッドの横にあり、サービス リクエストのみを処理するナニースレッドを使用して実現できます。それでも、うまく死なないロックされたスレッドになる可能性があります (特に Java)。

  • シングルスレッドプログラムでは、乳母プロセスを一番上に置くことができ、(危険な) 作業は子プロセスで行われます。

Ruby/MRI の場合、この 2 つの間のどこかにあります。win32/daemonサービス制御リクエストを処理する新しい Ruby スレッドを作成します。この「スレッド」は、前に扱った実際のスレッドとは関係がないことに注意してください。Ruby スレッドはすべて 1 つの MRI プロセスで実行されます。Ruby には Global Interpreter Lock (GIL) があり、Ruby ランドで一度に 1 つのスレッドだけが実行されるようにします。スレッドは、通常の操作で可能な場合に実行を相互にハンドオフしますが、スレッドがスタックしてservice_stopメソッドを開始しません。この問題はおそらく、GIL ルールが最も曲がっているネイティブ コード スレッドを実行する gem の 1 つで発生しています (Eventmachine、Thin パーサー、win32/daemonまたはその他の依存関係が取り込まれている可能性が高い)。

次にサービスの停止を試みて失敗した場合は、Process Explorerを使用して、サービスの下でruby.exe右クリックするか、強調表示されているときに kbd> を使用してサービス プロセスを強制終了できます。そのたびにサービスを再作成する必要はありません。ruby.exeKill ProcessDelruby.exe

懸念事項の分離のアイデアに戻ると、乳母とワーカーを個別の Ruby プロセスに分離することは、乳母をできるだけシンプルに保ち、制御できるようにするための最も確実な方法の 1 つです。このアプリケーションの問題を修正すれば、サービスの問題は解消されるかもしれませんが、プロセスをロックする次の問題では、同じサービスの問題が発生します。ナニー プロセスとワーカー プロセスを分離する場合、それらを分離しておくためのオペレーティング システム バリアがあります。欠点は、子プロセスの生成と、その後または何らかの形式の IPC 間のシグナル処理に対処する必要があるため、システム全体が Windows でより複雑になることです。また、ぶらぶらしているはぐれた子供たちを慎重に扱う必要があります。この設定では、次のようなプロセス ツリーになります。

- services.exe
  - ruby.exe daemon.rb - win32/daemon
    - ruby.exe app.rb - thin/sinatra

そのため、乳母が死亡する可能性がありますが、子供はまだ無人で走り回っています。


他のもの

Sinatra アプリはスレッド モードで Thin を起動します。Thin を直接実行すると、デフォルトでイベント モデルになります。シナトラは数年前からそうだけど、私はThin on Thinを信頼する傾向がある。スレッド化を無効にしてみてください:

set :threaded, false


ループインservice_mainは不要です。SinatrasApp.run!はループを実装します。run!アプリに を指示するまで、 は戻りませんquit

 class DemoDaemon < Daemon
  def service_main
    File.open(Logfile, "a"){ |f| f.puts "#{Time.now} - Service is starting" }
    Object.const_get("GG_Web_#{C_NAME}").run! port: PORT, server: 'thin'
  end


App.quit!Sinatra にクリーンアップを依頼するために 使用できますexit

  def service_stop
    File.open(Logfile, "a"){ |f| f.puts "#{Time.now} - Service stopping" }
    Object.const_get("GG_Web_#{C_NAME}").quit!
    File.open(Logfile, "a"){ |f| f.puts "#{Time.now} - Service stopped" }
  end
end

rubyw.exeコンソール ($stdout/$stderr) を削除するサービスにも使用できますが、重要でwin32/daemonはありません。

于 2014-11-28T15:10:02.490 に答える