首页 > 其他分享 >flask-信号、flask-script

flask-信号、flask-script

时间:2023-04-07 22:23:21浏览次数:48  
标签:__ name script flask app django signals 信号

1.django、flask高并发部署

1.1 协程产生背景

由于Python有把大锁GIL,会将多个线程在同一时刻,只能有一个线程执行,变成'串行',所以一个多线程python进程,并不能充分使用多核CPU资源,所以对于Python进程,可采用多进程部署方式比较有利于充分利用多核的CPU资源,而uWSGI服务器就是这么一个东西,可以以多进程方式执行WSGI app,其工作模式为 1 master进程 + N worker进程+m个线程(N*m线程),主进程负责接收客户端请求,然后将请求转发给worker进程,因此最终是worker进程负责处理客户端请求,这样很方便的将WSGI app以多进程方式进行部署此方式是企业常用部署方式。

但是存在一个问题:客户端向uwsgi的一个master发起一个HTTP请求,master分发给不同的worker进程,worker进程拉起线程处理请求,进入web框架,直到该请求处理完,这个线程才能处理其他请求(现在还没有协程),所以wsgi app是同步的。假如一个请求处理花费时间比较久,客户端请求数量又比较多的情况下,所有的线程都会被占满,所以就处理不了更多的请求(最大连接数取决于进程N*线程M)
image
我们的处理方案:
1)增大N,即worker的数量:在增加进程的数量的时候,进程是要消耗内存的,并且如果进程数量太多的情况下(并且进程均处于活跃状态),进程间的切换会消耗系统资源的,所以N并不是越大越好。一般情况下,可能将进程数目设置为CPU数量的2倍

2)增大m,即worker的线程数量:线程创建也是有限度的,由于线程栈是要消耗内存的,线程数量太多也不行

于是我们想,一条线程能不能同时处理多个请求呢?可以使用IO多路复用的模型

于是,gevent登场了

gevent是用户态的'线程',也就是协程,单线程下实现并发

gevent的好处就是无需等待I/O,当发生I/O调用是,gevent会主动切换到另一gevent进行运行,这样可以充分利用CPU资源

同时gevent通过monkey patch(猴子补丁)替换掉了标准库中阻塞的模块。
gevent是用户态的'线程',也就是协程,单线程下实现并发

gevent的好处就是无需等待I/O,当发生I/O调用是,gevent会主动切换到另一gevent进行运行,这样可以充分利用CPU资源
根据以上操作可以显著提高并发。
详见:https://zhuanlan.zhihu.com/p/358163330

1.2 不能在框架中使用全局变量

一般在部署Django或者Flask时,我们为了利用多核优势,一般使用uwsgi部署,原理如下:
如果我们设定uwsgi进程数为3,那么操作系统是开启3个进程来运行python的web程序。
如果我们在web项目中使用全局变量,由于多进程间数据是隔离的,所以定义的全局变量,分别在3个进程中,每个进程修改的也只是自己局部的变量。
举个例子:我们统计访问量,全局有一个访问量a=0。第一次请求来时分配到了进程A,进程A拿到全局变量加1,此时a=1是进程A自己局部的。此时第二个请求B被分配到了进程B,拿到全局变量,此时全局的a是0,进程B拿到加1结果还是1。由于进程间数据是隔离的,每个进程修改的a也只是自己内的a而不是全局的。

2.信号(signal)

2.1 补充:信号量

信号量和信号无关。之前我们在执行任务的时候由于有gil锁的存在,若干个线程会依次执行。但是如果有了信号量,在信号量中传入数字,假如是5,相当于同时获得了5把锁,5条线程会优先执行,5个程序执行完毕之后其他线程会抢这5把锁继续执行:

from threading import Thread, Lock, Semaphore
import time
import random

sp = Semaphore(5)

class MyThread(Thread):
    def run(self):
        sp.acquire()
        print(self.name)
        time.sleep(random.randint(1, 3))
        sp.release()

for i in range(10):
    t = MyThread()
    t.start()

image

2.2 信号

Flask框架中的信号基于blinker(模块需要安装),其作用是让开发者在flask中定制一些用户行为,比如在请求进来的时候到指定的某个位置执行某个函数。
比如用户表新增一条记录,就记录以下日志。我们可以直接在orm语句下执行操作日志的代码,但是这样代码的侵入性太高,也就是如果我们要删除只能去每个函数中找到该行代码进行删除。而使用信号我们直接写一个函数,绑定内置信号即可,只要函数执行到指定位置就会执行(同样请求扩展也可以完成该需求):
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')

