Orleans部署

1、配置指南html

1,客户端配置前端

2,服务端配置node

3,典型配置linux

4,配置.NET垃圾收集nginx

5,SQL系统存储git

2、监控程序员

1,运行时监视github

2,silo错误代码监测web

3,客户端错误代码监测sql

3、解决部署问题

4、异构silos

5、开始使用Azure Web Apps

6、Docker部署

7、服务结构托管

 

1、配置指南

本配置指南介绍了关键配置参数以及如何在大多数典型使用场景中使用这些参数。

Orleans Configuration xsd file is located here.

Orleans能够用于各类适合不一样使用场景的配置,例如用于开发和测试的本地单节点部署,服务器群集,多实例Azure工做者角色等等。全部不一样的目标场景都是经过指定特定 Orleans配置XML文件中的值。 本指南提供了使Orleans在其中一个目标方案中运行所需的关键配置参数的说明。 还有其余的配置参数,主要是为了更好地调整奥尔良的性能。 它们记录在XSD模式中,即便在生产中运行系统也不须要。

Orleans良是一个创建和运行高规模服务的框架。 Orleans应用程序的典型部署跨越了一组服务器。 在每台服务器上运行的Orleans运行时(称为silos)实例须要配置为相互链接。 除此以外,老是有一个链接到Orleans部署的客户端组件,一般是一个Web前端,须要配置它以链接到silos。 本指南的“服务器配置”和“客户端配置”部分分别介绍了这些方面。 “典型配置”部分提供了一些常见配置的摘要。

1,客户端配置

有两种方法:手动配置一个或多个网关端点,或者将客户端指向由silos的集群成员使用的Azure表。在后一种状况下,客户端会自动发如今部署中启用的客户机网关的silos,并在链接或离开集群时调整其与网关的链接。该选项是可靠的,并推荐用于生产部署。

①固定网关配置

ClientConfiguration.xml中使用一个或多个网关节点指定一组固定的网关: 

<ClientConfiguration xmlns="urn:orleans">
  <Gateway Address="gateway1" Port="30000"/>
  <Gateway Address="gateway2" Port="30000"/>
  <Gateway Address="gateway3" Port="30000"/>
</ClientConfiguration>

一个网关一般就足够了。 多个网关链接有助于提升系统的吞吐量和可靠性。

②基于群集成员的网关配置

要将客户端配置为从silo集群成员资格表中自动查找网关,您须要指定Azure表或SQL Server链接字符串以及目标部署标识

<ClientConfiguration xmlns="urn:orleans">
  <SystemStore SystemStoreType="AzureTable"
               DeploymentId="target deployment ID"
               DataConnectionString="Azure storage connection string"/>
</ClientConfiguration>

或者

<ClientConfiguration xmlns="urn:orleans">
  <SystemStore SystemStoreType="SqlServer"
               DeploymentId="target deployment ID"
               DataConnectionString="SQL connection string"/>
</ClientConfiguration>

或者

<ClientConfiguration xmlns="urn:orleans">
  <SystemStore SystemStoreType="ZooKeeper"
               DeploymentId="target deployment ID"
               DataConnectionString="ZooKeeper connection string"/>
</ClientConfiguration>

③本地silo

对于使用本地silo的本地开发/测试配置,应将客户端网关配置为“localhost”。

<ClientConfiguration xmlns="urn:orleans">
  <Gateway Address="localhost" Port="30000"/>
</ClientConfiguration>

④Web Azure中的Web角色客户端

当客户端是与silo 工做角色相同的Azure部署中运行的Web角色时,当调用OrleansAzureClient.Initialize()时,将从OrleansSiloInstances表中读取全部网关地址信息。 用于查找正确OrleansSiloInstances表的Azure存储链接字符串在部署和角色的服务配置中定义的“DataConnectionString”设置中指定。

<ServiceConfiguration  ...>
  <Role name="WebRole"> ...
    <ConfigurationSettings>
      <Setting name="DataConnectionString" value="DefaultEndpointsProtocol=https;AccountName=MYACCOUNTNAME;AccountKey=MYACCOUNTKEY" />
    </ConfigurationSettings>
  </Role>
  ...
</ServiceConfiguration>

silo 工做者角色和Web客户端角色都须要使用相同的Azure存储账户才能成功发现彼此。

当使用OrleansAzureClient.Initialize()和OrleansSiloInstances表进行网关地址发现时,客户端配置文件中不须要额外的网关地址信息。 一般,ClientConfiguration.xml文件将只包含一些最低限度的调试/跟踪配置设置,尽管这不是必需的。

<ClientConfiguration xmlns="urn:orleans">
  <Tracing DefaultTraceLevel="Info" >
    <TraceLevelOverride LogPrefix="Application" TraceLevel="Info" />
  </Tracing>
</ClientConfiguration>

基于代码的客户端配置。 这是仅供参考的示例,不该该按原样使用 - 您可能须要针对特定环境微调客户端参数。

var dataConnection = "DefaultEndpointsProtocol=https;AccountName=MYACCOUNTNAME;AccountKey=MYACCOUNTKEY";

var config = new ClientConfiguration
{
    //一些顶级功能
    GatewayProvider = ClientConfiguration.GatewayProviderType.AzureTable,
    ResponseTimeout = TimeSpan.FromSeconds(30),
    DeploymentId = RoleEnvironment.DeploymentId,
    DataConnectionString = dataConnection,
    PropagateActivityId = true,

    // 跟踪
    DefaultTraceLevel = Severity.Info,
    TraceToConsole = false,
    TraceFilePattern = @"Client_{0}-{1}.log",
    //TraceFilePattern = "false", // 将其设置为false或none,禁用文件跟踪,有效地设置config.Defaults.TraceFileName = null;

    TraceLevelOverrides =
    {
        Tuple.Create("ComponentName", Severity.Warning),
    }
};

config.RegisterStreamProvider<AzureQueueStreamProvider>("AzureQueueStreams",
    new Dictionary<string, string>
    {
        { "PubSubType", "ExplicitGrainBasedAndImplicit" },
        { "DeploymentId", "orleans-streams" }, // 这将是您的队列的前缀名称 - 因此要当心并使用对队列名称有效的字符串
        { "NumQueues", "4" },
        { "GetQueueMessagesTimerPeriod", "100ms" },
        { "DataConnectionString", dataConnection }
    });

config.RegisterStreamProvider<SimpleMessageStreamProvider>("SimpleMessagingStreams",
    new Dictionary<string, string>
    {
        { "PubSubType", "ExplicitGrainBasedAndImplicit" }
    });

IClusterClient client = null;
while (true)
{
    try
    {
        // 构建一个客户端,而后将其链接到群集。
        client = new ClientBuilder()
            .UseConfiguration(config)
            .ConfigureServices(
                services =>
                {
                    // 服务能够在这里提供给客户。 这些服务经过依赖注入来提供。 ConfigureServices能够屡次调用一个ClientBuilder实例。
                })
            .Build();

        // 将客户端链接到群集。 一旦链接成功,客户端将维护链接,根据须要自动从新链接。
        await client.Connect().ConfigureAwait(false);
        break;
    }
    catch (Exception exception)
    {
        // 若是链接尝试失败,则必须处理客户机实例。
        client?.Dispose();

        // TODO:记录异常。
        // TODO: Add a counter to break up an infinite cycle (circuit breaker pattern).
        await Task.Delay(TimeSpan.FromSeconds(5));
    }
}

// 使用客户端。
// 请注意,客户端能够在线程之间共享,而且一般是长期的。
var user client.GetGrain<IUserGrain>("leeroy77jenkins@battle.net");
Console.WriteLine(await user.GetProfile());

2,服务端配置

silo 配置有两个关键方面:

  • 连通性:silo的其余silo和客户的端点
  • 集群成员和可靠性:在部署中如何发现对方并检测节点故障。

取决于你想在这些参数中运行Orleans的环境可能重要,也可能不重要。 例如,对于单个silo开发环境,一般不须要可靠性,而且全部端点均可以是本地主机

如下部分详细介绍了上述四个关键方面的配置设置。 而后在场景部分中,能够找到最典型的部署场景的建议组合设置。

①链接

链接设置定义了两个TCP / IP端点:一个用于silo 间通讯,另外一个用于客户端链接,也称为客户端网关或简单的网关。

跨筒仓端点

<Networking Address=" " Port="11111" />

地址:使用的IP地址或主机名。 若是留空,silo 将选择第一个可用的IPv4地址。 Orleans支持IPv6以及IPv4。 

端口:要使用的TCP端口。 若是留空,silo将会随机选择一个端口。 若是机器上只有一个silo正在运行,建议指定一个端口以保持一致性,并便于配置防火墙。 要在同一台计算机上运行多个silo,能够为每一个silo提供不一样的配置文件,也能够将端口属性留空以便随机分配端口。

对于分配了多个IP地址的计算机,若是您须要从特定子网或IPv6地址中选择一个地址,则能够经过分别添加子网和PreferredFamily属性来执行此操做(请参阅XSD模式以获取准确的语法 这些属性)。

对于本地开发环境,您能够简单地使用localhost做为主机名:

<Networking Address="localhost" Port="11111" />

②客户端网关端点

