Orleans消除并发之痛,入门指南

Orleans 提供了 Stream 扩展编程模型。此模型提供了一套
API,使处理流更简单和更健壮。Stream 默认提供了两种 Provider,Simple
Message Stream Provider 和 Azure Queue Stream
Provider,不同的流类型可能使用不同的 Provider 来处理。Stream Providers
兼容现有的队列技术,比如: Event Hubs、ServiceBus、Azure Queues、Apache
Kafka,不需要编写额外的代码来配合这些队列技术的使用。

在.net用简单方法构建高并发、分布式的大型应用程序框架。

关于为什么Orleans会提供Stream扩展编程模型?

当今已经有一系列技术可以来构建一个流处理系统。包括持久存储流数据方面,如:Event
Hubs、Kafka;数据流计算操作方面,如: Azure Stream Analytics、Apache
Storm、Apache Spark Streaming,
而这些技术并不适合细粒度的自由格式的流数据计算,
或者支持的并不好,因为实际情况下可能需要对不同的数据流执行不同的操作,Orleans
Streams目的就是解决这类问题,Stream 编程模型和发布订阅模式挺相似。

上述提到的一些技术我并没有详细学习,后面会了解并对比。

Orleans Stream 大概实现的步骤如下:

  1. 获取 StreamProvider
  2. 获取 IAsyncStream<T>
  3. 订阅者订阅一个Stream
  4. 发布者向某个Stream发布消息

原文:

Silo 配置文件 OrleansConfiguration.xml 修改

在Globals节点中添加:

<StorageProviders> <Provider Type="Orleans.Storage.MemoryStorage" Name="PubSubStore" /></StorageProviders><StreamProviders> <Provider Type="Orleans.Providers.Streams.SimpleMessageStream.SimpleMessageStreamProvider" Name="SMSProvider"/></StreamProviders>

Name 为 PubSubStore(名字任意) 的 StorageProvider 是必须的,Stream
内部需要它来跟踪所有流订阅,记录各个流的发布者和订阅者的关系,本例测试使用
MemoryStorage,实际生产环境不能使用 Memory 方式。

Name 为 SMSProvider(名字任意)的 StreamProvider
指定了消息的发布形式,Orleans 当前提供的两种 StreamProvider:Simple
Message Stream Provider
Azure Queue Stream Provider
都是可靠的。

Simple Message Stream
Provider
:不保证可靠的交付,失败的消息不会自动重新发送,但可以根据返回的Task状态来判断是否重新发送,事件执行顺序遵循
FIFO 原则。

Azure Queue Stream Provider:事件被加入 Azure Queue,
如果传送或处理失败,事件不会从队列中删除,并且稍后会自动重新被发送,因此事件执行顺序不遵循
FIFO 原则。

在线文档:http://dotnet.github.io/orleans/What’s-new-in-Orleans

获取 StreamProvider

var streamProvider = this.GetStreamProvider("SMSProvider");

SMSProvider 对应配置文件中 Name 为 SMSProvider 的 StreamProvider

源码地址:

获取 IAsyncStream<T>

var streamId = this.GetPrimaryKey();var stream = streamProvider.GetStream<string>(streamId, "GrainStream");

GetStream 需要两个参数,通过两个值定位唯一的 Stream:streamId:guid
类型,stream 标识streamNamespace:字符串,stream 的命名空间

简介:Orleans
框架可以构建大规模、高并发、分布式应用程序,而不需要学习专业分布式以及并发知识框架。它是由微软研究和设计应用于云计算。

订阅一个Stream

订阅 Stream 分为隐式和显式订阅。

隐式订阅模式订阅者自动由发布者创建,订阅者是唯一的,不存在对一个 Stream
的多次订阅,也不可手动取消订阅。

Interface:

public interface IImplicitSubscriberGrain : IGrainWithGuidKey{}

Grain:

[ImplicitStreamSubscription("GrainImplicitStream")]public class ImplicitSubscriberGrain : Grain, IImplicitSubscriberGrain, IAsyncObserver<string>{ protected StreamSubscriptionHandle<string> streamHandle; public override async Task OnActivateAsync() { var streamId = this.GetPrimaryKey(); var streamProvider = this.GetStreamProvider("SMSProvider"); var stream = streamProvider.GetStream<string>(streamId, "GrainImplicitStream"); streamHandle = await stream.SubscribeAsync(OnNextAsync); } public override async Task OnDeactivateAsync() { if (streamHandle != null) await streamHandle.UnsubscribeAsync(); } public Task OnCompletedAsync() { return Task.CompletedTask; } public Task OnErrorAsync(Exception ex) { return Task.CompletedTask; } public Task OnNextAsync(string item, StreamSequenceToken token = null) { Console.WriteLine($"Received message:{item}"); return Task.CompletedTask; }}
  1. 在 Grain 上标记 ImplicitStreamSubscription 属性,变量值为命名空间;
  2. 在 Grain 的 OnActivateAsync 方法体中调用 SubscribeAsync ,Grain
    创建即订阅;
  3. 实现 IAsyncObserver 接口,当发布者向 Stream
    发送消息,订阅者接到消息后将执行 OnNextAsync;

