设计类网站策划书,做网站一般都是那些人 会做,网站开发程序是什么,引流推广的句子1. 引言是的#xff0c;Orleans v3.0.0 已经发布了#xff0c;并已经完全支持 .NET Core 3.0。所以#xff0c;Orleans 系列是时候继续了#xff0c;抱歉#xff0c;让大家久等了。万丈高楼平地起#xff0c;这一节我们就先来了解下Orleans的基本使用。2. 模板项目讲解在… 1. 引言是的Orleans v3.0.0 已经发布了并已经完全支持 .NET Core 3.0。所以Orleans 系列是时候继续了抱歉让大家久等了。万丈高楼平地起这一节我们就先来了解下Orleans的基本使用。2. 模板项目讲解在上一篇文章中我们了解到Orleans 作为.NET 分布式框架其主要包括三个部分Client、Grains、Silo HostServer。因此为了方便讲解创建如下的项目结构进行演示这里有几点需要说明Orleans.Grains类库项目用于定义Grain的接口以及实现需要引用 Microsoft.Orleans.CodeGenerator.MSBuild和 Microsoft.Orleans.Core.Abstractions NuGet包。Orleans.Server控制台项目为 Silo 宿主提供宿主环境需要引用 Microsoft.Orleans.Server 和 Microsoft.Extensions.Hosting NuGet包以及 Orleans.Grains 项目。Orleans.Client控制台项目用于演示如何借助Orleans Client建立与Orleans Server的连接需要引用 Microsoft.Orleans.Client 和 Microsoft.Extensions.Hosting NuGet包同时添加 Orleans.Grains项目引用。3. 第一个GrainGrain作为Orleans的第一公民以及Virtual Actor的实际代言人想吃透Orleans那Grain就是第一道坎。先看一个简单的Demo我们来模拟统计网站的实时在线用户。在 Orleans.Grains添加 ISessionControl接口主要用户登录状态的管理。public interface ISessionControlGrain : IGrainWithStringKey
{ Task Login(string userId); Task Logout(string userId); Taskint GetActiveUserCount();
}可以看见Grain的定义很简单只需要指定继承自IGrain的接口就好。这里面继承自 IGrainWithStringKey说明该Grain 的Identity Key(身份标识)为 string类型。同时需要注意的是Grain 的方法申明返回值必须是Task、Task、ValueTask。紧接着定义 SessionControlGrain来实现 ISessionControlGrain接口。public class SessionControlGrain : Grain, ISessionControlGrain
{ private Liststring LoginUsers { get; set; } new Liststring(); 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 Taskint GetActiveUserCount() { return Task.FromResult(LoginUsers.Count); }
}实现也很简单Grain的实现要继承自 Grain基类。代码中我们定义了一个 Liststring集合用于保存登录用户。4. 第一个Silo HostServer定义一个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() .ConfigureClusterOptions(options { options.ClusterId Hello.Orleans; options.ServiceId Hello.Orleans; }) .ConfigureEndpointOptions(options options.AdvertisedIPAddress IPAddress.Loopback) .ConfigureApplicationParts(parts parts.AddApplicationPart(typeof(ISessionControlGrain).Assembly).WithReferences()); } ) .ConfigureServices(services { services.ConfigureConsoleLifetimeOptions(options { options.SuppressStatusMessages true; }); }) .ConfigureLogging(builder { builder.AddConsole(); }) .RunConsoleAsync();
}Host.CreateDefaultBuilder()创建泛型主机提供宿主环境。UseOrleans用来配置Oleans。UseLocalhostClustering() 用于在开发环境下指定连接到本地集群。ConfigureClusterOptions用于指定连接到那个集群。ConfigureEndpointOptions用于配置silo与silo、silo与client之间的通信端点。开发环境下可仅指定回环地址作为集群间通信的IP地址。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 ILoggerClusterClientHostedService _logger; public ClusterClientHostedService(ILoggerClusterClientHostedService logger, ILoggerProvider loggerProvider) { _logger logger; Client new ClientBuilder() .UseLocalhostClustering() .ConfigureClusterOptions(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 集群。并通过 ConfigureClusterOptions指定连接到哪个集群。需要注意的是这里的ClusterId必须与Orleans.Server中配置的保持一致。Client new ClientBuilder() .UseLocalhostClustering() .ConfigureClusterOptions(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.AddSingletonClusterClientHostedService(); services.AddSingletonIHostedService(_ _.GetServiceClusterClientHostedService()); services.AddSingleton(_ _.GetServiceClusterClientHostedService().Client); services.AddHostedServiceHelloOrleansClientHostedService(); services.ConfigureConsoleLifetimeOptions(options { options.SuppressStatusMessages true; }); }) .ConfigureLogging(builder { builder.AddConsole(); }) .RunConsoleAsync();
}对于 ClusterClientHostedService并没有选择直接通过 services.AddHostedServiceT的方式注入是因为我们需要注入该服务中提供的 IClusterClient单例以供其他类去消费。紧接着定义一个 HelloOrleansClientHostedService用来消费定义的 ISessionControlGrain。public class HelloOrleansClientHostedService : IHostedService
{ private readonly IClusterClient _client; private readonly ILoggerHelloOrleansClientHostedService _logger; public HelloOrleansClientHostedService(IClusterClient client, ILoggerHelloOrleansClientHostedService logger) { _client client; _logger logger; } public async Task StartAsync(CancellationToken cancellationToken) { // 模拟控制台终端用户登录 await MockLogin(Hello.Orleans.Console); // 模拟网页终端用户登录 await MockLogin(Hello.Orleans.Web); } /// summary /// 模拟指定应用的登录 /// /summary /// param nameappName/param /// returns/returns public async Task MockLogin(string appName) { //假设我们需要支持不同端登录用户则只需要将项目名称作为身份标识。 //即可获取一个代表用来维护当前项目登录状态的的单例Grain。 var sessionControl _client.GetGrainISessionControlGrain(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个用户的并发登录。通过构造函数注入需要的 IClusterClient。通过指定Grain接口以及身份标识就可以通过Client 获取对应的Grain进而消费Grain中暴露的方法。 varsessionControl_client.GetGrainISessionControlGrain(appName); 这里需要注意的是指定的身份标识为终端应用的名称那么在整个应用生命周期内将有且仅有一个代表这个终端应用的Grain。使用 Parallel.For 模拟并发ParallelLoopResult.IsCompleted 只是返回所有循环任务创建完毕并不代表循环的内部任务执行完毕。6. 启动第一个 Orleans 应用先启动 Orleans.Server再启动 Orleans.Client从上面的运行结果来看模拟两个终端10000个用户的并发登录最终输出的活动用户数量均为10000个。回顾整个实现并没有用到诸如锁、并发集合等避免并发导致的线程安全问题但却输出正确的期望结果这就正好说明了Orleans强大的并发控制特性。public class SessionControlGrain : Grain, ISessionControlGrain
{ // 未使用并发集合 private Liststring LoginUsers { get; set; } new Liststring(); 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很多强大的特性后续再结合具体场景进行详细阐述。源码已上传至GitHubHello.Orleans