Orleans解决并发之痛(一):单线程

程序在运行过程中有时会莫名其妙出现代码的某些约束或者执行结果和理想状况不一样,正常逻辑怎么会出现这样的情况?到底发生了什么?好像见了鬼!瞬间好无助。


谁来救救我

大多数出现正常逻辑很难解释的时候,我们可能会想到并发问题,因为好像只有并发才会能说服自己。为了验证和解决这个问题,我们可能会尝试一些方案,在并发的情况下我相信很多人都使用过锁,锁确实也能帮忙我们解决问题,不然它干嘛存在。

但随着业务逻辑的持续复杂,锁的使用可能无处不在。首先大家都知道锁本身的机制很耗性能;然后锁本身不涉及什么编程模式,所以在业务代码中融入大量锁对代码本身的稳定性也有一定影响。

经过查找资料,因为本身的项目是基于.NET,所以发现Microsoft Orleans好像可以比较好的满足解决并发的需求。

Orleans之前,先来扯一扯Actor模型

  1. Actor是以单线程存在的,所有消息都是顺序到达的,每次收到消息后,就放入队列,而它每次也从队列中取出消息体来处理;

  2. 每一个Actor有一个Id和它对应,一个Id对应的Actor只会在集群中存在一个,使用者只需要通过Id就能随时访问不需要关注该Actor在集群的什么位置;

  3. 每一个Actor看作是一个独立的实体,拥有自己独立的状态。Actor与Actor之间可以进行消息通知;

注:有状态的 Actor在集群中一个Id只会存在一个实例,无状态的可配置为根据流量存在多个,无状态的情况看具体业务需求。


Actor System

再来扯一扯Orleans框架

Orleans 提供了一个简单的方法来构建大规模、高并发、分布式应用程序,被认为是Actor模型的分布式版本,是一种改进的Actor模型。在Orleans中,Actors被称作Grains,采用接口来表示,Actors的消息用异步方法来接收,方法返回值必须是Task or Task<T>。

Orleans几个核心角色:
Grains(Actors)

Grains是Orleans应用程序的业务逻辑实现与抽象,Grains是彼此孤立的原子单位,分布的,持久的。 一个典型的Grain是有状态和行为的一个单实例。

Silo

Silo是一个主机服务,里面主要用于执行Grains,也就是说Grains开发完成后需要注册到Silo中,然后等待调用。它监听一个端口,用来监听从Silo到Silo的消息或者从客户端到Silo的消息的,典型的Silo就是,每台机器运行一个Silo,会对外暴露网关地址供调用。

Cluster(集群搭建的时候会具体介绍)

大量的Silo同时在一起工作就形成了Orleans的集群,Orleans运行完全自动化的集群管理。

Client

具体的应用客户端,可以是控制台、Web应用程序、WPF等一切.NET端技术。


开始接触Orleans Sample的时候,第一感觉项目结构和gRPC还挺像的,如果你之前有接触,一定感觉很亲切:

  1. 定义一个接口(Interfaces)

  2. 实现接口(Grains) -- 添加引用Interfaces

  3. 启动服务端(Silo)-- 添加引用Interfaces,Grains

  4. 启动客户端 (Client)-- 添加引用Interfaces

练习过程中对Nuget安装Orleans相关依赖包可能会有一些模糊,这里说明一下我的具体步骤,希望尽快帮忙实现效果,所有程序集使用.Net Framework的版本都是4.6:

程序集名称类型Nuget依赖包
Microsoft.Orleans.
引用
Interfaces类库Core-
Grains类库CoreInterfaces
Silo控制台程序Core
OrleansCodeGenerator
OrleansProviders
OrleansRuntime
Interfaces
Grains
Client控制台程序Core
OrleansCodeGenerator
Interfaces

在Silo项目中添加配置文件 OrleansConfiguration.xml:

<?xml version="1.0" encoding="utf-8" ?>
<OrleansConfiguration xmlns="urn:orleans"><Globals><SeedNode Address="localhost" Port="11111" /></Globals><Defaults><Networking Address="localhost" Port="11111" /><ProxyingGateway Address="localhost" Port="30000" /></Defaults>
</OrleansConfiguration>

SeedNode:集群中主Silo地址,生产环境下不要这么使用。以这种方式配置主Silo的情况下,其他Silo加入集群需要等主Silo先启动。之后会介绍SystemStore来维护集群成员关系;
Networking:内部Silo与Silo之间通信地址;
ProxyingGateway:客户端调用的网关地址;

在Client项目中添加配置文件 ClientConfiguration.xml:

<?xml version="1.0" encoding="utf-8" ?>
<ClientConfiguration xmlns="urn:orleans"><Gateway Address="localhost" Port="30000"/>
</ClientConfiguration>

