CAP 7.0 版本发布通告 - 支持延迟消息,性能炸了?
前言
今天,我们很高兴宣布 CAP 发布 7.0 版本正式版,我们在这个版本中带来了大批新特性以及对性能的优化和改进。
自从今年 1月份发布 6.0 版本以来,已经过去了快1年的时间。在过去的将近1年的时间里,我们也发布了几个次要版本和小版本(6.0.1, 6.1.0, 6.2.0, 6.2.1),在这里要感谢这些版本的使用者以及向我们报告 Bug 和反馈问题的用户。
同时,还要感谢一些我们的贡献者,是他们在帮助 CAP 变得更加完善和易用,以下是我们的所有贡献者。
好了,废话不多说,接下来我们具体看一下 7.0 带来的新变化吧。
总览
项目地址:https://github.com/dotnetcore/CAP
开源协议:MIT
本次在 CAP 7.0 版本中我们主要带来了以下新特性:
- 性能改进
- 支持发布延迟消息
- Dashboard 支持对延迟消息查看和操作
- 添加支持度量(Metric)的可观测性指标
- Dashboard 添加新图表支持 Metric 实时查看
- 支持手动 Start/Stop CAP 进程
- 其他改进
- 新增 EnableConsumerPrefetch 配置项
- RabbitMQ 新增 PublishConfirms 配置项
- 更改框架目标从 netstandard 到 net6
- 更新 NuGet 到最新版
- 破坏性改变
- 过滤器接口方法由同步更改为异步
- IConsumerClient 的 event 改为 delegate
性能改进
这个必须放在第一章。 😃
在 .NET 7 中,官方做了大量对于性能的改进,是迄今为止最快的 .NET。
在 CAP 7 中,我们也做了大量对性能优化的改进,是迄今为止最快的 CAP。
在新版本我们内部做了大量对性能的调整,我们建议有条件的用户(项目框架为net6+)尽量升级,废话不多说直接压测看数据。
我们以使用量最高的 SQL Server + RabbitMQ 组合作为我们的测试基准。
这里是测试服务器配置:
实例: AWS EC2(c5.4xlarge, io1卷)
操作系统: Windows Server 2019
CPU:Intel Xeon Platinum 8275CL CPU@3.0Ghz
核心:16 cores
内存:32G
硬盘: SSD IOPS 3000
数据库:SQL Server 2019
消息队列:RabbitMQ 3.11.3
压力测试工具: Apache AB : 2.4.54-win64
压测命令:ab.exe -c 16 -n 50000 http://localhost:5000/xxx
压测数据:
await _cap.PublishAsync("topic", new {
Id = Guid.NewGuid(),
Name = "Hello World"
});
测试结果(TPS):
发布线程数 | .NET6 + CAP6 | .NET7 + CAP7 | CPU使用率 |
---|---|---|---|
1个线程 | 746/s | 1257/s | 7% |
4个线程 | 2288/s | 3124/s | 15% |
8个线程 | 4732/s | 6847/s | 30% |
下图蓝色为 CAP 7 , 绿色为 CAP 6.2版本。
说实话,看到这个数据我都被震惊了,我只能说4个字:“性能爆炸”。
为了大家有个概念,给大家个参考,一个不写任何代码的空接口大概是 9500/s,而 CAP 每次调用操作了 2 次数据库及 1 次 MQ,还能到 6847/s。以后再有人说性能先看看你的数据库和硬盘吧。
可能有人会说可能是 .NET 7 平台的性能提升的影响,所以这里也有一个 CAP 7 在 .net6 和 .net7 的对比:
发布线程 | .NET6 + CAP7 | .NET7 + CAP7 |
---|---|---|
1个线程 | 1196 | 1257 |
4个线程 | 3079 | 3124 |
8个线程 | 6733 | 6847 |
支持发布延迟消息
一直以来,我们都有用户反馈希望我们对延迟消息的支持,提出这些想法的主要出发点是系统中有相关需求,而他们了解到 RabbitMQ 提供了相关插件来做到发送延迟消息,所以很多用户在自己的项目中也是利用这一点来发送延迟消息。
在过去,我们一直没有对延迟消息提供支持主要是因为只有 RabbitMQ 才支持这一特性,而 RabbitMQ 中又需要开启相关的第三方延迟插件才能实现,而 CAP 的目标是对 Broker 提供上层抽象,所以我们需要提供一致的使用体验,这样用户不需要关心他使用的 Broker 是否能够发送延迟消息,所以在过去我们一直都没有支持。
现在,在 CAP 7.0 版本中,我们内置实现了一套轻量级的调度器,从而可以做到直接支持发送延迟消息而不需要关心 Broker 是否支持。新版本的延迟消息做到了不对目前数据结构做调整的情况下实现的,也就是说目前的数据库表结构不会发生任何变化也不会添加新的表,所以这不会存在任何升级兼容性问题,还是原来的配方和味道。
在 7.0 新版本中,我们的 ICapPublisher
接口新增加了两个新方法以支持直接发送延迟消息,分别是同步的 PublishDelay
和异步的 PublishDelayAsync
,同步只是对异步的包装,推荐大家使用异步接口。
下面是一个示例用于展示如何使用:
public class PublishController : Controller
{
[HttpGet]
public async Task PublishDelay([FromServices] ICapPublisher _capPubliser)
{
await _capPublisher.PublishDelayAsync(TimeSpan.FromSeconds(100), "topic", DateTime.Now);
}
}
在内部,延迟消息的状态为 Delayed,实际执行发送时间会被会存储在 ExpiresAt 字段中,内部实现为 CAP 的进程会每隔一分钟扫描数据库中要发送的下一分钟延迟消息,然后放置到内存中调度。异常场景的处理和规则的实现就不在这里展开讲了,感兴趣的朋友可以直接查看代码。
另外,我们同样在新版本中更新了我们的 Dashboard 以支持对延迟消息的查看,向下看。
Dashboard 支持对延迟消息查看和操作
在 7.0 中,我们更新了我们的 Dashboard 以支持对新增加延迟消息的查看功能,并且我们对我们的界面做出了一些调整,现在有更大的可视区域。
新版本的Dashboard 在底部增加了查看当前配置使用的 Broker种类或者 Storage 种类。
对于延迟消息,现在你可以在发送菜单的延迟选项卡界面上表格的最后一列看到“预计发送时间”,或者你可以点击“立即发布”按钮来立即触发消息发布而无需等待延迟结束。
添加支持度量(Metric)可观测性指标
我们知道OpenTelemetry主要有三部分组成,分别是Tracing,Mertics,Logging 。过去主要是对 Tracing(跟踪)和 Logging (日志)的支持,
其中我们在 2.6 版本中开始提供了Tracing的支持,我们在 SkyAPM.Diagnostics.CAP 包中提供了对 Skywalking 的支持以用于做分布式跟踪,并且在过去的 6.0 版本中,我们提供了 DotNetCore.CAP.OpenTelemerty 包对 OpenTelemetry 标准的直接支持。
上述更多的是对 Tracing 跟踪部分的支持,那么从 7.0 开始我们将更进一步。
在7.0 版本中,我们进一步提升了对可观测性的支持,我们添加了对 Metrics 的支持,现在你可以利用官方提供的 dotnet-counters 工具来查看我们对外提供的Metric指标。
使用 dotnet-counters ps
命令列出 CAP 所属的进程Id,然后使用 monitor 以查看收集的实时数据(每秒刷新一次)。
dotnet-counters ps
dotnet-counters monitor --process-id=25496 --counters=DotNetCore.CAP.EventCounter
CAP 7.0 提供了以下几个度量指标:
- 每秒发布速度
- 每秒消费速度
- 每秒调用订阅者速度
- 每秒执行订阅者平均耗时
同时,我们也在我们的Dashboard 中提供了对度量指标的直接观测,你可以直接以图表的形式查看更为直观,向下看。
Dashboard 支持 Metric 实时查看
在 CAP 7.0 中,我们改进了我们的Dashbaord,新增加了对实时度量(Metric)指标的查看。
同时我们也改进了我们的图表组件,从 ECharts 切换到了 uPlot,uPlot提供了更小体积的同时,也带来了更好的性能和渲染速度,还有嗯~ 附和我们的风格… :)
现在,你可以在CAP 中配置上 x.UseDashbaord() 来感受 Metric 的魅力吧。
从上图可以看到,在 Realtime Metric Graph 中,时间轴会随着时间实时滚动从而可以清晰的看出发布和消费每秒的速率,同时我们对消费者执行耗时以“打点”的方式体现到了 Y1 轴上(Y0轴为速率,Y1轴为执行耗时)。
值得提醒的是,Y1轴不代表消费执行次数,而是每秒钟内消费者的执行平均耗时。
支持手动 Start/Stop CAP 进程
在过去,默认情况下 CAP 随 ASP.NET Core 宿主进程启动,你无法(很难) 做到控制默认的停止或启动行为,只能按下 Ctrl+C 来停止宿主进行同时触发停止CAP。
现在,你可以通过 IBootstrapper
接口来动态控制 CAP 启动或停止。例如,在一个 Action 中添加如下两个方法来控制。
[Route("~/control/start")]
public async Task<IActionResult> Start([FromServices]IBootstrapper bootstrapper)
{
await bootstrapper.BootstrapAsync();
return Ok();
}
[Route("~/control/stop")]
public async Task<IActionResult> Stop([FromServices] IBootstrapper bootstrapper)
{
await bootstrapper.DisposeAsync();
return Ok();
}
注意:如果你停止了CAP进程,则无法进行发布或订阅消息。
通过这个特性,可以延申出来的应用场景有很多,比如在系统在某个时候开始发送/消费消息等。
其他改进
新增 EnableConsumerPrefetch 配置项
在过去,我们默认会在消费端预取一部分消息到内存队列,然后再分发给调度器执行,这在提高性能的同时也会产生一些问题。
例如,当订阅方法执行过慢耗时太久时,会导致重试线程拾取到还未执行的的消息。重试线程默认拾取4分钟前的消息,也就是说如果消费端积压了超过4分钟的消息就会被重新拾取到再次执行,会产生重复,虽然我们要求消费者保持幂等,但这不太友好。
在 7.0 版本中,我们改变了这个行为,现在默认情况只会从消息队列读取一条,然后执行订阅方法,执行完成后才会读取下一条来执行。我们新增了 EnableConsumerPrefetch 来使用以前的行为,默认为 false,通过设置 EnableConsumerPrefetch = true
来回到过去的行为。
如果想提高速度,你可以通过设置 ConsumerThreadCount 来并行执行。
RabbitMQ 新增 PublishConfirms 配置项
过去,在 RabbitMQ 发布端默认启用了发布确认,无法进行设置,对于一些要求高性能的场景中会限制发布速度,所以在新版本中我们提供了配置项来让用户可以手动配置。
考虑到大多数场景失败的概率很小,我们将默认值为 false,如果你需要考虑极端场景请将其设置为 true,这会降低吞吐量。
现在你可以通过设置 rabbitmqOption.PublishConfirms = true
来开启发布确认,你可以在这里查看更多关于发布确认的信息。
更改框架目标从 netstandard 到 net6
在CAP 7.0中,我们将所有的包目标框架由 netstandard 2.1 更改到了 net6 , 对于不需要支持 netframework 或者 Xamarin 之类的目标平台来说,支持 netstandard意义不大,这也是根据官方对库作者的建议,所以我们遵循最佳实践。
.NET 7 拥有向下兼容性,并且我们没有使用到 .NET 7 特有的东西,所以我们只需将net6 作为目标平台即可,这样 net6, net7 都可以使用。
更新NuGet到最新版
这个不用多说了,国际惯例在进行大版本发布时,都会升级依赖NuGet包到最新版。
值得一提的是,我们 DotNetCore.CAP.SqlServer 的 SqlClient 升级到了最新的版本,你的SQL Server连接字符串可能需要添加 TrustServerCertificate=True
信任一下证书,否则可能会报错。
破坏性改变
过滤器接口方法由同步更改为异步
考虑到现在大部分代码都是异步写法,所以我们将订阅过滤器接口提供的三个方法由同步改为了异步,这个是我们做出的破坏性改变,如果你使用到了过滤器特性,升级后可能需要调整一下你的代码。
分别是:
OnSubscribeExecuting
更改为OnSubscribeExecutingAsync
OnSubscribeExecuted
更改为OnSubscribeExecutedAsync
OnSubscribeException
更改为OnSubscribeExceptionAsync
IConsumerClient 的 event 改为 Action/Func
这个对一般用户没有影响,主要影响到的是我们的社区第三方 Broker 扩展。
在新版本 IConsumerClient
接口中,OnReceiveMessage 和 OnLog 回调由 event
改为下面两种形式,用法不变。因为 event 不支持异步。
public Func<TransportMessage, object?, Task>? OnMessageCallback { get; set; }
public Action<LogMessageEventArgs>? OnLogCallback { get; set; }
总结
以上,就是本版本我们做出的一些新特性和改动,感谢大家的支持,我们很开心能够帮助到大家 。
大家在使用的过程中遇到问题希望也能够积极的反馈,帮助CAP变得越来越好。😃
如果你喜欢这个项目,可以通过下面的连接点击 Star 给我们支持。
如果你觉得本篇文章对您有帮助的话,感谢您的【推荐】。
本文地址:http://www.cnblogs.com/savorboard/p/cap-7-0.html
作者博客:Savorboard
本文原创授权为:署名 - 非商业性使用 - 禁止演绎,协议普通文本 | 协议法律文本