sinatra app作为服务停止工作,但它的服务状态显示为“正在运行”

时间:2014-11-17 10:54:04

标签: sinatra chef win32-service-gem

我有一个sinatra应用程序设置为使用win32-service gem作为服务运行。应用程序启动正常,但一段时间后它会中断。然而,根据服务状态,它仍在运行,但我似乎无法从外部或计算机本身访问它,修复它的唯一方法是停止它,删除它,重新安装服务并启动它

该应用程序通常是自我部署(由于构建管道,但仅在我推送更新时),由于厨师安装和运行,其设置方式是厨师服务器每隔5分钟运行所有菜谱我认为以某种方式打破了我的应用程序,但我不确定如何。

非常感谢任何见解。

P.S。我知道我没有发布任何代码但只是因为我不确定会有什么帮助,告诉我你需要看什么,我会发布它。

编辑:事实证明它与厨师无关:

感谢一些日志记录,我知道我的应用程序运行了大约57分钟,然后它停止响应5秒健康ping(由清漆完成),厨师运行每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 runningService stopped打印到日志文件中。

编辑: 我把它缩小了很多,我知道它不是:

  1. 厨师
  2. 源代码加载机制
  3. 执行主要操作的应用程序的一部分,即处理请求, 从数据库等加载数据(我有两个相同的应用程序 问题,无论造成什么都与任何相同的问题有关 两者)
  4. 我已经回滚到没有这个问题的早期版本的宝石,它仍然在做它。

    只剩下一件事(我能想到的),我用来创建应用程序主循环的win32-system gem以及它与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
    

    并且您已经看到了我用来将应用程序作为守护程序运行的代码。

    我真的需要帮助来解决这个问题,我完全没有想法,看看是什么导致了这个以及如何修复它。

    编辑: 好吧,我现在已经确认了一件事,我的代码中没有任何东西导致它。它仍然死了,但我在本地机器上运行的版本却没有。它在服务器上运行,导致它无响应。

    进一步编辑:

    原来这根本不是问题,我什么都没有

    编辑:

    好吧,我不能让它撒谎,所以我回去了,我设法让我的应用程序作为一个守护进程运行而不使用system32守护进程类并猜测是什么,它仍然悬挂到大约50分钟后(好吧)这次是45分钟),自从我开始没有服务宝石并且它仍悬挂时,我​​想知道它还能是什么。什么可能导致sinatra挂?

    此外,如果我ping localhost收到回复(0%丢失),但如果我使用curl则无法连接。

    编辑:

    系统地删除所有内容,直到它只是一个hello world应用程序,我可以肯定地说它与我的ruby代码完全无关。到此为止我是:

    1. 使用rackup作为一个进程启动应用程序(虽然我必须手动终止主线程,但应用程序仍在运行),而不是将代码包装在deamon中并运行它。
    2. 关闭记录
    3. 摆脱了指南针/ sass(认为可能会挂起,因为对于某些人而言,这种情况并不那么明显,而且我意识到我没有使用随附的任何功能,所以没有必要保留它)< / LI>
    4. 评论了所有模块,只是: get '/' do 'Hello World' end
    5. 毕竟它仍然死了,到目前为止它只是一个纯粹的sinatra'你好世界'应用程序;所以要么sinatra有问题(我怀疑其他人会遇到这个问题),或者很可能服务器上有一些东西导致它。

      服务器上有什么可能导致它我不知道。但至少我离这个工作还有一步之遥。

2 个答案:

答案 0 :(得分:2)

看起来你有两个问题。

  1. 应用程序无响应
  2. 缺乏对服务的控制
  3. 这两者可能与您建议的直接相关。以下是步骤,以便您可以逐位分离每个问题,以便确定问题所在。所以不是答案,但它可能会让你得到答案......


    应用程序无响应

    获取Process Explorer并查看ruby.exe进程正在执行的操作。右键点击ruby.exe,然后选择Properties...。 它使用100%CPU吗?是否有任何特殊的计数器让你觉得奇怪?

    Process Explorer中查看该过程的Threads标签。是否有任何TID被列为100%的运行?对于每个线程,如果Stack(右下角按钮)提到Init_ffi,则它们是win32-daemon线程。事件机器线程rubyeventmachine.so,你猜对了。

    通过创建简单的app.rb或rackup文件,在不使用win32/daemon的情况下运行您的应用。是否会出现同样的问题?

    使用Webrick,Puma或Mongrel而非Thin来运行您的应用。是否会出现同样的问题?

    您可以通过运行msys.bat来查看process further with gdb中卡住的dev kits

    $ 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...
    

    通过几次运行,您应该能够识别出卡在同一位置的任何线程(而不仅仅是睡眠)。如果你有100%cpu的线程,你不必搜索,只需确定线程正在做什么。


    控制服务

    您的应用程序必须始终能够响应Windows所提供的服务请求,无论其状态如何。如果它无法响应,那么您的服务经理最终会处于您所经历的状态。这是管理Windows服务或任何服务的相当常见的陷阱。

    • 多线程程序可以使用 nanny 线程实现 它位于您的正常工作线程旁边,只处理 服务请求。即便如此,你最终还是会得到锁定的线程 不会很好地死(尤其是Java)。

    • 单线程程序,您可以拥有一个保姆进程 顶级和(风险)工作是在儿童流程中完成的。

    在Ruby / MRI案例中,它位于两者之间。 win32/daemon创建一个新的Ruby线程来处理服务控制请求。请注意,此“线程”与我们之前处理的实际线程无关,Ruby线程都在一个MRI进程中运行。 Ruby有全局解释器锁(GIL),它确保在Ruby域中一次只运行一个线程。当线程在正常操作中执行时,线程会将执行交给彼此,但是您的线程会卡住并且永远不会启动service_stop方法。这个问题可能发生在一个运行本机代码线程的gem中,因为GIL规则最容易弯曲(很可能在Eventmachine,Thin解析器,win32/daemon或其他任何依赖项被拉入)。 / p>

    下次尝试停止服务但失败时,您可以通过右键点击服务下的ruby.exeruby.exe,使用Process Explorer来终止Kill Process服务流程或使用 Del kbd&gt;当ruby.exe突出显示时。您不应该每次都重新创建服务。

    回到分离关注点的想法,将保姆和工作者分离成单独的Ruby进程是保持保姆简单和尽可能控制的最可靠方法之一。您可能会修复此应用程序问题并且服务问题将消失,但锁定进程的下一个问题将具有相同的服务问题。如果你分开保姆和工人流程,他们就会有一个操作系统屏障来保持它们的分离。不利的一面是它使得整个系统在Windows上变得更加复杂,因为您必须在当时或某种形式的IPC之间处理spawning子进程和信号处理。你还需要谨慎对待那些留下来的流浪儿童。在此设置中,您最终会得到一个过程树,如:

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

    因此,保姆有可能死去,但你仍然有孩子在无人看管的情况下跑来跑去。


    其他事项

    Sinatra应用程序在线程模式下启动Thin。如果直接运行Thin,则默认为evented模型。 Sinatra已经这样了几年,但我倾向于信赖Thin on Thin的东西。尝试禁用线程:

    set :threaded, false
    


    service_main中的循环是不必要的。 Sinatras App.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会为您解决这个问题。

答案 1 :(得分:0)

正如您所提到的,在没有win32守护程序的情况下运行时,问题不会发生,请尝试使用任务计划程序而不是Windows服务运行它。