C# .NET 中的反应式系统

概述:反应式系统已成为构建健壮、可扩展和响应迅速的应用程序的强大范式。这些系统被设计为更具弹性、弹性和消息驱动性,确保它们在各种条件下保持响应,包括高负载、网络延迟和故障。在本文中,我们将探讨 .NET 生态系统中反应式系统的概念,利用 (Rx.NET) 和 来说明一个复杂的用例:用于实时监控和分析股票市场数据的实时仪表板。Reactive ExtensionsActor model (Akka.NET)了解反应式系统反应式系统旨在对事件、负载、故障甚至用户做出反应。反应性宣言概述了四个关键特征:响应式:系统及时响应。弹性:系统在面对故障时保持响应。弹性:系统在不同的工作负载下保持响应。消息驱动:系反应式系统已成为构建健壮、可扩展和响应迅速的应用程序的强大范式。

这些系统被设计为更具弹性、弹性和消息驱动性,确保它们在各种条件下保持响应,包括高负载、网络延迟和故障。

在本文中,我们将探讨 .NET 生态系统中反应式系统的概念,利用 (Rx.NET) 和 来说明一个复杂的用例:用于实时监控和分析股票市场数据的实时仪表板。Reactive ExtensionsActor model (Akka.NET)

了解反应式系统

反应式系统旨在对事件、负载、故障甚至用户做出反应。反应性宣言概述了四个关键特征:

  • 响应式:系统及时响应。

  • 弹性:系统在面对故障时保持响应。

  • 弹性:系统在不同的工作负载下保持响应。

  • 消息驱动:系统依靠异步消息传递来确保松耦合、隔离和位置透明。

实时股市仪表板

想象一下这样的场景:金融机构希望为其用户提供一个实时仪表板,该仪表板显示股票市场趋势、重大股票变动警报并提供实时分析。

此应用程序需要处理大量数据,有效地处理数据,并立即更新用户界面。它是反应式系统的完美候选者。

工具:Rx.NET 和 Akka.NET

为了应对这一挑战,我们将使用两个强大的库:

  • **Rx.NET (Reactive Extensions for .NET):**一个库,用于使用可观察序列和 LINQ 样式的查询运算符编写异步和基于事件的程序。

  • Akka.NET:一个开源工具包和运行时,用于在 .NET 上构建高度并发、分布式和容错的事件驱动应用程序。

在 .NET 中构建反应式系统 --- 图片来源:由 Author 创建

构建我们的应用程序

我们的应用程序由几个组件组成:

  1. 数据摄取服务:连接到模拟股票市场数据流。

  2. 处理引擎:分析重大事件(例如,价格急剧上涨)的数据。

  3. 仪表盘服务:实时更新实时仪表盘。

先决条件

  • 安装 Rx.NET 软件包 (System.Reactive)

  • 安装 Akka.NET 软件包 (Akka)

项目结构

此示例的结构如下,全部位于单个控制台应用程序项目中:

  • StockTick类来表示股票数据。

  • StockMarketSimulator类来模拟股票数据流。

  • Akka.NET actors:用于更新仪表板和处理重大动作。DashboardActorSignificantMovementActor

  • 将所有内容连接在一起的主类。Program

步骤 1:设置数据引入服务

我们需要模拟股票市场价格的实时数据馈送。为简单起见,假设我们有一个返回流的函数,其中是一个表示股票符号、价格和时间戳的类。GetStockStream()IObservable<StockTick>StockTick

我们将使用 Rx.NET 定期生成对象流。 方法每秒(或您喜欢的任何其他合理间隔)创建一个价格变动,然后将这些变动映射到具有随机生成的价格和交易品种的对象。StockTickObservable.IntervalStockTick

复制代码
using System;  
using System.Reactive.Linq;  
using System.Reactive.Threading.Tasks;  
using System.Threading.Tasks;  
  
public class StockTick  
{  
    public string Symbol { get; set; }  
  
    public double Price { get; set; }  
  
    public DateTime Timestamp { get; set; }  
}  
  
public static class StockMarketSimulator  
{  
    private static readonly Random rand = new Random();  
    private static readonly string[] symbols = new[] {   
      "AAPL",   
      "MSFT",   
      "GOOGL",   
      "AMZN",   
      "FB" };  
  
    public static IObservable<StockTick> GetStockStream()  
    {  
        return Observable.Interval(TimeSpan.FromSeconds(1))  
            .Select(_ => new StockTick  
            {  
                Symbol = symbols[rand.Next(symbols.Length)],  
                Price = Math.Round(100 + (rand.NextDouble() * 1000), 2),  
                Timestamp = DateTime.Now  
            });  
    }  
}