除了XML元素名称以外,客户端网关端点的设置与silo间端点相同:

<ProxyingGateway Address="localhost" Port="30000" />

您必须指定与用于inter-silo端点的端口号不一样的端口号。

能够将客户端配置为链接到跨站点端点而不是网关,可是这须要在客户端上打开监听套接字(所以须要在客户端计算机防火墙上启用传入链接),而且一般不建议 对于一组很是有限的场景。

③集群成员和可靠性

一般,在Orleans上构建的服务部署在专用硬件或Azure节点集群上。 对于开发和基本测试,Orleans能够部署在单个节点配置中。 在部署到一个节点集群时,Orleans在内部实现了一套协议,以发现和维护集群中Orleans silos的成员资格,包括检测节点故障和自动从新配置。

为了可靠地管理集群成员资格,Orleans使用Azure Table,SQL Server或Apache ZooKeeper进行节点同步。 可靠的成员资格设置要求在silo配置文件中配置“SystemStore”元素设置: 

<SystemStore SystemStoreType="AzureTable"
             DeploymentId="..."
             DataConnectionString="..."/>

或者

<SystemStore SystemStoreType="SqlServer"
             DeploymentId="..."
             DataConnectionString="..."/>

或者

<SystemStore SystemStoreType="ZooKeeper"
             DeploymentId="..."
             DataConnectionString="..."/>

DeploymentId是定义特定部署的惟一字符串。 将基于Orleans的服务部署到Azure时,使用辅助角色的Azure部署标识是最有意义的。

对于开发,或者若是不可能使用Azure表,能够将silos配置为使用成员grain。这样的配置是不可靠的,由于它将没法在主仓的失败中存活下来,而主仓则承载着会员的grain。“MembershipTableGrain”是LivenessType的默认值。

<Liveness LivenessType ="MembershipTableGrain" />

④主silo

在可靠的部署中,使用Azure Table,SQL Server或ZooKeeper配置成员资格,全部建立的silo都是相同的,没有主要或次要silo的概念。 这是推荐用于生产的配置,可以在任何单个节点或节点组合的故障中幸免于难。 例如,Azure会按期推出操做系统修补程序,并最终致使全部角色实例从新启动。

当使用MembershipTableGrain进行开发或非可靠部署时,必须将其中一个silo指定为主要,而且必须在加入群集以前等待主要进行初始化的其余辅助silo以前启动和初始化。 在主节点出现故障的状况下,整个部署中止正常工做,必须从新启动。

主要是在配置文件中用全局部分中的如下设置指定的。

<SeedNode Address="<host name or IP address of the primary node>" Port="11111" />

下面是一个如何配置和启动托管在工做角色内的Orleans silo的例子。 这是仅供参考的示例,不该该按原样使用 - 您可能须要针对特定环境微调客户端参数。

var dataConnection = "DefaultEndpointsProtocol=https;AccountName=MYACCOUNTNAME;AccountKey=MYACCOUNTKEY";

var config = new ClusterConfiguration
{
    Globals =
    {
        DeploymentId = RoleEnvironment.DeploymentId,
        ResponseTimeout = TimeSpan.FromSeconds(30),
        DataConnectionString = dataConnection,

        LivenessType = GlobalConfiguration.LivenessProviderType.AzureTable,
        ReminderServiceType = GlobalConfiguration.ReminderServiceProviderType.AzureTable,
    },
    Defaults =
    {
        PropagateActivityId = true,

        // 跟踪
        DefaultTraceLevel = Severity.Info,
        TraceToConsole = false,
        TraceFilePattern = @"Silo_{0}-{1}.log",
        //TraceFilePattern =“false”,//将其设置为false或none来禁用文件跟踪,有效地设置了config.Defaults。TraceFileName =零;
        TraceLevelOverrides =
        {
            Tuple.Create("ComponentName", Severity.Warning),
        }
    }
};

// 注册引导provider类
config.Globals.RegisterBootstrapProvider<AutoStartBootstrapProvider>("MyAutoStartBootstrapProvider");

// Add Storage Providers
config.Globals.RegisterStorageProvider<MemoryStorage>("MemoryStore");

config.Globals.RegisterStorageProvider<AzureTableStorage>("PubSubStore",
    new Dictionary<string, string>
    {
        { "DeleteStateOnClear", "true" },
        //{ "UseJsonFormat", "true" },
        { "DataConnectionString", dataConnection }
    });

config.Globals.RegisterStorageProvider<AzureTableStorage>("AzureTable",
    new Dictionary<string, string>
    {
        { "DeleteStateOnClear", "true" },
        { "DataConnectionString", dataConnection }
    });

config.Globals.RegisterStorageProvider<AzureTableStorage>("DataStorage",
    new Dictionary<string, string>
    {
        { "DeleteStateOnClear", "true" },
        { "DataConnectionString", dataConnection }
    });

config.Globals.RegisterStorageProvider<BlobStorageProvider>("BlobStorage",
    new Dictionary<string, string>
    {
        { "DeleteStateOnClear", "true" },
        { "ContainerName", "grainstate" },
        { "DataConnectionString", dataConnection }
    });

// Add Stream Providers
config.Globals.RegisterStreamProvider<AzureQueueStreamProvider>("AzureQueueStreams",
    new Dictionary<string, string>
    {
        { "PubSubType", "ExplicitGrainBasedAndImplicit" },
        { "DeploymentId", "orleans-streams" },
        { "NumQueues", "4" },
        { "GetQueueMessagesTimerPeriod", "100ms" },
        { "DataConnectionString", dataConnection }
    });

try
{
    _orleansAzureSilo = new AzureSilo();
    var ok = _orleansAzureSilo.Start(config, config.Globals.DeploymentId, config.Globals.DataConnectionString);

    _orleansAzureSilo.Run(); //调用将阻塞直到silo关闭。
}
catch (Exception exc)
{
    //Log "Error when starting Silo"
}

3,典型配置

如下是可用于开发和生产部署的典型配置示例。

①本地开发

对于本地开发,在程序员的机器上只有一个本地运行的silo,配置已经包含在Microsoft Orleans Visual Studio的Orleans Dev / Test Host项目模板中。 能够经过运行使用Orleans Dev / Test Host模板建立的项目来启动的本地silo在DevTestServerConfiguration.xml中配置以下。

<OrleansConfiguration xmlns="urn:orleans">
  <Globals>
    <SeedNode Address="localhost" Port="11111" />
  </Globals>
  <Defaults>
    <Networking Address="localhost" Port="11111" />
    <ProxyingGateway Address="localhost" Port="30000" />
  </Defaults>
</OrleansConfiguration>

经过代码的silo配置以下。

var config = ClusterConfiguration.LocalhostPrimarySilo(11111, 30000);

要链接到本地silo,客户端须要配置为本地主机,而且只能从同一台机器链接。 能够经过运行Orleans Dev / Test Host模板建立的项目启动的Orleans客户端在DevTestClientConfiguration.xml中配置以下

<ClientConfiguration xmlns="urn:orleans">
  <Gateway Address="localhost" Port="30000"/>
</ClientConfiguration>

经过代码进行客户端配置以下。

var config = ClientConfiguration.LocalhostSilo(30000);

②使用Azure进行可靠的生产部署

对于使用Azure进行可靠的生产部署,您须要使用Azure Table选项来得到集群成员资格。 此配置是部署到本地服务器或Azure虚拟机实例的典型配置。

DataConnection字符串的格式是“DefaultEndpointsProtocol = https; AccountName =; AccountKey =”

<OrleansConfiguration xmlns="urn:orleans">
  <Globals>
    <SystemStore SystemStoreType="AzureTable"
         DeploymentId="<your deployment ID>"
         DataConnectionString="<<see comment above>>" />
    <Liveness LivenessType ="AzureTable" />
  </Globals>
  <Defaults>
    <Networking Address="" Port="11111" />
    <ProxyingGateway Address="" Port="30000" />
  </Defaults>
</OrleansConfiguration>

客户端须要配置为使用Azure表来发现网关,Orleans 服务器的地址不是静态地被客户知道的。

<ClientConfiguration xmlns="urn:orleans">
  <SystemStore SystemStoreType="AzureTable" DeploymentId="target deployment ID" DataConnectionString="<<see comment above>>" />
</ClientConfiguration>

③使用ZooKeeper进行可靠的生产部署

为了使用ZooKeeper进行可靠的生产部署,您须要使用ZooKeeper选项来得到集群成员资格。 此配置是部署到内部部署服务器的典型配置。

在“ZooKeeper程序员指南”中记录了DataConnection字符串的格式。 建议至少5台ZooKeeper服务器。

<?xml version="1.0" encoding="utf-8"?>
<OrleansConfiguration xmlns="urn:orleans">
  <Globals>
    <SystemStore SystemStoreType="ZooKeeper"
                   DeploymentId="<your deployment ID>"
                   DataConnectionString="<<see comment above>>"/>
  </Globals>
  <Defaults>
    <Networking Address="localhost" Port="11111" />
    <ProxyingGateway Address="localhost" Port="30000" />
  </Defaults>
</OrleansConfiguration>

客户端须要配置为使用ZooKeeper来发现网关,Orleans 服务器的地址不是静态地被客户知道的。

