一、信号的使用
信号(Signal)就是两个独立的模块用来传递消息的方式,它有一个消息的发送者Sender,还有一个消息的订阅者Subscriber。信号的存在使得模块之间可以摆脱互相调用的模式,也就是解耦合。发送者无需知道谁会接收消息,接收者也可自由选择订阅何种消息。
简单来说,flask请求进来,某个点关联上一个函数,只要到了这个点这个函数就会执行。
flask有一些内置信号:
request_started = _signals.signal('request-started') # 请求到来前执行
request_finished = _signals.signal('request-finished') # 请求结束后执行
before_render_template = _signals.signal('before-render-template') # 模板渲染前执行
template_rendered = _signals.signal('template-rendered') # 模板渲染后执行
got_request_exception = _signals.signal('got-request-exception') # 请求执行出现异常时执行
request_tearing_down = _signals.signal('request-tearing-down') # 请求执行完毕后自动执行(无论成功与否)
appcontext_tearing_down = _signals.signal('appcontext-tearing-down')# 应用上下文执行完毕后自动执行(无论成功与否)
appcontext_pushed = _signals.signal('appcontext-pushed') # 应用上下文push时执行
appcontext_popped = _signals.signal('appcontext-popped') # 应用上下文pop时执行
message_flashed = _signals.signal('message-flashed') # 调用flask在其中添加数据时,自动触发
内置信号执行使用步骤:
# 模板渲染后信号执行
# 功能:home页面被渲染,记录一条日志
# 第一步:写一个函数
def template_test(*args,**kwargs):
print(args)
print(kwargs)
print('模板渲染完了')
# # 第二步:绑定上内置信号:template_rendered
template_rendered.connect(template_test)
# 第三步:触发信号执行(不需要咱们操作)
@app.route('/home')
def home():
return render_template('home.html')
if __name__ == '__main__':
app.run()
自定义信号步骤:
# 第一步:自定义一个信号
xxxxx = _signals.signal('xxxxx')
# 第二步:写一个函数
def xxx_test(*args, **kwargs):
print(args)
print(kwargs)
print('xxx信号触发了')
# 第三步:绑定上自定义的信号信号:xxxxx
xxxxx.connect(xxx_test)
# 第四步:触发信号执行(手动触发,在某个位置触发,比如视图函数中)
@app.route('/')
def index():
xxxxx.send() # 触发信号
return 'hello'
if __name__ == '__main__':
app.run()
django的内置信号:
Model signals
pre_init # django的modal执行其构造方法前,自动触发
post_init # django的modal执行其构造方法后,自动触发
pre_save # django的modal对象保存前,自动触发
post_save # django的modal对象保存后,自动触发
pre_delete # django的modal对象删除前,自动触发
post_delete # django的modal对象删除后,自动触发
m2m_changed # django的modal中使用m2m字段操作第三张表(add,remove,clear)前后,自动触发
class_prepared # 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发
Management signals
pre_migrate # 执行migrate命令前,自动触发
post_migrate # 执行migrate命令后,自动触发
Request/response signals
request_started # 请求到来前,自动触发
request_finished # 请求结束后,自动触发
got_request_exception # 请求异常后,自动触发
Test signals
setting_changed # 使用test测试修改配置文件时,自动触发
template_rendered # 使用test测试渲染模板时,自动触发
Database Wrappers
connection_created # 创建数据库连接时,自动触发
方式一(放到__init__里):
from django.db.models.signals import pre_save
import logging
def callBack(sender, **kwargs):
print(sender)
print(kwargs)
# 创建对象写日志
logging.basicConfig(level=logging.DEBUG)
# logging.error('%s创建了一个%s对象'%(sender._meta.db_table,kwargs.get('instance').title))
logging.debug('%s创建了一个%s对象'%(sender._meta.model_name,kwargs.get('instance').title))
pre_save.connect(callBack)
方式二(装饰器方式):
from django.db.model.signals import pre_save
from django.dispatch import receiver
@receiver(pre_save)
def my_callback(sender, **kwargs):
print("创建对象成功")
print(sender)
print(kwargs)
django自定义信号:

