使用Flask的Werkzeug和类状态:当类没有重新初始化时,类成员变量如何重置?

Iva*_*nna 6 python werkzeug multiple-processes flask

我正在尝试编写一个烧瓶扩展,需要在请求之间保留一些信息.当我使用单个进程运行Werkzeug时这很好用,但是当我运行多个进程时,我得到了一些我不理解的奇怪行为.以这个简单的应用程序为例:

from flask import Flask
app = Flask(__name__)

class Counter(object):
    def __init__(self, app):
        print('initializing a Counter object')
        self.app = app
        self.value = 0

    def increment(self):
        self.value += 1
        print('Just incremented, current value is ', self.value)

counter = Counter(app)

@app.route('/')
def index():
    for i in range(4):
        counter.increment()
    return 'index'

if __name__ == '__main__':
    #scenario 1 - single process
    #app.run()
    #scenario 2 - threaded
    #app.run(threaded=True)
    #scenario 3 - two processes
    app.run(processes=2)
Run Code Online (Sandbox Code Playgroud)

对于前两个场景,它的行为完全符合我的预期:Counter对象初始化一次,然后随着对'/'路径的每个请求递增.当我使用第三个场景运行它(传递进程= 2)然后我得到它作为输出:

 initializing a Counter object
  * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 Just incremented, current value is  1
 Just incremented, current value is  2
 Just incremented, current value is  3
 Just incremented, current value is  4
 127.0.0.1 - - [30/Aug/2015 09:47:25] "GET / HTTP/1.1" 200 -
 Just incremented, current value is  1
 Just incremented, current value is  2
 Just incremented, current value is  3
 Just incremented, current value is  4
 127.0.0.1 - - [30/Aug/2015 09:47:26] "GET / HTTP/1.1" 200 -
 Just incremented, current value is  1
 Just incremented, current value is  2
 Just incremented, current value is  3
 Just incremented, current value is  4
 127.0.0.1 - - [30/Aug/2015 09:47:27] "GET / HTTP/1.1" 200 -
Run Code Online (Sandbox Code Playgroud)

似乎counter.value在初始化之后立即返回到它的状态而没有实际重新初始化.有人可以了解Werkzeug在内部做些什么来实现这一目标吗?我也非常有兴趣学习是否有办法使这个行为像我天真期望的那样(两个进程,每个进程都有自己的Counter实例).谢谢!

dav*_*ism 4

第一个示例(单线程)仅使用 one Counter,因此它可以工作。

第二个示例(多个线程),生成线程来处理每个请求。Counter它们与在生成之前创建的内存共享内存,因此从每个增量中增加它们会产生相同的结果。

最后一个示例(多个进程),生成进程来处理每个请求。 Flask 的开发服务器使用fork:每个子进程看到相同的起始点(计数器已经初始化),但在自己的地址空间中递增,当请求结束时,该地址空间就会消失。

import os

class Counter:
    def __init__(self):
        print('init')
        self.value = 0

    def increment(self):
        self.value += 1
        print('inc -> {}'.format(self.value))

counter = Counter()

def multi():
    if not os.fork():
        # child starts with copy of parent memory
        for _ in range(3):
            # increments three times
            counter.increment()

        # child is done
        os._exit(0)

# three processes run
for _ in range(3):
    multi()
Run Code Online (Sandbox Code Playgroud)
import os

class Counter:
    def __init__(self):
        print('init')
        self.value = 0

    def increment(self):
        self.value += 1
        print('inc -> {}'.format(self.value))

counter = Counter()

def multi():
    if not os.fork():
        # child starts with copy of parent memory
        for _ in range(3):
            # increments three times
            counter.increment()

        # child is done
        os._exit(0)

# three processes run
for _ in range(3):
    multi()
Run Code Online (Sandbox Code Playgroud)

使用数据库或其他外部存储来存储跨进程的全局状态,使用before_after_request。请注意,这并不完全简单,因为您必须将每个请求的计数器递增值存储为线程安全的,以便两个线程不会同时覆盖该值。

req 1 starts, gets stored value = 4
req 2 starts, gets stored value = 4
req 1 increments, value = 8
req 1 saves, value = 8
req 2 increments, value = 8
req 2 saves, value = 8 but should = 12
Run Code Online (Sandbox Code Playgroud)