Orleans 知多少 | 3. Hello Orleans

640?wx_fmt=png

1. 引言

是的,Orleans v3.0.0 已经发布了,并已经完全支持 .NET Core 3.0。所以,Orleans 系列是时候继续了,抱歉,让大家久等了。万丈高楼平地起,这一节我们就先来了解下Orleans的基本使用。

2. 模板项目讲解

640?wx_fmt=png

在上一篇文章中,我们了解到Orleans 作为.NET 分布式框架,其主要包括三个部分:Client、Grains、Silo Host(Server)。因此,为了方便讲解,创建如下的项目结构进行演示:640?wx_fmt=png

这里有几点需要说明:

  1. Orleans.Grains:类库项目,用于定义Grain的接口以及实现,需要引用 Microsoft.Orleans.CodeGenerator.MSBuild和 Microsoft.Orleans.Core.Abstractions NuGet包。

  2. Orleans.Server:控制台项目,为 Silo 宿主提供宿主环境,需要引用 Microsoft.Orleans.Server 和 Microsoft.Extensions.Hosting NuGet包,以及 Orleans.Grains 项目。

  3. Orleans.Client:控制台项目,用于演示如何借助Orleans Client建立与Orleans Server的连接,需要引用 Microsoft.Orleans.Client 和 Microsoft.Extensions.Hosting NuGet包,同时添加 Orleans.Grains项目引用。

3. 第一个Grain

Grain作为Orleans的第一公民,以及Virtual Actor的实际代言人,想吃透Orleans,那Grain就是第一道坎。先看一个简单的Demo,我们来模拟统计网站的实时在线用户。在 Orleans.Grains添加 ISessionControl接口,主要用户登录状态的管理。

public interface ISessionControlGrain : IGrainWithStringKey	
{	Task Login(string userId);	Task Logout(string userId);	Task<int> GetActiveUserCount();	
}

可以看见Grain的定义很简单,只需要指定继承自IGrain的接口就好。这里面继承自 IGrainWithStringKey,说明该Grain 的Identity Key(身份标识)为 string类型。同时需要注意的是Grain 的方法申明,返回值必须是:Task、Task、ValueTask。紧接着定义 SessionControlGrain来实现 ISessionControlGrain接口。

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的实现要继承自 Grain基类。代码中我们定义了一个 List<string>集合用于保存登录用户。

4. 第一个Silo Host(Server)

定义一个Silo用于暴露Grain提供的服务,在 Orleans.Server.Program中添加以下代码用于启动Silo Host。

static Task Main(string[] args)	
{	Console.Title = typeof(Program).Namespace;	// define the cluster configuration	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());	}	)	.ConfigureServices(services =>	{	services.Configure<ConsoleLifetimeOptions>(options =>	{	options.SuppressStatusMessages = true;	});	})	.ConfigureLogging(builder => { builder.AddConsole(); })	.RunConsoleAsync();	
}
  1. Host.CreateDefaultBuilder():创建泛型主机提供宿主环境。

  2. UseOrleans:用来配置Oleans。

  3. UseLocalhostClustering() :用于在开发环境下指定连接到本地集群。

  4. Configure<ClusterOptions>:用于指定连接到那个集群。

  5. Configure<EndpointOptions>:用于配置silo与silo、silo与client之间的通信端点。开发环境下可仅指定回环地址作为集群间通信的IP地址。

  6. ConfigureApplicationParts():用于指定暴露哪些Grain服务。

以上就是开发环境下,Orleans Server的基本配置。对于详细的配置也可以先参考Orleans Server Configuration。后续也会有专门的一篇文章来详解。

5. 第一个Client

客户端的定义也很简单,主要是创建 IClusterClient对象建立于Orleans Server的连接。因为 IClusterClient最好能在程序启动之时就建立连接,所以可以通过继承 IHostedService来实现。在 Orleans.Client中定义 ClusterClientHostedService继承自 IHostedService

