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

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

'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了,而且关键变化还挺大,真是坑啊!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,076评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,658评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,732评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,493评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,591评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,598评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,601评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,348评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,797评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,114评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,278评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,953评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,585评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,202评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,180评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,139评论 2 352

推荐阅读更多精彩内容