从发布-订阅模式谈谈 Flask 的 Signals
发布-订阅模式
发布-订阅模式,顾名思义,就像大家订报纸一样,出版社发布不同类型的报纸杂志不同的读者根据不同的需求预定符合自己口味的的报纸杂志,付费之后由邮局安排人员统一派送.
上面一段话,提到了发布-订阅模式三个比较重要的点:
- 发布者:报社
- 订阅者:读者
- 调度中心:邮局
不难看出上述过程中出版社和读者完全没有任何接触,在他们没有感知到对方的情况下通过邮局完成了整个流程,邮局就是传说中的中介(Broker)
那么使用发布-订阅模式的有什么优点呢?这里就简单的说两点:松耦合,可拓展性,稍后通过例子进行讲解. 关于更深入的理解可以参考:
对这个模式有所了解之后,让我们再回到 Flask 的 Signals
Flask - Signals
说明
有了前面这个铺垫,不难意识到 Flask 的 Singals 其实就是我们上面说的发布-订阅模式的实现.官方文档对 Signals 的介绍过于简单,容易让初学者直接忽略过去,但是实际上这知识点十分重要,尤其在开发比较复杂的系统中,正确地使用 Singals 能够帮助我们实现系统的松耦合.
这种松耦合是通过某些行为被触发时,自动发送定义好的一种信号,与这个信号绑定的一些业务逻辑或行为,接收到这个信号后,会自动执行各自相应的业务逻辑。这些行为的产生者就是我们在发布订阅模式中发布者,通过调度中心,消息被转发到相应的订阅者,然后每个订阅者执行自己的逻辑,互不干扰.
就像我们在发布-订阅模式看到的那样,我们可以随时添加订阅者. 同样地,与该信号绑定的业务逻辑,可以是我们事先预定义好的,也可以是在后续开发中随需求变动新增上去的. 在基于 Signals 的机制下,系统会更加稳定和可扩展,也使得系统的业务逻辑更加清晰.
既然有这么多好处,那么该怎么使用呢?别着急,看一下几个例子.
例子
Signal 的创建
两行代码就可以创建 Singals
from blinker import signal
test= signal('test')
不过 Flask 文档中有另外一种写法
from blinker import Namespace
my_signals = Namespace()
model_saved = my_signals.signal('model-saved')
两者本质上是没有任何区别的,原因我们可以看一下 blinker 的源码
# https://github.com/jek/blinker/blob/master/blinker/base.py
signal = Namespace().signal
很显然从源码看两者基本上可以等价起来,前者只是帮助我们简化了一个步骤
Signal 的发送
signal 创建好了之后,接下来就是使用了,使用很简单通过调用 send() 函数.需要注意的是,官方文档给了一个建议:
Try to always pick a good sender. If you have a class that is emitting a signal, pass self as sender. If you are emitting a signal from a random function, you can pass current_app._get_current_object() as sender.
也就说明我们在实际使用过程中,最好将 send() 函数的第一个参数为 signal 的发送者
- 在类中发送者(sender) 为 self
class Model(object):
def save(self):
model_saved.send(self)
- 在函数中发送者(sender) 为 current_app._get_current_object()
def save():
model_saved.send(current_app._get_current_object())
发送完消息,消息需要有人看,自然需要订阅者了.
Signal 的订阅
订阅指定的 signal 可以通过使用 connect() 函数,当通过 send() 发送 signal 时,会自动触发这些订阅者,然后执行相应逻辑,从而完成相应的功能. 使用起来很简单,只需要给指定的函数加上一个 connect_via 或者 connect 的装饰器就可以了
# connect_via
from flask import Flask,current_app
app = Flask(__name__)
from blinker import Namespace
my_signals = Namespace()
test = my_signals.signal('test')
@test.connect_via(app)
def subscriber(sender,**kwargs):
print(f'Got a signal sent by {sender},{kwargs}')
@app.route('/')
def hello_world():
test.send(current_app._get_current_object(),data=3)
test.send('test')
return 'Hello, World!'
if __name__ == '__main__':
app.run()
让我们执行一下然后看一下结果:
* Serving Flask app "test" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Restarting with stat
* Debugger is active!
* Debugger PIN: 326-510-904
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Got a signal sent by <Flask 'test'>,{'data': 3}
127.0.0.1 - - [24/Jun/2019 15:07:31] "GET / HTTP/1.1" 200 -
似乎少了依次输出?别着急,我们修改一下这个例子,使用 connect
# connect
@test.connect
def subscriber(sender,**kwargs):
print(f'Got a signal sent by {sender},{kwargs}')
再次执行看一下结果
* Serving Flask app "test" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Restarting with stat
* Debugger is active!
* Debugger PIN: 326-510-904
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Got a signal sent by <Flask 'test'>,{'data': 3}
Got a signal sent by test,{}
127.0.0.1 - - [24/Jun/2019 15:10:43] "GET / HTTP/1.1" 200 -
connect_via 和 connect
从上面最后一次输出,不难发现输出两次了,为什么第一次的时候只输出了一次呢? 很显然这就是 connect_via 和 connect 的区别,从上面的例子,我们不难看到, connect_via 多了一个参数,这个参数就是 sender,使用 connect 的订阅方式并不支持订阅指定的发布者,如果我们需要订阅指定的发布者需要使用 connect_via(sender)
最后
Signals 是个好东西,大家应该学会使用它.
参考
从发布-订阅模式谈谈 Flask 的 Signals的更多相关文章
- 理解JavaScript设计模式与开发应用中发布-订阅模式的最终版代码
最近拜读了曾探所著的<JavaScript设计模式与开发应用>一书,在读到发布-订阅模式一章时,作者不仅给出了基本模式的通用版本的发布-订阅模式的代码,最后还做出了扩展,给该模式增加了离线 ...
- 15天玩转redis —— 第九篇 发布/订阅模式
本系列已经过半了,这一篇我们来看看redis好玩的发布订阅模式,其实在很多的MQ产品中都存在这样的一个模式,我们常听到的一个例子 就是邮件订阅的场景,什么意思呢,也就是说100个人订阅了你的博客,如果 ...
- 第五章 --- 关于Javascript 设计模式 之 发布-订阅模式
先来个最简单的 发布订阅模式 document.body.addEventListener('click',function(){ alert(123); }); document.body.clic ...
- NetMQ(三): 发布订阅模式 Publisher-Subscriber
ZeroMQ系列 之NetMQ 一:zeromq简介 二:NetMQ 请求响应模式 Request-Reply 三:NetMQ 发布订阅模式 Publisher-Subscriber 四:NetMQ ...
- C# 委托和事件 与 观察者模式(发布-订阅模式)讲解 by天命
使用面向对象的思想 用c#控制台代码模拟猫抓老鼠 我们先来分析一下猫抓老鼠的过程 1.猫叫了 2.所有老鼠听到叫声,知道是哪只猫来了 3.老鼠们逃跑,边逃边喊:"xx猫来了,快跑啊!我是老鼠 ...
- 4.js模式-发布-订阅模式
1. 发布-订阅模式 var observe = (function(){ var events = {}, listen, trigger, remmove; listen = function(k ...
- RabbitMQ/JAVA (发布/订阅模式)
发布/订阅模式即生产者将消息发送给多个消费者. 下面介绍几个在发布/订阅模式中的关键概念-- 1. Exchanges (转发器) 可能原来我们都是基于一个队列发送和接收消息.现在介绍一下完整的消息传 ...
- Javascript中理解发布--订阅模式
Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 如何实现发布--订阅模式? 发布---订阅模式的代码封装 如何取消订阅事件? 全局--发布订阅对象代码封装 理解模块间通信 回到 ...
- ActiveMQ发布订阅模式
ActiveMQ的另一种模式就SUB/HUB即发布订阅模式,是SUB/hub就是一拖N的USB分线器的意思.意思就是一个来源分到N个出口.还是上节的例子,当一个订单产生后,后台N个系统需要联动,但有一 ...
随机推荐
- Java实现 LeetCode 636 函数的独占时间(栈)
636. 函数的独占时间 给出一个非抢占单线程CPU的 n 个函数运行日志,找到函数的独占时间. 每个函数都有一个唯一的 Id,从 0 到 n-1,函数可能会递归调用或者被其他函数调用. 日志是具有以 ...
- Java实现 蓝桥杯VIP 算法训练 薪水计算
算法训练 薪水计算 时间限制:1.0s 内存限制:512.0MB 提交此题 问题描述 编写一个程序,计算员工的周薪.薪水的计算是以小时为单位,如果在一周的时间内,员工工作的时间不超过40 个小时,那么 ...
- Java实现 LeetCode 98 验证二叉搜索树
98. 验证二叉搜索树 给定一个二叉树,判断其是否是一个有效的二叉搜索树. 假设一个二叉搜索树具有如下特征: 节点的左子树只包含小于当前节点的数. 节点的右子树只包含大于当前节点的数. 所有左子树和右 ...
- java实现最大连续和问题
/* 10 5 -3 12 -31 15 22 -7 6 -8 -9 10 .... 暴力:O(n^3) 分治:[ mid ) 三种情况求最大 基线法: O(n) 2个数组: 从左到本位:出现的最大累 ...
- 【工作Vlog】Jmeter响应结果乱码解决方案
资料:https://blog.51cto.com/ydhome/1864340 方法一:使用后置控制器"Beanshell PostProcessor"(动态修改,灵活) 添加后 ...
- portapack发射GPS的信号实现GPS脱机模拟器
要注意portapack必须要购买带高精度晶振的版本,另外固件要刷gridRF版本,用官方的或者havoc的都不行. 固件在这下载: 链接: https://pan.baidu.com/s/16flB ...
- 08_提升方法_AdaBoost算法
今天是2020年2月24日星期一.一个又一个意外因素串连起2020这不平凡的一年,多么希望时间能够倒退.曾经觉得电视上科比的画面多么熟悉,现在全成了陌生和追忆. GitHub:https://gith ...
- Python学习之计算机基础
计算机基础: (1)计算机俗称电脑,是现代用于高速计算的电子计算器,可以进行数值计算也可以进行逻辑计算,还有存储记忆功能.是能够按照程序运行,自动,高速处理海量数据的现代化智能 电子设备. (2)物理 ...
- 来看看阿里架构师Java 代码打日志姿势!你也是这样写的吗
使用slf4j 使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一. 实现方式统一使用: Logback框架 打日志的正确方式 什么时候应该打日志 当你遇到问题的时候,只能通过debug功能 ...
- 链家网 + gevent
import gevent from gevent import monkey monkey.patch_all() from gevent.queue import Queue import tim ...