冠军

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

2025年3月14日

NATS: 使用 work-queue Stream

提醒

请注意对 Consumer 的使用,见 NATS: Pull Consumers in JetStream

基于 JsStream 使用队列

借助 JetStream,还可以将流用作队列,方法是将保留策略设置为 WorkQueuePolicy,并利用拉取使用者 轻松实现处理的水平可扩展性(或将显式 ack push 使用者与订阅者队列组一起使用)。
排队异地亲和性
当连接到全球分布的 NATS 超级集群时,存在自动服务异地亲和性,因为如果集群上没有可用于本地处理请求的侦听器,则服务请求消息只会路由到另一个集群(即另一个区域)。

原文地址:https://docs.nats.io/nats-concepts/core-nats/queue#stream-as-a-queue

代码示例

原文地址:https://natsbyexample.com/examples/jetstream/workqueue-stream/dotnet2

提醒,在 NATS 中,我们可以通过 Consumer 视图来访问 Stream 中的消息。Consumer 概念提供了对于消息的中间映射。

A work-queue retention policy satisfies a very common use case of queuing up messages that are intended to be processed once and only once.

Work-queue 的保持策略用于处理非常常见的场景,将消息排队,期望它被处理一次并且仅仅一次。

This retention policy supports queuing up messages from publishers independent of consummption. Since each message is intended to be processed only once, this retention type allows for a set of consumers that have non-overlapping interest on subjects.
In other words, if multiple consumers are bound to a work-queue stream, they must have disjoint filter subjects. This is in contrast to a standard limits-based or interest-based stream which supports multiple consumers with overlapping interest.

此保留策略支持对来自发布者的消息进行排队,这与消费消息的处理速度无关。由于每条消息只处理一次,因此此保留类型允许一组对主题具有非重叠兴趣的 Consumer。
换言之,如果多个 Consumer 绑定到工作队列流,则它们必须具有不相交的筛选器主题。这与基于限制或基于兴趣的标准流相反,该流支持具有重叠兴趣的多个 Consumer。

注:如果有多个 Consumer 使用了相同的主题订阅,

  • 在没有使用过滤的情况下,会得到异常 multiple non-filtered consumers not allowed on workqueue stream
  • 使用了相同过滤的情况下,会得到异常 filtered consumer not unique on workqueue stream

Like the interest policy this retention policy is additive to any limits set on the stream. As a contrived example, if max-msgs is set to one with old messages being discarded, every new message that is received by the stream will result in the prior message being deleted regardless if any subscriptions were available to process the message.

与基于兴趣的策略一样,此保留策略是对流上设置的任何限制的附加。举个人为的例子,如果将 max-msgs 设置为丢弃旧消息的 max-msgs,则流收到的每条新消息都将导致删除前一条消息,而不管是否有任何订阅可用于处理该消息。

In this example, we will walk through the work-queue retention setup and behavior. If you are new to streams, it is recommended to read the limits-based stream example prior to reading this one.

在此示例中,我们将演练工作队列保留设置和行为。如果您不熟悉流,建议在阅读本文之前先阅读基于限制的流示例。

代码 Code

安装 NuGet 包 NATS.NET 和 Microsoft.Extensions.Logging.Console.

> dotnet add package NATS.Net
> dotnet add package NATS.Client.Serializers.Json
> dotnet add package Microsoft.Extensions.Logging.Console

示例代码

using Microsoft.Extensions.Logging;
using NATS.Client.Core;
using NATS.Client.JetStream;
using NATS.Client.JetStream.Models;

// 基于 Console 的日志器
using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
var logger = loggerFactory.CreateLogger("NATS-by-Example");

NATS_URL 环境变量用来提供 NATS 服务器的地址。

var url = Environment.GetEnvironmentVariable("NATS_URL") ?? "127.0.0.1:4222";

连接到 NATS 服务器。因为连接是 Disposable 的,在代码 Scope 的最后,我们应该刷新缓冲区并明确关闭连接。

var opts = new NatsOpts
{
    Url = url,
    LoggerFactory = loggerFactory,
    Name = "NATS-by-Example",
};
await using var nats = new NatsConnection(opts);

创建 JScontext,访问 JetStream 用来管理 Stream 和 Consumer,还包括用于发布消息到流中和消费流中的消息。

var js = new NatsJSContext(nats);
var streamName = "EVENTS";

创建 Stream

定义流的配置,指定保持策略为 WorkQueuePolicy,并创建流。以后不会基于 NATS 连接操作,而是基于 JS 的 Stream 进行操作了。

var stream = await js.CreateStreamAsync(new StreamConfig(streamName, new[] { "events.>" })
{
    Retention = StreamConfigRetention.Workqueue,
});

入队消息

就是普通的发布消息。

