通过Dapr实现一个简单的基于.net的微服务电商系统(六)——一步一步教你如何撸Dapr之Actor服务...

  我个人认为Actor应该是Dapr里比较重头的部分也是Dapr一直在讲的所谓“stateful applications”真正具体的一个实现(个人认为),上一章讲到有状态服务可能很多同学看到后的第一反应是“不就是个分布式缓存吗”。那今天就讲讲Actor,看看这个东西到底能不能算得上有状态服务,同时由于篇幅有限,这里只会快速的过一遍Actor相关的概念,着重还是代码层面的实现。

目录:

一、通过Dapr实现一个简单的基于.net的微服务电商系统

二、通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解

三、通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr

四、通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布

通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理

附录:(如果你觉得对你有用,请给个star)
一、电商Demo地址:https://github.com/sd797994/Oxygen-Dapr.EshopSample

二、通讯框架地址:https://github.com/sd797994/Oxygen-Dapr

  最早我接触到Actor应该是微软的Orleans框架(熟悉Actor或者Orleans的同学这一大段可以直接跳过),百度Actor关键词一大堆“通用并发编程模型”可能让人云里雾里的,其实它并不是一个特别复杂的概念。什么是并发编程?这个概念大家应该很熟悉了,现在主流的web服务器(如.netcore的kestrel或者dotnetty)几乎都是支持并行访问的,通过线程池充分调度操作系统的多线程来并行完成任务。在传统的多线程模式中如果多个线程同时访问某个数据并对其进行非幂等操作,往往是线程不安全的。

  在单应用时代我们可以很方便的通过lock关键字或者semaphore信号量或者concurrent线程安全集合或者Interlocked这样的CAS原子操作去规避多线程访问导致的数据不安全,亦或者直接采用以数据库事务为基础的乐观 or 悲观事务来实现,而一旦我们的应用由于吞吐瓶颈需要以集群的方式部署时或者分布式部署后对数据库也进行了拆分后,上面的那些方案都会失效或者会导致高昂的成本(比如数据库分布式事务协调机制)。这个时候往往需要引入一些分布式组件比如zookeeper或者redis锁来解决。这也是分布式系统比较常用的数据一致性方案。而actor则是提出了一个新的在分布式环境下解决多线程污染数据的思路。

  actor概念相对比较复杂这里就不展开了,简单粗暴的来理解就是在内存里为每一个actor对象维护了一个消息队列,当任意的请求不管该请求是来自于其他进程的线程亦或是当前进程的线程,都会将请求写入该消息队列,而Actor对象会监听该队列,当收到消息后Actor会处理该请求,在请求处理期间,外部线程会被阻塞在消息队列中,并且新的请求也会入队等待,直到actor对象完成操作后从队列里取出下一个请求处理直到整个队列为空。同时每一个actor对象在其临界区内的内存是私有的,并不会被其他线程共享,从而就实现了内存安全。这样当我们客户端发起数个请求访问一个或多个Actor对象时每个请求都会进入对应的Actor对象的消息队列(术语叫Mailboxs)并等待actor消费。同时Dapr框架会确保同一个Actor对象在同一时间在整个分布式系统中只会被激活一个实例!从而确保了你无论从分布式系统的任意角落访问某个Actor对象(user?id=1),总能得到唯一的一个实例

  Dapr框架会确保你的Actor实例永远能够被访问到(正确激活),哪怕对象在长时间未被访问后系统回收休眠亦或者在未处理的异常导致其崩溃后

  正确使用Actor唯一的要求就只有一条,由于Actor是一个内存并发模型所以不要在并发访问Actor时去做任意的可能的IO阻塞(比如读取数据库)!

  开始撸码,首先我们做一个RPC服务,看看多线程访问下的数据会是什么个情况,再对比一下Actor模式!在RPC层我们创建一个接口,代表产品服务,其有两个方法对应读取产品以及减扣库存

   接着我们在servicesample层实现一下这个服务(这里直接创建一个静态变量模拟多线程下访问共享内存数据的场景)

   接着我们在clientsample发起对着两个服务的RPC调用

   现在我们通过并发测试统计jmter对其进行并发测试,并发1000个线程去减100个库存,最后我们通过postman去访问get方法看看结果是什么

 