Gateway:配置Silo对外的网关地址;

集群下可以配置多个Gateway节点,如下:

<Gateway Address="gateway1" Port="30000"/>
<Gateway Address="gateway2" Port="30000"/>

注意:配置文件需要设置属性 "复制到输出目录"


configuration

Grain说明:

每个Grain都是单实例的,具有唯一标识。根据唯一标识获取Grain,这个标识可以是GUID、String、Long、混合类型。

在Grain内如果发送消息给其他Grain,需要使用 this.GrainFactory.GetGrain,不能通过 GrainClient.GrainFactory.GetGrain。

 var test = GrainClient.GrainFactory.GetGrain<ITest>(0); // long类型的primaryKey 0
public class TestGrain : Orleans.Grain, ITest
{private int num = 0;public Task AddCount(){num++;Console.WriteLine(num);return Task.CompletedTask;}
}
Client说明:

同时启动3个Task,每个Task内并行200次调用AddCount方法。如果没有做特殊的处理,num的结果肯定是乱的,并不会出现一直累加的效果。

private static void DoClientWork()
{var t1 = Task.Factory.StartNew(() =>{AddCount();});var t2 = Task.Factory.StartNew(() =>{AddCount();});var t3 = Task.Factory.StartNew(() =>{AddCount();});Task.WaitAll(t1, t2, t3);
}static void AddCount()
{var test = GrainClient.GrainFactory.GetGrain<ITest>(0);Parallel.For(0, 200, (i) =>{test.AddCount();});
}

实际上执行最终的结果是600,并不会出现不一致的变化效果,这足以说明同一个Grain内部是单线程执行。


Test Result

相关文章: 

  • .NET的Actor模型:Orleans

  • 微软分布式云计算框架Orleans(1):Hello World

  • 微软分布式云计算框架Orleans(2):容灾与集群(1)

  • Aaron Stannard谈Akka.NET 1.1

  • 使用Akka.net开发第一个分布式应用

  • Orleans入门例子

  • Orleans例子再进一步

  • Orleans稍微复杂的例子—互动

  • Orleans简单配置

  • Orleans配置---持久化

  • Orleans—一些概念

  • Orleans的集群构建

  • Oleans集群之Consul再解释

原文地址:http://www.jianshu.com/p/141ea382d242


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

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

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

相关文章

你们考试,我们都有点紧张呢…

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。”考试计划都好久好久了&#xff0c;一直没有时间&#xff0c;终于在今天考了一次。去监考的时候王老师还说&#xff0c;我都有点紧张呢&#xff0c;我说我也是&#xff0c;哈哈哈~上午几…

P2947-[USACO09MAR]向右看齐Look Up【单调栈】

正题 评测记录:https://www.luogu.org/recordnew/lists?uid52918&pidP2947 题目大意 有n头牛&#xff0c;高度不同&#xff0c;求每头牛右边第一头比他高的人。 解题思路 考虑单调栈&#xff0c;每次如果是比栈顶矮的就加入栈&#xff0c;如果是高的就将栈弹出直到比其…

汇编语言(三十一)之数字字符串加密与解密

输入一串数字&#xff0c;然后进行加密解密输出 程序运行&#xff1a; 代码&#xff1a; datas segmentline_max_len db 0ffh line db 0, 100h dup(?)linesecret db 0, 100h dup(?)linedesecret db 0, 100h dup(?)secret …

ASP.NET Core 运行原理解剖[1]:Hosting

SP.NET Core 是新一代的 ASP.NET&#xff0c;第一次出现时代号为 ASP.NET vNext&#xff0c;后来命名为ASP.NET 5&#xff0c;随着它的完善与成熟&#xff0c;最终命名为 ASP.NET Core&#xff0c;表明它不是 ASP.NET 的升级&#xff0c;而是一个重新设计的Web开发框架。而它一…

公众号文章

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。 常用的几个sql整理 1.按照日期统计今天的客流信息。 SELECT * FROM base_disanfang WHERE DATE_FORMAT(create_time,%Y-%m-%d) DATE_FORMAT(NOW(),%Y-%m-%d) 其中base_disanfang为表名…

POJ2259,luoguUVA540-Team Queue【队列】

正题 POJ题目链接&#xff1a;http://poj.org/problem?id2259 luogu评测记录&#xff1a;https://www.luogu.org/recordnew/lists?uid52918&pidUVA540 题目大意 有n个队伍排队&#xff0c;新进来一个人时那个人会排在他的队伍的最后一个人的后面&#xff0c;如果这里没…

2017(深圳) .NET技术分享交流会 第二期,将有网络直播