<?xml version="1.0" encoding="utf-8" ?>
<ClientConfiguration xmlns="urn:orleans">
  <SystemStore SystemStoreType="ZooKeeper" DeploymentId="target deployment ID" DataConnectionString="<<see comment above>>"/>
</ClientConfiguration>

④使用SQL Server进行可靠的生产部署

为了使用SQL Server进行可靠的生产部署,须要提供SQL Server链接字符串。

经过代码进行筒仓配置以下,包括日志配置。

var connectionString = @"Data Source=MSSQLDBServer;Initial Catalog=Orleans;Integrated Security=True;
    Max Pool Size=200;Asynchronous Processing=True;MultipleActiveResultSets=True";

var config = new ClusterConfiguration{    
    Globals =    
    {
        DataConnectionString = connectionString,
        DeploymentId = "<your deployment ID>",

        LivenessType = GlobalConfiguration.LivenessProviderType.SqlServer,
        LivenessEnabled = true,
        ReminderServiceType = GlobalConfiguration.ReminderServiceProviderType.SqlServer
    },
    Defaults =
    {        
        Port = 11111,
        ProxyGatewayEndpoint = new IPEndPoint(address, 30000),
        PropagateActivityId = true
    }};

var siloHost = new SiloHost(System.Net.Dns.GetHostName(), config);

客户端须要配置为使用SQL服务器来发现网关,就像Azure和Zookeeper同样,Orleans服务器的地址对于客户端来讲并非静态的。

var connectionString = @"Data Source=MSSQLDBServer;Initial Catalog=Orleans;Integrated Security=True;
    Max Pool Size=200;Asynchronous Processing=True;MultipleActiveResultSets=True";

var config = new ClientConfiguration{
    GatewayProvider = ClientConfiguration.GatewayProviderType.SqlServer,
    AdoInvariant = "System.Data.SqlClient",
    DataConnectionString = connectionString,

    DeploymentId = "<your deployment ID>",    
    PropagateActivityId = true
};

var client = new ClientBuilder().UseConfiguration(config).Build();
await client.Connect();

⑤不可靠的专用服务器集群部署

要在可靠性不是问题的状况下在专用服务器集群上进行测试,可使用MembershipTableGrain并避免依赖于Azure表。 您只须要将其中一个节点指定为主节点。

<OrleansConfiguration xmlns="urn:orleans">
  <Globals>
    <SeedNode Address="<primary node>" Port="11111" />
    <Liveness LivenessType ="MembershipTableGrain" />
  </Globals>
  <Defaults>
    <Networking Address=" " Port="11111" />
    <ProxyingGateway Address=" " Port="30000" />
  </Defaults>
</OrleansConfiguration>

对于客户:

<ClientConfiguration xmlns="urn:orleans">
  <Gateway Address="node-1" Port="30000"/>
  <Gateway Address="node-2" Port="30000"/>
  <Gateway Address="node-3" Port="30000"/>
</ClientConfiguration>

⑥Azure工做者角色部署

当Orleans部署到Azure Worker角色而不是VM实例时,大部分的服务器端配置其实是在OrleansConfiguration以外的其余文件中完成的,以下所示:

<OrleansConfiguration xmlns="urn:orleans">
  <Globals>
    <Liveness LivenessType="AzureTable" />
  </Globals>
  <Defaults>
    <Tracing DefaultTraceLevel="Info" TraceToConsole="true" TraceToFile="{0}-{1}.log" />
  </Defaults>
</OrleansConfiguration>

一些信息保存在服务配置文件中,其中worker角色部分以下所示:

<Role name="OrleansAzureSilos">
  <Instances count="2" />
  <ConfigurationSettings>
    <Setting name="DataConnectionString" value="<<see earlier comment>>" />
    <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="<<see earlier comment>>" />
  </ConfigurationSettings>
</Role>

数据链接字符串和诊断链接字符串没必要相同。

一些配置信息保存在服务定义文件中。 工做者角色也必须在那里配置:

<WorkerRole name="OrleansAzureSilos" vmsize="Large">
  <Imports>
    <Import moduleName="Diagnostics" />
  </Imports>
  <ConfigurationSettings>
    <Setting name="DataConnectionString" />
  </ConfigurationSettings>
  <LocalResources>
    <LocalStorage name="LocalStoreDirectory" cleanOnRoleRecycle="false" />
  </LocalResources>
  <Endpoints>
    <InternalEndpoint name="OrleansSiloEndpoint" protocol="tcp" port="11111" />
    <InternalEndpoint name="OrleansProxyEndpoint" protocol="tcp" port="30000" />
  </Endpoints>
</WorkerRole>

这就是托管Orleans运行时的工做者角色。 可是,在部署到Azure时,一般会有某种类型的前端,不管是网站仍是Web服务,由于公开发布Orleans 并非一个好主意。 所以,客户端配置是位于Orleans前面的Web或Worker角色(或Web站点)的配置。

重要说明:截至2017年11月,Azure云服务存在限制,若是Cloud Service中只有1个角色,则会阻止InternalEndpoint的防火墙配置。 若是您要经过虚拟网络链接到您的云服务,则必须将您的云服务扩展为两个实例,以便建立防火墙规则

假设前端是一个web角色,应该使用一个简单的ClientConfiguration文件:

<ClientConfiguration xmlns="urn:orleans">
  <Tracing DefaultTraceLevel="Info"
           TraceToConsole="true"
           TraceToFile="{0}-{1}.log"
           WriteTraces="false"/>
</ClientConfiguration>

Web角色在服务配置文件中须要与worker角色相同的链接字符串信息:

<Role name="WebRole">
  <Instances count="2" />
  <ConfigurationSettings>
    <Setting name="DataConnectionString" value="<<see earlier comment>>" />
    <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="<<see earlier comment>>" />
  </ConfigurationSettings>
</Role>

并在服务定义文件中:

<WebRole name="WebRole" vmsize="Large">
  <Imports>
    <Import moduleName="Diagnostics" />
  </Imports>
  <ConfigurationSettings>
    <Setting name="DataConnectionString" />
  </ConfigurationSettings>
  <!-- 还有其余的网络角色数据与Orleans没有任何关系 -->
</WebRole>

4,配置.NET垃圾收集

为了得到良好的性能,为silo进程配置.NET垃圾回收是很是重要的。 咱们找到的设置的最佳组合是设置gcServer = true和gcConcurrent = true。 当筒仓做为独立进程运行时,经过应用程序配置文件很容易设置。 您可使用Microsoft.Orleans.OrleansHost NuGet包中包含的OrleansHost.exe.config做为示例。

①.NET Framework

<configuration>
  <runtime>
    <gcServer enabled="true"/>
    <gcConcurrent enabled="true"/>
  </runtime>
</configuration>

②.NET Core

// .csproj
<PropertyGroup>
  <ServerGarbageCollection>true</ServerGarbageCollection>
  <ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
</PropertyGroup>

可是,若是silo做为Azure工做者角色的一部分运行,则默认状况下配置为使用工做站GC,这并不容易。 此博客文章展现了如何为Azure工做者角色设置相同的配置 - 

https://blogs.msdn.microsoft.com/cclayton/2014/06/05/server-garbage-collection-mode-in-microsoft-azure/

重要的提示服务器垃圾回收仅在多处理器计算机上可用。 所以,即便您经过应用程序配置文件(app.config或web.config)或经过引用的博客帖子上的脚原本配置垃圾收集,若是silo正在单核的(虚拟)机器上运行, 不会获得gcServer = true的好处。改善这个文件在这篇文章中.NET Framework

5,SQL系统存储

任何可靠的生产式Orleans部署都须要使用持久性存储来保持系统状态,特别是Orleans集群状态以及用于提醒功能的数据。 除了对Azure存储的开箱即用支持外,Orleans还提供了将此信息存储在SQL Server中的选项。

为了使用SQL Server做为系统存储,须要调整服务器端和客户端配置。

服务器配置应该以下所示:

<OrleansConfiguration xmlns="urn:orleans">
  <Globals>
      <SystemStore SystemStoreType ="SqlServer"
                 DeploymentId="OrleansTest"
                 DataConnectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Orleans;Integrated Security=True;Pooling=False;Max Pool Size=200;Asynchronous Processing=True;MultipleActiveResultSets=True" AdoInvariant="System.Data.SqlClient" />
  </Globals>
</OrleansConfiguration>

客户端配置应以下所示:

<ClientConfiguration xmlns="urn:orleans">
      <SystemStore SystemStoreType ="SqlServer"
                 DeploymentId="OrleansTest"
                 DataConnectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Orleans;Integrated Security=True;Pooling=False;Max Pool Size=200;Asynchronous Processing=True;MultipleActiveResultSets=True" AdoInvariant="System.Data.SqlClient" />
</ClientConfiguration>

DataConnectionString设置为任何有效的SQL Server链接字符串。 为了使用SQL Server做为系统数据存储,如今在Binaries \ OrleansServer文件夹中建立了一个脚本文件CreateOrleansTables _ *.sql(其中asterisk表示数据库供应商),该文件创建必要的数据库对象。 确保将要托管奥尔良仓库的全部服务器均可以访问数据库并拥有访问权限! 在咱们的测试过程当中,咱们已经绊倒了几回这个看起来微不足道的问题。 请注意,在Orleans 2.0.0中,这些SQL脚本已经被分红了每一个功能部件以匹配更细粒度提供者模型:集群,持久性,提醒和统计。

