程序员修神之路--有状态的服务其实可以做更多的事情 (3)

基于actor模型,系统设计的难点在于抽象业务模型,一旦业务模型稳定,我们完全可以用内存方式来保存对象状态(也可以定时去持久化),内存方式比用其他网络存储(例如redis)要快上几个量级,菜菜也有一篇文章大家可以去撸一下:https://mp.weixin.qq.com/s/6YL3SnSriKEnpCyB5qkk0g  ,既满足了一致性,又可以利用进程内对象状态来应对高并发业务场景,何乐而不为呢?

有不少同学问过我,actor模型要避免出现热点问题,就算有内存状态为其加速,那并发数还是超过actor的处理能力怎么办呢? 其实和传统做法类似,所有的高并发系统设计无非就是“分”一个字,无论是简单的负载均衡,还是复杂的分库分表策略,都是分治的一种体现。一台服务器不够,我就上十台,百台.....

程序员修神之路--有状态的服务其实可以做更多的事情

所有的高并发系统设计都是基于分治思想,把每一台服务器的能力发挥到极致,难度最大的还是其中的调度算法。

程序员修神之路--有状态的服务其实可以做更多的事情

用actor模型来应对高并发,我们可以采用读写分离的思想,主actor负责写请求,并利用某种通信机制把状态的变化通知到多个从actor,从actor负责对外的读请求,这个DB的读写分离思想一致,其中最难的当属actor的状态同步问题了,解决问题的方式千百种,总有一种适合你,欢迎你留言写下你认为最好的解决方案。

案例(玩家信息服务)

程序员修神之路--有状态的服务其实可以做更多的事情

由于菜菜是c#出身,对c#的Actor服务框架Orleans比较熟悉,这里就以Orleans为例,其他语言的coder不要见怪,Orleans是一个非常优秀的Actor模型框架,而且支持最新的netcore 3.0版本,地址为:https://github.com/dotnet/orleans  有兴趣的同学可以去看一下,而且分布式事物已经出正式版,非常给力。其他语言的也非常出色java:https://github.com/akka/akka

golang:https://github.com/AsynkronIT/protoactor-go


1. 首先我们定义玩家的状态信息

//玩家的信息,其实也就是玩家的状态信息
    public class Player {
        /// <summary>
        /// 玩家id,同时也是玩家这个服务的主键
        /// </summary>
        public long Id { getset; }
        /// <summary>
        /// 玩家姓名
        /// </summary>
        public string Name { getset; }
        /// <summary>
        /// 玩家等级
        /// </summary>
        public int Level { getset; }
    }

2. 接下来定义玩家的服务接口

 /// <summary>
    /// 玩家的服务接口
    /// </summary>
    interface IPlayerService: Orleans.IGrainWithIntegerKey
    {
        //获取玩家名称
        Task<string> GetName();
        //获取玩家等级
        Task<int> GetLevel();
        //设置玩家等级,这个操作会改变玩家的状态
        Task<int> SetLevel(int newLevel);
    }

3. 接下来实现玩家服务的接口

public class PlayerService : GrainIPlayerService
    {
        //这里可以用玩家的信息来代表玩家的状态信息,而且这个状态信息又充当了进程内缓存的作用
        Player playerInfo;
        public async Task<intGetLevel()
        
{
            return (await LoadPlayer()).Level;
        }

        public async Task<stringGetName()
        
{
            return (await LoadPlayer()).Name;
        }

        public async Task<intSetLevel(int newLevel)
        
{
            var playerInfo =await LoadPlayer();
            if (playerInfo != null)
            {
                //先进行数据库的更新,然后在更新缓存的状态, 进程内缓存更新失败的几率几乎为0
                playerInfo.Level = newLevel;                
            }
            return 1;
        }

        private async Task< Player> LoadPlayer()
        {
            if (playerInfo == null)
            {
                var id = this.GetPrimaryKeyLong();
                //这里模拟的信息,真实环境完全可以从持久化设备进行读取
                playerInfo= new Player() { Id = id, Name = "玩家姓名", Level = 1 };
            }
            return playerInfo;
        }
    }

程序员修神之路--有状态的服务其实可以做更多的事情

以上只是一个简单案例,有状态的服务还有更多的设计方案,以上只供参考

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzjgjg.html