发布-订阅模式

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

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

  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实现 LeetCode 374 猜数字大小 II

    375. 猜数字大小 II 我们正在玩一个猜数游戏,游戏规则如下: 我从 1 到 n 之间选择一个数字,你来猜我选了哪个数字. 每次你猜错了,我都会告诉你,我选的数字比你的大了或者小了. 然而,当你猜 ...

  2. java实现洛谷P1308统计单词数

    题目描述 一般的文本编辑器都有查找单词的功能,该功能可以快速定位特定单词在文章中的位置,有的还能统计出特定单词在文章中出现的次数. 现在,请你编程实现这一功能,具体要求是:给定一个单词,请你输出它在给 ...

  3. Java实现第十届蓝桥杯外卖店优先级

    试题 G: 外卖店优先级 时间限制: 1.0s 内存限制: 512.0MB 本题总分:20 分 [问题描述] "饱了么"外卖系统中维护着 N 家外卖店,编号 1 ∼ N.每家外卖店 ...

  4. [leetcode] 股票问题

    参考文章: [1] 团灭 LeetCode 股票买卖问题 [2] Most consistent ways of dealing with the series of stock problems 其 ...

  5. java之单点登录(SSO)

    单点登录(SSO):SSO是指在多个应用系统中个,用户只需要登陆一次就可以访问所有相互信任的应用系统.它包括可以将这次主要的登录映射到其他应用中用于同一用户的登陆的机制. SSO的实现过程: 通过上述 ...

  6. 使用百度地图时,Application类的onCreate执行两次的解决方案

    应用做的匆忙,很多地方只顾实现功能,没有兼顾好性能,所以停下来重构代码优化性能,结果在打log看启动时间的时候,发现Application的onCreate执行了多次,这样导致重复初始化资源,初始化了 ...

  7. 处理npm安装模块报错01

    报错:Error: EACCES: permission denied, mkdir '/usr/local/lib/node_modules/nodemon_tmp' 解决:sudo cnpm in ...

  8. 第一次前端实习机试题 很粗糙的一个天气预报demo

    <!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8 ...

  9. Jmeter基础001----jmeter的安装与配置

    一.Java环境安装 1.下载jdk----oracal官网   2.JDK版本要求:                     JMeter2.x- jdk1.6                   ...

  10. JVM面试题总结

    1.介绍下 Java 内存区域(运行时数据区) Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域. JDK 1.8之前主要分为:堆.方法区.虚拟机栈.本地方法栈 ...