①SQL度量和统计表

系统表目前只能存储在Azure表或SQL服务器中。 可是,对于度量标准和统计表,咱们提供一个通用的支持来将其托管在任何持久性存储中。 这是经过StatisticsProvider的概念提供的。 任何应用程序均可以编写任意提供程序来将统计信息和指标数据存储在他们选择的持久存储中。 Orleans提供了一个这样的提供者的实现:SQL Table Statistics Provider。

为了将SQL服务器用于统计和指标表,须要调整服务器端和客户端配置。

服务器配置应该以下所示:

<OrleansConfiguration xmlns="urn:orleans">
     <Globals>
         <StatisticsProviders>
             <Provider Type="Orleans.Providers.SqlServer.SqlStatisticsPublisher" Name="MySQLStatsProvider" ConnectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Orleans;Integrated Security=True;Pooling=False;Max Pool Size=200;Asynchronous Processing=True;MultipleActiveResultSets=True" />
         </StatisticsProviders>
     </Globals>
     <Defaults>
         <Statistics ProviderType="MySQLStatsProvider" WriteLogStatisticsToTable="true"/>
     </Defaults>
</OrleansConfiguration>

客户端配置应以下所示:

<ClientConfiguration xmlns="urn:orleans">
      <StatisticsProviders>
         <Provider Type="Orleans.Providers.SqlServer.SqlStatisticsPublisher" Name="SQL" ConnectionString="Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=Orleans;Integrated Security=True;Pooling=False;Max Pool Size=200;Asynchronous Processing=True;MultipleActiveResultSets=True" />
      </StatisticsProviders>
      <Statistics ProviderType="MySQLStatsProvider" WriteLogStatisticsToTable="true"/>
</ClientConfiguration>

 

2、监控

1,运行时监视

[[这是须要审查]]

经过利用Orleans自动向Azure存储写入的数据,外部操做员能够监控Orleans部署的五种方式。

下面提到的表格在这里有更详细的描述。

OrleansSilosTable for cluster membership -此表列出了部署中的全部silos(分区键DeploymentID,行密钥silosID)。 操做员可使用此表来检查群集运行情况,查看当前设置的活动silos,或了解某个silos发生故障的缘由和时间。 Orleans的集群成员协议在内部使用此表,并用重要的成员事件(silos上下)更新它。

OrleansSiloMetrics表- grain性能统计 - Orleans在这个表(分区键DeplomentID,行密钥仓库id)中写入了一个小数(约10)的grain性能统计。 该表格每隔X秒(每一个silo可配置)自动更新一次。 这些度量指标包括:siloCPU,内存使用状况,该silo中grain激活的数量,发送/接收队列中的消息数量等。这些数据能够用来比较筒仓,检查是否没有明显的异常值(例如, silo运行在更高的CPU上),或者只是简单地检查一下经过grains报告的指标是否在预期的范围内。 此外,若是系统过载,则可使用此数据决定是否添加新的silos,或者若是系统大部分空闲,则能够减小silos数量。

OrleansSiloStatistics表 - 这个表格包含了更多的性能统计数字(数百个计数器),这些统计数据提供了更详细,更深刻的内部silos状态视图。 目前不推荐此表格供外部操做员使用。 主要是Orleans开发商帮助他们解决复杂的生产问题。 Orleans团队正在构建工具来自动分析这些数据,并基于此向运营商提供紧凑的建议。 这些工具也能够由任何人独立建造。

Watching error codes in MDS -Orleans自动写入不一样的错误消息到记录器。 该记录器能够配置为输出其数据到不一样的目的地。 例如,Halo团队将生产中的全部日志重定向到MDS。 他们已经在MDS中编写了自定义警报,以监视特定的错误代码并计算它们的发生次数,并在这些阈值达到某个阈值时向其发出警报。 在这里指定要观察的重要错误代码列表:

Windows performance counters -Orleans运行时不断更新其中的一些。 CounterControl.exe帮助注册计数器,并须要以提高的权限运行。 显然,性能计数器可使用任何标准的监视工具进行监视。

2,silo错误代码监测

Group Log Type Log Code Values Threshold Description
Azure Problems Warning or Error 100800 - 100899 Any Error or Warning 读取或写入Azure表存储的瞬间问题将被记录为警告。 瞬时读取错误将自动重试。 最终的错误日志消息意味着链接到Azure表存储存在真正的问题。
Membership Connectivity Problems Warning or Error 100600 - 100699 Any Error or Warning 警告日志是网络链接问题和/或silo从新启动/迁移的早期指示。 Ping超时和silo投票将显示为警告消息。 筒仓厌恶它被投票死了将显示为错误消息。
Grain call timeouts Warning 100157 Multiple Warnings logged in short space of time grain调用超时问题一般是由临时网络链接问题或silo重启/重启问题引发的。 系统应该在短期后恢复(取决于Liveness配置设置),超时应该清除。 理想状况下,只监视这些警告的大量日志代码600157应该是足够的。
Silo Restart / Migration Warning 100601 or 100602 Any Warning silo检测到它在同一台机器上从新启动(100602)或迁移到其余机器(100601)
Network Socket Problems Warning or Error 101000 to 101999, 100307,100015, 100016 Any Error or Warning 套接字断开被记录为警告消息。 打开套接字或发送消息时出现的问题记录为错误。
Bulk log message compaction Any 500000 or higher Message summary based on bulk message threshold settings 若是在指定的时间间隔内出现了多个相同日志代码的日志(缺省值在1分钟内大于5),则会删除包含该日志代码的其余日志消息,并将其输出为日志代码等于原始日志的“批量”条目 代码+ 500000.例如,多个100157条目将在日志中显示为每分钟5 x 100157 + 1 x 600157条目。
Grain problems Warning or Error 101534 Any Error or Warning 检测对非折返grain的“stuck”请求。 每次请求花费超过5倍的请求超时时间执行时,都会报告错误代码。

3,客户端错误代码监测

Group Log Type Log Code Values Threshold Description
Azure Problems Warning or Error 100800 - 100899 Any Error or Warning 读取或写入Azure表存储的瞬间问题将被记录为警告。 瞬时读取错误将自动重试。 最终的错误日志消息意味着链接到Azure表存储存在真正的问题。
Gateway connectivity problems Warning or Error 100901 - 100904, 100912, 100913, 100921, 100923, 100158, 100161, 100178, , 101313 Any Error or Warning 链接到网关的问题。 Azure表中没有活动的网关。 链接到活动网关丢失。
Grain call timeouts Warning 100157 Multiple Warnings logged in short space of time grain调用超时问题一般是由临时网络链接问题或silos重启/重启问题引发的。 系统应该在短期后恢复(取决于Liveness配置设置),超时应该清除。 理想状况下,只监视这些警告的大量日志代码600157应该是足够的。
Network Socket Problems Warning or Error 101000 to 101999, 100307, 100015, 100016 Any Error or Warning 套接字断开被记录为警告消息。 打开套接字或发送消息时出现的问题记录为错误。
Bulk log message compaction Any 500000 or higher Message summary based on bulk message threshold settings 若是在指定的时间间隔内出现了多个相同日志代码的日志(缺省值在1分钟内大于5),则会删除包含该日志代码的其余日志消息,并将其输出为日志代码等于原始日志的“批量”条目 代码+ 500000.例如,多个100157条目将在日志中显示为每分钟5 x 100157 + 1 x 600157条目。

3、解决部署问题

 本页提供了一些常规指导,可用于解决部署到Azure云服务时出现的任何问题。 这些是须要注意的常见问题。 请务必检查日志以获取更多信息。

1,得到一个SiloUnavailableException

首先检查一下,确保在尝试初始化客户端以前确实启动了筒仓。 有时silo须要很长时间才能启动,因此尝试屡次初始化客户端可能会有所帮助。 若是它仍然抛出一个异常,那么silos可能会有另外一个问题。检查silo配置,确保silo正确启动。

2,常见链接字符串问题

  • 部署到Azure时使用本地链接字符串 - 网站将没法链接
  • 对silos和前端(web和worker角色)使用不一样的链接字符串 - 网站将没法初始化客户端,由于它没法链接到silos

能够在Azure门户中检查链接字符串配置。 若是链接字符串设置不正确,日志可能没法正确显示。

3,修改配置文件不当

确保在ServiceDefinition.csdef文件中配置了正确的端点,不然部署将不起做用。 它会给出错误,说明它不能获取端点信息。

4,缺乏日志

确保链接字符串设置正确。

Web角色中的Web.config文件或worker角色中的app.config文件可能被错误地修改。 这些文件中的不正确版本可能会致使部署问题。 处理更新时要当心。

6,版本问题

确保解决方案中的每一个项目都使用相同版本的Orleans。 不这样作能够致使员工角色回收。 检查日志以获取更多信息。 Visual Studio在部署历史记录中提供了一些silo启动错误消息。

