冠军

导航

< 2025年2月 >
26 27 28 29 30 31 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 1
2 3 4 5 6 7 8

统计

2025年2月10日

NATS: Pull Consumers in JetStream

原文地址:https://natsbyexample.com/examples/jetstream/pull-consumer/csharp

对 Consumer 使用拉取模式,支持订阅到 Consumer 的应用程序根据需要来提取一个或者多个消息。这样应用程序可以自己来控制消息到达的流程,应用程序可以在自己控制的合适的时间范围来处理它。

Consumer 本身可以是持久的或者暂时的。持久的 Consumer 将在 NATS 服务器上跟踪自己的状态,特别重要的是,来自客户端的最新的确认消息。

对于一次性使用的场景来说,暂时的 Consumer 更加方便,对于资源使用和管理来说也更加轻量。不过,在订阅者取消订阅之后,暂时的 Consumer 不会持久化。NATS 服务器将会在一个时间间隔之后,自动清理 (删除) 该 Consumer.

因为每个订阅者根据需要来提取消息,多个订阅者可以创建绑定到相同的拉取 Consumer,而不需要任何其它的配置。每个订阅者都可以提取一批消息,然后并发的处理它们。

特别要提醒的是,在一批中的消息相对其它消息来说是有序的,但是,每个订阅者会独立的处理成批的消息,如果需要确定性分区以实现可扩展的订单处理,请在此处了解更多信息

代码示例

1. 安装 NATS.Net 包

using System.Diagnostics;
using NATS.Client.JetStream;
using NATS.Client.JetStream.Models;
using NATS.Net;

2. 通过环境变量获得 NATS 连接串

这里尝试使用环境变量 NATS_URL 来获得 NATS 连接串。

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

3. 连接到 NATS

连接到 NATS 服务器。因为连接在我们指定的 Scope 之外是 Disposable 可丢弃的,我们应该清理缓存并清楚关闭连接。

await using var nc = new NatsClient(url);

4. 获得 JetStream

底层是 JetStream, 通过 JetStream 来管理 Stream,使用基于 JetStream 的 Consumer 发布消息和消息消息。

var js = nc.CreateJetStreamContext();

5. 定义流

定义一个简单的基于限制的流

var streamName = "EVENTS";
var stream = await js.CreateStreamAsync(new StreamConfig(streamName, subjects: ["events.>"]));

6. 发布一些消息

通过流发布消息

await js.PublishAsync(subject: "events.1", data: "event-data-1");
await js.PublishAsync(subject: "events.2", data: "event-data-2");
await js.PublishAsync(subject: "events.3", data: "event-data-3");

7. 创建基于前述创建的流的 Consumer

基于前面创建的 Stream 创建 Consumer。如果没有提供持久化的名称,当没有活动的消息的时候,Consumer 将在 InactiveThreshold (默认是 5 秒钟) 时间到达之后之后被删除掉。

Consumer 的名称是可选的,如果没有提供,Consumer 的名字将被自动生成。对于本示例来说,我们使用了没有提供的方式,这样它将是临时的,使用了自动生成的名称。

var consumer = await stream.CreateOrUpdateConsumerAsync(new ConsumerConfig());

8. 通过 Consumer 消费消息

使用 Consume() 方法,可以在循环中持续消息消息。Consume() 方法支持多种形式的选项,不过在本例中,我们使用默认的 ones.break 作为本例来确保在处理了 3 个消息之后停止处理 (这样它不会影响其它的示例)

var count = 0;
await foreach (var msg in consumer.ConsumeAsync<string>())
{
    await msg.AckAsync();
    Console.WriteLine($"received msg on {msg.Subject} with data {msg.Data}");
    if (++count == 3)
        break;
}

此时前面发布的 3 个消息应该被处理掉了。

9. 再发布一些消息

await js.PublishAsync(subject: "events.1", data: "event-data-1");
await js.PublishAsync(subject: "events.2", data: "event-data-2");
await js.PublishAsync(subject: "events.3", data: "event-data-3");

10. 批量提取消息

可以批量提取消息。第一个参数是批量的尺寸,它指定了最多可以提取的消息数量。对第一次提取,我们使用了 2 个消息,我们会得到 2 个消息,因为前面我们已经准备了 3 个消息。

var fetchCount = 0;
await foreach (var msg in consumer.FetchAsync<string>(opts: new NatsJSFetchOpts { MaxMsgs = 2 }))
{
    await msg.AckAsync();
    fetchCount++;
}