减库存前

 

 

 

 并行访问1000次

  

  可以看到由于没有并发控制,我们的库存被扣负了。现在我们开始对其进行Actor改造。首先我们将接口继承iactorservice并申明服务的方法为actor(这一步的目的是为类型生成actor代理)

    [RemoteService("servicesample", "product")]public interface IProductService : IActorService{[RemoteFunc(FuncType.Actor)]Task<ProductOutput> Get(ProductInput input);[RemoteFunc(FuncType.Actor)]Task<ProductOutput> ReduceStock(ProductInput input);}

  接着我们让入参类继承一个基类,这个基类需要派生类重写其Actorid字段。原因是Actor是通过全局唯一标识符在内部被标识的,访问相同标识会被路由到同一个actor。

    public class ProductInput : ActorSendDto{public int PorductId { get; set; }public int ReduceStock { get; set; }public override string ActorId { get; set; }}

  接下来我们改造一下clientsample的调用方法,这里修改的部分不多,只是把代理生成的方式替换了一下

        public async Task<dynamic> GetProduct(){var actorService = serviceProxyFactory.CreateActorProxy<IProductService>();return await actorService.Get(new ProductInput() { ActorId = "1", PorductId = 1 });}public async Task<dynamic> ProductReduceStock(){var actorService = serviceProxyFactory.CreateActorProxy<IProductService>();return await actorService.ReduceStock(new ProductInput() { ActorId = "1", PorductId = 1, ReduceStock = 1 });}

  接着我们对servicesample进行改造,首先我们需要在hostbuilder里替换掉默认的OxygenStartup,OxygenActorStartup会帮我们扫描类型生成对应的actor代理(其他代码无变化,略)

           .ConfigureWebHostDefaults(webhostbuilder => {//注册成为oxygen服务节点webhostbuilder.StartOxygenServer<OxygenActorStartup>((config) => {config.Port = 80;config.PubSubCompentName = "pubsub";config.StateStoreCompentName = "statestore";config.TracingHeaders = "Authentication";});})

  接着我们需要将之前的商品持久化PO类继承一个基类ActorStateModel,该基类会强制派生类重写两个属性AutoSave和ReminderSeconds,前者代表是否自动持久化(调用Actor SDK的Statemanage持久化到中间件,第二个代表如果开启持久化,是瞬时持久化还是由Actor的Timer按照周期持久化,这里的设计有点类似于redis aof模式下的always和everysec,前者(ReminderSeconds=0)采用每一次变更同步一次,性能损耗较大,后者采用每n(取决于ReminderSeconds设置)秒通过timer异步同步一次,同时我在Actor代理中添加了版本管理,并不会导致你的ReminderSeconds设置了周期同步后到时间就会请求你的同步委托,而是检测到版本变化后才会请求),这里我测试就直接开启自动同步并使用always模式

    public class ProductPo : ActorStateModel{public int Id { get; set; }public string Name { get; set; }public int Stock { get; set; }public override bool AutoSave { get; set; } = true;public override int ReminderSeconds => 0;}

  最后我们对ProductService进行改造,如下:

    public class ProductService : BaseActorService<ProductPo>, IProductService{static int visitCount = 0;static ProductPo ProductPoInstance;public async Task<ProductOutput> Get(ProductInput input){ActorData ??= new ProductPo() { Id = 1, Name = "小白菜", Stock = 100 };return new ProductOutput() { Message = $"第{visitCount}次请求成功,当前库存剩余{ActorData.Stock}" };}public async Task<ProductOutput> ReduceStock(ProductInput input){Interlocked.Increment(ref visitCount);await Task.Delay(new Random(Guid.NewGuid().GetHashCode()).Next(20, 50));//模拟数据库耗时ActorData ??= new ProductPo() { Id = 1, Name = "小白菜", Stock = 100 };if (ActorData.Stock >= input.ReduceStock){await Task.Delay(new Random(Guid.NewGuid().GetHashCode()).Next(50, 100));//模拟数据库耗时ActorData.Stock -= input.ReduceStock;}return new ProductOutput() { Message = $"第{visitCount}次请求成功,当前库存剩余{ActorData.Stock}" };}public override async Task SaveData(ProductPo model, ILifetimeScope scope){Console.WriteLine("同步请求被调用了,此处可以进行数据库持久化!");await Task.CompletedTask;}}

  可以看到我的服务继承了一个基类BaseActorService,并需要传递一个类型为ActorStateModel的泛型,这样在我的服务里不再通过IO去拉取ProductPoInstance,而是直接使用ActorData这个泛型实例进行各种操作即可,所以我删除掉了对应的数据库模拟耗时(避免actor队列访问阻塞),最后你必须重写BaseActorService的SaveData方法,该方法就是上文提到的同步委托,当我们开启AutoSave时,ReminderSeconds=0会在actor被调用操作完成后激活该委托,ReminderSeconds>0时会被定时器定期根据actor对比版本后判断是否需要激活。同时无论哪种方式我都在actor代理内部维护了一个channel异步队列通过异步订阅发布的方式实现非阻塞式的actor持久化而不用担心持久化导致的io阻塞问题。SaveData入参返回的一个ILifetimeScope容器可以很方便的获取到你的repository或者直接获取ef的上下文进行对应的数据库持久化操作(这里需要注意一下,Actor持久化有两层意思,第一层意思是Actor sdk会自带一个StateManager,当Component开启actor支持后,可以通过StateManager将actor对象写入中间件,而这里提供的SaveData是我封装的一个通过订阅发布异步调用的委托,方便开发人员持久化到数据库用的,非actor原生自带的设计)。

  最后我们需要扩展我们的Component,需要开启Actor持久化支持,编辑文件后用kubectl apply -f x.yaml即可:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:name: statestore
spec:type: state.redisversion: v1metadata:- name: actorStateStorevalue: "true"- name: redisHostvalue: redis.infrastructure.svc.cluster.local:6379- name: keyPrefixvalue: none

  接下来我们看看通过jmter重新请求后的情况

 

  可以看到Actor确实解决了并发访问安全的问题,同时也能看到我们的委托被正确的调用了。

  总结一下,Actor确实通过其特殊的设计模式解决了并发访问数据安全的问题,同时也带来了一些问题诸如需要特定框架支持,诸如Actor行为内不能阻塞等等限制,不过相比其带来的无锁对象访问来讲,这点限制都是可以克服的,至少在特定场景下比如抢票、发红包等等有一定并发同时又需要确保数据一致的场景,Actor算是一个可选方案。至于更多的场景探索则需要同学们自己去摸索了,今天的分享就到这里。下期不出意外的话我们会分享一下Dapr的服务限流

相关文章:

  • Dapr能否引领云原生中间件的未来?

  • 云原生 | 阿里巴巴的Dapr实践与探索

  • Dapr | 云原生的抽象与实现

  • Dapr 可视化指南

  • Dapr 知多少 | 分布式应用运行时

  • Dapr 正式发布 1.0

  • Dapr 交通流量控制示例

  • Dapr是如何简化微服务的开发和部署

  • 微软开源微服务运行时Dapr,赋能云原生应用开发

  • YARP实现Dapr服务调用的反向代理

  • Dapr微服务应用开发系列0:概述

  • Dapr微服务应用开发系列1:环境配置

  • Dapr微服务应用开发系列2:Hello World与SDK初接触

  • Dapr微服务应用开发系列3:服务调用构件块

  • Dapr微服务应用开发系列4:状态管理构件块

  • Dapr微服务应用开发系列5:发布订阅构建块

  • Windows环境下Dapr入门

  • 云原生 | .NET 5 with Dapr 初体验

  • 通过Dapr实现一个简单的基于.net的微服务电商系统

  • 通过Dapr实现一个简单的基于.net的微服务电商系统(二)——通讯框架讲解

  • 通过Dapr实现一个简单的基于.net的微服务电商系统(三)——一步一步教你如何撸Dapr

  • 通过Dapr实现一个简单的基于.net的微服务电商系统(四)——一步一步教你如何撸Dapr之订阅发布

  • 通过Dapr实现一个简单的基于.net的微服务电商系统(五)——一步一步教你如何撸Dapr之状态管理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/302999.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

想转行人工智能?哈佛博士后有话说!

从17年开始&#xff0c;各大高校的数据科学与大数据技术专业持续火爆&#xff0c;2018年&#xff0c;北京大学、西安交通大学等高校更在本科阶段设立人工智能一级学科&#xff0c;中国顶尖人才的流向在悄然改变……据目前最新的数据显示&#xff0c;AI行业开发人员的月薪基本上…

转载标明出处用英语_英语原版阅读:At the beach

今天分享一篇阅读理解。可以学完一般现在时后进行同步阅读&#xff0c;也可以作为日常的阅读材料。每日10分钟英语阅读&#xff0c;养成习惯&#xff0c;孩子的英语学习不用愁。这篇文章的题目是At the beach图片来源于网络先来读文章&#xff1a;图片来源于网络1.In summer I …

12 月份 10 个新鲜的 jQuery 插件和教程

1. MASHA (Mark & Share) MASHA (Mark & Share 的缩写) 是一个可以让你分享网页部分内容的 JavaScript 库。 2. JScraft scroller 通过点击某个图片&#xff0c;该图将移到网页中央&#xff0c;其他相应的图片进行位置滑动。在线演示&#xff1a;here. 3. Windows-like…

大数据时代,如何才能提高自身竞争力?

暑假咻地一下过完啦&#xff0c;前几天&#xff0c;小天介绍了关于数模课程的开学季限时优惠&#xff08;传送门&#xff09;&#xff0c;今天要介绍的是python课程。接下来&#xff0c;小天来详细说明一下&#xff01;领取方式&#xff1a;公众号后台对话框回复“人工智能”免…

从容器到容器云,什么才是Kubernetes的本质?

这两年&#xff0c;Kubernetes 击败了 Swarm 和 Mesos&#xff0c;几乎成为容器编排的事实标准&#xff0c;BAT、滴滴、京东、头条等大厂&#xff0c;都争相把容器和 K8S 项目作为技术重心&#xff0c;试图“放长线钓大鱼”。就说阿里吧&#xff0c;目前基本所有业务都跑在云上…

Java 的日子屈指可数,这是真的吗?

斯坦福大学的计算机科学入门课采用JavaScript&#xff0c;摈弃Java&#xff0c;但是它的基数很大。年4月初&#xff0c;斯坦福大学开始试推行计算机科学入门课CS 106A的新版本。这个名为CS 106J的新版本用JavaScript来教&#xff0c;而不是用Java来教。斯坦福大学的官方网站解释…

5gh掌上云计算认证不通过_【众志成城战疫情】法官助理告诉你“移动微法院”、“掌上法庭”有多便捷、有多硬核~!...

今天中午&#xff0c;小编收到了一篇来自普定法院白岩法庭法官助理的投稿&#xff0c;她一方面告知小编白岩法庭今天上午通过微法院“掌上法庭”成功审理了一起买卖合同纠纷案纷&#xff0c;一方面强烈给小编安利了这个小程序&#xff0c;并且要求在本文结尾附上微法院的操作手…

“杀”一个程序员不需要用枪,改三次需求就可以了!

在很多软件公司&#xff0c;特别是一些创业型的团队中&#xff0c;对于这样的情景可能大家都很熟悉&#xff1a;项目经理或者产品经理&#xff08;产品狗&#xff09;口头或者简单记录一下软件产品的大致要做的功能&#xff0c;直接就让研发团队的兄弟&#xff08;程序猿&#…

WPF 仿QQ登录框翻转效果

突然发现qq登录窗体的翻转特效看起来不错&#xff0c;决定用wpf试试。主要知识点就是Viewport3D和AxisAngleRotation3D看一下效果&#xff1a;下面看一下代码&#xff1a;主要xaml代码&#xff1a;<UserControl x:Class"GQ.DirectContentSample"xmlns"http:/…

机器人J中WPR_优傲:协作机器人的未来在哪里?

2019年9月17日&#xff0c;第21届中国国际工业博览会于上海国家会展中心如期举办&#xff0c;期间优傲机器人(Universal Robots)公司推出新品&#xff0c;UR16e。有效载荷高达16kg&#xff0c;引起业界广泛的关注。优傲机器人总裁Jrgen von Hollen表示&#xff1a;“在当今不明…

你试过不用if撸代码吗?

试着不用if撸代码&#xff0c;是件很有趣的事&#xff0c;而且&#xff0c;万一你领会了什么是“数据即代码&#xff0c;代码即数据”呢&#xff1f;我在教新手编程时&#xff0c;喜欢给他们一些小小的挑战&#xff0c;比如&#xff1a;不使用if语句(或者三元运算符、switch语句…

NET问答: 对 Linq 中的 Union 和 Concat 的用法困惑

咨询区 Prasad Kanaparthi&#xff1a;我在使用 Union 和 Concat 上有一个困惑&#xff0c;从字面上理解&#xff1a;一个是并集&#xff0c;一个是连接&#xff0c;下面的例子就是我对这两个扩展方法的理解。static void Main(string[] args){var a1 (new[] { 1, 2 }).Union(…

中止是怎么用的_多士炉怎么用 使用多士炉注意事项

阅读本文前&#xff0c;请您先点击上面的蓝色字体&#xff0c;再点击“关注”&#xff0c;这样您就可以继续免费收到最新文章了。每天都有分享。完全是免费订阅&#xff0c;请放心关注。 …

国内 GitHub 造假黑色产业链曝光;开源开发者撤销对 ICE 禁用的决定

0、国内 GitHub 被爆造假&#xff0c;起底背后的黑色产业链作为全球最大的开源社区&#xff0c;GitHub 对于程序员群体而言像是空气般重要的存在&#xff0c;而互联网公司也会通过 GitHub 来进一步了解面试者的编程习惯&#xff0c;技术水平等。但最近知乎上有一篇《中国内地 G…

Azure data studio 跨平台数据库管理工具试用

最近折腾 azure sql database 的时候发现了微软的一款新的数据库管理工具&#xff1a;azure data studio。从名字上看 azure data studio 好像是专门为 azure 开发的&#xff0c;其实并不是这样的 。它同样支持对传统sql server的查询与管理。azure data studio 是一款跨平台数…

顺丰gis产品经理_线上面试季丰图科技—顺丰旗下专注GIS领域

WHITer内推——每天9点发布武汉优质互联网企业最新岗位内推机会。内推微信号&#xff1a;whxiaowai 内推邮箱&#xff1a;770554595qq.com01 Java高级开发工程师 12-25K点击查看职位详情 申请内推02高级python开发工程师 12-25K点击查看职位详情 申请内推03 物联网数据运营分析…

腾讯发布95页重磅报告:全面预测中国互联网未来5年趋势

腾讯科技企鹅智酷联合63位互联网行业领袖与专家发布了《企鹅智酷中国科技&互联网创新趋势白皮书&#xff08;2017&#xff09;》&#xff0c;预测了未来5年的中国互联网发展趋势。此次公开发布的版本共95页PPT&#xff0c;其中“移动直播产业”、“媒体与内容创业”、“互联…

.NET 6 平台系列1 .NET Framework发展历程

自1995年互联网战略日以来最雄心勃勃的事业 —— 微软.NET战略, 2000年6月30日。微软公司于2002年2月13日正式推出第一代.NET平台 .NET Framewrok 1.0。借助于自家强大易用的 Windows 系统&#xff0c;.NET Framework1.0 主要提供了面向 Windows 桌面&#xff08;Windows Form&…

3 src 获取_CVE-2019-15846:Exim远程获取root权限漏洞分析

报告编号&#xff1a;B6-2019-103101报告来源&#xff1a;360-CERT报告作者&#xff1a;360-CERT更新日期&#xff1a;2019-10-310x00 漏洞背景2019年9月6日18&#xff1a;00&#xff0c;exim发布exim-4.92.2版本修复了CVE-2019-15846&#xff0c;攻击者可以利用此漏洞远程获取…

jwt与token+redis,哪种方案更好用?

问&#xff1a;jwt与tokenredis&#xff0c;哪种方案更好用&#xff1f;其实JWT就是Json Web Token&#xff0c;就是Token的典型方式。题主的JWT和TokenRedis的区别&#xff0c;其实都是Token&#xff0c;只是JWT的可靠性保障是来源于加密算法(对称加密和非对称两种)&#xff0…