7,角色不断回收

  • 检查全部合适的Orleans程序集是否在解决方案中,并将Copy Local设置为True。
  • 检查日志以查看初始化时是否有未处理的异常。
  • 确保链接字符串是正确的。
  • 查看Azure云服务疑难解答页面以获取更多信息。

8,如何检查日志

  • 使用Visual Studio中的云资源管理器导航到存储账户中适当的存储表或blob。 WADLogsTable是查看日志的好起点。
  • 您可能只会记录错误。 若是您还须要信息日志,则须要修改配置以设置日志严重性级别。

编程配置:

  • 建立ClusterConfiguration对象时,请设置config.Defaults.DefaultTraceLevel = Severity.Info。
  • 建立ClientConfiguration对象时,请设置config.DefaultTraceLevel = Severity.Info。

声明式配置:

  • 将<Tracing DefaultTraceLevel =“Info”/>添加到OrleansConfiguration.xml和/或ClientConfiguration.xml文件中。

在Web和辅助角色的diagnostics.wadcfgx文件中,请确保将Logs元素中的scheduledTransferLogLevelFilter属性设置为Information,由于这是一个额外的跟踪筛选层,它定义将哪些跟踪发送到Azure存储中的WADLogsTable。

在“配置指南”中能够找到关于此的更多信息。

8,与ASP.NET兼容

ASP.NET中包含的razor视图引擎使用与Orleans(Microsoft.CodeAnalysis和Microsoft.CodeAnalysis.CSharp)相同的代码生成程序集。 这可能会在运行时出现版本兼容性问题。

要解决此问题,请尝试将Microsoft.CodeDom.Providers.DotNetCompilerPlatform(这是ASP.NET用来包含上述程序集的NuGet程序包)升级到最新版本,并设置绑定重定向,以下所示:

<dependentAssembly>
  <assemblyIdentity name="Microsoft.CodeAnalysis.CSharp" publicKeyToken="31bf3856ad364e35" culture="neutral" />
  <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="1.3.1.0" />
</dependentAssembly>
<dependentAssembly>
  <assemblyIdentity name="Microsoft.CodeAnalysis" publicKeyToken="31bf3856ad364e35" culture="neutral" />
  <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="1.3.1.0" />
</dependentAssembly>

 

4、异构silos

 概观在给定的集群上,筒仓能够支持一组不一样的grain类型:

在这个例子中,集群支持类型A,B,C,D,E:

  • grain种类A和B能够放置在silo1和2上。
  • grain类型C能够被放置在silo1,2或3。
  • grain类型D只能放在silo3上
  • grain类型E只能放在silo4上。

全部的silo都应该引用全部的grain类型的接口,可是grain类只应该被容纳它们的silo所引用。

客户端不知道哪一个silo支持给定的grain类型。

给定的Grain Type实现必须在支持它的每一个silo中相同。 如下状况无效:

在silo1和2:

public class C: Grain, IMyGrainInterface
{
   public Task SomeMethod() { … }
}

在silo3:

public class C: Grain, IMyGrainInterface, IMyOtherGrainInterface
{
   public Task SomeMethod() { … }
   public Task SomeOtherMethod() { … }
}

1,配置

不须要配置,您能够在群集中的每一个silo上部署不一样的二进制文件。 可是,若有必要,能够更改silos检查ClusterConfig.Globals.TypeMapRefreshInterval中支持的类型更改的时间间隔。出于测试目的,您能够在NodeConfiguration中使用ExcludedGrainTypes属性。 在基于代码的配置中,您能够在ClusterConfig.Defaults.ExcludedGrainTypes中找到它,它是要排除的类型的列表名称。

2,限制

  • 若是支持的“grain类型”集合发生更改,则不会通知链接的客户端。 在前面的例子中:
    • 若是Silo 4离开集群,客户端仍然会尝试调用E类型的grain。在运行时OrleansException会失败。
    • 若是在Silo 4加入以前,客户端链接到群集,客户端将没法调用E类型的谷物。它将失败一个ArgumentException
  • 不支持无状态grain:群集中的全部silo必须支持同一组无状态grain。

 

5、开始使用Azure Web Apps

若是您想从Azure Web App链接到Azure Silo,而不是在同一云服务中托管的Web Role,则能够。 

为了安全工做,您须要将Azure Web App和托管Silo的Worker角色分配给Azure虚拟网络。

首先,咱们将设置Azure Web App,您能够按照本指南建立虚拟网络并将其分配给Azure Web App。

如今咱们能够经过修改ServiceConfiguration文件来将云服务分配给虚拟网络。

<NetworkConfiguration>
  <VirtualNetworkSite name="virtual-network-name" />
  <AddressAssignments>
    <InstanceAddress roleName="role-name">
      <Subnets>
        <Subnet name="subnet-name" />
      </Subnets>
    </InstanceAddress>
  </AddressAssignments>
</NetworkConfiguration>

还要确保silo端点已配置。

<Endpoints>
  <InternalEndpoint name="OrleansSiloEndpoint" protocol="tcp" port="11111" />
  <InternalEndpoint name="OrleansProxyEndpoint" protocol="tcp" port="30000" />
</Endpoints>

最后,您须要为silos和Web应用程序客户端指定相同的部署ID。

您如今可使用GrainClient从Web App链接到silo。

1,潜在的问题

若是Web应用程序没法链接到筒仓:

  • 确保您的Azure云服务中至少有两个角色或两个角色,或者不能生成InternalEndpoint防火墙规则。
  • 检查Web应用程序和silo是否使用相同的DeploymentId。
  • 确保网络安全组已设置为容许内部虚拟网络链接。 若是你没有一个你可使用下面的PowerShell轻松地建立和分配一个:
New-AzureNetworkSecurityGroup -Name "Default" -Location "North Europe"
Get-AzureNetworkSecurityGroup -Name "Default" | Set-AzureNetworkSecurityGroupToSubnet -VirtualNetworkName "virtual-network-name" -SubnetName "subnet-name"

6、Docker部署

1,部署Orleans解决方案到Docker

考虑到Docker协调器和集群堆栈的设计方式,将Orleans部署到Docker可能会很是棘手。 最复杂的是从Docker Swarm和Kubernets Networking模型中理解覆盖网络的概念。

Docker容器和网络模型被设计为运行大多数无状态和不可变的容器。 所以,启动一个运行node.js或nginx应用程序的集群是至关容易的。 可是,若是您尝试使用更复杂的东西,好比真正的集群或分布式应用程序(例如基于Orleans的应用程序),您最终会遇到麻烦。 这是可能的,但不像基于网络的应用程序那样简单。

Docker集群包括将多个主机放在一块儿,做为使用Container Orchestrator管理的单个资源池。 Docker Inc.提供Swarm做为Container Orchestration的选项,而Google则提供Kubernetes(又名K8s)。 还有其余的Orchestrator,如DC / OS,Mesos等,可是在这个文档中咱们将会讨论Swarm和K8s,由于它们被更普遍地使用。

在Orleans的任何地方运行的相同的grain接口和实现已经被支持,也将在Docker容器上运行,因此为了可以在Docker容器中运行你的应用程序,不须要特别的考虑。

Orleans-Docker示例提供了如何运行两个控制台应用程序的工做示例。 一个做为Orleans客户,另外一个做为silo,细节描述以下。

这里讨论的概念能够用在Orleans的.Net Core和.Net 4.6.1版本中,但为了说明Docker和.Net Core的跨平台性质,咱们将着重考虑你正在使用的例子。 净核心。 本文可能会提供特定于平台(Windows / Linux / OSX)的详细信息。

2,先决条件

本文假定您已经安装了如下先决条件:

  • Docker - Docker4X有一个易于使用的主要支持平台的安装程序。 它包含Docker引擎和Docker Swarm。
  • Kubernetes (K8s) - Google提供的Container Orchestration。 它包含安装Minikube(K8s的本地部署)和kubectl及其全部依赖项的指导。
  • .Net Core - .Net的跨平台
  • Visual Studio Code (VSCode) - 你可使用任何你想要的IDE。 VSCode是跨平台的,因此咱们正在使用它来确保它在全部平台上都能正常工做。 一旦你安装VSCode,安装 C# extension.

注意:若是您不打算使用它,则不须要安装Kubernetes。 Docker4X安装程序已经包含了Swarm,所以不须要额外安装便可使用它。

Windows用户注意事项:在Windows上,Docker安装程序将在安装过程当中启用Hyper-V。 因为本文及其示例使用.Net Core,所使用的容器映像基于Windows Server NanoServer。 若是你不打算使用.Net coew,并将目标.NET 4.6.1完整的框架,使用的图像应该是Windows Server Core和Orleans的1.4 +版本(只支持.net完整框架)。

3,建立Orleans解决方案

如下说明显示了如何使用新的dotnet工具建立常规的Orleans解决方案。

注意:请根据您的平台适当调整命令。 并且,目录结构只是一个建议。 请随意调整它。