Interface:

public interface IExplicitSubscriberGrain : IGrainWithGuidKey{ Task<StreamSubscriptionHandle<string>> SubscribeAsync(); Task ReceivedMessageAsync(string data);}

Grain:

public class ExplicitSubscriberGrain : Grain, IExplicitSubscriberGrain{ private IAsyncStream<string> stream; public async override Task OnActivateAsync() { var streamProvider = this.GetStreamProvider("SMSProvider"); stream = streamProvider.GetStream<string>(this.GetPrimaryKey(), "GrainExplicitStream"); var subscriptionHandles = await stream.GetAllSubscriptionHandles(); if (subscriptionHandles.Count > 0) { subscriptionHandles.ToList().ForEach(async x => { await x.ResumeAsync((payload, token) => this.ReceivedMessageAsync; }); } } public async Task<StreamSubscriptionHandle<string>> SubscribeAsync() { return await stream.SubscribeAsync((payload, token) => this.ReceivedMessageAsync; } public Task ReceivedMessageAsync(string data) { Console.WriteLine($"Received message:{data}"); return Task.CompletedTask; }}
  1. 订阅者通过调用 SubscribeAsync 方法完成订阅,并返回
    StreamSubscriptionHandle ,这个对象提供了 UnsubscribeAsync
    方法,支持手动取消订阅;

  2. 本例子中支持对同一个 Stream 订阅多次,被订阅多次的结果是当向这个
    Stream 发送消息的时候,ReceivedMessageAsync
    会执行多次。如果不希望对同一个 Stream 订阅多次,在 SubscribeAsync
    方法中可以通过GetAllSubscriptionHandles
    获取当前已订阅者的数量,然后进行限制;

  3. 订阅者是一直存在的,除了被显示调用了 UnsubscribeAsync 方法。在
    OnActivateAsync 中我们加入了 ResumeAsync 操作, 当 Grain
    由未激活状态变为激活状态的时候,先通过 GetAllSubscriptionHandles
    获取这个 Stream 中存在的订阅者,然后通过 ResumeAsync
    可以把它们重新唤醒。(模拟方式:杀掉Silo,重新启动即可,不过前提条件是
    PubSubStore 不能使用 MemoryStorage ,因为使用 MemoryStorage
    存储一旦重启后订阅者和发布者的关系都会丢失)

Orleans 被广泛应用于微软云产品,值得注意的是微软官方游戏:Halo4、Halo5(光环|光晕)的云服务全部由它来承载,以及越来越多的公司采用它。

发布消息

Interface:

public interface IPublisherGrain: IGrainWithGuidKey{ Task PublishMessageAsync(string data);}

Grain:

public class PublisherGrain : Grain, IPublisherGrain{ private IAsyncStream<string> stream; public override Task OnActivateAsync() { var streamId = this.GetPrimaryKey(); var streamProvider = this.GetStreamProvider("SMSProvider"); this.stream = streamProvider.GetStream<string>(streamId, "GrainExplicitStream"); //隐式:GrainImplicitStream return base.OnActivateAsync(); } public async Task PublishMessageAsync(string data) { Console.WriteLine($"Sending data: {data}"); await this.stream.OnNextAsync; }}

通过调用 IAsyncStream 的 OnNextAsync 发布消息即可。这里可以针对返回的
Task 状态再作一些操作,如果不成功,重新发送或记录日志等。

特点:1.默认的可拓展性,构建复杂的分布式应用程序,可以使您的程序轻易拓展到数百个服务。

Client 发布消息:

while { Console.WriteLine("Press 'exit' to exit..."); var input = Console.ReadLine(); if (input == "exit") break; var publisherGrain = GrainClient.GrainFactory.GetGrain<IPublisherGrain>(Guid.Empty); publisherGrain.PublishMessageAsync;}

图片 1发布消息

var subscriberGrain = GrainClient.GrainFactory.GetGrain<IExplicitSubscriberGrain>(Guid.Empty);var streamHandle = subscriberGrain.SubscribeAsync().Result;Console.WriteLine("Press enter to exit...");Console.ReadLine();streamHandle.UnsubscribeAsync();

图片 2显示订阅下发布消息

    
2.低延时,它可以使你的程序状态保存于内存中,所以你的应用程序可以快速响应请求。

参考链接:

  • Actor模型
  • Orleans
  • 案例Demo-OrleansStreams

   3.简化并发,Orleans 允许你使用C#代码构建Actors
之间的异步处理消息。

注:(Actor模型,请查看著名的Actor模型论文)

在Orleans,actors 被称作grains,采用一个接口来表示,Actors的消息用异步方法来接受,如下:

public interface IMyGrain : IGrainWithStringKey
{
    Task<string> SayHello(string name);
}

运行在Orleans 框架里的实现代码

public class MyGrain : IMyGrain
{
    public async Task<string> SayHello(string name)
    {
        return "Hello " + name;
    }
}

然后通过创建代理对象,并调用Grains的方法来发送消息

var grain = GrainClient.GrainFactory.GetGrain<IMyGrain>("grain1");
await grain.SayHello("World");



接下来该做什么呢?
如果要学习更多关于Orleans的概念,请阅读 Orleans简介
如果要学习入门教程请学习 一步一步教程

 

Microsoft Orleans 之
入门指南

 

前言: Orleans
提供了构建大型分布式运算的应用程序框架,而不需要学习那些复杂的并发或者可拓展的模式。
它由微软研究院创建和维护,并且它已被一些微软产品组广泛应用于微软的Azure,以及其他一些公司。

Orleans
在2015年一月微软将其源代码开源,迅速得到开发者的认可,借助于活跃的社区和开发团队的贡献,框架不断的被完善和新功能的增加,微软研究院也将一直为该项目投资下去。

背景:云应用程序和服务本质上是并行的和分布式的。
他们也互动性和动态性,往往需要近实时的云实体之间的直接相互作用。
对于今天的程序构建来说这样的应用是非常困难的。
随着工作量的增长,通常需要昂贵的设计和架构的迭代,开发过程需要专家级的程序员。

今天大多数高性能应用程序是通过标准的SOA构建的,比如,亚马逊、google或者Facebook
一个简单页面的渲染展示,是通过数以百计的复杂的SOA服务协作和管理。
事实上,这样一个复杂的服务是很难通过一个服务来构建完成的。

SOA的模块化服务化、大数据、高并发已经服务动态分配和不间断的运行,不停的挑战程序员处理这些问题的能力,但是现有提供的这些工具或者框架不足以解决这些问题。

无状态的多层模型到存储层划分问题。
它经常需要在无状态层中进行缓存以获得可接受的性能,增加了复杂性,并引入了缓存一致性问题。

Actors: Actor
模型对细粒度的单个Actor对象的支持很好,每个Actor对象之间彼此孤立并且都是轻量级的,可以保证这些Actor独立建模。
他们通过异步消息传递,从而使Actor彼此直接沟通。

值得注意的是,一个Actor是在逻辑意义的单线程上执行,同时Actor的状态的封装和其他Actor之间的隔离,这大大的简化了Actor代码层面的复杂性,开发者使用的Actor不必担心临界区、互斥锁,锁的水准,和其他复杂的难以预测的问题,这些与实际应用程序逻辑是无关的,Actor是在可用的硬件资源池内动态创建的。
这使得Actors模型在负载均衡上比SOA的模块化分割更容易维护和拓展。

在过去的十年里,Erlang
一直是传统Actor模式的最流行的实现,面对上述SOA的挑战,业内重新审视Actor
模式,这促使出现了新的解决方案:Scala actors,Akka,DCell

Virtual Actors:

Orleans
是一种改进的Actors模式,大量借鉴了Erlang和分布式对象系统中的实现,添加静态类型,消息indirection
和Actors的虚拟化,将它们暴露在一个集成的编程模型中。

而Erlang是一个纯粹的函数式语言有其自己特定的虚拟机,Orleans
的编程模型,直接利用.NET
和面向对象的能力,它提供了一个框架,使复杂的分布式应用程序的开发更容易,并使所得到的应用程序的可扩展性更强。

不像其他系统如Erlang或Akka 的Actors , Orleans
Grains
的Actors 是虚拟Actors。 他们通过异步消息传递,这大大不同于同步方法调用。

在Orleans 框架里 grains 的管理和激活类似于操作系统的虚拟内存管理,grain
的激活是通过创建一个服务端的copy
,如果后续它不再使用的时就自动变为未激活状态。

如果一个消息被发送到grain且在任何服务器上都没有激活状态的grain时,运行时管理器将自动创建一个新的grain,因为grains是虚拟的,所以他们永远不会失败,即使目前主机所有服务器激活失败。

这使得没必要测试是否有grain存在的必要性,以及故障跟踪;Orleans
运行时做这一切都是自动的。

更多的内容细节请阅读如下连接

Read the MSR Technical Report on
Orleans

优点:

Orleans的优点有 1,提高开发人员的开发效率,即使不是专业开发人员。

2,不需要做太多的工作,默认支持很强的可拓展性。

下面详细讲解这些优点

开发人员的生产率

Orleans
的编程模型提供了以下关键的抽象,提出了专家和非专家的程序员的生产率,保证系统服务。

1.遵循面向对象的规范:Actor是实现声明的.net类,与Actors接口的异步方法,因此,开发人员可以把Actor的方法作为远程对象的方法直接调用。
透明地调用Actor的方法和故障的处理。

2.单线程执行:确保Actor只在一个线程上执行,与其他Actor的隔离,保证不用担心并发的问题,也不会担心使用数据共享而必须使用锁机制等问题,对于非专家程序员开发分布式程序变得更容易。

3.透明激活:只有当需要处理一个消息时,运行时才激活一个Actor,区分开了通过代码控制创建Actor引用
和通过物理内存激活Actor
的概念,这中机制在应用程序中是透明的,开发人员不需要关系一个Actor是如何创建的。
很多适合一个Actor的激活或者销毁,是由虚拟内存决定的。
应用程序不断轮训所有的“内存空间”的逻辑上创建的Actors,无论他们是在物理内存在任何特定的点。
Actor的生命周期是应用管理,此功能明显改善了传统Actor模型。

4.位置透明:通过Actor的对象引用来调用其他机器上的包含业务逻辑的Actor,在Orleans
运行时,这两个Actor之间是相互透明的。

如果应用程序找不到一个Actor,这有可能是当前这个Actor被垃圾回收了或者是还没有被激活。

5.透明集成持久化存储:Orleans
允许发布持久化到内存中的Actor的状态的映射,它同步更新确保成功持久化到内存中后可以获得一个正确的结果。

可以直接拓展或者更改现有持久化机制。

6.错误自动传播:运行时调用链上分布式的try/catch将未处理的错误与异常自动处理。
因此,错误不会在应用程序中丢失。
这允许开发人员在适当的地方放置错误处理逻辑,而无需在每个级别手动传播错误。

默认的透明的可扩展性

实践证明Orleans 编程模型
提供一个较低级别的系统功能就可以很轻易的实现开发人员的程序拓展和服务器拓展,下面详解在性能和拓展点上的关键点:

1.应用状态的细粒度的分割:

通过使用Actor作为直接寻址实体,程序员隐式地打破了他们的应用程序的整体状态,虽然orleans
没有明确指出如何划分一个大或者小的Actor模型,但是在大多数情况下,每一个自然程序实体代表一个Actor来多划分出更多的Actor来说是有好处的,比如一个用户账户、一个订单等等。
随着Actor被单独寻址,它们的物理逻辑在运行时被抽象出来了

2.自适应资源管理:假设在负载均衡和通信的请求不失败的情况下因为Actor位置的透明或者运行时的管理动态调整了硬件资源的配置
决定迁移整个计算集群的Actors,而相互通信的Actor之间没有彼此的位置,可以根据吞吐量的需要动态创建一个或多个运行时的演员特定的Actor副本,而无需对应用程序代码进行任何更改。

3.多路转换通信:在Orleans
Actors的逻辑终结点(endpoints)和消息,他们之间是复用在一组固定的所有Tcp连接(TCP
sockets),这使得主机一组数量庞大的Actors组的寻址不会影响到系统的性能,另外actors的激活和停用不会影响物理终结点的注册/注销的性能成本,
如TCP端口或HTTP URL,甚至关闭TCP连接。

4.高效的调度:大量单线程Actors的运行时的调度执行运行在每个处理器核心的的自定义线程池中,和用非阻断连续Actor的代码(在Orleans
的编程模型必须的)在一个高效协作多线程方式运行应用程序代码没有竞争。
这使得系统能够达到高吞吐量和大大提高了中央处理器的利用率(高达90%),具有极高的稳定性。

事实上,在系统中的Actor的数量的增长和负载不会导致额外的线程或其他操作系统有助于单个节点和整个系统的可扩展性。

5.显式同步:Orleans
的编程模式明确指导开发人员使用异步执行编写异步非阻塞代码的分布式应用程序,

Orleans
的编程模型使分布式应用程序的异步性和指导程序员编写的非阻塞异步代码,结合异步消息传递和高效调度,这使一个大程度的分布式并行性和整体吞吐量,没有明确的使用多线程。

Grains:

分布式的单位Grains(Actors)

并发是分布式应用程序固有的特点,这使得增加了它的复杂性,而Actor模型的特殊性和具有创造性的特点,使这样的事情变得简单了。

Actor以两种方式做到这一点:

1.通过提供单个线程访问一个Actor实例的内部状态。

2.除通过消息传递外,不在Actor实例之间共享数据。

Grains 是Orleans
应用程序的构建块,它们是彼此孤立的原子单位,分布的,持久的。
一个典型的grain是有状态和行为的一个单实例。

执行单元:单线程执行模型背后的原理是Actor(remote)轮流调用其方法。
如,从Actor A 到 Actor B的消息将被放置在一个队列中
,当所有优先级比较高的服务处理完成才去调用队列相关的处理程序。
这使我们能够避免所有使用锁来保护Actor的状态,因为它本质上是保护,防止数据竞争。
然而,它也可能会出现一些问题,消息的循环传递。
如:如果A发送消息给B从它的一个方法,并等待它的完成,和B发送一个消息,也等待其完成,应用程序就将会出现死锁的问题。

Grains 激活-grain运行时实例

当运行一个grain的时候,Orleans 确保在一个Orleans silos
有一个grain的一个实例,当发现任何的silo中都没有一个grain的一个实例时,运行时就会创建一个,这个过程被称为激活。

如果使用的是一个已经持久化了的grain,运行时就自动去后台读取并激活,Orleans
控制着整个激活或者停用的过程,当编码一个grain时,开发人员假定所有的grain都是被激活的。

grain激活在块中执行工作,并在它移动到下一个之前完成每一个块。
大块的工作包括响应来自其他grain或外部客户请求的方法调用,并止于前一块完成关闭。
对应于一个工作块的基本的执行单元被称为一个turn。

在并行期间orleans
执行很多属于不同的激活的turn,每个激活是允许同时执行它的turns,这意味着没必要使用锁或者其他同步的方法去阻止数据的争夺和多线程的危险,如上所述,无论如何,预定关闭的truns的不可预测的交错,可能会导致grain的状态与计划关闭时时不同的,所以开发人员仍然需要注意交错错误。

激活模式

Orleans 支持两种模式:

1.单激活模式(默认):单激活模式(默认),创建每一个grain的同时激活。

2.无边界工作模式:创建自主激活的grain,以提高吞吐量。
“自主”意味着有相同grain不同的激活之间状态不一致。
因此,这种模式适合于无本地状态保留grain,或grain的本地状态是不变的,比如作为持久态的高速缓存grain

Silos:

Orleans silo 是一个主机服务,用来执行Orleans
grains,它监听一个端口,用来监听从silo到silo的消息或者从其他客户端到silo的消息的,典型的silo就是,每台机器运行一个silo。

cluster:

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

所有silo都使用一个动态更新的共享成员存储库,并有助于协调集群管理,通过阅读共享存储库了解对方的位置,在任何时候,一个silo可以通过注册在共享存储中连接到一个集群。

这种方式的集群可以在运行时动态扩展。 Orleans
提供弹性和可用性从群集中删除无效的silos。

对于Orleans 管理集群深入详细的文档,请阅读集群管理。

接下来我们看看Orleans框架客户端的使用 。

Clients:

Orleans 和客户端代码

Orleans 包括两个不同的部分:Orleans 基础部分(grains) 和客户端部分

Orleans 的一部分是由应用程序的运行时服务称silos grains
组成,在调度限制下的运行时执行的Grain代码和确保内部在Orleans 编程模型。

客户端部分通常是一个web前端,通过少量的Orleans 客户端库连接到Orleans
部分,使得客户端代码可以通过引用服务端的一个grain的引用进行通讯。

例如:一个ASP.NET web应用程序运行在服务端的部分可以是Orleans 的客户端。
客户端部分运行在.net 应用程序池的主线程中,和不受调度的限制和Orleans
运行时的保证。

下一步,我们将看看一个grains如何接收异步消息,或推送数据。

客户端观察者

有一种情况,其中一个简单的消息/响应模式是不够的,客户端需要接收异步通知。
例如,一个用户可能希望在一个新的即时消息发布的时候被一个朋友通知。

客户端观察者是一允许异步通知客户端的一种机制。
观察者是一个继承自IGrainObserver的单向异步接口,它的所有方法都的返回值必须是void。
grain
给这个观察者发送一个通知是通过调用这个接口的一个方法,除了它没有返回值,因此grain不需要依赖于结果,Orleans运行时将确保单项通知,grain
发布了这样的一个通知同时也提供了相应的add observers或者remove
observers的 API。

另外,通常它也提供了一些使用的用来取消这些订阅的方法,Grain
开发者可以使用Orleans
的这个ObserverSubscriptionManager<T>泛型类简化observed
的grain类型的开发。

订阅一个通知,客户端必须先创建一个实现了观察者接口的C#本地类的对象,它调用观察者工厂的方法(CreateObjectReference()),重定向到C#对象进入grain对象引用,然后可以将其传递给通知grain的订阅方法。

该模型也可以被其他grains用于接收异步通知。
不像在客户端订阅的情况,订阅grain只是实现Observer接口的一个面,并通过自身的引用(如:this.
AsReference<IMyGrainObserverInterface>)

代码示例

假设我们有一个周期性发送信息给客户的grain,为简单起见,在我们的例子中的消息将是一个字符串。
我们首先定义客户端接收消息的的接口。

该接口将看起来像这样

public interface IChat : IGrainObserver
{
    void ReceiveMessage(string message);
}


唯一特殊的是继承了IGrainObserver,现在任何用户想观察这些信息应该实现一个实现了IChat接口的类。
如下:

public class Chat : IChat
{
    public void ReceiveMessage(string message)
    {
        Console.WriteLine(message);
    }
}


现在在服务器上,我们应该有一个grain,发送这些聊天信息给客户端。grain也应该有一个为客户端订阅和退订的消息机制。
订阅的grain可以用类ObserverSubscriptionManager:

class HelloGrain : Grain, IHello
{
    private ObserverSubscriptionManager<IChat> _subsManager;