public class ClusterClientHostedService : IHostedService	
{	public IClusterClient Client { get; }	private readonly ILogger<ClusterClientHostedService> _logger;	public ClusterClientHostedService(ILogger<ClusterClientHostedService> logger, ILoggerProvider loggerProvider)	{	_logger = logger;	Client = new ClientBuilder()	.UseLocalhostClustering()	.Configure<ClusterOptions>(options =>	{	options.ClusterId = "Hello.Orleans";	options.ServiceId = "Hello.Orleans";	})	.ConfigureLogging(builder => builder.AddProvider(loggerProvider))	.Build();	}	public Task StartAsync(CancellationToken cancellationToken)	{	var attempt = 0;	var maxAttempts = 100;	var delay = TimeSpan.FromSeconds(1);	return Client.Connect(async error =>	{	if (cancellationToken.IsCancellationRequested)	{	return false;	}	if (++attempt < maxAttempts)	{	_logger.LogWarning(error,	"Failed to connect to Orleans cluster on attempt {@Attempt} of {@MaxAttempts}.",	attempt, maxAttempts);	try	{	await Task.Delay(delay, cancellationToken);	}	catch (OperationCanceledException)	{	return false;	}	return true;	}	else	{	_logger.LogError(error,	"Failed to connect to Orleans cluster on attempt {@Attempt} of {@MaxAttempts}.",	attempt, maxAttempts);	return false;	}	});	}	public async Task StopAsync(CancellationToken cancellationToken)	{	try	{	await Client.Close();	}	catch (OrleansException error)	{	_logger.LogWarning(error, "Error while gracefully disconnecting from Orleans cluster. Will ignore and continue to shutdown.");	}	}	
}

代码讲解:

1.构造函数中通过借助 ClientBuilder() 来初始化 IClusterClient。其中 UseLocalhostClustering()用于连接到开发环境中的localhost 集群。并通过 Configure<ClusterOptions>指定连接到哪个集群。(需要注意的是,这里的ClusterId必须与Orleans.Server中配置的保持一致。

Client = new ClientBuilder()	.UseLocalhostClustering()	.Configure<ClusterOptions>(options =>	{	options.ClusterId = "Hello.Orleans";	options.ServiceId = "Hello.Orleans";	})	.ConfigureLogging(builder => builder.AddProvider(loggerProvider))	.Build();

2. 在 StartAsync方法中通过调用 Client.Connect建立与Orleans Server的连接。同时定义了一个重试机制。

紧接着我们需要将 ClusterClientHostedService添加到Ioc容器,添加以下代码到 Orleans.Client.Program中:

static Task Main(string[] args)	
{	Console.Title = typeof(Program).Namespace;	return Host.CreateDefaultBuilder()	.ConfigureServices(services =>	{	services.AddSingleton<ClusterClientHostedService>();	services.AddSingleton<IHostedService>(_ => _.GetService<ClusterClientHostedService>());	services.AddSingleton(_ => _.GetService<ClusterClientHostedService>().Client);	services.AddHostedService<HelloOrleansClientHostedService>();	services.Configure<ConsoleLifetimeOptions>(options =>	{	options.SuppressStatusMessages = true;	});	})	.ConfigureLogging(builder =>	{	builder.AddConsole();	})	.RunConsoleAsync();	
}

对于 ClusterClientHostedService,并没有选择直接通过 services.AddHostedService<T>的方式注入,是因为我们需要注入该服务中提供的 IClusterClient(单例),以供其他类去消费。

紧接着,定义一个 HelloOrleansClientHostedService用来消费定义的 ISessionControlGrain

public class HelloOrleansClientHostedService : IHostedService	
{	private readonly IClusterClient _client;	private readonly ILogger<HelloOrleansClientHostedService> _logger;	public HelloOrleansClientHostedService(IClusterClient client, ILogger<HelloOrleansClientHostedService> logger)	{	_client = client;	_logger = logger;	}	public async Task StartAsync(CancellationToken cancellationToken)	{	// 模拟控制台终端用户登录	await MockLogin("Hello.Orleans.Console");	// 模拟网页终端用户登录	await MockLogin("Hello.Orleans.Web");	}	/// <summary>	/// 模拟指定应用的登录	/// </summary>	/// <param name="appName"></param>	/// <returns></returns>	public async Task MockLogin(string appName)	{	//假设我们需要支持不同端登录用户,则只需要将项目名称作为身份标识。	//即可获取一个代表用来维护当前项目登录状态的的单例Grain。	var sessionControl = _client.GetGrain<ISessionControlGrain>(appName);	ParallelLoopResult result = Parallel.For(0, 10000, (index) =>	{	var userId = $"User-{index}";	sessionControl.Login(userId);	});	if (result.IsCompleted)	{	//ParallelLoopResult.IsCompleted 只是返回所有循环创建完毕,并不保证循环的内部任务创建并执行完毕	//所以,此处手动延迟5秒后再去读取活动用户数。	await Task.Delay(TimeSpan.FromSeconds(5));	var activeUserCount = await sessionControl.GetActiveUserCount();	_logger.LogInformation($"The Active Users Count of {appName} is {activeUserCount}");	}	}	public Task StopAsync(CancellationToken cancellationToken)	{	_logger.LogInformation("Closed!");	return Task.CompletedTask; ;	}	
}

代码讲解:这里定义了一个 MockLogin用于模拟不同终端10000个用户的并发登录。

  1. 通过构造函数注入需要的 IClusterClient

  2. 通过指定Grain接口以及身份标识,就可以通过Client 获取对应的Grain,进而消费Grain中暴露的方法。 varsessionControl=_client.GetGrain<ISessionControlGrain>(appName); 这里需要注意的是,指定的身份标识为终端应用的名称,那么在整个应用生命周期内,将有且仅有一个代表这个终端应用的Grain。

  3. 使用 Parallel.For 模拟并发

  4. ParallelLoopResult.IsCompleted 只是返回所有循环任务创建完毕,并不代表循环的内部任务执行完毕。

6. 启动第一个 Orleans 应用

先启动 Orleans.Server640?wx_fmt=png再启动 Orleans.Client640?wx_fmt=png

640?wx_fmt=png

从上面的运行结果来看,模拟两个终端10000个用户的并发登录,最终输出的活动用户数量均为10000个。回顾整个实现,并没有用到诸如锁、并发集合等避免并发导致的线程安全问题,但却输出正确的期望结果,这就正好说明了Orleans强大的并发控制特性。

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;	}	....	
}

