发布-订阅模式

发布-订阅模式,顾名思义,就像大家订报纸一样,出版社发布不同类型的报纸杂志不同的读者根据不同的需求预定符合自己口味的的报纸杂志,付费之后由邮局安排人员统一派送.

上面一段话,提到了发布-订阅模式三个比较重要的点:

  1. 发布者:报社
  2. 订阅者:读者
  3. 调度中心:邮局

不难看出上述过程中出版社和读者完全没有任何接触,在他们没有感知到对方的情况下通过邮局完成了整个流程,邮局就是传说中的中介(Broker)

那么使用发布-订阅模式的有什么优点呢?这里就简单的说两点:松耦合,可拓展性,稍后通过例子进行讲解. 关于更深入的理解可以参考:

  1. Publish/Subscribe-MSDN
  2. Publish/Subscribe-Wikipedia

对这个模式有所了解之后,让我们再回到 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 的发送者

  1. 在类中发送者(sender) 为 self
class Model(object):
def save(self):
model_saved.send(self)
  1. 在函数中发送者(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_viaconnect 的区别,从上面的例子,我们不难看到, connect_via 多了一个参数,这个参数就是 sender,使用 connect 的订阅方式并不支持订阅指定的发布者,如果我们需要订阅指定的发布者需要使用 connect_via(sender)

最后

Signals 是个好东西,大家应该学会使用它.

参考

  1. 发布-订阅模式解释

  2. Publish/Subscribe-MSDN

  3. Publish/Subscribe-Wikipedia

  4. bliker

  5. Signals

从发布-订阅模式谈谈 Flask 的 Signals的更多相关文章

  1. 理解JavaScript设计模式与开发应用中发布-订阅模式的最终版代码

    最近拜读了曾探所著的<JavaScript设计模式与开发应用>一书,在读到发布-订阅模式一章时,作者不仅给出了基本模式的通用版本的发布-订阅模式的代码,最后还做出了扩展,给该模式增加了离线 ...

  2. 15天玩转redis —— 第九篇 发布/订阅模式

    本系列已经过半了,这一篇我们来看看redis好玩的发布订阅模式,其实在很多的MQ产品中都存在这样的一个模式,我们常听到的一个例子 就是邮件订阅的场景,什么意思呢,也就是说100个人订阅了你的博客,如果 ...

  3. 第五章 --- 关于Javascript 设计模式 之 发布-订阅模式

    先来个最简单的 发布订阅模式 document.body.addEventListener('click',function(){ alert(123); }); document.body.clic ...

  4. NetMQ(三): 发布订阅模式 Publisher-Subscriber

    ZeroMQ系列 之NetMQ 一:zeromq简介 二:NetMQ 请求响应模式 Request-Reply 三:NetMQ 发布订阅模式 Publisher-Subscriber 四:NetMQ ...

  5. C# 委托和事件 与 观察者模式(发布-订阅模式)讲解 by天命

    使用面向对象的思想 用c#控制台代码模拟猫抓老鼠 我们先来分析一下猫抓老鼠的过程 1.猫叫了 2.所有老鼠听到叫声,知道是哪只猫来了 3.老鼠们逃跑,边逃边喊:"xx猫来了,快跑啊!我是老鼠 ...

  6. 4.js模式-发布-订阅模式

    1. 发布-订阅模式 var observe = (function(){ var events = {}, listen, trigger, remmove; listen = function(k ...

  7. RabbitMQ/JAVA (发布/订阅模式)

    发布/订阅模式即生产者将消息发送给多个消费者. 下面介绍几个在发布/订阅模式中的关键概念-- 1. Exchanges (转发器) 可能原来我们都是基于一个队列发送和接收消息.现在介绍一下完整的消息传 ...

  8. Javascript中理解发布--订阅模式

    Javascript中理解发布--订阅模式 阅读目录 发布订阅模式介绍 如何实现发布--订阅模式? 发布---订阅模式的代码封装 如何取消订阅事件? 全局--发布订阅对象代码封装 理解模块间通信 回到 ...

  9. ActiveMQ发布订阅模式

    ActiveMQ的另一种模式就SUB/HUB即发布订阅模式,是SUB/hub就是一拖N的USB分线器的意思.意思就是一个来源分到N个出口.还是上节的例子,当一个订单产生后,后台N个系统需要联动,但有一 ...

随机推荐

  1. Java实现蓝桥杯二项式的系数规律

    二项式的系数规律,我国数学家很早就发现了. 如[图1.png],我国南宋数学家杨辉1261年所著的<详解九章算法>一书里就出现了. 其排列规律: 1 1 1 2 1 3 3 1 4 6 4 ...

  2. java实现公式解析

    在某些应用中,为了支持灵活性,往往用到自定义的公式. 比如,有如下的原始公式集合: int add(int x, int y): 返回x与y的和 int add(int x, int y, int z ...

  3. java实现排列为平方数

    ** 排列为平方数** 若干不同的数字,排列组合后能产生多少个平方数? 下面的代码解决了这个问题. 对于:1,6,9 排列后,可产生3个平方数: 169 196 961 请阅读下面的代码,填写缺失的部 ...

  4. iOS-Code Data的快速体验

    Code Data Core Data 是iOS SDK 里的一个很强大的框架,允许程序员以面向对象的方式储存和管理数据.使用Core Data框架,程序员可以很轻松有效地通过面向对象的接口管理数据 ...

  5. 95题--不同的二叉搜索树II(java、中等难度)

    题目描述:给定一个整数 n,生成所有由 1 ... n 为节点所组成的 二叉搜索树 . 示例如下: 分析:这一题需要对比LeetCode96题来分析:https://www.cnblogs.com/K ...

  6. boost常用库(一):boost数值转换

    在STL中有一些字符转换函数,例如atoi,itoa等,在boost里面只需用一个函数lexical_cast进行转换,lexical_cast是模板方法,使用时需要传入类型.只能是数值类型转字符串. ...

  7. Apache(httpd)详解

    httpd详解(思维导图) 1. httpd服务 ASF 服务器类型 http服务器 应用程序服务器 httpd的特性 高度模块化 DSO机制 MPM httpd的并发响应模型 prefork wor ...

  8. charles 破解方法

    1.https://www.charlesproxy.com/latest-release/download.do 官网下载charles 2.傻瓜式安装完成(路径可以默认c盘) 3.安装完成后去c盘 ...

  9. 通信中的错误代码 (repost from https://blog.csdn.net/zzhuan_1/article/details/80066716)

    • 100 - 继续.• 101 - 切换协议.• 110 重新启动标记答复.• 120 服务已就绪,在 nnn 分钟后开始.• 125 数据连接已打开,正在开始传输.• 150 文件状态正常,准备打 ...

  10. cc26a_demo-CppPrimer_动态绑定_多态-代码示范

    //多态性    //从派生类到基类的转换    //引用或者指针既可以指向基类对象,也可以指向派生类对象    //只有通过引用或者指针调用虚函数才会发生动态绑定.    //为什么定义虚的函数?可 ...