zoukankan      html  css  js  c++  java
  • Orleans 知多少 | 4. 有状态的Grain

    Grain

    引言

    Orleans 的优势之一就是:支持有状态服务的水平扩展。那这一节我们就来看看如何来了解下有状态的Grain。

    第一个有状态的Grain

    先来看下上节中定义的Grain:SessionControlGrain

    public class SessionControlGrain : Grain, ISessionControlGrain
    {
        private List<string> LoginUsers { get; set; } = new List<string>();
    
        public Task Login(string userId)
        {
            //获取当前Grain的身份标识(因为ISessionControlGrain身份标识为string类型,GetPrimaryKeyString());
            var appName = this.GetPrimaryKeyString();
    
            LoginUsers.Add(userId);
    
            Console.WriteLine($"Current active users count of {appName} is {LoginUsers.Count}");
            return Task.CompletedTask;
        }
    
        public Task Logout(string userId)
        {
            //获取当前Grain的身份标识
            var appName = this.GetPrimaryKey();
            LoginUsers.Remove(userId);
    
            Console.WriteLine($"Current active users count of {appName} is {LoginUsers.Count}");
            return Task.CompletedTask;
        }
    
        public Task<int> GetActiveUserCount()
        {
            return Task.FromResult(LoginUsers.Count);
        }
    }
    

    上面的Grain中定义属性private List<string> LoginUsers { get; set; } = new List<string>();用来保存登录状态,其是保存在内存中的,一旦服务奔溃或重启,维护的状态数据就会丢失。
    很显然,这在真实应用场景中不被允许。

    在第一节中,已经对有状态和无状态有了解释,关键的区别在于:状态数据的是否持久化。因此上面针对ISessionControlGrain的实现SessionControlGrain是无状态的。

    那接下来就来看看如何用有状态的Grain来实现!

    针对统计登录用户的需求来说,其中的状态数据就是在线用户列表,所以可以直接定义一个LoginState来将行为和数据解耦。

    /// <summary>
    /// 登录状态
    /// </summary>
    public class LoginState
    {
        public List<string> LoginUsers { get; set; } = new List<string>();
    
        public int Count => LoginUsers.Count;
    }
    

    紧接着就可以重新实现一个ISessionControlGrain,如下:

    /// <summary>
    /// 有状态的Grain
    /// </summary>
    public class SessionControlStateGrain : Grain<LoginState>, ISessionControlGrain
    {
        public Task Login(string userId)
        {
            var appName = this.GetPrimaryKeyString();
            this.State.LoginUsers.Add(userId);
            this.WriteStateAsync();
    
            Console.WriteLine($"Current active users count of {appName} is {this.State.Count}");
            return Task.CompletedTask;
        }
    
        public Task Logout(string userId)
        {
            //获取当前Grain的身份标识
            var appName = this.GetPrimaryKey();
            this.State.LoginUsers.Remove(userId);
            this.WriteStateAsync();
    
            Console.WriteLine($"Current active users count of {appName} is {this.State.Count}");
            return Task.CompletedTask;
        }
    
        public Task<int> GetActiveUserCount()
        {
            return Task.FromResult(this.State.Count);
        }
    }
    

    对比两个Grain的实现,有状态的Grain主要有以下变化:

    1. 继承自Grain<T>,其中T用来指定当前Grain的附属状态对象。
    2. Grain中通过this.State来操作状态
    3. 通过调用this.WriteStateAsync();来显式持久化状态。

    那Grain的状态保存到哪里去了呢?

    Grain 状态仓库(Grain Storage)

    持久化方式

    开发环境下,可使用内存作为Grain的状态仓库。仅需在构建Orleans Silo时配置AddMemoryGrainStorageAsDefault()即可,如下所示:

    return Host.CreateDefaultBuilder()
        .UseOrleans((builder) =>
            {
                builder.UseLocalhostClustering()
                    .AddMemoryGrainStorageAsDefault()
                    .Configure<ClusterOptions>(options =>
                    {
                        options.ClusterId = "Hello.Orleans";
                        options.ServiceId = "Hello.Orleans";
                    })
                    .Configure<EndpointOptions>(options => options.AdvertisedIPAddress = IPAddress.Loopback)
                    .ConfigureApplicationParts(parts =>
                        parts.AddApplicationPart(typeof(ISessionControlGrain).Assembly).WithReferences());
            }
        )
    

    存在内存中,只是为了方便开发,显然在生产环境中是万万不可的。因此,可选择其他存储介质进行持久化。比如数据库等,Orleans 官方维护的状态持久化提供者有以下几种:

    当然除此之外,社区也维护系列开源项目支持将状态数据持久化到其他介质。
    接下来就来讲解如何持久化状态数据到SQL Server 数据库。

    持久化到 SQL Server

    SqlServer的配置并没有想象的那样简单,根据官方文档: Configuring ADO.NET ProvidersADO.NET Database Configuration,你会发现需要执行以下几步:

    1. Orleans Server 端添加对Microsoft.Orleans.Persistence.AdoNet NuGet包的引用
    2. 添加SQL Server 客户端驱动System.Data.SqlClient NuGet包的引用
    3. 创建SQL Server数据库,可使用VS 自带的localdb。
    4. 依次执行以下脚本,SQLServer-Main.sqlSQLServer-Persistence.sql 创建用于存储相关状态表。
    5. 添加配置代码

    为了简化配置,我做了一个简单的包装项目Orleans.AdoNet.Extensions,以简化SqlServer、MySql、Oracle和PostgreSql 的配置。以Sql Server 为例,仅需:

    1. 通过Nuget包管理器安装Orleans.AdoNet.SqlServer
    2. 安装后会打开一个readme.txt,复杂全部,并执行到数据库
    3. 服务端添加以下配置即可。
    Host.CreateDefaultBuilder()
        .UseOrleans((builder) =>
         {
             var connectionString =
                 @"Data Source=(localdb)MSSQLLocalDB;Initial Catalog=Hello.Orleans;Integrated Security=True;Pooling=False;Max Pool Size=200;MultipleActiveResultSets=True";
             
            //use AdoNet for Persistence
            builder.AddSqlServerGrainStorageAsDefault(options =>
            {
                options.ConnectionString = connectionString;
                options.UseJsonFormat = true;
            }); 
    

    重新运行项目,查询数据库,你会发现状态数据,实际上是持久化到Storage表中了。如下图所示:

    代码已上传至stategrain:Orleans/Hello.Orleans

  • 相关阅读:
    bash 笔记
    lvs: linux virtual server
    学习html第一天
    学习c语言的第14天
    c语言学习的第13天2
    c语言学习的第13天1
    c语言学习的第12天
    c语言学习的第11天 指针
    移动端点击延迟300毫秒----FastClick用法
    H5移动端复制功能实现
  • 原文地址:https://www.cnblogs.com/sheng-jie/p/orlans-state-grain.html
Copyright © 2011-2022 走看看