Python Tornado在请求之间更新共享数据

9 投票
2 回答
4332 浏览
提问于 2025-04-18 15:33

我有一个用Python Tornado写的应用程序。这个应用里有一些请求处理器,我需要给它们传递一些数据,比如下面的代码(这段代码不完整,只是为了说明我的意思):

configs = {'some_data': 1, # etc.
          }

class Application(tornado.web.Application):
    def __init__(self):
        handlers = [('/pageone', PageOneHandler, configs),
                    ('/pagetwo', PageTwoHandler, configs)]
        settings = dict(template_path='/templates',
                    static_path='/static', debug=False)
        tornado.web.Application.__init__(self, handlers, **settings)

# Run the instance
# ... code goes here ...
application = Application()
http_server = tornado.httpserver.HTTPServer(application)
# ... other code (bind to port, etc.)

# Callback function to update configs
some_time_period = 1000 # Once an second
tornado.ioloop.PeriodicCallback(update_configs, some_time_period).start()
tornado.ioloop.IOLoop.instance().start()

我希望update_configs这个函数能更新上面定义的configs变量,并且这个变化能在处理器中生效。例如(我知道这样写是不对的):

def update_configs():
    configs['some_data'] += 1

# Now, assuming PageOneHandler just prints out configs['some_data'], I'd expect
# the output to be: "1" on the first load, "2" if I load the page a second
# later, "4" if I load the page two seconds after that, etc.

问题是,configs变量是在创建Application类的时候传给处理器的构造函数的。我该如何在定期回调函数中更新configs['some_data']呢?

我实际想用这个机制的目的是定期从数据库中刷新存储在configs字典里的数据。

有没有简单的方法可以做到这一点,而不用去折腾application.handlers(我已经试了一个多小时了)?

2 个回答

6

除了dano提到的策略之外,还有一种方法是把共享的数据放到应用程序对象里。

class MyApplication(tornado.web.Application):

    def __init__(self):

       self.shared_attribute = foo;

        handlers = [#your handlers here]        
        settings = dict(#your application settings here)
        super().__init__(handlers, **settings)


server = tornado.httpserver.HTTPServer(MyApplication())
server.listen(8888)
tornado.ioloop.IOLoop.instance().start()

接下来,你可以在所有的请求处理器中通过 self.application.shared_attribute 来访问上面定义的 shared_attribute

你只需要在一个地方更新这个值,它会立刻在你后续的请求处理器调用中反映出来。

7

最简单的方法就是把整个配置字典传给处理器,而不是只传字典里的单个值。因为字典是可变的,你对字典中值的任何修改都会影响到所有的处理器:

import tornado.web
import tornado.httpserver

configs = {'some_data': 1, # etc.
          }

def update_configs():
    print("updating")
    configs['some_data'] += 1

class PageOneHandler(tornado.web.RequestHandler):
    def initialize(self, configs):
        self.configs = configs
    def get(self):
        self.write(str(self.configs) + "\n")


class PageTwoHandler(tornado.web.RequestHandler):
    def initialize(self, configs):
        self.configs = configs

    def get(self):
        self.write(str(self.configs) + "\n")


class Application(tornado.web.Application):
    def __init__(self):
        handlers = [('/pageone', PageOneHandler, {'configs' : configs}),
                ('/pagetwo', PageTwoHandler, {'configs': configs})]
        settings = dict(template_path='/templates',
                    static_path='/static', debug=False)
        tornado.web.Application.__init__(self, handlers, **settings)

# Run the instance
application = Application()
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)

# Callback function to update configs
some_time_period = 1000 # Once an second
tornado.ioloop.PeriodicCallback(update_configs, some_time_period).start()
tornado.ioloop.IOLoop.instance().start()

输出:

dan@dantop:~> curl localhost:8888/pageone
{'some_data': 2}
dan@dantop:~> curl localhost:8888/pageone
{'some_data': 3}
dan@dantop:~> curl localhost:8888/pagetwo
{'some_data': 4}
dan@dantop:~> curl localhost:8888/pageone
{'some_data': 4}

在我看来,这种方法最合理;configs里面的数据其实并不属于某一个RequestHandler的实例,它是所有RequestHandler和你的PeriodicCallback共享的全局状态。所以,我觉得没必要去创建多个这个状态的副本,然后手动保持这些不同副本之间的同步。相反,直接通过一个带有类变量的自定义对象或者字典来在整个程序中共享这个状态,就像上面展示的那样。

撰写回答