Orleans:基于 Virtual Actor Model 构建可扩展分布式应用的 .NET 框架实战指南
【免费下载链接】orleansCloud Native application framework for .NET项目地址: https://gitcode.com/gh_mirrors/or/orleans
Orleans 是一个跨平台、面向 .NET 的云原生应用框架,它把单机开发中熟悉的对象、接口、async/await、try/catch等概念扩展到多服务器环境,帮助开发者以接近单机编程的体验构建弹性、可扩展的云服务。本文以本仓库 README.md 为主线,结合 samples/HelloWorld 等示例与 src 下的源码实现,系统讲解 Grain 编程模型、Silo 运行时架构、核心特性(持久化、事务、流、定时器与提醒、灵活放置、版本化与异构集群等)以及从零构建与运行 Orleans 应用的完整路径。
读完本文,你将掌握:Grain 接口与实现的定义规范、IGrainFactory获取与调用 Grain 的机制、UseOrleans/UseLocalhostClustering宿主配置方法、Grain 生命周期与放置策略的原理,以及各核心特性在源码中的落点,能够直接动手编写并运行自己的第一个 Orleans 应用。
Orleans 是什么:把“分布式”藏进编程模型
Orleans 是微软研究院(Microsoft Research)创建并开源的框架,其核心贡献是提出了Virtual Actor Model(虚拟 Actor 模型)作为构建云时代分布式系统的新方法。它带来的核心收益在于:编程模型本身驯服了高度并行分布式系统固有的复杂性,同时不限制能力、也不给开发者强加繁重约束——这也是它常被称为 “Distributed .NET” 的原因。
- 规模弹性:从单台本地服务器,到云中全局分布的高可用应用,Orleans 都能平滑覆盖。
- 开发体验:开发者把注意力放在业务逻辑上,由运行时处理消息路由、故障转移、激活管理等问题。
- 技术基础:基于 .NET 生态,运行于 Windows、Linux、macOS,兼容 .NET Standard 2.0 及以上,可在 .NET Framework 或 .NET Core 上运行。
本仓库根目录 README.md 即项目权威入口,仓库主体源码位于 src,官方示例位于 samples,测试代码位于 test。
Grains:一切应用的基础构件
什么是 Grain
在任何 Orleans 应用中,最基本的构建单元是grain。Grain 是由**用户定义的标识(identity)、行为(behavior)与状态(state)**构成的实体:
- 稳定的标识:Grain 的标识是用户定义的键(key),这使得 Grain 无论是否已加载进内存都始终可被调用。
- 强类型契约:Grain 可被其他 Grain 或外部客户端(如 Web 前端)通过强类型的通信接口(contracts)调用。
- 实现类:每个 Grain 都是实现了这些接口之一的类的实例。
Grain 可以拥有易失性(volatile)和/或持久化(persistent)状态,状态可存放于任意存储系统。由于状态随 Grain 分区,系统天然获得自动扩展能力,也简化了故障恢复。Grain 激活期间状态驻留内存,从而获得更低的延迟与更小的存储负载。
架构与生命周期示意可参见仓库根目录下的 grain_formulation.svg(Grain 由稳定标识、行为、状态组成)与 managed_lifecycle.svg(Grain 的受管生命周期)。
Grain 的受管生命周期:激活/停用由运行时负责
Grain 的实例化由 Orleans 运行时按需自动完成;一段时间未使用的 Grain 会被自动从内存移除以释放资源。这一切之所以可行,正源于其稳定标识——调用方无需关心 Grain 当前位于哪台服务器、是否已被加载。运行时统一负责 Grain 的激活/停用(activation/deactivation)与放置/定位(placement/locating),因此开发者可以像“所有 Grain 永远驻留内存”那样编写代码。
从源码看,这一抽象的基础定义在 src/Orleans.Core.Abstractions/Core/Grain.cs:所有 Grain 实现类继承的抽象基类Grain实现了IGrainBase与IAddressable,通过GrainContext暴露运行时上下文、通过GrainFactory访问其他 Grain。Grain 的标识(GrainId)、激活标识(ActivationId)、接口类型等 ID 类型定义集中在 src/Orleans.Core.Abstractions/IDs。
稳定标识 + 状态性 + 受管生命周期,三者共同构成了基于 Orleans 构建的系统“可扩展、高性能、可靠”的核心因素——且这一切都无需开发者编写复杂的分布式系统代码。
键类型:如何标识一个 Grain 实例
Grain 接口通过继承特定的“带键标记接口”来声明自己用什么类型的键标识实例,定义见 src/Orleans.Core.Abstractions/Core/IGrain.cs:
| 标记接口 | 键类型 | 典型场景 |
|---|---|---|
IGrainWithGuidKey | Guid | 全局唯一标识,如订单、会话 |
IGrainWithIntegerKey | long | 自增/数值 ID,如用户编号 |
IGrainWithStringKey | string | 可读键,如设备序列号、租户名 |
实战示例:用 Grain 建模 IoT 云后端
考虑一个物联网(IoT)系统的云后端:它需要处理海量设备上报数据、过滤聚合、并向设备下发命令。在 Orleans 中,很自然的建模方式是每台设备对应一个 Grain,成为物理设备的“数字孪生”(digital twin)。这些 Grain 将最新设备数据保存在内存中,使查询与处理无需直接与物理设备通信;通过观察设备的时间序列数据流,Grain 还能检测到“测量值超过阈值”等条件变化并触发动作。
一个简单的温控器可以这样建模。首先定义设备上报侧接口:
public interface IThermostat : IGrainWithStringKey { Task<List<Command>> OnUpdate(ThermostatStatus update); }来自 Web 前端的温控器事件通过调用OnUpdate方法发送到其 Grain,该方法可选地返回一条命令给设备:
var thermostat = client.GetGrain<IThermostat>(id); return await thermostat.OnUpdate(update);同一个温控器 Grain 还可以实现另一个供控制系统交互的接口:
public interface IThermostatControl : IGrainWithStringKey { Task<ThermostatStatus> GetStatus(); Task UpdateConfiguration(ThermostatConfiguration config); }这两个接口(IThermostat与IThermostatControl)由同一个实现类实现:
public class ThermostatGrain : Grain, IThermostat, IThermostatControl { private ThermostatStatus _status; private List<Command> _commands; public Task<List<Command>> OnUpdate(ThermostatStatus status) { _status = status; var result = _commands; _commands = new List<Command>(); return Task.FromResult(result); } public Task<ThermostatStatus> GetStatus() => Task.FromResult(_status); public Task UpdateConfiguration(ThermostatConfiguration config) { _commands.Add(new ConfigUpdateCommand(config)); return Task.CompletedTask; } }注意:上面的ThermostatGrain并未持久化状态。关于带持久化的完整示例,可参考仓库 samples 中的持久化相关示例,如 samples/BankAccount(账户转账)、samples/JournaledTodoList(事件溯源待办列表)等。
Orleans 运行时:Silo 与集群
Orleans 运行时负责实现上述编程模型。运行时的主要组件是silo,负责托管 Grain。通常一组 silo 组成**集群(cluster)**运行,以获得可扩展性与容错能力:
- 集群中 silo 相互协调,分发工作负载、检测并恢复故障。
- 运行时让集群内托管的 Grain 彼此通信,仿佛处于同一进程。
- 除核心编程模型外,silo 还为 Grain 提供一系列运行时服务:定时器(timers)、提醒(reminders,即持久化定时器)、持久化(persistence)、事务(transactions)、流(streams)等。
Web 前端等外部客户端使用客户端库调用集群中的 Grain,该库自动管理网络通信;为简化部署,客户端也可与 silo 共置(co-hosted)于同一进程。
从 HelloWorld 示例理解完整运行链路
仓库中的 samples/HelloWorld 是官方最小可运行示例,完整展示了“定义接口 → 实现 Grain → 配置宿主 → 获取引用并调用”的全流程,代码可在 Program.cs、IHelloGrain.cs、HelloGrain.cs 中查看。
① 定义 Grain 接口(IHelloGrain.cs):
public interface IHelloGrain : IGrainWithStringKey { ValueTask<string> SayHello(string greeting); }继承IGrainWithStringKey即表明该接口是 Grain 接口,且实例以字符串键标识。
② 实现 Grain 类(HelloGrain.cs):
public sealed class HelloGrain : Grain, IHelloGrain { public ValueTask<string> SayHello(string greeting) => ValueTask.FromResult($"Hello, {greeting}!"); }继承Grain基类即标识其为 Grain 实现(参考 src/Orleans.Core.Abstractions/Core/Grain.cs)。
③ 配置宿主并运行(Program.cs):
// Configure the host using var host = new HostBuilder() .UseOrleans(builder => builder.UseLocalhostClustering()) .Build(); // Start the host await host.StartAsync(); // Get the grain factory var grainFactory = host.Services.GetRequiredService<IGrainFactory>(); // Get a reference to the HelloGrain grain with the key "friend" var friend = grainFactory.GetGrain<IHelloGrain>("friend"); // Call the grain and print the result to the console var result = await friend.SayHello("Good morning!"); Console.WriteLine($"\n\n{result}\n\n"); Console.WriteLine("Orleans is running.\nPress Enter to terminate..."); Console.ReadLine(); Console.WriteLine("Orleans is stopping..."); await host.StopAsync();关键点解读:
UseOrleans(...)是 Orleans 的宿主接入扩展方法;UseLocalhostClustering()配置本地单机集群,用于开发与测试场景。IGrainFactory从依赖注入容器中获取,其扩展方法定义在 src/Orleans.Core.Abstractions/Core/IGrainFactory.cs 附近;通过GetGrain<T>(key)拿到的是 Grain 的引用(GrainReference,见 src/Orleans.Core.Abstractions/Core/Grain.cs 中的GrainReference属性),首次调用时运行时才会真正激活对应实例。- 调用
friend.SayHello("Good morning!")后,控制台输出Hello, Good morning!!。此时运行时已经自动实例化了键为"friend"的HelloGrain,开发者无需管理其生命周期——按需激活、空闲停用。
工程文件 HelloWorld.csproj 显示该示例通过Microsoft.Orleans.Server包引入完整的服务端(silo)能力,并配合Microsoft.Extensions.Hosting使用 .NET 通用主机。
构建与运行示例
在仓库 samples/HelloWorld 目录下执行:
dotnet run即可看到Hello, Good morning!!输出。更多示例的构建脚本可参考 samples/Build-Samples.ps1 与各示例目录下的run.cmd/run.sh。
核心特性纵览
以下是 Orleans 提供的核心服务与能力,仓库根目录 README.md 的 Features 一节有权威概述,源码落点均在 src 对应项目。
持久化(Persistence)
Orleans 提供简单的持久化模型:在请求处理前确保状态可用,并维持一致性。要点:
- 一个 Grain 可以有多个命名的持久化数据对象,例如用户资料叫
"profile"、库存叫"inventory",且可存放于不同存储系统(资料在数据库 A、库存在数据库 B)。 - Grain 运行期间状态保存在内存,读请求无需访问存储。
- 状态更新时调用
state.WriteStateAsync()将后备存储同步更新,以保证持久性与一致性。
相关实现分布于 src/Orleans.Persistence.Memory(内存存储)、src/Azure/Orleans.Persistence.AzureStorage、src/Azure/Orleans.Persistence.Cosmos、src/AWS/Orleans.Persistence.DynamoDB、src/Google/Orleans.Persistence.Firestore 等。
分布式 ACID 事务(Transactions)
在简单持久化模型之上,Grain 还可拥有事务状态。多个 Grain 可以共同参与 ACID 事务,无论其状态最终存储在哪里。Orleans 的事务是分布式且去中心化的——没有中心事务管理器或协调器,并提供可串行化隔离(serializable isolation)。
相关实现位于 src/Orleans.Transactions 及各存储后端(如 src/Azure/Orleans.Transactions.AzureStorage、src/AWS/Orleans.Transactions.DynamoDB),测试见 test/Transactions。
流(Streams)
流帮助开发者近实时地处理序列化数据项。关键特性:
- 托管式:流无需在使用前显式创建或注册,发布者/订阅者可以自由解耦。
- 可靠:Grain 可保存检查点(cursor),并在激活时或之后任意时刻回滚到已保存的检查点。
- 批量投递:支持向消费者批量投递消息,提升效率与恢复性能。
- 后端多样:由 Azure Event Hubs、Amazon Kinesis 等队列服务支撑;任意数量的流可多路复用(multiplex)到较少队列上,处理这些队列的职责在集群内均匀平衡。
实现见 src/Orleans.Streaming,以及各适配器项目如 src/Azure/Orleans.Streaming.EventHubs、src/AWS/Orleans.Streaming.Kinesis、src/AWS/Orleans.Streaming.SQS、src/Orleans.Streaming.NATS。示例可参考 samples/Streaming。
定时器与提醒(Timers & Reminders)
- 提醒(Reminders)是面向 Grain 的持久化调度机制:即使 Grain 当前未激活,也能确保未来某个时刻执行某个动作。
- 定时器(Timers)是提醒的非持久化对应物,适用于高频、不需要可靠性的场景。
定时器 API 的基类实现可参见 src/Orleans.Core.Abstractions/Core/Grain.cs 中的RegisterTimer/RegisterGrainTimer;提醒相关实现位于 src/Orleans.Reminders 及各存储后端(如 src/Azure/Orleans.Reminders.AzureStorage)。
灵活的 Grain 放置(Flexible Grain Placement)
当 Grain 被激活时,运行时决定在哪台 silo 上激活它,这就是放置(placement)。Orleans 的放置过程完全可配置:
- 开箱即用的策略:随机(random)、偏好本地(prefer-local)、基于负载(load-based)等。
- 也支持配置自定义放置逻辑,从而完全自由地决定 Grain 创建位置,例如放到靠近其依赖资源或其他通信 Grain 的服务器上。
实现位于 src/Orleans.Core/Placement 与 src/Orleans.Runtime/Placement,测试见 test/Orleans.Placement.Tests。
Grain 版本化与异构集群(Grain Versioning & Heterogeneous Clusters)
应用代码会不断演进,而安全地升级有状态的生产系统颇具挑战。Orleans 的支持方式:
- Grain 接口可可选地加版本号。
- 集群维护一份映射:哪些 silo 上部署了哪些 Grain 实现、以及这些实现的版本。
- 运行时结合放置策略,在路由调用时利用版本信息做放置决策。
这既支持版本化 Grain 的安全更新,也支持异构集群——不同 silo 可拥有不同的 Grain 实现集合。相关实现见 src/Orleans.Core.Abstractions/Versions 与 src/Orleans.Runtime/Versions。
弹性扩展与容错(Elastic Scalability & Fault Tolerance)
Orleans 天然支持弹性扩展:
- silo 加入集群即可接受新的激活;
- silo 离开集群(缩容或宕机)时,其上激活的 Grain 会按需在其他 silo 上重新激活;
- 集群可缩容到单 silo;
- 弹性扩展的同一套性质同时带来容错:集群自动检测并快速从故障中恢复。
随处运行(Run Anywhere)
只要 .NET Core 或 .NET Framework 受支持,Orleans 就能运行:Linux、Windows、macOS;可部署到 Kubernetes、虚拟机或物理机、本地或云端,以及 Azure Container Apps、Azure App Service、Azure Kubernetes Service 等 PaaS 服务。仓库 samples/Deployment 下提供了 Azure App Service 与 Azure Container Apps 的部署示例。
无状态工作器(Stateless Workers)
无状态工作器是特殊标记的 Grain:不关联任何状态,可同时在多个 silo 上激活,从而为无状态函数带来更高并行度。相关实现可参考 src/Orleans.Core/Placement 中的放置策略。
Grain 调用过滤器(Grain Call Filters)
适用于多个 Grain 的公共逻辑可以表达为拦截器(interceptor)/ Grain 调用过滤器。Orleans 同时支持入站与出站调用过滤器,常见用途包括:授权、日志与遥测、错误处理。接口定义见 src/Orleans.Core.Abstractions/Core/IGrainCallFilter.cs 与 src/Orleans.Core.Abstractions/Core/IGrainCallContext.cs。
请求上下文(Request Context)
元数据及其他信息可以借助请求上下文沿一系列请求传递,典型用途是承载分布式追踪信息或任意用户自定义值。
从源码构建 Orleans 与使用官方制品
构建解决方案
面向贡献者:构建、测试与使用本地源码项目的前置要求,参见仓库根目录 CONTRIBUTING.md。
直接构建整个解决方案:
dotnet build Orleans.slnx -bl在 Windows 上,Build.cmd还会打包解决方案并把包输出到Artifacts/<Configuration>。
官方制品渠道
- 稳定版:最新稳定、生产级质量的发布版本通过 NuGet 分发。
- 夜间版(Nightly builds):发布到专门的 NuGet 源,通过全部功能测试,但不如稳定版/预发布版测试充分。
在项目中使用夜间版包有两种方式:
方式一:修改 .csproj 增加 RestoreSources:
<ItemGroup> <RestoreSources> $(RestoreSources); https://pkgs.dev.azure.com/dnceng/public/_packaging/orleans-nightly/nuget/v3/index.json </RestoreSources> </ItemGroup>方式二:在解决方案目录创建NuGet.config:
<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <clear /> <add key="orleans-nightly" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/orleans-nightly/nuget/v3/index.json" /> <add key="nuget" value="https://api.nuget.org/v3/index.json" /> </packageSources> </configuration>配套模板与更多示例
- 项目模板位于 templates/Microsoft.Orleans.Templates,可快速生成新应用骨架。
- 仓库 samples 汇集了从 Hello World、银行账户转账到事件溯源待办列表、实时聊天、股票行情、TicTacToe、GPS 追踪等各类场景示例,是学习各特性的第一手材料。
- 基于本仓库源码的 API 文档入口位于 docs/index.md,可结合具体模块的 src 源码与 test 测试代码交叉验证行为细节。
结语
Orleans 通过 Virtual Actor Model 把分布式系统的复杂性收敛到运行时内部:Grain 的稳定标识、状态性与受管生命周期让应用天然具备可扩展性、性能与可靠性;Silo 集群提供了弹性伸缩、故障恢复与丰富的运行时服务(持久化、事务、流、提醒、灵活放置、版本化等)。无论是 IoT 数字孪生、实时消息类应用还是事务密集型业务系统,你都可以从本文的 Grain 建模方法与 Hello World 运行链路出发,结合 samples 中的真实示例,快速把 Orleans 应用到自己的分布式项目中。
【免费下载链接】orleansCloud Native application framework for .NET项目地址: https://gitcode.com/gh_mirrors/or/orleans
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考