mkdir Orleans-Docker
cd Orleans-Docker
dotnet new sln
mkdir -p src/OrleansSilo
mkdir -p src/OrleansClient
mkdir -p src/OrleansGrains
mkdir -p src/OrleansGrainInterfaces
dotnet new console -o src/OrleansSilo --framework netcoreapp1.1
dotnet new console -o src/OrleansClient --framework netcoreapp1.1
dotnet new classlib -o src/OrleansGrains --framework netstandard1.5
dotnet new classlib -o src/OrleansGrainInterfaces --framework netstandard1.5
dotnet sln add src/OrleansSilo/OrleansSilo.csproj
dotnet sln add src/OrleansClient/OrleansClient.csproj
dotnet sln add src/OrleansGrains/OrleansGrains.csproj
dotnet sln add src/OrleansGrainInterfaces/OrleansGrainInterfaces.csproj
dotnet add src/OrleansClient/OrleansClient.csproj reference src/OrleansGrainInterfaces/OrleansGrainInterfaces.csproj
dotnet add src/OrleansSilo/OrleansSilo.csproj reference src/OrleansGrainInterfaces/OrleansGrainInterfaces.csproj
dotnet add src/OrleansGrains/OrleansGrains.csproj reference src/OrleansGrainInterfaces/OrleansGrainInterfaces.csproj
dotnet add src/OrleansSilo/OrleansSilo.csproj reference src/OrleansGrains/OrleansGrains.csproj

到目前为止,咱们所作的仅仅是样板代码来建立解决方案结构,项目,并在项目之间添加引用。 没有什么比普通的Orleans项目不一样。

在写这篇文章的时候,Orleans 2.0(这是惟一支持.Net Core和跨平台的版本)在技术预览版中,因此它的核心部分被托管在一个MyGet源中,而不是发布到Nuget.org的官方源。 为了安装预览版本,咱们将使用dotnet cli强制MyGet的源代码和版本:

dotnet add src/OrleansClient/OrleansClient.csproj package Microsoft.Orleans.Core -s https://dotnet.myget.org/F/orleans-prerelease/api/v3/index.json -v 2.0.0-preview2-201705020000
dotnet add src/OrleansGrainInterfaces/OrleansGrainInterfaces.csproj package Microsoft.Orleans.Core -s https://dotnet.myget.org/F/orleans-prerelease/api/v3/index.json -v 2.0.0-preview2-201705020000
dotnet add src/OrleansGrains/OrleansGrains.csproj package Microsoft.Orleans.Core -s https://dotnet.myget.org/F/orleans-prerelease/api/v3/index.json -v 2.0.0-preview2-201705020000
dotnet add src/OrleansSilo/OrleansSilo.csproj package Microsoft.Orleans.Core -s https://dotnet.myget.org/F/orleans-prerelease/api/v3/index.json -v 2.0.0-preview2-201705020000
dotnet add src/OrleansSilo/OrleansSilo.csproj package Microsoft.Orleans.OrleansRuntime -s https://dotnet.myget.org/F/orleans-prerelease/api/v3/index.json -v 2.0.0-preview2-201705020000
dotnet restore

好的,如今你拥有了运行一个简单的Orleans应用程序的全部基本的依赖关系。 请注意,迄今为止,您的常规Orleans应用程序没有任何变化。 如今,让咱们添加一些代码,以便咱们能够作一些事情。

4,实施你的Orleans应用程序

假设您正在使用VSCode,从解决方案目录中运行代码。这将打开VSCode中的目录并加载解决方案。

这是咱们之前建立的解决方案结构。

咱们还将Program.cs,OrleansHostWrapper,IGreetingGrain和GreetingGrain文件分别添加到接口和grain项目,这里是这些文件的代码:

IGreetingGrain.cs:

using System;
using System.Threading.Tasks;
using Orleans;

namespace OrleansGrainInterfaces
{
    public interface IGreetingGrain : IGrainWithGuidKey
    {
        Task<string> SayHello(string name);
    }
}
IGreetingGrain.cs

GreetingGrain.cs:

using System;
using System.Threading.Tasks;
using OrleansGrainInterfaces;

namespace OrleansGrains
{
    public class GreetingGrain : Grain, IGreetingGrain
    {
        public Task<string> SayHello(string name)
        {
            return Task.FromResult($"Hello from Orleans, {name}");
        }
    }
}
GreetingGrain.cs

OrleansHostWrapper.cs:

using System;
using System.Net;
using Orleans.Runtime;
using Orleans.Runtime.Configuration;
using Orleans.Runtime.Host;

namespace OrleansSilo
{
    public class OrleansHostWrapper
    {
        private readonly SiloHost siloHost;

        public OrleansHostWrapper(ClusterConfiguration config)
        {
            siloHost = new SiloHost(Dns.GetHostName(), config);
            siloHost.LoadOrleansConfig();
        }

        public int Run()
        {
            if (siloHost == null)
            {
                return 1;
            }

            try
            {
                siloHost.InitializeOrleansSilo();

                if (siloHost.StartOrleansSilo())
                {
                    Console.WriteLine($"Successfully started Orleans silo '{siloHost.Name}' as a {siloHost.Type} node.");
                    return 0;
                }
                else
                {
                    throw new OrleansException($"Failed to start Orleans silo '{siloHost.Name}' as a {siloHost.Type} node.");
                }
            }
            catch (Exception exc)
            {
                siloHost.ReportStartupError(exc);
                Console.Error.WriteLine(exc);
                return 1;
            }
        }

        public int Stop()
        {
            if (siloHost != null)
            {
                try
                {
                    siloHost.StopOrleansSilo();
                    siloHost.Dispose();
                    Console.WriteLine($"Orleans silo '{siloHost.Name}' shutdown.");
                }
                catch (Exception exc)
                {
                    siloHost.ReportStartupError(exc);
                    Console.Error.WriteLine(exc);
                    return 1;
                }
            }
            return 0;
        }
    }
}
OrleansHostWrapper.cs

Program.cs (Silo):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using Orleans.Runtime.Configuration;

namespace OrleansSilo
{
    public class Program
    {
        private static OrleansHostWrapper hostWrapper;

        static int Main(string[] args)
        {
            int exitCode = InitializeOrleans();

            Console.WriteLine("Press Enter to terminate...");
            Console.ReadLine();

            exitCode += ShutdownSilo();

            return exitCode;
        }

        private static int InitializeOrleans()
        {
            var config = new ClusterConfiguration();
            config.Globals.DataConnectionString = "[AZURE STORAGE CONNECTION STRING HERE]";
            config.Globals.DeploymentId = "Orleans-Docker";
            config.Globals.LivenessType = GlobalConfiguration.LivenessProviderType.AzureTable;
            config.Globals.ReminderServiceType = GlobalConfiguration.ReminderServiceProviderType.AzureTable;
            config.Defaults.PropagateActivityId = true;
            config.Defaults.ProxyGatewayEndpoint = new IPEndPoint(IPAddress.Any, 10400);
            config.Defaults.Port = 10300;
            var ips = Dns.GetHostAddressesAsync(Dns.GetHostName()).Result;
            config.Defaults.HostNameOrIPAddress = ips.FirstOrDefault()?.ToString();
            hostWrapper = new OrleansHostWrapper(config);
            return hostWrapper.Run();
        }

        private static int ShutdownSilo()
        {
            if (hostWrapper != null)
            {
                return hostWrapper.Stop();
            }
            return 0;
        }
    }
}
Program.cs (Silo)

Program.cs(client):

using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Orleans;
using Orleans.Runtime.Configuration;
using OrleansGrainInterfaces;

namespace OrleansClient
{
    class Program
    {
        private static IClusterClient client;
        private static bool running;

        static void Main(string[] args)
        {
            Task.Run(() => InitializeOrleans());

            Console.ReadLine();

            running = false;
        }

        static async Task InitializeOrleans()
        {
            var config = new ClientConfiguration();
            config.DeploymentId = "Orleans-Docker";
            config.PropagateActivityId = true;
            var hostEntry = await Dns.GetHostEntryAsync("orleans-silo");
            var ip = hostEntry.AddressList[0];
            config.Gateways.Add(new IPEndPoint(ip, 10400));

            Console.WriteLine("Initializing...");

            client = new ClientBuilder().UseConfiguration(config).Build();
            await client.Connect();
            running = true;
            Console.WriteLine("Initialized!");

            var grain = client.GetGrain<IGreetingGrain>(Guid.Empty);

            while(running)
            {
                var response = await grain.SayHello("Gutemberg");
                Console.WriteLine($"[{DateTime.UtcNow}] - {response}");
                await Task.Delay(1000);
            }
            client.Dispose();
        }
    }
}
Program.cs(client)

咱们不在这里详细介绍grain的实施状况,由于它超出了本文的范围。 请检查与其相关的其余文件。 这些文件本质上是一个最小的Orleans应用程序,咱们将从它开始继续本文的其他部分。

5,网关配置

您可能已经注意到咱们在客户端配置中没有使用成员资格提供者。 之因此这样(我经历了与Docker人讨论的日子以后才想到)是由于咱们的客户端 - >membership - >网关 - >silo的工做方式与Docker覆盖网络的设计方式不兼容。

咱们发现解决这个限制的方法是手动将网关添加到客户端配置中,以下所示:

var hostEntry = await Dns.GetHostEntryAsync("orleans-silo");
var ip = hostEntry.AddressList[0];
config.Gateways.Add(new IPEndPoint(ip, 10400));

