前言

 前篇文章对Dapr的状态管理进行了解,本篇继续对 订阅/发布 构建块进行了解。

一、定义:

 发布订阅的概念来自于事件驱动架构(EDA)的设计思想,这是一种让程序(应用、服务)之间解耦的主要方式,通过发布订阅的思想也可以实现服务之间的异步调用。而大部分分布式应用都会依赖这样的发布订阅解耦模式。

 

 步骤:

  1. 发布服务器将消息发送到消息代理。
  2. 订阅服务器将绑定到消息代理上的订阅。
  3. 消息代理将消息的副本转发给感兴趣的订阅。
  4. 订阅服务器从其订阅使用消息。

 但是不同的消息中间件之间存在细微的差异,项目使用不同的产品需要实现不同的实现类,虽然是明智的决策,但必须编写和维护抽象及其基础实现。此方法需要复杂、重复且容易出错的自定义代码。

 Dapr为了解决这种问题,提供开箱即用的消息传送抽象和实现,封装在 Dapr 构建基块中。业务系统只需调用跟据Dapr的要求实现订阅发布即可。

二、工作原理:

 Dapr 发布&订阅构建基块提供了平台无关的 API 框架来发送和接收消息。你的服务将消息发布到一个命名主题(topic)。服务订阅主题(topic)来使用消息。

 服务在 Dapr Sidecar上调用 pub/sub API。 然后,Sidecar将调用一个预定义的 Dapr pub/sub 组件来封装特定的消息代理产品。 下图 显示了 Dapr 发布/订阅 消息传递堆栈。

 

三、功能:

  • 发布/订阅API

  Dapr 发布&订阅构建基块提供了一个与平台无关的 API 框架来发送和接收消息。

  服务将消息发布到指定主题, 业务服务订阅主题以使用消息。

  服务在 Dapr sidecar 上调用 pub/sub API。然后,sidecar 调用预定义 Dapr pub/sub 组件。

  任何编程平台都可以使用 Dapr 本机 API 通过 HTTP 或 gRPC 调用构建基块。若要发布消息,请进行以下 API 调用:

http://localhost:<dapr-port>/v1.0/publish/<pub-sub-name>/<topic>

  上述调用中有几个特定于 Dapr 的 URL 段:

    • <dapr-port> 提供 Dapr sidecar 侦听的端口号。

    • <pub-sub-name> 提供所选 Dapr pub/sub 组件的名称。

    • <topic> 提供消息发布到的主题的名称。

  • 消息格式

  要启用消息路由并为每个消息提供附加上下文,Dapr 使用 CloudEvents 1.0 规范 作为其消息格式。 使用 Dapr 应用程序发送的任何信息都将自动包入 Cloud Events 信封中,datacontenttype 属性使用 Content-Type 头部值。

  Dapr 实现以下 Cloud Events 字段:

    • id 
    • source
    • specversion
    • type
    • datacontenttype (可选)

  下面的示例显示了 CloudEvent v1.0 中序列化为 JSON 的 XML 内容:

{
"specversion" : "1.0",
"type" : "xml.message",
"source" : "https://example.com/message",
"subject" : "Test XML Message",
"id" : "id-1234-5678-9101",
"time" : "2020-09-23T06:23:21Z",
"datacontenttype" : "text/xml",
"data" : "<note><to>User1</to><from>user2</from><message>hi</message></note>"
}
  • 订阅消息

  Dapr 应用程序可以订阅已发布的 topics。 Dapr 允许您的应用程序有两种方法来订阅 topics:

   声明式:其中定义在外部文件中:

apiVersion: dapr.io/v1alpha1
kind: Subscription
metadata:
name: myevent-subscription
spec:
topic: test_topic //主题
route: /TestPubSub //路由
pubsubname: pubsub //名称
scopes:
- frontend       //为该应用启用订阅

   上面的示例显示了 test_topic主题的事件订阅,使用组件 pubsub

    • route 告诉 Dapr 将所有主题消息发送到应用程序中的 /TestPubSub 端点。

    • scopes 为 frontend 应用启用订阅

   编程方式:订阅在用户代码中定义

  • 消息传递

   Dapr 保证消息传递 at-least-once 语义。 这意味着,当应用程序使用发布/订阅 API 将消息发布到主题时,Dapr 可确保此消息至少传递给每个订阅者一次(at least once)

  • 消费者群体和竞争行消费者模式

   多个消费组、多个应用程序实例使用一个消费组,这些都将由 Dapr 自动处理。 当同一个应用程序的多个实例(相同的 ID) 订阅主题时,Dapr 只将每个消息传递给该应用程序的一个实例。

   

   同样,如果两个不同的应用程序 (不同的 ID) 订阅同一主题,那么 Dapr 将每个消息仅传递到每个应用程序的一个实例。

  • Topic作用域:

   默认情况下,支持Dapr发布/订阅组件的所有主题 (例如,Kafka、Redis、RabbitMQ) 都可用于配置该组件的每个应用程序。 为了限制哪个应用程序可以发布或订阅 topic,Dapr 提供了 topic 作用域限定。 这使您能够让应用程序允许发布哪些主题以及应用程序允许订阅哪些主题。

