在一个python flask的应用开发中, 经常会遇到需要跨函数, 路由或者请求来共用数据或参数的情况, 笔者先提2个问题: 如果要在多个请求中共用一个信息,比如同一个用户多次post中的用户信息,应该怎么处理? 大型分布式网站如何确保用户取到一致的信息?
这和变量的作用域有关. 一般跨函数,路由或请求的信息共用考虑使用全局对象,在具体的flask业务中, 有g, session, request, current_app, 和 缓存如redis可以考虑. 他们的区别和使用分别是怎么样的呢? 一起来看下:
1. g
-
原理:
g
是 Flask 提供的一个全局对象,用于在处理单个请求的过程中存储和共享数据。它是临时的,数据只在当前请求的生命周期内存在,处理完请求后会被清除。 -
作用范围:只在当前请求中有效,不同的请求不会共享
g
中的数据。 -
典型用途:在请求过程中传递数据库连接、用户信息等。
-
示例:
from flask import Flask, g
app = Flask(__name__)
@app.before_request
def before_request():
# 设置临时数据,比如当前用户
g.user = "Alice"
@app.route('/')
def index():
# 访问 `g` 中的临时数据
return f"Hello, {g.user}!"
2. session
-
原理:
session
是一个字典,用于在客户端和服务器之间持久化存储用户的会话数据,比如登录状态、用户偏好等。它的数据存储在客户端的 Cookie 中,并由服务器进行签名和加密以防篡改。 -
作用范围:
session
在客户端和服务器之间共享,可以跨请求使用,直到会话结束或手动清除。 -
典型用途:管理用户登录状态、存储用户数据。
-
示例:
from flask import Flask, session, redirect, url_for, request
app = Flask(__name__)
app.secret_key = 'supersecretkey'
@app.route('/login', methods=['POST'])
def login():
# 登录成功后将用户信息存储到 session
session['user'] = request.form['username']
return redirect(url_for('index'))
@app.route('/')
def index():
if 'user' in session:
return f"欢迎回来, {session['user']}!"
return "您还未登录。"
@app.route('/logout')
def logout():
session.pop('user', None)
return redirect(url_for('index'))
3. request
-
原理:
request
对象表示客户端发来的 HTTP 请求,包含所有请求相关的数据,如请求方法、表单数据、查询参数、请求头等。 -
作用范围:
request
对象只在当前请求中有效,不同请求间的数据相互独立。 -
典型用途:获取和处理客户端发来的数据。
-
示例:
from flask import Flask, request
app = Flask(__name__)
@app.route('/submit', methods=['POST'])
def submit():
username = request.form.get('username')
return f"提交的用户名是: {username}"
4. current_app
-
原理:
current_app
是一个指向当前 Flask 应用实例的代理对象,用于在请求上下文中访问应用实例。它可以用来获取应用的配置、日志记录器等。 -
作用范围:只能在应用上下文中使用(例如请求处理过程中)。
-
典型用途:访问应用配置、记录日志。
-
示例:
from flask import Flask, current_app
app = Flask(__name__)
app.config['MY_SETTING'] = '设置值'
@app.route('/')
def index():
setting_value = current_app.config['MY_SETTING']
return f"应用配置值: {setting_value}"
5. _request_ctx_stack
-
原理:
_request_ctx_stack
是 Flask 内部使用的一个栈结构,用于管理请求上下文。每当一个请求到达时,Flask 会创建一个新的请求上下文,并将其推入_request_ctx_stack
,处理完请求后再将其弹出。这确保了请求相关的数据(如g
、request
)在不同的请求中是独立的。 -
作用范围:主要用于内部处理,不直接用于业务逻辑中。
-
典型用途:确保在多线程或异步环境中,不同的请求可以独立管理自己的上下文数据。
-
示例:这个对象通常不直接使用,主要是 Flask 用来处理上下文的管理机制。
总结
g
:用于当前请求的临时存储,生命周期仅限于单个请求。
session
:用于跨请求的用户数据存储,数据持久化在客户端 Cookie 中。
request
:表示当前的 HTTP 请求,包含所有请求相关的信息。
current_app
:指向当前 Flask 应用实例的代理,用于访问应用级别的信息。
_request_ctx_stack
:内部机制,用于管理请求上下文,确保每个请求都有独立的上下文环境。由于_request_ctx_stack 是flask内部使用, 我们转向讨论缓存/redis来作为第五种用法:
缓存
还有web应用中常用的cache机制如Redis, Memcached, 数据库等别忘了, 以使用较普遍的redis为例, 大型分布式网站常用的一个做法就是将session信息缓存与统一的redis集群,方便在负载均衡的情况下, 用户访问时能够取到一致的缓存信息避免分裂问题.
以Redis为例, Redis 是一个高性能的键值存储数据库,常用于缓存、会话管理、消息队列等场景。使用 Redis 可以帮助提升 Flask 应用的性能和响应速度,特别是当某些数据需要频繁访问,但更新较少的情况下。
在 Flask 中使用 Redis 的场景
-
跨请求共享数据:Flask 的
session
数据存储在客户端 Cookie 中,而如果你有需要跨请求共享但不适合放在客户端的数据(如临时统计数据、用户状态等),Redis 可以作为后端缓存存储。 -
函数级缓存:对于一些计算复杂、结果不经常变化的函数,可以将计算结果缓存到 Redis 中,避免重复计算。这种场景在 Flask 中特别常见,比如:数据库查询、API 调用结果缓存。
-
全局状态管理:如果应用中有一些全局状态需要在多个请求中保持一致,如访问统计、在线用户数等,Redis 可以作为一个高效的状态管理工具。
-
分布式会话管理:在多台服务器上运行 Flask 应用时,使用 Redis 可以实现会话共享。用户的会话数据存储在 Redis 中,所有的服务器实例都可以访问这些会话数据,从而实现分布式会话管理。
-
任务队列和消息队列:Flask 中配合 Celery 使用 Redis,可以实现异步任务队列,提升应用的响应速度。
如何在 Flask 中集成 Redis
以下是一个使用 Redis 缓存数据的示例:
示例:使用 Redis 缓存数据
from flask import Flask, request, jsonify
import redis
import time
app = Flask(__name__)
# 连接 Redis
cache = redis.StrictRedis(host='localhost', port=6379, db=0, decode_responses=True)
def cache_data(key, value, timeout=60):
"""
将数据缓存到 Redis 中
:param key: 缓存的键
:param value: 缓存的值
:param timeout: 缓存的过期时间(秒)
"""
cache.setex(key, timeout, value)
def get_cached_data(key):
"""
从 Redis 中获取缓存数据
:param key: 缓存的键
:return: 缓存的值,如果不存在则返回 None
"""
return cache.get(key)
@app.route('/data')
def get_data():
key = 'expensive_data'
# 先检查缓存
data = get_cached_data(key)
if data:
return jsonify({"source": "cache", "data": data})
# 模拟数据获取过程,例如复杂计算或数据库查询
data = f"Computed data at {time.time()}"
# 缓存结果
cache_data(key, data, timeout=30) # 缓存 30 秒
return jsonify({"source": "computed", "data": data})
if __name__ == '__main__':
app.run()
示例代码解释
-
连接 Redis:使用
redis.StrictRedis
连接到 Redis 服务器。 -
缓存数据:
cache_data
函数将数据存入 Redis,设置一个超时时间来自动清理过期数据。 -
获取缓存数据:
get_cached_data
函数尝试从 Redis 获取缓存数据,如果存在就直接返回。 -
数据获取逻辑:在 Flask 路由中,首先检查缓存是否命中,如果命中直接返回缓存数据,否则进行计算并将结果缓存。
回到开头提出的问题: 如果要在多个请求中共用一个信息,比如同一个用户多次post中的用户信息,应该怎么处理? 大型分布式网站如何确保用户取到一致的信息?
1. 同一个用户多次post中的用户信息,可以用session来处理
2. 大型分布式网站可以用redis来存储用户session信息确保分布式应用取到一致信息
关注生产力联盟, 为组织和个人提高生产力。欢迎关注留言交流.