Console.WriteLine($"Got {fetchCount} messages");

11. 使用 FetchNoWait() 方法提取消息

通过返回的 Messages() 通道来获取发布到其中的消息。

该通道仅仅在请求数量的消息被接收到之后,或者超时之后才会被关闭。如果我们并不希望等待剩下的消息,并且希望在获得当前存在的消息之后快速返回 (基于提供的批的尺寸),我们应该使用 FetchNoWait() 方法来替代 Fetch() 方法。

所以,因为我们已经接收了 2 个消息,我们会仅仅得到 1 个剩下的消息。

注意:FetchNoWait() 方法并不鼓励使用,因为没有正确使用的话,它会导致额外的负担。例如,在没有规避空队列处理的循环中使用这个方法,会导致在 Stream 中没有消息的情况下,还持续尝试提取消息。

fetchCount = 0;
await foreach (var msg in ((NatsJSConsumer)consumer).FetchNoWaitAsync<string>(opts: new NatsJSFetchOpts { MaxMsgs = 100 }))
{
    await msg.AckAsync();
    fetchCount++;
}
Console.WriteLine($"Got {fetchCount} messages");

12. Fetch() 导致阻塞

最后,如果流已经为空,我们到达了流的末尾,并且调用了 Fetch() 方法,该调用将会被阻塞直到 "max wait" 时间,默认是 30s 秒钟,不过,这个时间可以作为选项显式配置。

var fetchStopwatch = Stopwatch.StartNew();
fetchCount = 0;
await foreach (var msg in consumer.FetchAsync<string>(opts: new NatsJSFetchOpts { MaxMsgs = 100, Expires = TimeSpan.FromSeconds(1) }))
{
    await msg.AckAsync();
    fetchCount++;
}
Console.WriteLine($"Got {fetchCount} messages in {fetchStopwatch.Elapsed}");

13. 持久化的 Consumer

持久化的 Consumer 需要通过提供持久化的名称来创建。持久化的 Consumer 不会因为 InactiveThreshold 时间被自动删除。它需要通过调用 DeleteConsumer() 来删除。

var durable = await stream.CreateOrUpdateConsumerAsync(new ConsumerConfig("processor"));

14. 通过持久化 Consumer 提取消息是不变的

从使用端来说,使用持久化的 Consumer 和暂时的 Consumer 没有区别。

await foreach (var msg in durable.FetchAsync<string>(opts: new NatsJSFetchOpts { MaxMsgs = 1 }))
{
    Console.WriteLine($"Received {msg.Subject} from durable consumer");
}

15. 删除持久 Consumer

对于暂时的 Consumer 来时,在 InactiveThreshold 之后会自动删除,在不需要之后,持久的 Consumer 必须显式删除。

await stream.DeleteConsumerAsync("processor");

16. 确认不存在的持久化 Consumer

使用 try/catch 来确认持久化的 Consumer 已经被删除了。

try
{
    await stream.GetConsumerAsync("processor");
}
catch (NatsJSApiException e)
{
    if (e.Error.Code == 404)
    {
        Console.WriteLine("Consumer is gone");
    }
}

17. 完成

就这样了。

Console.WriteLine("Bye!");

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

2024年12月20日

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

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

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

2024年12月19日

.NET Aspire Apps 集成测试

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

posted @ 2024-12-19 11:09 冠军 阅读(59) 评论(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 冠军 阅读(119) 评论(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 冠军 阅读(41) 评论(0) 推荐(0) 编辑

2024年9月30日

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

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

posted @ 2024-09-30 10:18 冠军 阅读(153) 评论(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 冠军 阅读(24) 评论(0) 推荐(0) 编辑

2024年8月27日

NATS: 请求-响应消息

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

posted @ 2024-08-27 15:46 冠军 阅读(87) 评论(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 冠军 阅读(37) 评论(0) 推荐(0) 编辑

2024年7月18日

NATS: 对依赖注入支持

摘要: NuGet: NATS.Net 使用方法: serviceCollection.AddNats(); 在容器中添加了 2 个单例服务: NATS.Client.Core.NatsConnection 实际类型 NATS.Client.Core.INatsConnection ,接口类型 所以在注入的 阅读全文

posted @ 2024-07-18 14:58 冠军 阅读(52) 评论(0) 推荐(0) 编辑

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