// 发布一些消息到流中。
await js.PublishAsync("events.us.page_loaded", "event-data");
await js.PublishAsync("events.us.mouse_clicked", "event-data");
await js.PublishAsync("events.us.input_focused", "event-data");
logger.LogInformation("published 3 messages");

// 检查流的信息,我们可以看到有 3 条消息已经入队了。
logger.LogInformation("# Stream info without any consumers");
await PrintStreamStateAsync(stream);

增加一个 Consumer

Consumer 相当于数据库中的视图。

现在我们增加一个 Consumer 来消费消息,并发布更多的消息。

var consumer = await stream.CreateConsumerAsync(new ConsumerConfig("processor-1"));

// 提取并响应入队的消息
await foreach (var msg in consumer.FetchAsync<string>(opts: new NatsJSFetchOpts { MaxMsgs = 3 }))
{
    // 响应消息已经处理
    await msg.AckAsync();
    /* await msg.AckAsync(new AckOpts { DoubleAck = true }); */
}

// 再次检查 stream 的信息,注意到已经没有消息在队列中了。
logger.LogInformation("# Stream info with one consumer");
await PrintStreamStateAsync(stream);

独占非过滤的 Consumer

As noted in the description above, work-queue streams can only have at most one consumer with interest on a subject at any given time. Since the pull consumer above is not filtered, if we try to create another one, it will fail.

如前所述,对于 work-queue 流来说,在任何时间,最多只能一个 Consumer 对一个主题。由于上面的 Consumer 是非过滤的,如果我们试图创建同一主题的另外一个 Consumer ,新的将会失败。

logger.LogInformation("# Create an overlapping consumer");
try
{
    await stream.CreateConsumerAsync(new ConsumerConfig("processor-2"));
}
catch (NatsJSApiException e)
{
    logger.LogInformation("Error: {Message}", e.Error);
}

However if we delete the first one, we can then add the new one.

不过,如果我们删除了前面的那个 Consumer,然后我们可以创建一个新的。

await stream.DeleteConsumerAsync("processor-1");
await stream.CreateConsumerAsync(new ConsumerConfig("processor-2"));
logger.LogInformation("Created the new consumer");

await stream.DeleteConsumerAsync("processor-2");

多过滤的 Consumer

为了创建多个 Consumer,需要使用主题 subject 的过滤器。例如,我们需要限制每个 Consumer 到事件发布的地理位置上,在下面的示例中,是 us 或者 eu

注意,这里增加了对 FilterSubject 的配置。

var consumer1 = await stream.CreateConsumerAsync(new ConsumerConfig("processor-us") { FilterSubject = "events.us.>" });
var consumer2 = await stream.CreateConsumerAsync(new ConsumerConfig("processor-eu") { FilterSubject = "events.eu.>" });

await js.PublishAsync("events.eu.mouse_clicked", "event-data");
await js.PublishAsync("events.us.page_loaded", "event-data");
await js.PublishAsync("events.us.input_focused", "event-data");
await js.PublishAsync("events.eu.page_loaded", "event-data");
logger.LogInformation("Published 4 messages");

await foreach (var msg in consumer1.FetchAsync<string>(opts: new NatsJSFetchOpts { MaxMsgs = 2 }))
{
    logger.LogInformation("us sub got: {Subject}", msg.Subject);
    await msg.AckAsync();
}

await foreach (var msg in consumer2.FetchAsync<string>(opts: new NatsJSFetchOpts { MaxMsgs = 2 }))
{
    logger.LogInformation("eu sub got: {Subject}", msg.Subject);
    await msg.AckAsync();
}

logger.LogInformation("Bye!");

async Task PrintStreamStateAsync(INatsJSStream jsStream)
{
    await jsStream.RefreshAsync();
    var state = jsStream.Info.State;
    logger.LogInformation(
        "Stream has messages:{Messages} first:{FirstSeq} last:{LastSeq} consumer_count:{ConsumerCount} num_subjects:{NumSubjects}",
        state.Messages,
        state.FirstSeq,
        state.LastSeq,
        state.ConsumerCount,
        state.NumSubjects);
}

NatsJSPubOpts

public record NatsJSPubOpts : NatsPubOpts
{
    public static readonly NatsJSPubOpts Default = new();

    // ttl time.Duration
    // id  string
    public string? MsgId { get; init; }

    // lid string  // Expected last msgId
    public string? ExpectedLastMsgId { get; init; }

    // str string  // Expected stream name
    public string? ExpectedStream { get; init; }

    // seq *uint64 // Expected last sequence
    public ulong? ExpectedLastSequence { get; init; }

    // lss *uint64 // Expected last sequence per subject
    public ulong? ExpectedLastSubjectSequence { get; init; }

    // Publish retries for NoResponders err.
    // rwait time.Duration // Retry wait between attempts
    public TimeSpan RetryWaitBetweenAttempts { get; init; } = TimeSpan.FromMilliseconds(250);

