一次tornado升级导致的bug,排查了好久,比较坑

2017-09-11  本文已影响0人  潇湘浅唱

之前一直稳定运行的代码,我引进新项目的时候,忽然报错

'Application' object has no attribute 'handlers'"

困扰许久。而且是时而正常,时而出错。

无法忍受,后开启debug模式排查,发现原来是tornado4.5起升级了,引入了新的tornado.routing模块。

4.4的Application代码如下:

class Application(httputil.HTTPServerConnectionDelegate):

"""A collection of request handlers that make up a web application.

``static_handler_class`` setting.

"""

def __init__(self, handlers=None, default_host="", transforms=None,

**settings):

if transforms is None:

self.transforms = []

if settings.get("compress_response") or settings.get("gzip"):

self.transforms.append(GZipContentEncoding)

else:

self.transforms = transforms

self.handlers = []

self.named_handlers = {}

self.default_host = default_host

self.settings = settings

self.ui_modules = {'linkify': _linkify,

'xsrf_form_html': _xsrf_form_html,

'Template': TemplateModule,

}

self.ui_methods = {}

self._load_ui_modules(settings.get("ui_modules", {}))

self._load_ui_methods(settings.get("ui_methods", {}))

if self.settings.get("static_path"):

path = self.settings["static_path"]

handlers = list(handlers or [])

static_url_prefix = settings.get("static_url_prefix",

"/static/")

static_handler_class = settings.get("static_handler_class",

StaticFileHandler)

static_handler_args = settings.get("static_handler_args", {})

static_handler_args['path'] = path

for pattern in [re.escape(static_url_prefix) + r"(.*)",

r"/(favicon\.ico)", r"/(robots\.txt)"]:

handlers.insert(0, (pattern, static_handler_class,

static_handler_args))

if handlers:

self.add_handlers(".*$", handlers)

if self.settings.get('debug'):

self.settings.setdefault('autoreload', True)

self.settings.setdefault('compiled_template_cache', False)

self.settings.setdefault('static_hash_cache', False)

self.settings.setdefault('serve_traceback', True)

# Automatically reload modified modules

if self.settings.get('autoreload'):

from tornado import autoreload

autoreload.start()

到4.5代码已经变成:

class Application(ReversibleRouter):

"""A collection of request handlers that make up a web application.

.. versionchanged:: 4.5

Integration with the new `tornado.routing` module.

"""

def __init__(self, handlers=None, default_host=None, transforms=None,

**settings):

if transforms is None:

self.transforms = []

if settings.get("compress_response") or settings.get("gzip"):

self.transforms.append(GZipContentEncoding)

else:

self.transforms = transforms

self.default_host = default_host

self.settings = settings

self.ui_modules = {'linkify': _linkify,

'xsrf_form_html': _xsrf_form_html,

'Template': TemplateModule,

}

self.ui_methods = {}

self._load_ui_modules(settings.get("ui_modules", {}))

self._load_ui_methods(settings.get("ui_methods", {}))

if self.settings.get("static_path"):

path = self.settings["static_path"]

handlers = list(handlers or [])

static_url_prefix = settings.get("static_url_prefix",

"/static/")

static_handler_class = settings.get("static_handler_class",

StaticFileHandler)

static_handler_args = settings.get("static_handler_args", {})

static_handler_args['path'] = path

for pattern in [re.escape(static_url_prefix) + r"(.*)",

r"/(favicon\.ico)", r"/(robots\.txt)"]:

handlers.insert(0, (pattern, static_handler_class,

static_handler_args))

if self.settings.get('debug'):

self.settings.setdefault('autoreload', True)

self.settings.setdefault('compiled_template_cache', False)

self.settings.setdefault('static_hash_cache', False)

self.settings.setdefault('serve_traceback', True)

self.wildcard_router = _ApplicationRouter(self, handlers)

self.default_router = _ApplicationRouter(self, [

Rule(AnyMatches(), self.wildcard_router)

])

# Automatically reload modified modules

if self.settings.get('autoreload'):

from tornado import autoreload

autoreload.start()

很容易的看到,Application继承的父类都变了。

新版本的父类ReversibleRouter正是新的tornado.routing模块里面定义的。

我们的项目代码如下:

class Application(object):

def __init__(self):

from tornado.options import options

self._options = options

self._settings = settings

self._beforecallback = None

self._shutdown_callback = []

self._app = None

def call_shutdown_callback(self):

for callback in self._shutdown_callback:

callback()

def init_settings(self):

from config import FILE_UPLOAD_PATH

import tornado.options

tornado.options.parse_command_line()

self._settings['static_path'] = FILE_UPLOAD_PATH

self._settings['static_url_prefix'] = '/upload/'

self._settings["debug"] = self._options.debug

self._settings['module'] = self._options.module

if not self._settings['module']:

print("the module parameter is required.")

exit(0)

else:

context['module'] = self._settings['module']

if self._settings["debug"]:

self._settings["autoreload"] = True

self.install_autoreload_hook()

if not self._options.debug:

args = sys.argv

args.append("--log_file_prefix=%s" % settings['logfile'])

tornado.options.parse_command_line(args)

@property

def options(self):

return self._options

@property

def handlers(self):

from urls import handlers

return handlers

@property

def settings(self):

return self._settings

def get_app(self):

self._beforecallback(self)

self.init_settings()

self.install_event_hooks()

self.install_middleware()

self.install_rbac()

self.install_message_backend()

from tornado.web import Application

return Application(self.handlers, **self._settings)

本来计划对着新的代码修改下我们的代码, 适配新的版本,不过发现变化比较大。比如这个Route里面的URLSpec也从tornado.web移动到tornado.routing里面了,代码整体变动太大,所以还是去修改服务器的tornado版本吧!

class Route(object):

urls = []

def __call__(self, url, name=None):

def _(cls):

if url.startswith("/"):

_url = r"%s" % url

else:

_url = r"/api/%s/%s" % (API_VERSION, url)

self.urls.append(tornado.web.URLSpec(_url, cls, name=name))

return cls

return _

结论:

虽然问题不是代码层面解决的,但是也是感慨万千。还有就是感觉运气有点爆棚,上周在服务器一pip install tornado装的4.4, 隔了一周在服务器二pip install tornado就装到4.5了,而且关键变化还挺大,真是坑啊!

上一篇下一篇

猜你喜欢

热点阅读