基于Http协议订阅发布系统设计
--物联网系统架构设计
1,订阅发布(subscriber-publisher)
订阅发布模式最典型的应用场景就是消息系统的设计。在消息系统的架构中,消息的发送者称作(publisher),消息的接收者称作(subscriber),参见wikipedia: Publish–subscribe pattern。整个消息系统的架构可以用如下图1来描述:
图1
由图1可知消息系统主要包括3个组件: 发布者,订阅者和消息代理(Broker),而整个消息系统的核心即是Broker,而目前就业务能力而言Broker的实现难点主要在于它的吞吐量。拿手机消息推送举例,在当前的移动互联时代,就我们很常见的大多数app用户数基本都是百万级别以上(流行app基本是千万级别),这意味着Broker至少要能支持百万台设备的订阅,使用单台服务器做Broker显然不能解决问题。而在物联网时代,订阅者将不再只有手机,订阅者可以是任何电子设备,这种场景的级别将是手机数量的百倍。
2,Mqtt协议的发布订阅系统实现方案
2.1,Mqtt协议
根据官方的定义,mqtt协议即是
machine-to-machine (M2M)的连接协议,该协议就是为发布订阅模式设计的非常轻量的消息传输协议。具体参见:http://mqtt.org/
从mqtt协议定义可知,该mqtt就是为发布订阅系统而设计,并且非常轻量。
2.2,实现方案
实现一套完整的发布订阅系统,主要就是两个组件(client和broker)一个协议规范(mqtt)。
2.3, 架构设计
发布订阅的服务系统架构非常简单,基本都遵照图1的基本架构模式。对于一个家庭的物联网应用,如果设备仅想要在局域网内访问,则broker只需要安装在(基于NanoPi或RasPi开发的)小型的设备中或者直接集成到路由器中。当然对于真正的物联网应用,我们还是希望设备可以通过互联网就可以管理和控制,所以很多broker实际应当在互联网服务器中。
2.4, Mqtt协议的订阅发布系统交互原理
首先引用一下开源项目paho提供的python版客户端执行订阅和发布动作的demo,代码非常简短
#susbscriber
import paho.mqtt.client as mqtt
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, rc):
client.subscribe("$SYS/#")
# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
print(msg.topic+" "+str(msg.payload))
client = mqtt.Client()
client.on_connect = on_connect
client.on_message = on_message
client.connect("iot.eclipse.org", 1883, 60)
# Blocking call that processes network traffic
client.loop_forever()
Subscriber: 从订阅者客户端代码可知,订阅者只需做2个动作(连接broker和建立循环等待的长连接)和提供2个接口函数(订阅请求函数和处理broker响应结果的函数)。基本要素无非请求连接、订阅指定topic消息、和处理响应结果,但loop_forever()是一个无限循环,这意味着客户端和borker之间保持着一个socket长连接,所以从这里可以认识到broker的瓶颈之一便是能处理多少个这样的长连接。
#publisher
import paho.mqtt.client as mqtt
client = mqtt.Client()
client.connect("iot.eclipse.org")
client.loop_start()
res = mqttc.publish("$SYS/#", "HELLO")
client. loop_stop(force=False)
Publisher: 从发布者客户端代码可知,发布者操作比订阅者更加简单,基本要素无非是建立连接、向broker发布指定topic消息,忽略结果响应处理过程。
subscriber和publisher的交互逻辑本质是基于tcp协议的socket实现,对于server端的socket打开mqtt协议端口,并开启一个异步线程来持续监听端口,等待client端(subscriber和publisher )的socket发出mqtt请求,client端的subscriber的mqtt请求有些不一样,那就是subscriber的socket实际和server一直保持长连接,随时等待server那边推送过来的消息,直到连接关闭 。所以抛开细节处理问题,完全可以使用netty框架,基于mqtt协议很快的开发出一套server和client端的应用。
3,http协议broker设计实现
图2 订阅发布系统Broker设计
http协议和mqtt协议比较:
优点:http在互联网时代得到最广泛的应用, 充分检验了它的有效性和稳定性,充分的社区支持和成熟的开源资源可用
缺陷:相对mqtt协议太重,对网络要求更高,直接基于http1.x无法实现发布订阅(http1.x是单工协议,需要依赖websocket、servlet3.0等技术实现双工,也可以使用http2.0,但目前支持较少)
本文是使用servlet3.0的技术实现基于http协议的发布/订阅系统broker, 图2所示即为物联网broker系统设计架构。后台broker分成两大模块:发布中心(用户和设备)和订阅中心(用户和设备),以及事件总线。这样的设计或许会有疑惑,为什么不直接抽象成事件的发布和订阅中心,如此不久和mqtt broker一致了么? 的确,既然是使用http协议实现,那为什么要完全仿照mqtt协议的模式呢,而且我们要设计的实际是一个“物联网的业务系统“而不是一个“中间件“,所以如果你换了一个业务场景,你又得重新设计系统,而恰巧基于http协议servlet应用正是为业务系统提供了丰富的开源资源。
下面详细解释用户发布中心和订阅中心的设计,因为在物联网的应用场景中,主要业务交互逻辑是围绕用户和设备之间做publish和subscribe.
用户发布中心(publisher):
在物联网场景中用户充当了核心业务的publisher,对于broker的发布中心,接收到所有的前端用户请求过来的数据都将被封装成event在broker的内部系统中由发布中心广播到订阅中心。以摩拜单车为例,app是publisher的终端,摩拜单车的核心业务逻辑就是开锁指令和一系列的交易逻辑。就开锁动作而言,发布中心收到开锁event,在publish这个event之前,针对这个event不同业务场景或许有不同的业务需求,典型场景有:该事件是否需要群发、该事件是否需要定时功能,该事件是否需要可靠发布。特别的,对于事件的可靠发布,在交易类系统中属于必备要求。拿摩拜单车来说,开锁指令发出后就会开始计时计费和扣钱,这时候就需要依赖broker在应用层面对数据做事务保证,而不能依赖基础系统服务的稳定。
订阅中心(subscriber):
对于订阅中心(无论是用户或设备)的设计,完全遵照table或key-value的数据结构来设计,也即是对于每一个请求,broker都将为其关联一个handler以及和其对应id标识。当事件被发布到订阅中心,订阅中心的processor便会用事件ID(或唯一标识的设备ID)去查询对应的handler,并作结果响应。由于是基于http协议,所以在具体实现时需要依赖servlet3.0或websoket技术。
4,领域建模
4.1 发布中心领域建模
发布中的核心功能是发布事件,因此Event是发布中心的核心领域对象。在图2中已经阐明,事件发布所需要实现的基本功能要素,Event设计也就主要是达到第3部分所描述功能。
图3 发布中心领域模型抽象
在图3中可知,AbstractEvent即是Event的顶层的Entity抽象设计。因为发布中心可能会发布多种不同类型的Event,所以AbstractEvent必须有EventType属性来表述事件的类型。无论是那种类型的Event实际都是一个Entity,既然是Entity就意味着有自己的ID,EventId作为event的唯一标识符,需要有一个明确的说明的是EventId表示意义实际相当于topic,这就是说不是每发布一个Event就会生成一个新的EventId。例如在摩拜单车的应用中,就开锁这一类事件,对于每一辆单车,都有对应一个唯一的EventId。对于之前第3部分提到的关于事件需要实现周期、延时以及可靠发布,AbstractEvent定义了cronExpression和deliveryStatus属性,其中cron表达式可以非常简洁的描述和实现周期和延时的事件设定, 而deliveryStatus则需要使用状态来保证分布式网络环境下事件动作的事务。此外,定义GroupEvent是为了解决第3部分中提到的发布一个事件,响应多台设备。
4.2 订阅中心领域建模
订阅中心领域核心抽象是Handler,每一个handler对应为一个订阅http request。每一个订阅请求handler都持有其希望响应的EventId,携带的业务数据以及结果响应的回调方法。订阅者期望的是当EventId标识的event发生时,可以立刻收到对应的事件响应,也即是说订阅http request是作为一个保持长时间等待的网络连接。因此所有的handler应当有一个holder将其缓存起来管理,这就是单例模式的HandlerHolder存在的意义。对于HandlerHolder在对handler缓存策略可以有两种选择:1, 以table形式缓存;2,以map形式缓存。相较两种缓存策略各有优缺点,table形式节约存储但查找代价高(可有序存放提高速度,实际在使用Java HashMap或ConcurrentHashMap实现的保持10^5个连接时,并不会多消耗太多内存,相对于List也仅仅是多几十M的内存而已,因为map中的空桶实际存储量极小),map形式查找快但耗存储,但无论那种形式缓存都可以通过分级缓存来提高缓存能力(例如一级缓存简要数据在系统内存,二级缓存主要数据在redis等缓存系统)。
在图4中的状态图描述了(用户和设备)订阅中心以event驱动的handler转移流程。初始时刻,设备发起订阅CommandEvent请求,等待发布中心收到用户发过来的CommandEvent请求,此时发布中心会去判断该事件是否需要记录事件交付状态,如需要得到设备响应的OKEvent,则会去订阅中心生成对应handler。此时,响应给设备的handler将携带deliveryStatus=Waiting 标识,等待设备返回确认结果。随后,设备返回的确认响应即可通过发布中心发布OKEvent响应用户处理结果。(实际处理流程应当更复杂,因为没有考虑异常情况,如设备没有收到响应结果、备响应结果丢失等,这些都需要做一些补偿策略)
图4 订阅中心领域建模抽象
- 通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布
之前的章节我们介绍了如何通过dapr发起一个服务调用,相信看过前几章的小伙伴已经对dapr有一个基本的了解了,今天我们来聊一聊dapr的另外一个功能--订阅发布 目录:一.通过Dapr实现一个简单的基 ...
- 基于Redis消息的订阅发布应用场景
目录 基于Redis消息的订阅发布应用场景 1.应用背景 2.困境 2.1 锁表风险 2.2 实时性差 2.3 增加编程复杂性 2.4 实时效果 3.解决方案 3.1 前端传值给服务端 3.2 服务端 ...
- RabbitMQ下的生产消费者模式与订阅发布模式
所谓模式,就是在某种场景下,一类问题及其解决方案的总结归纳.生产消费者模式与订阅发布模式是使用消息中间件时常用的两种模式,用于功能解耦和分布式系统间的消息通信,以下面两种场景为例: 数据接入 假设 ...
- 基于web的图书管理系统设计与实现
原文链接:基于web的图书管理系统设计与实现 系统演示链接:点击这里查看演示 01 系统简述 图书管理系统就是利用计算机,结合互联网对图书进行结构化.自动化管理的一种软件,来提高对图书的管理效 ...
- 基于web的图书管理系统设计与实现(附演示地址)
欢迎访问博主个人网站,记得收藏哦,点击查看 - - - >>>> 公众号推荐:计算机类毕业设计系统源码,IT技术文章分享,游戏源码,网页模板 小程序推荐:网站资源快速收录--百 ...
- 基于Z-WAVE 协议的LED智能照明系统的研究笔记
LED调光基础: ☆:LED照明调光控制信号的方式有两种: 1. 通过PWM信号控制LED灯具开关电源的占空比从而实现调光: 2. 通过调光控制信号和交流电源供电线合用的两线式或三线式(例如LED相控 ...
- 基于XMPP协议的手机多方多端即时通讯方案
一.开发背景 1.国际背景 随着Internet技术的高速发展,即时通信已经成为一种广泛使用的通信方式.1996年Mirabilis公司推出了世界上第一个即时通信系统ICQ,不到10年间,即时通信(I ...
- Kcptun 是一个非常简单和快速的,基于KCP 协议的UDP 隧道,它可以将TCP 流转换为KCP+UDP 流
本博客曾经发布了通过 Finalspeed 加速 Shadowsocks 的教程,大家普遍反映能达到一个非常不错的速度.Finalspeed 虽好,就是内存占用稍高,不适合服务器内存本来就小的用户:而 ...
- 【转】 基于TFTP协议的远程升级设计
版权声明:本文为博主原创文章,未经博主允许不得转载.联系邮箱:zhzhchang@126.com 说明:由于CSDN博客编辑器对word格式近乎不支持,因此对表格使用了图片方式(最后一个表格未使用图片 ...
随机推荐
- Principal Components Regression, Pt.1: The Standard Method
In this note, we discuss principal components regression and some of the issues with it: The need fo ...
- A function to help graphical model checks of lm and ANOVA(转)
As always a more colourful version of this post is available on rpubs. Even if LM are very simple mo ...
- 如何编写Hexo主题
完成一个Hexo的主题其实很简单,和写静态页面差不多,只是内容部分通过Hexo的变量去获取,而且Hexo还内置了一些辅助函数帮你快速方便地完成繁琐的处理. 起步 在写代码之前要先把项目结构搭建好,一个 ...
- 抓包工具 - HttpWatch
HttpWatch是功能强大的网页数据分析工具,集成在IE工具栏,主要功能有网页摘要.cookies管理.缓存管理.消息头发送/接收,字符查询.POST数据.目录管理功能和报告输出.HttpWatch ...
- loadrunner学习理论之一
1.负载测试.压力测试的区别? 答:负载测试是在被测系统所承受的正常范围内进行的 压力测试可以在极端的条件下进行 2.loadrunner的三大组件是什么,有什么作用? 答:虚拟用户生成器(virtu ...
- 如何判断img标签是否有src属性
前几天,写一个小项目,需要判断img标签是否有src属性,想了半天,只能想到用jq实现,如下: if($(".img").attr("src")==undefi ...
- Hibernate入门(一)
一 Hibernate介绍 Hibernate 是一个开源.轻量级的ORM(对象关系映射)工具,该工具简化了数据创建.数据处理和数据访问,它是一种将对象映射到数据库中表的编程技术.ORM工具内部使用J ...
- WPF MVVM 架构 Step By Step(4)(添加bindings - 完全去掉后台代码)
之前的改进已经挺棒的,但是我们现在知道了后台代码的问题,那是否可能把后台代码全部去除呢?这时候就该WPF binding 和 commands 来做的事情了. WPF就是以超吊的binding,com ...
- vue2.0 配置 选项 属性 方法 事件 ——速查
全局配置 silent 设置日志与警告 optionMergeStrategies 合并策略 devtools 配置是否允许vue-devtools errorHandler 错误 ...
- vue组件(Vue+webpack项目实战系列之三)
组件(Component)是 Vue.js 最强大的功能之一.组件可以扩展 HTML 元素,封装可重用的代码.特别对于大型应用开发来说,尽量组件化,并且先造好轮子库,不要重复去写组件,这会显著提升项目 ...