了解:
多线程并发安全的问题:多个线程同时操作同一个数据,出现错乱。
使用同步锁---->修改数据之前先拿到---->开始修改---->释放锁---->别人再拿
几个锁的概念:
线程锁(线程级)---->分布式锁(应用级别):即分布式系统中的锁。为了解决分布式系统中控制共享资源访问的问题。
悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。
同步锁(互斥锁),递归锁(可重入锁,递归锁可以acquire多次)
线程死锁:多个线程使用同一资源,资源还没有被释放,那么多个线程会阻塞在那里。
触发场景:多把(互斥/递归)锁并且在多个线程中交叉使用。
解决:整合成一把互斥锁或所有互斥锁改成一把递归锁
Event事件(Event锁)(一些线程需要等到其他线程执行完成之后才能执行,类似于发射信号)
Semaphore(信号量)可以理解为多把锁,同时允许多个线程来更改数据。
二、flask-script的使用
实现类似于这样的命令:python manage.py runserver
django自定义命令,比如python manage.py initdb xx.xsl article,只要执行这个命令,就向数据库的article表中写入xx.xls的数据。
安装:pip3 install flask-script
基本使用:
from flask import Flask
app = Flask(__name__)
# 第一步导入
from flask_script import Manager
# 第二步包装
manager = Manager(app)
@app.route('/')
def index():
return 'hello'
if __name__ == '__main__':
# app.run()
# 第三步使用
manager.run()
自定义命令使用:
from flask import Flask
app = Flask(__name__)
# 第一步导入
from flask_script import Manager
# 第二步包装
manager = Manager(app)
# 自定义命令
@manager.command
def custom(arg):
"""
自定义命令
python manage.py custom 123
:param arg:
:return:
"""
print(arg)
@manager.option('-n', '--name', dest='name')
@manager.option('-u', '--url', dest='url')
def cmd(name, url):
"""
自定义命令(-n也可以写成--name)
执行: python manage.py cmd -n lqz -u http://www.baidu.com
执行: python manage.py cmd --name lqz --url http://www.baidu.com
:param name:
:param url:
:return:
"""
print(name, url)
# 有什么用?
# 把excel的数据导入数据库,定制个命令,去执行
@app.route('/')
def index():
return 'hello'
if __name__ == '__main__':
# 第三步使用
manager.run()
三、flask请求上下文源码分析
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'hello'
if __name__ == '__main__':
app.run()
请求来了,会干什么事?
第一:会执行app(environ, start_response)---->对象(),触发类的 __call__方法
第二:app.__call__---->Flask类的方法.wsgi_app(environ, start_response)---->整个的请求生命周期
核心代码如下:
ctx = self.request_context(environ)
error = None
try:
try:
ctx.push()
response = self.full_dispatch_request()
except Exception as e:
error = e
response = self.handle_exception(e)
except: # noqa: B001
error = sys.exc_info()[1]
raise
return response(environ, start_response)
finally:
if self.should_ignore_error(error):
error = None
ctx.auto_pop(error)
第一行的ctx叫请求上下文,ctx = self.request_context(environ)---->RequestContext(self, environ)实例化得到了一个RequestContext类的对象---->__init__方法中
def __init__(self, app, environ, request=None, session=None):
self.app = app
if request is None:#创造出一个请求对象
request = app.request_class(environ)
self.request = request
self.flashes = None
self.session = session
总结:ctx对象中放了哪些东西?当次请求的request对象、app对象、flashes和session。
重点:flask的request、session对象是全局的,那它是怎么区分不同的请求的?
使用了python的Local对象(java threadlocal),多个线程可以同时读写这个变量,并且不会有并发安全的问题---->不同线程操作的是自己的数据---->大字典---->key值为线程id号
l=local()
# 线程1
l.name='cb'
# 线程2
l.name='blee'
总结:
1 flask-session
- redis、文件
- 两种方式
2 数据库连接池
3 wtforms
- 表单验证
- 页面渲染
4 信号
- 在源码中安插了很多切面,只要绑定了函数,就会执行函数,
- 内置信号
- 自定义信号
5 flask-script
- 可以使用命令,启动项目
6 自定义命令
- flask可以自定义
- django也可以
补充:
Web项目的全局变量
- 进程是资源分配的最小单位,线程是cpu调度(执行)的最小单位
- uwsgi启动---->操作系统启动4个进程运行(flask,django)
- uwsgi启动Python的Web项目中不要动态修改全局变量
uwsgi多进程启动Python的Web项目

示例:
写一个flask程序x1.py
from flask import Flask
import time
app = Flask(__name__)
count = 0
@app.route('/')
def hello_world():
global count
count+=1
return 'Hello World!'+str(count)
if __name__ == '__main__':
app.run(host='0.0.0.0')
编写uwsgi的配置文件uwsgi.ini
[uwsgi]
http = 0.0.0.0:5000
chdir = /root/
wsgi-file = /root/x1.py
processes = 1
threads = 2
buffer-size = 32768
master = true
pidfile = uwsgi.pid
daemonize = uwsgi.log
callable = app
使用一个进程启动uwsgi:uwsgi uwsgi.ini
压测:
import requests
from threading import Thread
import time
def task():
res=requests.get('http://127.0.0.1:5000/')
print(res.text)
if __name__ == '__main__':
for i in range(1000):
t=Thread(target=task)
t.start()
# time.sleep(0.9)
如果使用python x1.py(一个进程运行),不会有重复的数据
如果使用1个进程启动uwsgi,返回的数据中也不会有重复的数字;
但如果使用多个进程启动uwsgi,返回的数据中会有重复的数据,由于不同的进程中同一个变量不共享。
文章介绍了PythonFlask和Django框架中的信号机制,用于模块间解耦合。在Flask中,详细展示了如何使用内置信号如request_started和template_rendered,并解释了如何自定义信号。同样,文中也提及了Django的Model信号,如pre_save和post_delete。此外,文章讨论了Flask请求上下文的工作原理,以及全局变量如request和session的管理。最后,文章提到了Flask-Script的使用,包括创建自定义命令来实现数据库操作等功能。
326

被折叠的 条评论
为什么被折叠?