使用内置信号:
1.写一个函数(调用时系统会自动传入参数,所以要用可变长形参接收)
2 绑定内置信号
3.等待被触发

from flask import Flask,render_template,signals

app = Flask(__name__)

def test(*args,**kwargs):
    print(args)
    print(kwargs)
    print('test执行了')
# 2.绑定内置信号
signals.before_render_template.connect(test)

# 1.写一个函数
@app.route('/')
def hello():
    return render_template('index.html',name='max')

if __name__ == '__main__':
    app.run()

image

2.2 自定义信号

以上我们使用的都是内置信号,我们还可以自定义信号:
顺序:
1.定义出信号
2.写一个函数
3.绑定自定义的信号
4.触发信号的执行
代码实现:

from flask.signals import _signals
from flask import session, Flask, render_template

app = Flask(__name__)
app.secret_key = 'fhyrue6t78y34'
# 1.定义出信号
session_set = _signals.signal('session_set')

# 2.写一个函数
def index(*args, **kwargs):
    print('111')
    print(args)
    print(kwargs)
    print('session设置值了')

# 3.绑定自定义的信号
session_set.connect(index)
# session_set.send('max')
# 如果设置在函数外面项目一启动就会触发
@app.route('/')
def index1():
    session['name'] = 'max'
    #  4.触发信号的执行
    session_set.send('max')
    return 'index1页面'

if __name__ == '__main__':
    app.run()

访问根目录就可以触发index函数执行:
image

3.django信号

3.1 内置信号

观察者模式,又叫发布-订阅模式,类似于单例模式是23种设计模式之一。当发生一些动作的时候,发出信号,然后监听了这个信号的函数就会执行。通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者。用于在框架执行操作时解耦(代码尽量精简,分成多个方法写,也就是代码侵入性低)。
django内置信号:

Model signals
    pre_init                    # django的model执行其构造方法前,自动触发
    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       # 请求异常后,自动触发
Database Wrappers
    connection_created          # 创建数据库连接时,自动触发

3.2 使用信号

方式一:
将以下内容放到utils或一个包的__init__.py中:

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.models.signals import pre_save
from django.dispatch import receiver
@receiver(pre_save)
def my_callback(sender, **kwargs):
    print("对象创建成功")
    print(sender)
    print(kwargs)

3.3 django自定义信号

1.定义信号(一般创建一个py文件)(toppings,size 是接受的参数)

import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

2.注册信号

def callback(sender, **kwargs):
    print("callback")
    print(sender,kwargs)
pizza_done.connect(callback)

3.触发信号

from 路径 import pizza_done
pizza_done.send(sender='seven',toppings=123, size=456)

4.flask-script

在django中,启动项目可以使用命令:

python manage.py runserver

我们想把flask做成像django一样用命令启动:
1.安装:

pip3.10 install flask-script
#  注意:flask需要使用2.2.2版本,flask_script需要使用2.0.3版本

2.修改代码:

'''当前文件名修改为manage.py'''
from flask import Flask,render_template
from flask_script import Manager

app = Flask(__name__)
manager = Manager(app)

@app.route('/')
def hello():
    return render_template('index.html',name='max')

if __name__ == '__main__':
    # 需要改成Manager对象名点run()
    manager.run()

首先我们执行命令的路径需要在当前文件的目录下,其次我们的文件名为什么,就需要执行 python 文件名.py runserver,文件名为manage111那么执行命令就为:

python manage111.py runserver

image
了解了命令的前半段,我们现在需要了解runserver的由来以及定义:
首先我们需要自定义命令需要使用@manager.command装饰器(manager是类Manager的对象,命名成任何名字都可以,代替这里的manager就可以),在装饰器下写一个函数,函数名就是替代runserver的关键字,并且该函数可以接受一个参数,该参数就是在命令行函数名后面传入的:
1.制作一个简单的命令:

from flask import Flask
from flask_script import Manager

app = Flask(__name__)
manager = Manager(app)

@manager.command
def func(arg):
    print(arg)

@app.route('/')
def index():
    return 'indexxxx'

if __name__ == '__main__':
    manager.run()

我们的函数名为maxmax.py,这时我们的命令就变成了:

python maxmax.py func max