    public override async Task OnActivateAsync()
    {
        // We created the utility at activation time.
        _subsManager = new ObserverSubscriptionManager<IChat>();
        await base.OnActivateAsync();
    }

    // Clients call this to subscribe.
    public async Task Subscribe(IChat observer)
    {
        _subsManager.Subscribe(observer);
    }

    //Also clients use this to unsubscribe themselves to no longer receive the messages.
    public async Task UnSubscribe(IChat observer)
    {
        _SubsManager.Unsubscribe(observer);
    }
}

可以使用ObserverSubscriptionManager<IChat>的实例给客户端发送通知消息,这个发送消息的方法接受一个Action<T>的委托或者一个表达式,您可以调用接口上的任何方法,将其发送给客户端。
在我们的案例只有一个方法ReceiveMessage,我们的服务器上的发送代码会看起来像这样:

public Task SendUpdateMessage(string message)
{
    _SubsManager.Notify(s => s.ReceiveMessage(message));
    return TaskDone.Done;
}


现在我们的服务器来发送信息观察客户的方法,用于订阅/退订和客户端两种方法实现了一个类能够观察grain的消息。最后一步是创建基于我们以前实现的聊天类客户观察员 Chat 引用 ,并让它收它订阅的消息。

代码看起来像这样:

//First create the grain reference
var friend = GrainClient.GrainFactory.GetGrain<IHello>(0);
Chat c = new Chat();

//Create a reference for chat usable for subscribing to the observable grain.
var obj = await GrainClient.GrainFactory.CreateObjectReference<IChat>(c);
//Subscribe the instance to receive messages.
await friend.Subscribe(obj);


现在我们的服务器上调用grain的方法SendUpdateMessage ,所有订阅的客户端将收到消息。在我们的客户代码,Chat 的实例将变量c输出到控制台。
注意:支持观察员可能会在未来的版本中删除,取而代之的是一个简单的消息流短信,它可以支持相同的概念,更多的灵活性和可靠性。

常见问题:

微软是否支持orleans?

orleans的源代码已经在GitHub上的MIT许可下发布。
微软将继续投资在orleans和接受社区的代码贡献。

可以获的一个“启动”的License?

License 已经放在源代码releases 下

Orleans 适用于生产环境吗?

答案是肯定的

什么时候使用Grain 什么时候使用一个普通对象?

有两种方法来回答这个问题,运行时和建模的角度。

从运行时角度看:grain内创建的对象是不能远程调用,Grain
的位置透明在系统的任何位置都可以访问,这样他们就可以被自动放置或部署任何服务器,和当他们寄宿的服务器故障时可以重启。

从建模的角度看:在你的
Orleans-based应用程序中有四种基本的组件,通信接口、消息载体、grains和grains的数据保存,对象用户承载grains的数据,通信接口是一些有一定限制的常规接口。

仍然存在一些问题就是,在一个给定的系统中的实体应被建模为grain吗?

一般来说,你应该使用一个grain来模拟一个独立的实体,它有一个公开的通信接口与系统的其他组件,并有自己的生命周期,即,它可以独立存在于其他组件中。
例如,在社会网络中的一个用户是一个grain,而它的名字不是。
用户的新闻墙可能是一个grain,而它所接收到的消息的列表不是(因为墙是由其他用户访问,而列表的消息是一个私人数据)。
希望这个网站上的样本将有助于你识别一些模式,并看到你自己的场景的相似之处。

如何提高Grain 的高访问?

一个grain的吞吐量是有限的,它是运行在一个单线程中,因此,最好是避免设计一个单一的grain来接收不成比例的请求。

有各种各样的模式,有助于防止单一grain的超载,即使在逻辑上它是一个通信的中心点。

例如:如果grain是 一个 大量grains的
统计或者计数的聚合器,一个行之有效的方法是增加一个控制数量的中间聚合grains和分配每个报告的grains(使用module在key或hashcode)到中间聚合器,使负载或多或少均匀地分布在所有中间聚合器的grains,在他们的定期部分聚集到中央聚合器grains

 

如何取消或者是取消激活grain?

一般不需要强制一个grain失活,作为orleans运行时自动检测并关闭闲置激活的grain并回收系统资源。
在特殊的情况下,当你认为你需要立即使grain失活时,可以通过调用基类方法base.
DeactivateOnIdle()。

我可以告诉orleans,在哪里激活grain吗?

它可以通过限制策略( restrictive placement
strategies)来实现,但我们一般认为这是一个反模式,所以不建议。
如果你发现自己需要指定激活特定的silo
grain,应该让你的系统来处理,这样就可以充分利用orleans框架。

通过解决这样的问题,表明应用程序承担了资源管理器的负担,而不一定有足够的信息系统的全局状态多的好。

可以多个数据中心独立部署吗?

orleans部署目前仅限于一个单一的数据中心。

我可以热部署grain添加或更新他们吗?

目前是不可以。

如何升级grains的版本?

orleans目前不支持即时更新grain代码。
要升级到新版本的grain代码,您需要提供并启动一个新的部署,切换过来,然后关闭旧部署。

云缓存服务中可以保存grain的状态吗?

它可以通过云存储,但是默认没有提供,你可以自己创建一个。

我可以从公共网络连接到orleans 的 slios?

orleans是托管服务的后端部分,你应该创建一个前端服务器的客户端连接到你的服务端。
它可以基于Web API项目,HTTP,Socket服务器,一个signalr服务器或其他东西。
你真的可以从Internet连接到orleans,但从安全的角度考虑,不是一个好的实践。

如果调用一个grain返回之前slio失败,会发生什么?

您将收到一个异常,您可以捕获和重试或做任何其他在您的应用程序逻辑中有意义的。
原文如下,

你会收到一个异常,您可以捕获和重试或做任何事情在你的应用程序逻辑中有意义的。奥尔良运行时没有立即重新创建从一个失败的筒仓谷物因为他们很多人可能并不需要立即或在所有。相反,运行时单独再现这种谷物,只有当一个新的请求到达特定的粮食。为每一粒它选择其中一个可用的筒仓作为一个新的主机。这种方法的好处是,只能对谷物的实际使用执行恢复过程,它传播的时空,跨越所有可用筒仓,从而提高了系统的响应和恢复的速度。还要注意是筒仓失败时的时间之间有延迟和奥尔良群集时检测到故障。延迟时间是检测的可配置速度和误报的可能性之间的折衷。在此过渡期间对粮食的所有调用会都失败,但后失败的检测粮食将会创建后一个新的呼吁,另一个筒仓,,
所以才会最终可用。可以找到更多的信息在这里。

如果一个grain调用需要太多的时间来执行,会发生什么?

 

 

由于奥尔良使用合作多任务模型,它将不会自动抢占一粒执行但奥尔良为生成警告长执行粮食调用以便您可以检测到它们。合作多任务中有很多更好的吞吐量相比,抢占式多任务。你应该牢记那粒电话不应该执行任何长时间运行的任务,像
IO
同步和不应阻止对要完成的其他任务。所有的等待,应该是使用异步等待关键字或其他异步等待机制。谷物应该尽快返回,让其他谷物执行的最大吞吐量。

什么情况下分割用例(同时在多个silos中激活的同一个grain)?

这可以永远不会发生在正常操作过程中,每一粒会有且仅有一个实例每
id。唯一的一次发生这种情况是当筒仓崩溃时或如果被杀了不允许正确关机。在这种情况下,还有一粒可以存在多个筒仓中就有一个的窗口
30-60 秒 (根据配置) 是从系统中删除。从粮食 Id 到其激活 (实例)
地址的映射存储在分布式目录 (采用 DHT)
和每个筒仓拥有此目录分区。当会员各两个筒仓持有不同意见时,都可以请求创建的实例和作为一个结果。两个实例的粮食可能并存。一旦群集筒仓达成会员,其中一个实例将被停用,只有一个激活会生存。你可以了解更多有关如何奥尔良管理集群在群集管理页面。此外你可以看看奥尔良的纸更详细的信息,但是你不需要了解它完全能够编写您的应用程序代码。你只被需要考虑的在编写您的应用程序时有两个实例的演员罕见的可能性。

 

先决条件

Orleans 是一个.net 类库集,为了使用它,你需要.net 4.5.1
或者更高版本,开发工具集需要visual studio 2015
或者更高版本或者其他支持的开发工具,不支持Visual
Studio的简化版本或者拓展包,但是你可以直接引用Orleans ,通过NuGet.

在生产环境中,Orleans 需要持久化存储,目前只支持一下技术产品之一:

  • Azure – Tested
    with Azure SDK 2.4 –
    2.8
  • SQL
    Server 2008
    or higher
  • ZooKeeper 3.4.0 or higher
  • MySQL 5.0 or higher
  • Consul 0.6.0 or higher

 

Azure
就不多说了微软的云平台产品,当然就是要花钱买微软的云平台、云数据库了

SQL Server 

Zookeeper
这又是个什么东东呢,说起它那就得提到在大数据存储、大数据运算下大名鼎鼎的Hadoop
了,它是属于Hadoop项目下的一个子项目,用来存储数据的,具体详细你就问度娘吧。

MySQL 一个数据库

Consul
这是什么,不会吧,我孤陋寡闻了,从来没听说过这么高达上的东西,好吧,问度娘:

Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其他分布式服务注册与发现的方案,Consul的方案更”一站式”,内置了服务注册与发现框
架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,不再需要依赖其他工具(比如ZooKeeper等)。使用起来也较
为简单。Consul用Golang实现,因此具有天然可移植性(支持Linux、windows和Mac
OS
X);安装包仅包含一个可执行文件,方便部署,与Docker等轻量级容器可无缝配合。

注意:以上产品使用我们会在后续章节中用示例的方式给大家展示,就期待吧。

 

Orleans Tools for Visual Studio

说起来也多亏visual studio
这个集大成的开发工具,做的那么贴心,犹如毒品,难以戒掉:

正如一如既往的vs Orleans
也给我们提供了模版化的项目创建,我们只需要安装这个vs
插件,然后就可以在vs 中看到Orleans的项目模版了,

下载地址 Orleans Tools for Visual
Studio

然后就是安装了,如果成功安装好了,启动vs 就可以看到如下结构

图片 3

如上图所示:1 是安装好后Orleans
的一个模版集节点,选中后左边面板中会有三个项目模版。

2
是我们项目中一个服务的承载项目,这里是用来测试用的,所以是一个控制台应用程序,同时也将host所需的类库nuget进来了。

针对你项目的需要,可以是一个windows 服务,一个控制台应用,一个Windows
forms 的程序,或者也可以寄宿到iis的web中。

3 是grain的实现
,我们大部分时间就是与里面的东西打交道,它里面是一些实现了grain借口,或者业务类的一系列类集合。

4 是grain
接口定义处,为何要孤立出来呢,因为将来这些接口是要暴露出来让其他需要的地方调用吗。

好了,就介绍到这里吧,你可以去试试。

 

ETG.Orleans.Templates.VSIX

这个也是vs 插件,就不多啰嗦了,直接下载安装就ok

Orleans NuGet Packages

orleans(1.2.2) 包

在大多数情况下,有4个关键的NuGet包你需要使用:

Microsoft.Orleans.OrleansCodeGenerator.Build

PM> Install-Package Microsoft.Orleans.OrleansCodeGenerator.Build 


grain 的接口或者实现项目提供构建支持,把他们添加到你的grain的接口和实现项目中可以实现grain引用和序列化的代码生成,
Microsoft.Orleans.Templates.Interfaces 和 Microsoft.Orleans.Templates.Grains 包已经废弃,仅仅是为了向后兼容而留用。

Microsoft.Orleans.Core

PM> Install-Package Microsoft.Orleans.Core

包含了Orleans.dll,定义了胆量的Orleans 的公共类型定义和客户端的部分,引用他来构建你的类库和客户端项目,不需要包括更多的其他提供者。

发表评论

电子邮件地址不会被公开。 必填项已用*标注