第 2 步:使用 Rx.NET 分析数据

Rx.NET 在处理数据流方面大放异彩。我们可以订阅并使用 LINQ 查询来实时处理和分析股票即时报价。IObservable<StockTick>

让我们假设,一个重大的变动是在 5 秒缓冲窗口内价格变化超过 30%。

复制代码
using System;  
using System.Linq;  
using System.Reactive.Linq;  
  
public class StockAnalysis  
{  
    public static void AnalyzeStockTicks(IActorRef significantMovementActor)  
    {  
        IObservable<StockTick> stockStream = StockMarketSimulator.GetStockStream();  
  
        return stockStream  
            .GroupBy(tick => tick.Symbol)  
            .SelectMany(group => group.Buffer(TimeSpan.FromSeconds(30)))  
            .Select(buffer =>  
            {  
                var firstTick = buffer.First();  
                var lastTick = buffer.Last();  
  
                // Avoid potential issues if the buffer is empty  
                if (firstTick == null || lastTick == null) return null;  
  
                var priceChange = Math.Abs((lastTick.Price - firstTick.Price) / firstTick.Price);  
  
                return new  
                {  
                    Symbol = firstTick.Symbol,  
                    PriceChange = priceChange,  
                    StartPrice = firstTick.Price,  
                    EndPrice = lastTick.Price,  
                    Timestamp = DateTime.Now  
                };  
            })  
            .Where(x => x != null && x.PriceChange > 0.05); // Filter for more than 5% price change   
    }  
}

对每个股票品种的报价进行分组,缓冲 30 秒,然后进行分析以发现重大的价格变化。如果价格变化超过 5%,则认为价格变动显著,并且有关变动的信息将打印到控制台。

调用是 Rx.NET 模式的重要组成部分,其中订阅了处理的结果(重大变动)。该方法是触发可观察序列的执行,并定义如何处理每个发出的项目(重大的股价变动)。.Subscribe(movement => { ... })Subscribe

步骤 3:集成仪表板更新的 Akka.NET

我们将创建一个 Akka.NET 参与者系统,以处理我们的 Rx.NET 分析检测到的重大股票变动。此执行组件系统将由两个主要执行组件组成:

  • DashboardActor:负责接收重要的移动消息并更新仪表板。

  • SignificantMovementActor:订阅可观察对象,并在检测到重大移动时向 发送消息。significantMovementsDashboardActor

    public class SignificantMovement
    {
    public string Symbol { get; set; }

    复制代码
      public double PriceChange { get; set; }  
    
      public double StartPrice { get; set; }  
    
      public double EndPrice { get; set; }  
    
      public DateTime Timestamp { get; set; }  

    }

    using Akka.Actor;

    // Akka.NET Actor for dashboard updates
    public class DashboardActor : ReceiveActor
    {
    public DashboardActor()
    {
    Receive<SignificantMovement>(movement =>
    {
    // Logic to update the dashboard with the significant movement
    Console.WriteLine($"Dashboard updated for {movement.Symbol}: {movement.PriceChange * 100:F2}% change, from {movement.StartPrice} to {movement.EndPrice}");
    });
    }
    }

    using Akka.Actor;
    using System;
    using System.Reactive.Linq;

    // Akka.NET Actor to handle significant movements
    public class SignificantMovementActor : ReceiveActor
    {
    private readonly IActorRef _dashboardActor;

    复制代码
      public SignificantMovementActor(IActorRef dashboardActor, IObservable<dynamic> significantMovements)  
      {  
          this._dashboardActor = dashboardActor;  
    
          significantMovements.Subscribe(movement =>  
          {  
              var significantMovement = new SignificantMovement  
              {  
                  Symbol = movement.Symbol,  
                  PriceChange = movement.PriceChange,  
                  StartPrice = movement.StartPrice,  
                  EndPrice = movement.EndPrice,  
                  Timestamp = movement.Timestamp  
              };  
    
              _dashboardActor.Tell(significantMovement);  
          });  
      }  

    }

创建执行组件系统,并在主应用程序逻辑中将所有内容绑定在一起。

复制代码
using Akka.Actor;  
using System;  
  
class Program  
{  
    static void Main(string[] args)  
    {  
        var system = ActorSystem.Create("StockMonitorSystem");  
        var dashboardActor = system.ActorOf<DashboardActor>("dashboardActor");  
  
        var significantMovements = StockAnalysis.AnalyzeStockTicks();  
        var props = Props.Create(() => new SignificantMovementActor(dashboardActor, significantMovements));  
        system.ActorOf(props, "significantMovementActor");  
  
        Console.WriteLine("System is running. Press any key to exit...");  
        Console.ReadLine();  
        system.Terminate().Wait();  
    }  
}