7. 小结

通过简单的演示,想必你对Orleans的编程实现有了基本的认知,并体会到其并发控制的强大之处。这只是简单的入门演练,Orleans很多强大的特性,后续再结合具体场景进行详细阐述。源码已上传至GitHub:Hello.Orleans

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

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

相关文章

.NET Core 3.0之深入源码理解ObjectPool(二)

写在前面前文主要介绍了ObjectPool的一些理论基础&#xff0c;本文主要从源码角度理解Microsoft.Extensions.ObjectPool是如何实现的。下图为其三大核心组件图&#xff1a;核心组件ObjectPoolObjectPool是一个泛型抽象类&#xff0c;里面只有两个抽象方法&#xff0c;Get和Retu…

VC维学习

http://www.flickering.cn/machine_learning/2015/04/vc维的来龙去脉/ 说说历史Hoeffding不等式Connection to Learning学习可行的两个核心条件Effective Number of HypothesesGrowth FunctionBreak Point与ShatterVC BoundVC dimension深度学习与VC维小结参考文献 VC维在机器学…

.NET Core 3.0 一个 jwt 的轻量角色/用户、单个API控制的授权认证库

作者&#xff1a;痴者工良&#xff08;朋友合作原创&#xff09;来源&#xff1a;https://www.cnblogs.com/whuanle/p/11743406.html目录说明一、定义角色、API、用户二、添加自定义事件三、注入授权服务和中间件三、如何设置API的授权四、添加登录颁发 Token五、部分说明六、验…

.NET Core 3.0 构建和部署

Default Executables 默认可执行文件 在 dotnet build 或 dotnet publish 期间&#xff0c;将创建一个与你使用的 SDK 的环境和平台相匹配的可执行文件。 和其他本机可执行文件一样&#xff0c;可以使用这些可执行文件执行相同操作&#xff0c;例如&#xff1a; 可以双击可执行…

为什么我会了SOA,你们还要逼我学微服务?

菜菜哥&#xff0c;我最近需要做一个项目&#xff0c;老大让我用微服务的方式来做那挺好呀&#xff0c;微服务现在的确很流行我以前在别的公司都是以SOA的方式&#xff0c;SOA也是面向服务的方式呀的确&#xff0c;微服务和SOA有相同之处面向服务的架构&#xff08;SOA&#xf…

面对万物互联的智能世界,你是否也想分一杯羹

第六届世界互联网大会于10月20日至22日在浙江乌镇顺利举行。作为世界互联网大会“13”架构的重要组成部分&#xff0c;“互联网之光”博览会以“智能互联网、开放合作——携手共建网络空间命运共同体”为主题&#xff0c;集中展示了全球范围内的互联网新技术、新成果、新产品、…

你必须知道的容器监控 (2) cAdvisor

# 实验环境&#xff1a;阿里云ECS主机&#xff08;两台&#xff09;&#xff0c;CentOS 7.401—cAdvisor简介为了解决容器的监控问题&#xff0c;Google开发了一款容器监控工具cAdvisor&#xff08;Container Advisor&#xff09;&#xff0c;它为容器用户提供了对其运行容器的…

代码阅读

http://alanse7en.github.io/caffedai-ma-jie-xi-4/ 三. 从一个比较宏观的层面上去了解caffe怎么去完成一些初始化的工作和使用Solver的接口函数&#xff0c;本文将主要分为四部分的内容&#xff1a; Google Flags的使用Register Brew Function的宏的定义和使用train()函数的…