    // rnum  int           // Retry attempts
    public int RetryAttempts { get; init; } = 2;
}

posted @ 2025-03-14 16:04 冠军 阅读(4) 评论(0) 推荐(0) 编辑

2025年2月10日

NATS: Pull Consumers in JetStream

摘要: 原文地址:https://natsbyexample.com/examples/jetstream/pull-consumer/csharp 对 Consumer 使用拉取模式,支持订阅到 Consumer 的应用程序根据需要来提取一个或者多个消息。这样应用程序可以自己来控制消息到达的流程,应用程序 阅读全文

posted @ 2025-02-10 11:23 冠军 阅读(28) 评论(0) 推荐(0) 编辑

2024年12月20日

.NET 单文件执行程序拆解器 SingleFileExtractor

摘要: .NET 现在支持将程序打包为单文件格式,这方便了部署,问题是,我们不能直接看到程序中使用了哪些 DLL,更不能简单地通过查看文件属性的方式,看到这些 DLL 的版本。要是可以像使用 Zip 一样,可以打开这个合成的文件,直接查看内容就好了。 SingleFileExtractor 就是这样的工具。 阅读全文

posted @ 2024-12-20 09:23 冠军 阅读(554) 评论(5) 推荐(12) 编辑

2024年12月19日

.NET Aspire Apps 集成测试

摘要: 传统上,集成测试相当难以应用于编排的分布式应用程序。这是因为此类应用程序有许多必须相互协调的移动组件。这些组件的计算成本通常很高;因此,集成测试通常是为了模拟有限数量的服务间交互。 但是,.NET Aspire 非常巧妙地解决了这个问题。Microsoft 的设计人员提前考虑了集成测试。因此,针对它运行集成测试几乎与运行低级组件/单元测试一样简单。 阅读全文

posted @ 2024-12-19 11:09 冠军 阅读(64) 评论(0) 推荐(0) 编辑

2024年12月12日

ASCII 与 Unicode 中的引号

摘要: 原文地址:https://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html 摘要 请不要使用 ASCII 中的重音符号 ` (0x60) 作为左边与 ASCII 中的撇号 Apostrophe ' (0x27) 作为右边一起使用。就像这样 ( `quote' )。否则, 阅读全文

posted @ 2024-12-12 09:14 冠军 阅读(152) 评论(0) 推荐(0) 编辑

2024年10月28日

各种各样的 Host Builder

摘要: 各种各样的 Host Buider If you're building for the web or writing a distributed application, you might need to use a different host builder. Consider the fo 阅读全文

posted @ 2024-10-28 09:31 冠军 阅读(48) 评论(0) 推荐(0) 编辑

2024年9月30日

[.NET Blog] .NET Aspire 测试入门

摘要: 自动化测试是软件开发的重要一环。它可以帮助我们尽早确认软件中的缺陷和防止回归问题。在本文中,我们将探讨如何在 .NET Aspire 中开始测试,支持我们进行跨分布式应用的测试场景。 阅读全文

posted @ 2024-09-30 10:18 冠军 阅读(177) 评论(0) 推荐(1) 编辑

2024年9月11日

WCF Bindings Needed For HTTPS

摘要: 原文地址:https://weblogs.asp.net/srkirkland/wcf-bindings-needed-for-https 我刚刚完成了我的第一个 WCF 应用,它在我的开发机上顺利工作,直到我将它部署到产品环境下。所有的 WCF 服务突然都不工作了。我得到的是一个 javaScri 阅读全文

posted @ 2024-09-11 17:16 冠军 阅读(29) 评论(0) 推荐(0) 编辑

2024年8月27日

NATS: 请求-响应消息

摘要: 请求-回复消息 https://docs.nats.io/nats-concepts/core-nats/reqreply 请求-回复 在分布式系统中,请求-回复是一种常见的模式。发送请求之后,应用程序或者基于特定的超时等待回复,或者 同步 收到响应内容 现代系统不断增长的复杂性需要诸如 位置透明性 阅读全文

posted @ 2024-08-27 15:46 冠军 阅读(94) 评论(0) 推荐(0) 编辑

2024年7月22日

在 .NET 下,Fiddler 不再抓取 Web Service 流量问题

摘要: 在 .NET 下,Fiddler 不再抓取 Web Service 流量问题 问题现象 原来的一个应用中,需要访问 SOAP 服务。在原来的 .NET Framework 版本中,使用 Fiddler 是可以抓取到访问这个 Web Service 的 SOAP 流量的。在迁移到 .NET 之后, F 阅读全文

posted @ 2024-07-22 17:40 冠军 阅读(39) 评论(0) 推荐(0) 编辑

点击右上角即可分享
微信分享提示