从发布-订阅模式谈谈 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 606 根据二叉树创建字符串(遍历树)
606. 根据二叉树创建字符串 你需要采用前序遍历的方式,将一个二叉树转换成一个由括号和整数组成的字符串. 空节点则用一对空括号 "()" 表示.而且你需要省略所有不影响字符串与原 ...
- 关于C#委托三种调用的分享
一.同步调用 1.同步调用会按照代码顺序来执行2.同步调用会阻塞线程,如果是要调用一项繁重的工作(如大量IO操作),可能会让程序停顿很长时间,造成糟糕的用户体验,这时候异步调用就很有必要了. 举个栗子 ...
- 性能测试中TPS上不去的原因
TPS(Transaction Per Second):每秒事务数,指服务器在单位时间内(秒)可以处理的事务数量,一般以request/second为单位. 压测中为什么TPS上不去的原因: .网络带 ...
- MMDVM中继板测试软件MMDVMCal
运行方法: 只支持windows 64位系统 32位下载:https://share.weiyun.com/52uHAO5 64位下载:https://share.weiyun.com/5IgdqvL ...
- 关于nginx的源码安装方式
Nginx(engine x)是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器, 也是一个 IMAP/POP3/SMTP 代理服务器.在高连接并发的情况下, ...
- QPS、TPS、并发用户数、吞吐量关系
1.QPS QPS Queries Per Second 是每秒查询率 ,是一台服务器每秒能够相应的查询次数,是对一个特定的查询服务器在规定时间内所处理流量多少的衡量标准, 即每秒的响应请求数,也即 ...
- CSAPP 5 - 优化程序性能
CSAPP 5 - 优化程序性能 1. 概述 首当其冲的,还是要编写出好的算法和数据结构,优化内部结构 其次才是编写出能让编译器 易优化的,高效的可执行代码.这点在特定的机器上可能有着特定的不同的优化 ...
- intput子系统
1.按键驱动程序的第一个版本:day07/04 //内核模块的基本要求 init.h module.h LICENSE struct cdev btn_cdev; b ...
- 如何让json_encode不转义斜杠
当服务器返回一些数据时需要返回一些地址,但是默认的json_code是会对 / 转义成 \/ 的处理... 解决办法: 1. 正则替换: echo str_replace("\\/" ...
- (转)Zookeeper全解析——Paxos作为灵魂
原计划在介绍完ZK Client之后就着手ZK Server的介绍,但是发现ZK Server所包含的内容实在太多,并不是简简单单一篇Blog就能搞定的.于是决定从基础搞起比较好. 那么ZK Serv ...