pub/sub 主题作用域限定

为每个 pub/sub 组件定义发布/订阅范围。 您可能有一个名为 pubsub 的 pub/sub 组件,它有一组范围设置,另一个 pubsub2 另有一组范围设置。

要使用这个主题范围,可以设置一个 pub/sub 组件的三个元数据属性:

    • spec.metadata.publishingScopes

      • 分号分隔应用程序列表& 逗号分隔的主题列表允许该 app 发布信息到主题列表
      • 如果在 publishingScopes (缺省行为) 中未指定任何内容,那么所有应用程序可以发布到所有主题
      • 要拒绝应用程序发布信息到任何主题,请将主题列表留空 (app1=;app2=topic2)
      • 例如, app1=topic1;app2=topic2,topic3;app3= 允许 app1 发布信息至 topic1 ,app2 允许发布信息到 topic2 和 topic3 ,app3 不允许发布信息到任何主题。
    • spec.metadata.subscriptionScopes
      • 分号分隔应用程序列表& 逗号分隔的主题列表允许该 app 订阅主题列表
      • 如果在 subscriptionScopes (缺省行为) 中未指定任何内容,那么所有应用程序都可以订阅所有主题
      • 例如, app1=topic1;app2=topic2,topic3 允许 app1 订阅 topic1 ,app2 可以订阅 topic2 和 topic3
    • spec.metadata.allowedTopics
      • 一个逗号分隔的允许主题列表,对所有应用程序。
      • 如果未设置 allowedTopics (缺省行为) ,那么所有主题都有效。 subscriptionScopes 和 publishingScopes 如果存在则仍然生效。
      • publishingScopes 或 subscriptionScopes 可用于与 allowedTopics 的 conjuction ,以添加限制粒度
  • 消息生存时间:

   Dapr 可以在每个消息的基础上设置超时。 表示如果消息未从 Pub/Sub 组件读取,则消息将被丢弃。 这是为了防止未读消息的积累。 在队列中超过配置的 TTL 的消息就可以说它挂了。  

四、.NET Core 应用

 1、设置Pub/Sub组件:

  本机默认下安装了Redis Staram,在 Windows 上打开%UserProfile%\.dapr\components\pubsub.yaml 组件文件以验证:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
spec:
type: pubsub.redis
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""

 2、实现发布/订阅功能 :

  添加控制器(PubSubController)

[Route("api/[controller]")]
[ApiController]
public class PubSubController : ControllerBase
{
private readonly ILogger<PubSubController> _logger;
private readonly DaprClient _daprClient;
public PubSubController(ILogger<PubSubController> logger, DaprClient daprClient)
{
_logger = logger;
_daprClient = daprClient;
} /// <summary>
/// 发布消息
/// </summary>
/// <returns></returns>
[HttpGet("pub")]
public async Task<ActionResult> PubAsync()
{
var data = new WeatherForecast() { Summary = "city", Date = DateTime.Now };
await _daprClient.PublishEventAsync<WeatherForecast>("pubsub", "test_topic", data);
return Ok();
} /// <summary>
/// 消费消息
/// </summary>
/// <returns></returns>
[Topic("pubsub", "test_topic")]
[HttpPost("sub")]
public async Task<ActionResult> Sub()
{
Stream stream = Request.Body;
byte[] buffer = new byte[Request.ContentLength.Value];
stream.Position = 0L;
await stream.ReadAsync(buffer, 0, buffer.Length);
string content = Encoding.UTF8.GetString(buffer);
_logger.LogInformation("testsub" + content);
return Ok(content);
}
}

  Startup.cs中调整:

public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//注入Dapr
services.AddControllers().AddDapr();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// 使用CoudEvent
app.UseCloudEvents();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Use((context, next) =>
{
context.Request.EnableBuffering();
return next();
});
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers()
//订阅处理
endpoints.MapSubscribeHandler();;
});
}
}

 3、dapr运行程序:

dapr run --dapr-http-port 3501 --app-port 5001  --app-id frontend dotnet  .\FrontEnd.dll

 4、调用发布命令:

http://127.0.0.1:3501/v1.0/invoke/frontend/method/api/pubsub/pub

 5、通过Dapr cli 发布消息:

dapr publish --publish-app-id frontend --pubsub pubsub --topic test_topic --data '{"date":"0001-01-01T00:00:00","temperatureC":0,"temperatureF":32,"summary":null}'

总结

 pub/sub 模式可帮助你分离分布式应用程序中的服务。 Dapr 发布&订阅构建基块简化了在应用程序中实现此行为。

 通过 Dapr pub/sub,可以将消息发布到特定 主题。构建基块还将查询服务,以确定 (订阅) 主题。

 可以通过 HTTP 或特定于语言的 SDK 之一(例如用于 Dapr 的 .NET SDK)本机使用 Dapr pub/sub。 .NET SDK 与 ASP.NET 平台紧密集成。

 使用 Dapr,可以将受支持的消息代理产品插入应用程序。 然后,无需更改应用程序的代码,即可交换消息代理。