2017 .NET技术分享交流会第一期已在5月13日成功举办&#xff0c;但是有同学反馈哪个地方有点偏&#xff0c;又过去了3个月&#xff0c;这期间一直没找到合适的地方举办活动&#xff0c;一直在南山科技园寻找经济适合的场地&#xff0c;终于找到一个安静&#xff0c;风景好的深圳…

汇编语言(三十二)之读写文件

读取文件的内容写到另一个文件 程序运行&#xff1a; 暂无 代码&#xff1a; datas segmentfile1 db d:\abc.txt,00 handle1 dw 0file2 db d:\def.txt,00 handle2 dw 0line_max_len db 0ffhline db 0, 100h dup(?)buffer db 0input …

整理几个常用的sql和其他代码

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。”常用的几个sql整理1.按照日期统计今天的客流信息。SELECT * FROM base_disanfang WHERE DATE_FORMAT(create_time,%Y-%m-%d) DATE_FORMAT(NOW(),%Y-%m-%d)其中base_disanfang为表名&…

汇编语言(三十三)之四进制转十进制

输入四进制的数&#xff0c;转为十进制输出 程序运行&#xff1a; 代码&#xff1a; datas segmentN_string_max_length db 0ffhN_string db 0, 100h dup(?)N dw 0,0 sum dd 0 input db inp…

tyvj/joyOI 1305-最大子序和【单调队列】

正题 题目链接&#xff1a;http://www.joyoi.cn/problem/tyvj-1305 题目大意 求一段长度不超过m的最大子序和。 解题思路 用前缀和&#xff0c;我们可以枚举最右边的点&#xff0c;然后取左边合法范围内最小的前缀和&#xff0c;这个我们可以用单调队列进行优化。 code #in…

HTML中常用知识点整理

html复习HTML是一个超文本标记语言w3c标准&#xff1a;结构标准&#xff0c;表现标准&#xff0c;行为标准。基本结构<!doctype html> <html><head><title>网页的标题</title><meta charset"utf-8"/></head><body>&…

Orleans解决并发之痛(二):Grain状态

Grains是Orleans应用程序的构建块&#xff0c;它们是彼此孤立的原子单位&#xff0c;分布的&#xff0c;持久的&#xff0c; 一个典型的Grain是有状态和行为的一个单实例&#xff0c;每个Grain实例的在单线程内执行&#xff0c;Grain之间共享数据通过消息传递&#xff0c;Grain…

汇编语言(三十四)之输出中文

输出中文 程序运行&#xff1a; 代码&#xff1a; daones segmentfull_name db 0,1,2,3,4,5 full_name_length dw $-full_name start_char db 0 change_char_count dw 6color db 1 x dw 40 y …

vue使用element ui实现下拉列表分页的功能!!!

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。”前几天做了个功能&#xff0c;需求是使用利用element ui如何给下拉列表分页&#xff0c;经过网上查找&#xff0c;自己摸索&#xff0c;已经完成&#xff0c;今天来记录一下吧。实现的…

POJ2373-Dividing the Path【单调队列优化dp】

正题 题目链接:http://poj.org/problem?id2373 题目大意 长度为L&#xff0c;要求每个区域都被洒水器覆盖&#xff0c;而且在每只奶牛的喜爱区域只能由一个洒水器覆盖&#xff0c;洒水器必须放在整数点&#xff0c;喷洒半径只能在a∼ba\sim ba∼b区间。 解题思路 我们考虑d…

ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍

在上一章ASP.NET Core 运行原理解剖[1]:Hosting中&#xff0c;我们介绍了 ASP.NET Core 的启动过程&#xff0c;主要是对 WebHost 源码的探索。而本文则是对上文的一个补充&#xff0c;更加偏向于实战&#xff0c;详细的介绍一下我们在实际开发中需要对 Hosting 做一些配置时经…

汇编语言(三十五)之输入字符串以$结束然后输出字母个数

输入字符串以$结束然后输出字母个数 程序运行&#xff1a; 代码&#xff1a; datas segment buff db 100h dup(?)letter_count dw 0nextline db 0dh,0ah,$datas ends codes segment assume cs:codes,ds:datas main proc far push dsmov ax,0push ax mov ax,datasmov ds,ax…

vue中如何使用vi-for限制遍历的条数?只查询前三条、查询4-6条怎么实现?

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;雄雄的小课堂。”前言今天整理个简单的功能&#xff0c;vue中的v-for如何限制遍历输出的数据&#xff0c;比如我想在一个存放10条数据的集合中只输出3条怎么写&#xff1f;只想从第四条开始输出到第10条…

P3370-[模板]字符串哈希【hash】

正题 评测记录:https://www.luogu.org/recordnew/lists?uid52918&pidP3370 大意 输出若干个字符串&#xff0c;求输入的字符串的总个数。 解题思路 就是用hash表就好了。 code #include<cstdio> #include<iostream> #include<string> #define p 300…