10

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

在开发模式下运行时,类会自动重新加载。但是,如果存在未捕获的异常(随后由 Failsafe 中间件处理),则自动重新加载将停止工作。constantize仍在被调用,但它似乎返回了旧课程。

似乎还有其他东西可以卸载类,并且未捕获的异常会破坏它。这可能是什么?

运行 Ruby 1.8.7、Rails 2.3.3 和 Thin 1.2.2。

4

2 回答 2

1

我认为这种效果来自于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

然而这并没有帮助,因为 if @app.callinActionController::Reloader#call抛出异常,BodyWrapper不会被实例化,Dispatcher.cleanup_application也不会被调用。

想象以下场景:

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

当传统控制器引发错误时不会发生这种情况,因为这些错误是由ActionController::Rescue. 此类异常不命中ActionController::Reloader

最简单的解决方案是将后备救援子句放入 API 路由中间件,这是一些变体:

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

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

于 2012-08-22T13:52:16.237 回答
0

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

不要告诉你你做错了,但重载 String#constantize 似乎是一种重新加载代码的 hacky 方式。您是否考虑过使用 watchr 之类的东西在开发中运行您的应用服务器,并在您将文件保存在 API 子树中时重新启动它? https://github.com/mynyml/watchr/

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

于 2012-08-19T22:24:06.877 回答