动手造轮子:实现一个简单的依赖注入(一)

动手造轮子&#xff1a;实现一个简单的依赖注入(一)Intro在上一篇文章中主要介绍了一下要做的依赖注入的整体设计和大概编程体验&#xff0c;这篇文章要开始写代码了&#xff0c;开始实现自己的依赖注入框架。类图首先来温习一下上次提到的类图服务生命周期服务生命周期定义&am…

Qt 调试Caffe

http://blog.csdn.net/xg123321123/article/details/52817658 1.下载并安装Qt Creator 下载页面&#xff0c;推荐使用4.x版本&#xff0c;比如&#xff1a; Qt Creator 4.1.0 for Linux 64-bit下载的是run包&#xff0c;安装方法&#xff1a; cd到下载目录 chmod x xxx.run …

.NET Core 3.0 本地工具

.NET Core从最早期的版本就开始支持全局工具了。如果仅仅需要在某个项目中或某个文件夹中使用特定的工具&#xff0c;那么.NET Core 3.0就允许您这样做。 使用.NET Core 3.0&#xff0c;您可以在特定的文件夹下安装“本地”工具&#xff0c;它的作用范围仅限于该文件夹及其子文…

Nsight 调试 Caffe

http://blog.csdn.net/u014114990/article/details/47723877 右键属性菜单的General->Code Analysis->Paths and Symbols下进行加入&#xff1a; Includes下加入程序需要用到的头文件的路径&#xff1a; Library Path下添加需要用到库文件的路径&#xff1a; 具体用到的…

张高兴的 .NET Core IoT 入门指南:(五)PWM 信号输出

什么是 PWM在解释 PWM 之前首先来了解一下电路中信号的概念&#xff0c;其中包括模拟信号和数字信号。模拟信号是一种连续的信号&#xff0c;与连续函数类似&#xff0c;在图形上表现为一条不间断的连续曲线。数字信号为只能取有限个数值的信号&#xff0c;比如计算机中的高电平…

.NET Core 3 对 IoT 应用程序的高级支持:System.Device.Gpio

System.Device.Gpio 是一个全新的 .Net Core 开源库&#xff0c;它旨在使 IoT&#xff08;物联网&#xff09;应用程序能够通过其 GPIO 引脚或其他 I/O 控制硬件与传感器、显示器和输入设备进行交互。该库是由社区维护的多个设备绑定集合来进行增强实现的。正如微软 .NET 项目…

代码阅读2

http://blog.csdn.net/u014568921/article/details/53995455 首先&#xff0c;要知道caffe里的卷积核都是三维的 在caffe中卷积核是三维的还是二维的&#xff1f; 下面分割线之间的内容来自http://blog.csdn.NET/u014114990/article/details/51125776 /*********************…

一文带你了解华为云DevCloud为何能全面领跑中国DevOps云服务市场

近日&#xff0c;国际权威调研机构IDC发布了《IDC MarketScape&#xff1a;中国DevOps云服务市场2019厂商评估》报告&#xff0c;该报告从战略和能力两个维度对国内主流DevOps云厂商进行了评估&#xff0c;报告显示&#xff0c;华为云位于 IDC MarketScape “中国DevOps云服务 …

[电子书制作]Excel催化剂输出内容汇总PDF及Word版本分享

Excel催化剂在2018年开始&#xff0c;陆续写出了230篇高质量原创性文章&#xff0c;将Excel催化剂插件的开发过程及使用方法全方位地通过文字的方式给广大网友们分享了。电子书下载方式同样地&#xff0c;为了减少大家过多繁琐的资料下载途径&#xff0c;电子书的下载路径和之前…

深入理解.NET Core的基元: deps.json, runtimeconfig.json, dll文件

原文链接&#xff1a; Deep-dive into .NET Core primitives: deps.json, runtimeconfig.json, and dlls作者&#xff1a; Nate McMasterC#编译器(The C# Compiler)C#的编译器可以将cs文件转换为dll文件, 即程序集文件。程序集文件是一个便携的可执行格式文件, 借助.NET Core,它…

C# 8.0 的默认接口方法

例子直接看例子有这样一个接口&#xff1a; 然后有三个它的实现类&#xff1a; 然后在main方法里面调用&#xff1a; 截至目前&#xff0c;程序都可以成功的编译和运行。 IPerson接口变更 突然&#xff0c;我想对所有的人类添加一个新的特性&#xff0c;例如&#xff0c;添加一…