代码之家  ›  专栏  ›  技术社区  ›  Mark Wubben

自定义中间件中发生意外异常后停止类重新加载

  •  10
  • Mark Wubben  · 技术社区  · 16 年前

    我已经编写了自己的中间件来为我们的应用程序提供API端点。中间件加载提供API方法的类,并将请求路由到适当的类/方法。类通过 String#constantize .

    在开发模式下运行时,会自动重新加载类。但是,如果有一个意外异常,随后由故障保护中间件处理,则自动重新加载将停止工作。 constantize 仍在调用,但它似乎返回了旧类。

    似乎还有其他东西卸载类,而一个意外的异常会破坏它。这可能是什么?

    运行Ruby1.8.7、Rails2.3.3和Thin1.2.2。

    2 回复  |  直到 16 年前
        1
  •  1
  •   Serge Balyuk    13 年前

    我认为这种影响来自于 ActionController::Reloader 是书面的。这是 ActionController::Reloader#call 从2.3.3中,注意注释:

    def call(env)
      Dispatcher.reload_application
      status, headers, body = @app.call(env)
      # We do not want to call 'cleanup_application' in an ensure block
      # because the returned Rack response body may lazily generate its data. This
      # is for example the case if one calls
      #
      #   render :text => lambda { ... code here which refers to application models ... }
      #
      # in an ActionController.
      #
      # Instead, we will want to cleanup the application code after the request is
      # completely finished. So we wrap the body in a BodyWrapper class so that
      # when the Rack handler calls #close during the end of the request, we get to
      # run our cleanup code.
      [status, headers, BodyWrapper.new(body)]
    end
    

    Dispatcher.reload_application 不移除自动加载的常量, Dispatcher.cleanup_application 做。 BodyWrapper#close 是在考虑到可能的例外情况下编写的:

    def close
      @body.close if @body.respond_to?(:close)
    ensure
      Dispatcher.cleanup_application
    end
    

    但是这没有帮助,因为如果 @app.call 在里面 ActionController::重载器#调用 抛出异常, BodyWrapper 没有被实例化,并且 Dispatcher.cleanup_应用程序 不会接到电话。

    想象一下下面的场景:

    • 我更改了一个文件,这会影响API调用
    • 我点击了API调用,看到了错误,此时所有的文件包括有bug的文件都没有被卸载
    • 我做了一个代码修复并命中 相同的 API调用来检查它是否有效
    • 调用以与以前相同的方式路由到旧类/对象/模块。这将抛出相同的错误,并再次将加载的常量保留在内存中

    当传统控制器引发错误时,不会发生这种情况,因为这些错误由 ActionController::Rescue . 这种例外情况不会发生 ActionController::重载程序 .

    最简单的解决方案是将fallback rescue子句放入API路由中间件中,这有一些变化:

    def call(env)
      # route API call
    resuce Exception
      Dispatcher.cleanup_application
      raise
    end
    

    注意,这是我对3年前的问题的回答,我遵循了2.3.3的调用堆栈。较新版本的rails可能会处理不同的事情。

        2
  •  0
  •   Community Mohan Dere    9 年前

    Rails会缓存很多类,并在开发模式下或config.cache_classes设置为true时卸载并重新加载它们。这里有一些关于这个主题的想法,也解释了它是如何工作的。 http://www.spacevatican.org/2008/9/28/required-or-not/

    并不是说你做错了,但是重载字符串constantize似乎是重新加载代码的一种老套方法。您是否考虑过使用类似watchr的工具来运行开发中的应用服务器,并在将文件保存到API子树中时重新启动它? https://github.com/mynyml/watchr/

    另外,对于如何进一步调试的一些随机想法,请查看以下答案: https://stackoverflow.com/a/7907289/632022