该方法设置并运行一个 Akka.NET actor 系统,该系统与 Rx.NET 集成,以处理和显示显着的库存变动。Main

让我们分解该方法的每个部分,以了解它是如何工作的,以及它如何实现对股票市场数据模拟和处理的持续监听。Main

var system = ActorSystem.Create("StockMonitorSystem");

此行初始化名为 的 Akka.NET 执行组件系统的新实例。执行组件系统是一个分层的执行组件组,它为创建执行组件、调度消息和管理执行组件生命周期提供基础结构。这是使用 Akka.NET 的入口点。StockMonitorSystem

复制代码
var dashboardActor = system.ActorOf<DashboardActor>("dashboardActor");

这将在执行组件系统中创建类型的执行组件。 是用于实例化 actor 的方法,返回对新创建的 actor 的引用。 是为此执行组件实例提供的名称,可用于在系统中查找它。DashboardActorActorOfdashboardActor

复制代码
var props = Props.Create(() => new SignificantMovementActor(dashboardActor, significantMovements));

Props 是一个配置类,用于描述如何创建 actor 的实例。通过使用 ,您可以指定应如何构造 ,包括其依赖项。在这里,它采用一个 lambda 表达式,该表达式构造一个新的 ,传入引用和可观察序列。Props.CreateSignificantMovementActorSignificantMovementActordashboardActorsignificantMovements

复制代码
system.ActorOf(props, "significantMovementActor");

此行创建使用前面定义的 props 的实例。它类似于 的创建方式,但使用 用于更复杂的初始化。SignificantMovementActordashboardActorprops

现在,从 Rx.NET 创建的参与者和可观察序列被设置为连续处理和显示重要的库存变动。侦听可观察对象并将其收到的任何消息转发到 .反过来,将处理这些消息(在本例中,将信息打印到控制台)。SignificantMovementActorsignificantMovementsdashboardActordashboardActor

复制代码
Console.ReadLine();

此行至关重要,因为它可以防止应用程序立即退出。它等待用户按下一个键,然后再继续,有效地保持应用程序(以及参与者系统)运行并能够处理传入的模拟股票数据。

复制代码
system.Terminate().Wait();

system.Terminate()启动执行组件系统的关闭,停止所有执行组件并释放资源。这是一个异步操作,返回 .Task

.Wait()在终止任务上,确保应用程序在执行组件系统完全关闭之前不会退出。这对于干净和优雅的退出非常重要,确保完成所有正在进行的处理并正确释放资源。

因此,该方法建立了一个连续的、反应性的处理管道,使用 Akka.NET 参与者来处理和显示重大的股票市场走势。Main

问:我能否通过发布到队列并从队列中侦听而不是使用 Rx.NET 和 Akka.NET 来实现相同的功能?

是的,队列可用于类似的数据流和处理,但 Rx.NET 提供了更具表现力和简洁的流处理能力,而 Akka.NET 提供了一个强大的框架,用于使用基于参与者的模型构建并发和分布式系统,从而增强容错能力和系统响应能力。

相关推荐
CJi0NG16 分钟前
【自用】MySQL-多表查询
数据库·mysql
雨落在了我的手上22 分钟前
MySQL数据库基础(12):事务
数据库·mysql
D姐聊门店25 分钟前
门店收银系统断网兜底怎么设计:本地账本、离线续传与日终对账的工程实现
数据库·saas
用户86629784800843 分钟前
抓包看 KingbaseES 内部协议?官方给了个现成工具
数据库
wefg11 小时前
【Redis】安装与启动
数据库·redis·缓存
大圣编蚕1 小时前
常见的 Redis 面试题(高频整理)
数据库·redis·缓存
派小心.2 小时前
网站统计采样数据的误差核对
大数据·服务器·前端·数据库·人工智能·小程序·数据分析
龙腾AI白云2 小时前
数字孪生、具身智能、世界模型三者交叉
数据库·人工智能·机器学习·flask·pygame
SelectDB技术团队2 小时前
Apache Doris 基于 Lance 的向量检索深度解析
数据库·数据仓库·数据分析·湖仓一体·apache doris·lakehouse·多模湖仓
这个DBA有点耶3 小时前
从INDEX到JOIN_ORDER:MySQL 8.0 Hint语法体系与五类实战场景
数据库·mysql·架构