这里的字符串orleans-silo基本上是docker-compose中使用的服务名称,稍后咱们将深刻研究更多细节(继续阅读!)。 它将解析到同一服务中的其中一个容器的ips(如Load Balancer)。 在docker-compose项目中,服务是一个给定容器的多个(彻底相同)的实例。 例如,咱们将为咱们的OrleansSilo项目提供服务。 在该服务中,咱们将可以水平地上下扩展服务,并将这些容器中的每个添加到Orleans集群中,并自动遵照Orleans成员协议。 稍后更多。

注意:在本文中,咱们使用OrleansAzureUtils成员资格提供者,可是您可使用Orleans已经支持的任何其余资源。

6,Dockerfile

为了建立你的容器,Docker使用图像。 有关如何建立本身的更多详细信息,能够查看Docker文档。 在这篇文章中,咱们将使用官方的微软图像。 基于目标和开发平台,您须要选择合适的图像。 在本文中,咱们使用microsoft / dotnet:1.1.2-sdk这是一个基于linux的镜像。 例如,您可使用microsoft / dotnet:1.1.2-sdk-nanoserver。 选一个适合你的需求。

Windows用户注意:如前所述,为了实现跨平台,本文使用.Net Core和Orleans Technical preview 2.0。 若是您想在Windows上使用Docker,而且彻底发布Orleans 1.4+,则须要使用基于NanoServer和Linux的图像的基于Windows Server Core的映像,仅支持.Net Core。

Dockerfile.debug:

FROM microsoft/dotnet:1.1.2-sdk
ENV NUGET_XMLDOC_MODE skip
WORKDIR /vsdbg
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        unzip \
    && rm -rf /var/lib/apt/lists/* \
    && curl -sSL https://aka.ms/getvsdbgsh | bash /dev/stdin -v latest -l /vsdbg 
WORKDIR /app
ENTRYPOINT ["tail", "-f", "/dev/null"]

这dockerfile本质上下载并安装VSdbg调试器,并启动一个空的容器,并保持它永远活着,因此咱们不须要拆除/调试时。

如今,为了生产,图像比较小,由于它只包含.Net Core运行时,而不是整个SDK,并且dockerfile有点简单:

Dockerfile:

FROM microsoft/dotnet:1.1.2-runtime
WORKDIR /app
ENTRYPOINT ["dotnet", "OrleansSilo.dll"]
COPY . /app

7,docker-compose

docker-compose.yml文件实质上是在服务级别上打包了一组服务及其依赖关系。 每一个服务都包含给定容器的一个或多个实例,该实例基于您在Dockerfile上选择的映像

对于Orleans部署,一个常见的用例是有一个包含两个服务的docker-compose.yml。 一个为Orleans silo,另外一个为Orleans客户端。 客户将依赖于silo,这意味着,只有在silo服务启动后才会开始。 另外一种状况是添加一个存储/数据库服务/容器,好比SQL Server,它应该在客户端和服务器以前先启动,因此两个服务都应该依赖它。

注意:在进一步阅读以前(最终会发疯),请注意在docker-compose文件中缩进很重要。 因此若是你有任何问题,请注意。

如下是咱们将如何描述这篇文章的服务:

docker-compose.override.yml (Debug):

version: '3.1'

services:
  orleans-client:
    image: orleans-client:debug
    build:
      context: ./src/OrleansClient/bin/PublishOutput/
      dockerfile: Dockerfile.Debug
    volumes: 
      - ./src/OrleansClient/bin/PublishOutput/:/app
      - ~/.nuget/packages:/root/.nuget/packages:ro
    depends_on: 
      - orleans-silo
  orleans-silo:
    image: orleans-silo:debug
    build:
      context: ./src/OrleansSilo/bin/PublishOutput/
      dockerfile: Dockerfile.Debug
    volumes: 
      - ./src/OrleansSilo/bin/PublishOutput/:/app
      - ~/.nuget/packages:/root/.nuget/packages:ro

docker-compose.yml (production):

version: '3.1'

services:
  orleans-client:
    image: orleans-client
    depends_on: 
      - orleans-silo
  orleans-silo:
    image: orleans-silo

请注意,在生产中,咱们不映射本地目录,也没有build:操做。 缘由是在生产中,图像应该已经被构建并推送到你本身的Docker Registry。

8,把一切放在一块儿

如今咱们有运行你的Orleans应用程序所需的全部移动部件,咱们将把它放在一块儿,以便咱们能够在Docker中运行Orleans解决方案(最后!)。

注:应从解决方案目录执行如下命令。

首先,请确保咱们从咱们的解决方案中恢复全部NuGet软件包。 你只须要作一次。 若是您更改了项目的任何软件包依赖项,只须要再次执行该操做。

# dotnet restore

如今,让咱们像往常同样使用dotnet CLI构建解决方案,并将其发布到输出目录:

# dotnet publish -o ./bin/PublishOutput

注意:咱们在这里使用发布而不是构建,以免咱们在Orleans中动态加载的程序集的问题。 咱们仍然在寻找更好的解决方案。

随着应用程序的构建和发布,您须要构建Dockerfile图像。 这个步骤只须要在每一个项目中执行一次,而且只有在您更改Dockerfil,docker-compose或出于任何缘由清理本地映像注册表时才须要执行此步骤。

# docker-compose build

在Dockerfile和docker-compose.yml中使用的全部图像都从注册表中获取并缓存在开发机器上。 你的图像已经建好了,并且你都准备好运行了。

如今让咱们运行它!

# docker-compose up -d
Creating network "orleansdocker_default" with the default driver
Creating orleansdocker_orleans-silo_1 ... 
Creating orleansdocker_orleans-silo_1 ... done
Creating orleansdocker_orleans-client_1 ... 
Creating orleansdocker_orleans-client_1 ... done
#

如今,若是您运行docker-compose ps,则会看到2个容器正在为orleansdocker项目运行:

# docker-compose ps
             Name                     Command        State   Ports 
------------------------------------------------------------------
orleansdocker_orleans-client_1   tail -f /dev/null   Up            
orleansdocker_orleans-silo_1     tail -f /dev/null   Up 

Windows用户注意事项:若是您在Windows上,而且您的容器使用Windows映像做为基础,那么命令列将向您显示Powershell相对命令到* NIX系统上的尾部,以便容器保持相同的方式。

如今你已经有了你的容器,每次你想要启动你的Orleans应用程序时都不须要停下来。 全部你须要的是集成你的IDE调试应用程序在之前映射在您的docker-compose.yml容器。

9,缩减

一旦你的撰写项目正在运行,你可使用docker-compose scale命令轻松地扩展或缩减应用程序:

# docker-compose scale orleans-silo=15
Starting orleansdocker_orleans-silo_1 ... done
Creating orleansdocker_orleans-silo_2 ... 
Creating orleansdocker_orleans-silo_3 ... 
Creating orleansdocker_orleans-silo_4 ... 
Creating orleansdocker_orleans-silo_5 ... 
Creating orleansdocker_orleans-silo_6 ... 
Creating orleansdocker_orleans-silo_7 ... 
Creating orleansdocker_orleans-silo_8 ... 
Creating orleansdocker_orleans-silo_9 ... 
Creating orleansdocker_orleans-silo_10 ... 
Creating orleansdocker_orleans-silo_11 ... 
Creating orleansdocker_orleans-silo_12 ... 
Creating orleansdocker_orleans-silo_13 ... 
Creating orleansdocker_orleans-silo_14 ... 
Creating orleansdocker_orleans-silo_15 ... 
Creating orleansdocker_orleans-silo_6
Creating orleansdocker_orleans-silo_5
Creating orleansdocker_orleans-silo_3
Creating orleansdocker_orleans-silo_2
Creating orleansdocker_orleans-silo_4
Creating orleansdocker_orleans-silo_9
Creating orleansdocker_orleans-silo_7
Creating orleansdocker_orleans-silo_8
Creating orleansdocker_orleans-silo_10
Creating orleansdocker_orleans-silo_11
Creating orleansdocker_orleans-silo_15
Creating orleansdocker_orleans-silo_12
Creating orleansdocker_orleans-silo_14
Creating orleansdocker_orleans-silo_13

几秒钟后,您将看到服务缩放到您请求的特定数量的实例。

# docker-compose ps
             Name                     Command        State   Ports 
------------------------------------------------------------------
orleansdocker_orleans-client_1   tail -f /dev/null   Up            
orleansdocker_orleans-silo_1     tail -f /dev/null   Up            
orleansdocker_orleans-silo_10    tail -f /dev/null   Up            
orleansdocker_orleans-silo_11    tail -f /dev/null   Up            
orleansdocker_orleans-silo_12    tail -f /dev/null   Up            
orleansdocker_orleans-silo_13    tail -f /dev/null   Up            
orleansdocker_orleans-silo_14    tail -f /dev/null   Up            
orleansdocker_orleans-silo_15    tail -f /dev/null   Up            
orleansdocker_orleans-silo_2     tail -f /dev/null   Up            
orleansdocker_orleans-silo_3     tail -f /dev/null   Up            
orleansdocker_orleans-silo_4     tail -f /dev/null   Up            
orleansdocker_orleans-silo_5     tail -f /dev/null   Up            
orleansdocker_orleans-silo_6     tail -f /dev/null   Up            
orleansdocker_orleans-silo_7     tail -f /dev/null   Up            
orleansdocker_orleans-silo_8     tail -f /dev/null   Up            
orleansdocker_orleans-silo_9     tail -f /dev/null   Up 

注意:这些例子中的命令列仅显示了tail命令,由于咱们正在使用调试器容器。 若是咱们在生产,它会显示dotnet OrleansSilo.dll为例。

10,Docker集 群

Docker集群堆栈被称为Swarm,你能够在这里阅读它的文档来找到更多documentation here.

要在Swarm集群中运行这篇文章,您没有任何额外的工做。 在Swarm节点中运行docker-compose -d时,它将根据配置的规则调度容器。 其余基于Swarm的服务(例如Docker Datacenter,Azure ACS(Swarm模式),AWS ECS Container Service等)也是如此。 您只需在部署dockerized Orleans应用程序以前部署您的Swarm集群。

注意:若是您使用的Swarm模式已经支持堆栈,部署和组合v3的Docker引擎,则部署解决方案的更好方法是使用docker stack deploy -c docker-compose.yml <name>。 请记住,它须要在您的Docker引擎上支持v3撰写文件,大多数托管服务(如Azure和AWS)仍然使用v2和更旧的引擎。

11,Google Kubernetes(K8s)

12,[奖金主题]调试容器内的Orleans

那么,如今你知道如何在一个容器中从头开始运行Orleans,将会很好地利用Docker中最重要的原则之一。 容器是不可变的。 他们在开发中应该具备(几乎)相同的图像,依赖性和运行时。 这确保了良好的陈述“在个人机器上工做!” 永远不会再发生。 为了作到这一点,你须要有一种方法来在容器内部进行开发,而且包含一个调试器链接到容器内的应用程序。

有多种方法可使用多个工具来实现这一点。 通过几回评估以后,我写这篇文章的时候,我选择了一个看起来更简单,而且在应用程序中不那么干扰的程序。

正如本文前面提到的,咱们使用VSCode来开发示例,因此下面是如何让调试器附加到容器内的Orleans应用程序。

首先,在您的解决方案中更改.vscode目录中的两个文件:

tasks.json:

{
    "version": "0.1.0",
    "command": "dotnet",
    "isShellCommand": true,
    "args": [],
    "tasks": [
        {
            "taskName": "publish",
            "args": [
                "${workspaceRoot}/Orleans-Docker.sln", "-c", "Debug", "-o", "./bin/PublishOutput"
            ],
            "isBuildCommand": true,
            "problemMatcher": "$msCompile"
        }
    ]
}

这个文件基本上告诉VSCode,不管什么时候你构建项目,它都会像咱们之前手动执行的那样,执行publish命令。

launch.json:

{
   "version": "0.2.0",
   "configurations": [
        {
            "name": "Silo",
            "type": "coreclr",
            "request": "launch",
            "cwd": "/app",
            "program": "/app/OrleansSilo.dll",
            "sourceFileMap": {
                "/app": "${workspaceRoot}/src/OrleansSilo"
            },
            "pipeTransport": {
                "debuggerPath": "/vsdbg/vsdbg",
                "pipeProgram": "/bin/bash",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": [
                    "-c",
                    "docker exec -i orleansdocker_orleans-silo_1 /vsdbg/vsdbg --interpreter=vscode"
                ]
            }
        },
        {
            "name": "Client",
            "type": "coreclr",
            "request": "launch",
            "cwd": "/app",
            "program": "/app/OrleansClient.dll",
            "sourceFileMap": {
                "/app": "${workspaceRoot}/src/OrleansClient"
            },
            "pipeTransport": {
                "debuggerPath": "/vsdbg/vsdbg",
                "pipeProgram": "/bin/bash",
                "pipeCwd": "${workspaceRoot}",
                "pipeArgs": [
                    "-c",
                    "docker exec -i orleansdocker_orleans-client_1 /vsdbg/vsdbg --interpreter=vscode"
                ]
            }
        }
    ]
}

如今,您能够从VSCode(将发布)构建解决方案,并启动silo和客户端。 它会发送一个docker exec命令给正在运行的docker-compose服务实例/容器,以启动调试器到应用程序,就是这样。 你有调试器附加到容器,并使用它,就好像它是一个本地运行的Orleans应用程序。 如今的区别在于它在容器内,一旦你完成了,你能够发布容器到你的注册表,并把它拖到你的Docker主机上。

7、服务结构托管

Orleans能够在服务结构上托管。 目前有两个与Service Fabric的集成点:

  • Hosting: silos能够托管在服务结构可靠服务内的服务结构中。 因为Orleans公司使用细粒度,动态的分布来管理grain的分配,所以silo应该做为未分区的无国籍服务托管。 其余主机选项(分区,有状态)目前未经测试和不受支持。
  • Clustering (beta): silos和客户端能够利用Service Fabric的服务发现机制来造成群集。 此选项须要服务结构托管,但服务结构托管不须要服务结构集群。

展现托管和集群的样本存在于 Samples/ServiceFabric.

1,主机

主机支持可在Microsoft.Orleans.Hosting.ServiceFabric包中找到。 它容许Orleans silo做为服务结构ICommunicationListener运行。 Silo生命周期遵循典型的通讯监听器生命周期:它经过ICommunicationListener.OpenAsync方法初始化,并经过ICommunicationListener.CloseAsync方法正常终止,或经过ICommunicationListener.Abort方法忽然终止。

OrleansCommunicationListener提供了ICommunicationListener实现。 推荐的方法是使用Orleans.Hosting.ServiceFabric命名空间中的OrleansServiceListener.CreateStateless(Action <StatelessServiceContext,ISiloHostBuilder> configure)建立通讯侦听器。 这可确保侦听器具备Clustering所需的端点名称(以下所述)。

每次打开通讯侦听器时,调用传递给CreateStateless的配置委托来配置新的silo。

主机能够与服务结构集群提供者结合使用,可是也可使用其余集群提供者。

2,示例:配置服务结构托管。

如下示例演示托管Orleans孤岛的Service Fabric StatelessService类。 完整的示例能够在Orleans存储库的Samples / ServiceFabric目录中找到。

internal sealed class StatelessCalculatorService : StatelessService
{
    public StatelessCalculatorService(StatelessServiceContext context)
        : base(context)
    {
    }

    protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
    {
        // 监听器能够在服务实例的生命周期中屡次打开和关闭。 每当侦听器打开时,新的Orleans silo都将被建立和初始化,而且当侦听器关闭时将被关闭。
        var listener = OrleansServiceListener.CreateStateless(
            (serviceContext, builder) =>
            {
                //可选:使用服务结构做为集群成员资格。
                builder.UseServiceFabricClustering(serviceContext);

                //备选方法:使用Azure存储做为群集成员资格。
                builder.UseAzureTableMembership(options =>
                {
                    /* 配置链接字符串*/
                });

                // 可选:配置日志记录。
                builder.ConfigureLogging(logging => logging.AddDebug());

                var config = new ClusterConfiguration();
                config.Globals.RegisterBootstrapProvider<BootstrapProvider>("poke_grains");
                config.Globals.ReminderServiceType =
                    GlobalConfiguration.ReminderServiceProviderType.ReminderTableGrain;

                // Service Fabric管理端口分配,所以使用这些端口更新配置。
                config.Defaults.ConfigureServiceFabricSiloEndpoints(serviceContext);

                // 告诉Orleans 使用这个配置。
                builder.UseConfiguration(config);

                // 添加你的应用程序集。
                builder.ConfigureApplicationParts(parts =>
                {
                    parts.AddApplicationPart(typeof(CalculatorGrain).Assembly).WithReferences();

                    // 可选:在当前基本路径中添加全部可装载的程序集(请参阅AppDomain.BaseDirectory)。
                    parts.AddFromApplicationBaseDirectory();
                });
            });

        return new[] { listener };
    }

    protected override async Task RunAsync(CancellationToken cancellationToken)
    {
        while (true)
        {
            cancellationToken.ThrowIfCancellationRequested();
            await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
        }
    }
}

3,集群(测试版)

注意:目前推荐在生产中使用存储支持的集群提供程序,例如SQL,ZooKeeper,Consul或Azure表。

在Microsoft.Orleans.Clustering.ServiceFabric包中支持使用Service Fabric的服务发现(命名服务)机制来得到群集成员资格。 该实现要求还使用Service Fabric Hosting支持,而且从StatelessService.CreateServiceInstanceListeners()返回的值中将Silo端点命名为“Orleans”。 确保这种最简单的方法是使用OrleansServiceListener.CreateStateless(...)方法,如前一节所述。

经过silo上的ISiloHostBuilder.UseServiceFabricClustering(ServiceContext)扩展方法和客户端上的IClientBuilder.UseServiceFabricClustering(Uri)扩展方法来启用Service Fabric Clustering。

目前的建议是使用存储支持的集群提供程序来执行生产服务,如SQL,ZooKeeper,Consul或Azure Storage。 这些提供程序(特别是SQL和Azure存储)已经在生产使用中进行了充分的测试。

相关文章
相关标签/搜索