并且这时func函数的参数args就是我们在命令行输入的max:
image
此后我们可以根据命令进行一些操作。
2.复杂命令:

from flask import Flask
from flask_script import Manager

app = Flask(__name__)
manager = Manager(app)

@manager.option('-n','--name',dest='name')
@manager.option('-u','--url',dest='url')
def test(name,url):
    print(name,url)

if __name__ == '__main__':
    manager.run()

此后我们可以通过

python maxmax.py test -n max -u xxx

或者

python maxmax.py test --name max --url xxx

的方式来触发:
image

标签:__,name,script,flask,app,django,signals,信号
From: https://www.cnblogs.com/ERROR404Notfound/p/17297129.html

相关文章

  • Flask框架之信号、sqlalchemy
    目录信号flask-scriptsqlalchemysqlalchemy介绍sqlalchemy快速使用sqlalchemy创建表和操作数据信号Flask框架中的信号基于blinker(安装这个模块pipinstallblinker),其主要就是让开发者可是在flask请求过程中定制一些用户行为,flask和django都有信号观察者模式,又叫发布-订阅(Pu......
  • flask05
    1信号#Flask框架中的信号基于blinker(安装这个模块),其只要就是让开发者可以在flask请求过程中定制一些用户星为flask和django都有#观察者模式:又叫发布-订阅(Publish/Subscribe)23种设计模式之一pipinstallblinker#信号:signial翻译过来的,并发编程种学过信号量Semap......
  • flask-day5——python项目高并发异步部署、uwsgi启动python的web项目不要使用全局变量
    目录一、python项目高并发异步部署二、uwsgi启动Python的Web项目中不要使用全局变量三、信号3.1flask信号3.2django信号四、微服务的概念五、flask-script六、sqlalchemy快速使用七、sqlalchemy快速使用4.1原生操作的快速使用八、创建操作数据表九、作业1、什么是猴子补丁,有什......
  • flask-script
    flask-script我们启动flask项目可以通过执行程序app.run()来启动,而flask_script可以帮助我们通过脚本命令的形式启动项目。不过要注意,flask_script是第三方模块,flask兼容版本需要注意,可以按照以下的版本对应:Flask==2.2.2Flask_Script==2.0.3基本使用##manage.py中fromfla......
  • flask信号
    flask信号什么是信号?项目功能复杂,代码量越大,就越需要做业务解耦,我们在程序的某些环节发出信号,我们如果给这个信号绑定了方法,那么就会触发方法的执行。flask框架的信号基于blinker模块,在框架的一些核心功能做扩展时,我们可以只让其执行一句发出信号的代码,扩展的功能写在别处。......
  • [Javascript] Improve performance of Array.reduce
    Comparetwocodesnippetconstpeople=[{id:1,name:'John',age:45},{id:2,name:"Op",age:32},{id:3,name:"Wade",age:39}]//option1constres=people.reduce((acc,curr)=>{return({......
  • django/flask高并发部署
    django和flask是同步框架,部署的时候使用uwsgi部署,uwsgi是多进程多线程框架,并发量不高大概几十。我们可以通过uwsgi加gevent部署成异步程序,普通的部署方式uwsgi-x./luffyapi.xml这是使用genvent提高并发部署uwsgi--gevent50--gevent-monkey-patch./luffyapi.xml......
  • flask源码解析
    flask源码解析本篇主要是针对于以下一些问题进行源码剖析,并补充解释一些python语法的用法与应用场景。flask生命周期流程flask的request、session等都是导入进来的,也就意味着每次请求,我们所用的都是同一个request对象,它为什么能够按照同种方式取到自己request对象值呢flask......
  • flask5
    今日内容1信号#Flask框架中的信号基于blinker(安装这个模块),其主要就是让开发者可是在flask请求过程中定制一些用户行为flask和django都有#观察者模式,又叫发布-订阅(Publish/Subscribe)23种设计模式之一pip3.8installblinker#信号:signial翻译过来的,并发编程中学过......
  • flask框架04 导出项目 local flask生命执行流程 wtforms
    今日内容详细目录今日内容详细1请求上下文分析(源码:request原理)1.1导出项目的依赖1.2函数和方法1.3threading.local对象1.4偏函数1.5flask整个生命执行流程(1.1.4版本为例)2wtforms(了解)1请求上下文分析(源码:request原理)1.1导出项目的依赖#之前pipfreeze>requ......