Microsoft Orleans - 一个实例





5.00/5 (11投票s)
一个关于如何使用 Microsoft Orleans 库开发分布式、容错基金会计系统的快速示例
引言
Microsoft Orleans 是一个框架,它声称通过抽象化在这些系统中出现的持久性和线程同步的复杂性,从而大大简化了容错、异步分布式系统的创建。它通过使用 Actor 模型(尽管将 Actor 称为“Grains”)并通过限制您可以在其上执行的操作来实现这一点,从而使它们线程安全。
必备组件
- 本文中的代码和 Microsoft Orleans 框架需要 .NET Framework v4.5(或更高版本)才能运行
- 使用 Orleans Tools for Visual Studio 插件创建了该项目
- Visual Studio 2015(专业版或更高版本,如果您想使用插件)
- Orleans 模板插件简化了操作
背景
为了演示这项技术,我整理了一个金融服务应用程序的概要,该应用程序用于跟踪资金(共同基金、对冲基金等)。当然,这是一个 hello-world 级别的应用程序,但我认为它足以展示这项技术所蕴含的潜力。
在我们的简单系统中,我们将有这些实体要建模
- 投资者,即向基金投入资金的人
- 基金,投资者可以投资的一种交易账户
- 资产,基金买卖的物品
- 经纪人,我们通过其交易资产的公司
入门
第一步是创建一个新项目,该项目定义 grain 接口(不同的 grain“实体”可以相互通信的方式)。
对于我们中的每个实体,我们需要决定如何唯一地识别它。开箱即用,选项是使用唯一的字符串、整数或全局唯一 ID (GUID)。对于这个 hello-world 级别的演示,我将使用string
,但如果您的实际实体没有任何固有的唯一标识符,则可以使用 GUID 或增量整数。
接口定义了 grain(实体)的实例可能发生的事情——例如,我们可以说投资者可以订阅或赎回基金
namespace FundGrainInterfaces
{
/// <summary>
/// Grain interface for an investor
/// </summary>
/// <remarks>
/// For this example, each investor gets an unique string identifier to identify them
/// </remarks>
public interface IInvestorGrain : IGrainWithStringKey
{
/// <summary>
/// The investor subscribes into the fund - paying money in
/// </summary>
/// <param name="fundToSubscribe">
/// The fund that the investor is subscribing to
/// </param>
/// <param name="amountToSubscribe">
/// The amount of money being subscribed
/// </param>
Task Subscribe(IFundGrain fundToSubscribe, decimal amountToSubscribe);
/// <summary>
/// The investor takes money out of the fund
/// </summary>
/// <param name="fundToRedeem">
/// The fund that the investor has previously subscribed to
/// </param>
/// <param name="redemptionAmount">
/// The amount redeemed
/// </param>
Task<decimal> Redeem(IFundGrain fundToRedeem, decimal redemptionAmount);
}
}
然后我们需要创建一个项目来实现这些接口的具体类。每个具体的 grain 类都必须继承自abstract
类“Grain
”并实现上述声明的接口。
为了说明 grain 之间的通信,当投资者订阅或从基金赎回时,该信息将传递给 Fund grain
/// <summary>
/// Grain implementation class for investor.
/// </summary>
public class InvestorGrain : Grain, IInvestorGrain
{
// The investors own holdings in the funds
private Dictionary<string, decimal> _holdings = new Dictionary<string, decimal>();
public Task<decimal> Redeem(IFundGrain fundToRedeem, decimal redemptionAmount)
{
if (_holdings.ContainsKey(fundToRedeem.GetPrimaryKeyString()))
{
if (redemptionAmount <= _holdings[fundToRedeem.GetPrimaryKeyString()])
{
// cannot redeem what you do not hold
redemptionAmount = _holdings[fundToRedeem.GetPrimaryKeyString()];
}
return fundToRedeem.Redemption(redemptionAmount);
}
else
{
// No holding therefore cannot redeem
return Task.FromResult(decimal.Zero);
}
}
public Task Subscribe(IFundGrain fundToSubscribe, decimal amountToSubscribe)
{
fundToSubscribe.Subscription(amountToSubscribe);
if (_holdings.ContainsKey(fundToSubscribe.GetPrimaryKeyString()))
{
_holdings[fundToSubscribe.GetPrimaryKeyString()] += amountToSubscribe;
}
else
{
_holdings.Add(fundToSubscribe.GetPrimaryKeyString(), amountToSubscribe);
}
// Indicate that all went well
return TaskDone.Done;
}
}
相应的 Fund grain
可以在基金自己的可用流动现金中处理订阅和赎回
public class FundGrain : Grain, IFundGrain
{
private decimal _liquidCash ; // Liquid cash available in the fund...
public Task<decimal> Redemption(decimal redemptionAmount)
{
if (_liquidCash >= redemptionAmount)
{
_liquidCash -= redemptionAmount;
}
else
{
// Redeem as much cash as is available
redemptionAmount = _liquidCash;
_liquidCash = 0;
}
return Task.FromResult(redemptionAmount);
}
public Task Subscription(decimal subscriptionAmount)
{
_liquidCash += subscriptionAmount;
return TaskDone.Done;
}
}
创建 silo
这些 grain(实体)的实例需要由 silo 托管,silo 实际上是该 grain
的虚拟机环境。为了测试这些,我们需要创建一个 silo 测试项目
在此主机中,您可以实例化 grain(实体)的实例,然后可以通过其定义的接口与它们交互
// - - - 8< - - - - - - - - - - - - - - - - - - - - - - -
IFundGrain myTestFund = GrainClient.GrainFactory.GetGrain
现在,如果运行此 silo 的多个实例,它们将允许在相同的底层基金 grain 上运行,而开发人员不必实现(或关心)任何并发检查。
关注点
- 您可以使用许多 存储提供程序来保留 grain 之间的使用,包括各种 Azure 云存储选项。
历史
- 2016 年 7 月 19 日:初始版本