写点什么

.NET 分布式 Orleans - 2 - Grain 的通信原理与定义

  • 2024-03-25
    福建
  • 本文字数:2931 字

    阅读完需:约 10 分钟

Grain 是 Orleans 框架中的基本单元,代表了应用程序中的一个实体或者一个计算单元。

每个 Silo 都是一个独立的进程,Silo 负责加载、管理和执行 Grain 实例,并处理来自客户端的请求以及与其他 Silo 之间的通信。


通信原理


在相同的 Silo 中,Grain 与 Grain 之间的通信通过直接的方法调用实现。每个 Silo 都维护了一个 Grain 的运行时环境,当一个 Grain 需要调用另一个 Grain 时,它可以直接调用目标 Grain 的方法,无需经过网络传输,示意图如下所示:



在不同的 Silo 中,Grain 与 Grain 之间的通信需要通过消息传递的方式实现。当一个 Grain 需要与另一个 Silo 中的 Grain 通信时,它会将消息发送给目标 Grain 所在的 Silo,目标 Silo 接收到消息后,将其路由到目标 Grain,然后目标 Grain 处理消息并返回结果。示意图如下所示:



外部客户端与 Silo 之间的通信是通过网络消息传输实现的。客户端需要使用 Orleans 提供的客户端库与 Silo 建立连接,并发送请求消息到目标 Silo,目标 Silo 接收到消息后,进行处理并返回结果。在 Orleans 中,客户端与 Silo 之间的通信使用了一种名为 Orleans Messaging Protocol (OMP)的自定义协议,用于保证通信的可靠性和效率。示意图如下所示:



内置端口


默认情况下,Orleans 将侦听端口 11111 用于 silo 之间通信,在端口 30000 上进行客户端到接收器通信。可以通过以下方式设置这些端口

siloBuilder.Configure<EndpointOptions>(options =>{    // Port to use for silo-to-silo    options.SiloPort = 11_111;    // Port to use for the gateway    options.GatewayPort = 30_000;    // IP Address to advertise in the cluster    options.AdvertisedIPAddress = IPAddress.Parse("172.16.0.42");    // The socket used for client-to-silo will bind to this endpoint    options.GatewayListeningEndpoint = new IPEndPoint(IPAddress.Any, 40_000);    // The socket used by the gateway will bind to this endpoint    options.SiloListeningEndpoint = new IPEndPoint(IPAddress.Any, 50_000);})
复制代码


在内部,silo 将侦听 0.0.0.0:40000 和 0.0.0.0:50000,但在持久化提供程序中发布的值将是 172.16.0.42:11111 和 172.16.0.42:30000

 

GrainKey 类型


在 Orleans 中,可以使用不同类型的键来标识 Grain。下面是几种常用的 GrainKey 接口:

  • IGrainWithStringKey: 使用字符串作为键的接口。适用于需要以字符串形式标识的场景,比如用户名称、订单号等。

  • IGrainWithGuidKey: 使用 Guid 作为键的接口。适用于需要全局唯一标识的场景,比如唯一的实体对象、全局唯一的标识符等。

  • IGrainWithIntegerKey: 使用整数作为键的接口。适用于需要连续递增或递减的序列标识的场景,比如自增主键、序列号等。

  • IGrainWithGuidCompoundKey: 使用复合的 Guid 作为键的接口。

  • IGrainWithIntegerCompoundKey: 使用复合的整数作为键的接口。

  • IGrainWithGuidCompoundKey: 使用复合的字符串作为键的接口。

 

下面是使用 IGrainWithStringKey 定义的 IPlayerGrain 接口,并为其增加了买装备的动作,并将买完的装备保存至内存中:

public interface IPlayerGrain : IGrainWithStringKey{    Task BuyEquipment(string equipmentName);    Task<List<string>> GetOwnedEquipments();}
public class PlayerGrain : Grain, IPlayerGrain{ private IPersistentState<List<string>> _ownedEquipments;
public PlayerGrain([PersistentState("ownedEquipments", "playerGrainStorage")] IPersistentState<List<string>> ownedEquipments) { _ownedEquipments = ownedEquipments; }

public async override Task OnActivateAsync(CancellationToken cancellationToken) { await base.OnActivateAsync(cancellationToken);
// 在激活时从持久化状态中加载数据 await _ownedEquipments.ReadStateAsync(); if (_ownedEquipments.State == null) { _ownedEquipments.State = new List<string>(); await _ownedEquipments.WriteStateAsync(); // 将空列表持久化到存储中 } }
public async Task BuyEquipment(string equipmentName) { _ownedEquipments.State.Add(equipmentName); await _ownedEquipments.WriteStateAsync(); // 将更新后的装备列表持久化到存储中 }
public Task<List<string>> GetOwnedEquipments() { return Task.FromResult(_ownedEquipments.State); }}
复制代码


调用时使用 IGrainFactory.GetGrain 方法即可

var host = Host.CreateDefaultBuilder()    .ConfigureServices((context, services) =>    {        services.AddOrleans(builder =>        {            builder                .UseLocalhostClustering()                .Configure<ClusterOptions>(options =>                {                    options.ClusterId = "dev";                    options.ServiceId = "OrleansExample";                })                .AddMemoryGrainStorage("playerGrainStorage");        });    })    .ConfigureLogging(l => l.AddConsole())    .Build();
await host.StartAsync();
var client = host.Services.GetRequiredService<IGrainFactory>();
var palyer = client.GetGrain<IPlayerGrain>(Guid.NewGuid().ToString());await palyer.BuyEquipment("Sword");(await palyer.GetOwnedEquipments()).ForEach(Console.WriteLine);
复制代码


IGrainFactory 和 IClusterClient


在 Orleans 中,IGrainFactory 和 IClusterClient 都是用于创建和获取 Grains 的接口,但它们的作用和使用场景略有不同。


IGrainFactory:

IGrainFactory 是 Orleans 用于集群中创建 Grains 的工厂接口。 它通常用于在 Orleans Silo 或者 Orleans Client 中创建 Grains 实例。

  • 在 Silo 中,您可以通过依赖注入或者直接实例化一个 IGrainFactory 对象来创建 Grains。

  • 在 Silo 外部,比如 Orleans Client 中,您也可以通过依赖注入或者直接实例化一个 IGrainFactory 对象来创建 Grains。


// 通过依赖注入或直接实例化一个 IGrainFactory 对象IGrainFactory grainFactory = serviceProvider.GetRequiredService<IGrainFactory>();var grain = grainFactory.GetGrain<IMyGrain>(grainId);
复制代码


IClusterClient:


IClusterClient 是 Orleans 中用于与 Orleans 集群进行通信的客户端接口。 它通常在 Orleans Client 中使用,用于与 Orleans Silo 进行通信,以调用 Grains 的方法或者获取 Grains 的引用。

IClusterClient 是 IGrainFactory 的一个超集,除了可以创建 Grains,还可以执行其他集群相关的操作,比如管理 Silo 的生命周期、订阅集群中的事件等。

// 通过依赖注入或直接实例化一个 IClusterClient 对象IClusterClient clusterClient = serviceProvider.GetRequiredService<IClusterClient>();var grain = clusterClient.GetGrain<IMyGrain>(grainId);
复制代码


文章转载自: chester·chen

原文链接:https://www.cnblogs.com/chenyishi/p/18091260

体验地址:http://www.jnpfsoft.com/?from=001

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
.NET分布式Orleans - 2 - Grain的通信原理与定义_.net_不在线第一只蜗牛_InfoQ写作社区