今天之前的章节我们介绍了如何通过dapr发起一个服务调用,相信看过前几章的小伙伴已经对dapr有一个基本的了解了,今天我们来聊一聊dapr的另外一个功能——订阅发布 目录: 二、通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解 三、通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr 附录:(如果你觉得对你有用,请给个star) 二、通讯框架地址:https://github.com/sd797994/Oxygen-Dapr 惯例我们还是再老生常谈一下什么是订阅发布,订阅发布是根据设计模式之观察者模式发展出来的一种软件系统设计思想,它的核心是指“多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新”。假设一个系统有ABC三个模块其中BC依赖于A,当A进行改变后需要A主动调用BC进行相应改变,而观察者模式则将A的控制权剥离,A改变之后只是发送一个事件给消息总线“我改变了”,BC通过预先订阅该主题而获取到A的状态变化,再进行自身的状态变更。 强耦合调用模式 通过消息中间件订阅发布模式 聪明的同学应该发现了,通过订阅发布其实我们是将以往强耦合的ABC通过巧妙的控制权转移的方式进行了解耦,由之前的BC依赖于A改为了BC依赖于消息组件,通过消息组件接受到A的消息后进行分发,这样设计系统的目的当然有好有坏,好处是这会大大提高A的吞吐量,假设以往操作ABC总耗时300ms平均单个操作耗时100ms,通过解耦后A耗时100ms后就可以马上返回线程。那坏处是什么呢,由于对BC进行了解耦往往状态的一致性就得不到保障了。当ABC处于同一个粗粒度的原子操作里(比如数据库事务操作、比如lock锁),我们很容易控制ABC的强一致性,ABC有一个操作失败可以很轻松的进行回滚而不用影响我们持久化设备的数据一致。另外由于需要依赖第三方中间件,整个系统的健壮性是会有一定影响的。另外还需要考虑订阅方消费失败、异常后如何处理。关于这部分内容这里我们就不展开了,如果大家确实感兴趣,推荐大家看看国内开源作者@杨晓东写的.netcore分布式一致性解决方案CAP,地址:https://github.com/dotnetcore/cap OK,老生常谈的部分唠完,咱今天就来唠唠在Dapr中如何实现订阅发布的。通过上面的部分大家应该知道如果要实现一个进程间的订阅发布系统,我们需要准备很多东西,其中一个是事件总线,事件总线的作用是让事件发布者可以通过该模块进行事件发布。第二个需要选型一个消息组件,第三需要订阅器对订阅特定类型组件的技术支持。由于每一种组件其协议和接口实现方式都不同,在传统的订阅发布设计中我们往往需要对某种特定类型的消息组件在基础设施层进行相应的SDK集成,通过为业务层提供事件总线接口和订阅接口来进行技术解耦。就算做到了业务系统中不耦合技术实现,往往我们也很难替换消息组件。而Dapr在这方面为开发者集成了相当一部分的主流订阅发布组件(通过该支持列表可预览支持)。同时在业务层面是完全基于http+谓词的形式来实现订阅发布的。这就大大降低了引入SDK产生的成本。 订阅发布的API如下: POST http://localhost:<daprPort>/v1.0/publish/<pubsubname>/<topic>[?<metadata>] GET http://localhost:<appPort>/dapr/subscribe 稍微说一下这两个接口的含义,第一个接口告诉sidecar,我们将会调用某个已申明的类型为pubsub的component发送我们的事件到特定的topic 第二个接口是告诉sidecar我们当前这个服务会订阅哪些topic,我们提供的订阅器入口地址是什么,我们只接受哪一个component发布的数据 也就是说和服务调用一样,我们只需要一个weapi+httpclient就可以实现一个订阅发布模式而,其他的一切都交给dapr好了。 现在我们来看看如何实现它,首先我们还是需要选一个特定的订阅发布组件,这里我就选择redis,通过redis5.0新增的stream来做订阅发布。搭建redis这里不赘述,搭建好之后,我们需要创建一个dapr的特定CRD Component来申明引用该组件,其申明yaml文件如下 apiVersion: dapr.io/- 其中的type是由Dapr预定义的,可以参考这里,不要随意改变。metadata是对组件的一组描述包括其地址、账号密码等等,由于是演示这里我就直接使用k8s创建了一个无密码的redis pod并暴露其6379端口到svc。当我们创建好并通过kubectl apply -f x.yaml后,可以在dashboard的Componsents页面观察是否已经创建成功 基础设施准备完毕,接下来就是打开我们的项目看看如何实现订阅发布了。首先我们还是要把上一章的解决方案打开,上一章不是通过client发起一个对service的调用吗,今天我们反着来演示,我们在client创建一个订阅器,当clientsample调用servicesample时让servicesample发布一个事件,由该订阅器订阅并打印出文本到控制台。 首先我们在clientsample创建一个订阅器,订阅器类必须继承ieventhandle接口,其订阅方法体必须添加EventHandlerFunc注解并申明需要订阅的主题(topic),订阅器接收参数必须以EventHandleRequest<T>的方式接收,否则反序列化器可能会收不到请求。最后ack必须以DefaultEventHandlerResponse.Default的方式返回,否则事件总线会认为本次订阅器消费失败,会重复推送。 需要在我们的hostbuilder里注册这个对象到ioc容器: 接着我们在RPC接口项目创建事件Data HelloEventData,其中之包含一个演示用的words字段(图略)。 接下来我们在上一章的HelloServiceImpl中注入一个事件总线,并发送事件: 一切就绪,我们重新按照上一章的内容打包并部署,然后开启postman和控制台日志观看结果,可以看到我们的clientsample发起一个服务调用后,我们的serversample回调并发送了事件,而clientsample成功订阅到了该事件并消费掉了。 今天的分享到此为止,demo只是对dapr订阅发布最基础功能的演示,真正到生产环境还需要客服诸多问题来提高系统健壮,系统建设是一个长期持续的过程。欢迎大家评论区留言讨论~ 下期我们将讲一下dapr里如何做状态管理以及actor模型 |
|