Dapr-发布/订阅的更多相关文章

  1. Blazor+Dapr+K8s微服务之事件发布订阅

    我们要实现的是:在blazorweb服务中发布一个事件,并传递事件参数,然后在serviceapi1服务中订阅该事件,接收到blazorweb服务中发布的事件和参数. 1         在blazo ...

  2. 手把手教你学Dapr - 6. 发布订阅

    上一篇:手把手教你学Dapr - 5. 状态管理 介绍 发布/订阅模式允许微服务使用消息相互通信.生产者或发布者在不知道哪个应用程序将接收它们的情况下向主题发送消息.这涉及将它们写入输入通道.同样,消 ...

  3. 通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布

    之前的章节我们介绍了如何通过dapr发起一个服务调用,相信看过前几章的小伙伴已经对dapr有一个基本的了解了,今天我们来聊一聊dapr的另外一个功能--订阅发布 目录:一.通过Dapr实现一个简单的基 ...

  4. 本地使用 Docker Compose 与 Nestjs 快速构建基于 Dapr 的 Redis 发布/订阅分布式应用

    Dapr(分布式应用程序运行时)介绍 Dapr 是一个可移植的.事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的.无状态和有状态的应用程序,并可运行在云平台或边缘计算中,它同时也支持多种编程语言 ...

  5. RedisRepository封装—Redis发布订阅以及StackExchange.Redis中的使用

    本文版权归博客园和作者本人吴双共同所有,转载请注明本Redis系列分享地址.http://www.cnblogs.com/tdws/tag/NoSql/ Redis Pub/Sub模式 基本介绍 Re ...

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

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

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

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

  8. 分布式消息总线,基于.NET Socket Tcp的发布-订阅框架之离线支持,附代码下载

    一.分布式消息总线以及基于Socket的实现 在前面的分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载一文之中给大家分享和介绍了一个极其简单也非常容易上的基于.N ...

  9. 分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载

    一.分布式消息总线 在很多MIS项目之中都有这样的需求,需要一个及时.高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已经完成,在通常的情况下,开发人中都是在使用者B所使用 ...

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

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

随机推荐

  1. mysql从零开始之MySQL LIKE 子句

    MySQL LIKE 子句 我们知道在 MySQL 中使用 SQL SELECT 命令来读取数据, 同时我们可以在 SELECT 语句中使用 WHERE 子句来获取指定的记录. WHERE 子句中可以 ...

  2. 前段之BOM ----DOM

    一.介绍 BOM(Browser Object Model)是指浏览器对象模型,它使 JavaScript 有能力与浏览器进行"对话". DOM (Document Object ...

  3. ❤️【Python从入门到精通】(二十七)更进一步的了解Pillow吧!

    您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦. 进一步介绍Pillow库的使用,详细了解 干货满满,建议收藏,需要用到时常看看. 小伙伴们如有问题及需要,欢迎踊跃留言哦~ ~ ~. 前言 本文是 ...

  4. 【高热FAQ】关于智慧康养物联网加速器 ,你想知道的都在这

    摘要:从软硬件解决方案.设备接入到资源扶持,一文梳理智慧康养物联网加速器中ISV最关心的问题. 本文分享自华为云社区<[高热FAQ]关于智慧康养物联网加速器 ,你想知道的都在这>,作者:技 ...

  5. captcha_trainer 验证码识别-训练 使用记录

    captcha_trainer 验证码识别-训练 使用记录 在爬数据的时候,网站出现了验证码,那么我们就得去识别验证码了.目前有两种方案 接入打码平台(花钱,慢) 自己训练(费时,需要GPU环境,快) ...

  6. WSL (Windows Subsystem for Linux)

    WSL (Windows Subsystem for Linux) :适用于 Linux 的 Windows 子系统. References Install WSL with a single com ...

  7. Mybatis初始化机制

    对于任何框架而言,在使用前都要进行一系列的初始化,MyBatis也不例外.本章将通过以下几点详细介绍MyBatis的初始化过程. 1.MyBatis的初始化做了什么 2. MyBatis基于XML配置 ...

  8. 一文看懂JVM内存区域分布与作用

    那么我们在开始介绍Java内存区域之前,我们先放一张内存区域的图,方便我们后面介绍的时候可以对照着看. 须知,本文是根据JDK8来介绍的. 程序计数器 首先它是线程私有的,它也称为代码的行号指示器,字 ...

  9. Coursera Deep Learning笔记 序列模型(一)循环序列模型[RNN GRU LSTM]

    参考1 参考2 参考3 1. 为什么选择序列模型 序列模型能够应用在许多领域,例如: 语音识别 音乐发生器 情感分类 DNA序列分析 机器翻译 视频动作识别 命名实体识别 这些序列模型都可以称作使用标 ...

  10. Java:线程池

    Java:线程池 本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记 获取多线程的方法: 实现 Runnable 接口 实现 Callable 接口 实例化 Thre ...