Orleans 容灾与集群

集群是什么?

下面摘抄自百度百科:

    集群(cluster)技术是一种较新的技术,通过集群技术,可以在付出较低成本的情况下获得在性能、可靠性、灵活性方面的相对较高的收益,其任务调度则是集群系统中的核心技术。

    集群是一组相互独立的、通过高速网络互联的计算机,它们构成了一个组,并以单一系统的模式加以管理。一个客户与集群相互作用时,集群像是一个独立的服务器。集群配置是用于提高可用性和可缩放性。

简单的概述一下就是:通过技术手段将多台计算机(服务器)组合成“一台计算器”对外提供计算服务。

容灾是什么?

下面摘抄自百度百科:

    容灾系统是指在相隔较远的异地,建立两套或多套功能相同的IT系统,互相之间可以进行健康状态监视和功能切换,当一处系统因意外(如火灾、地震等)停止工 作时,整个应用系统可以切换到另一处,使得该系统功能可以继续正常工作。容灾技术是系统的高可用性技术的一个组成部分,容灾系统更加强调处理外界环境对系 统的影响,特别是灾难性事件对整个IT节点的影响,提供节点级别的系统恢复功能。

简单的概述一下就是:当一些服务器不可用时可以切换到可用的备用服务器上,提高服务的可用性。

容灾和集群的关系?

    个人认为容灾是一种需求,目的,目标,而集群是一种技术手段,使用集群可以提供可用性和可缩放性,那么也解决了容灾的问题,当然方式不仅限于集群镜像站点也能解决容灾问题。就好比IOC和DI的关系一样。

搭建Orleans集群

一个简单的集群

针对上一篇的经验,这一次将“Sample.Interfaces”和“Sample.Implements”层的Grain进行了一些变更,代码如下:

using Orleans;
using System.Threading.Tasks;

namespace Sample.Interfaces
{
    public interface IUserGrain : IGrainWithStringKey
    {
        Task<bool> Exist();
    }
}
using Orleans;
using Sample.Interfaces;
using System;
using System.Threading.Tasks;

namespace Sample.Implements
{
    public class UserGrain : Grain, IUserGrain
    {
        #region Implementation of IUserService

        public Task<bool> Exist()
        {
            Console.WriteLine("我处理了一个请求。");
            return Task.FromResult(this.GetPrimaryKeyString() == "13888888888");
        }

        #endregion Implementation of IUserService
    }
}

代码逻辑没有变,但更贴近Grain的意图,下面我们看看客户端的调用。

using Orleans;
using Sample.Interfaces;
using System;

namespace Client
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            GrainClient.Initialize();

            while (true)
            {
                Console.WriteLine("请输入用户的手机号码来判断是否存在:");
                var mobileNumber = Console.ReadLine();
                var userService = GrainClient.GrainFactory.GetGrain<IUserGrain>(mobileNumber);
                Console.WriteLine($"用户{mobileNumber},{(userService.Exist().Result ? "已经存在" : "还不存在")}");
                if (mobileNumber == "exit")
                    GrainClient.Uninitialize();
            }
        }
    }
}

可以看到这一次将手机号码当做key传给了Grain,这样更符合Actor思想,感觉有点像RESTful。

下面来看看需要集群的服务端:

其实在上次的demo基础上不需要改任何代码就可以实现集群了,但为了不拷贝多份程序,我对Server端加载配置文件和设置节点名称做了动态的输入,代码如下:

using Orleans.Runtime.Host;
using System;
using System.IO;

namespace Server
{
    internal class Program
    {
        private static void Main()
        {
            while (true)
            {
                Console.WriteLine("请输入配置文件名称:");
                var configName = Console.ReadLine();
                var configFile = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
                    $"{Path.GetFileNameWithoutExtension(configName)}.xml"));
                if (!configFile.Exists)
                {
                    Console.WriteLine($"配置文件:{configFile.FullName},不存在!");
                    continue;
                }
                Console.WriteLine("请输入节点名称:");
                var nodeName = Console.ReadLine() ?? string.Empty;
                using (var host = new SiloHost(nodeName, configFile))
                {
                    host.LoadOrleansConfig();
                    host.InitializeOrleansSilo();
                    host.StartOrleansSilo();

                    Console.WriteLine("已启动,按下任意键退出。");
                    Console.ReadLine();

                    host.StopOrleansSilo();
                }
                break;
            }
        }
    }
}

没有太多的变化,只是可以指定加载哪一个配置文件和启动哪个节点,下面我们重点看看Orleans的配置文件。

<?xml version="1.0" encoding="utf-8"?>
<OrleansConfiguration xmlns="urn:orleans">
  <Globals>
    <StorageProviders>
      <Provider Type="Orleans.Storage.MemoryStorage" Name="PubSubStore" NumStorageGrains="1" />
    </StorageProviders>
    <StreamProviders>
      <Provider Type="Orleans.Providers.Streams.SimpleMessageStream.SimpleMessageStreamProvider" Name="SMSProvider" FireAndForgetDelivery="false" />
    </StreamProviders>
    <SeedNode Address="localhost" Port="22222" />
    <Messaging ResponseTimeout="30s" />
  </Globals>
  <Defaults>
    <Tracing DefaultTraceLevel="Info" TraceToConsole="true" TraceToFile="{0}-{1}.log" PropagateActivityId="false" BulkMessageLimit="1000">
      <TraceLevelOverride LogPrefix="Application" TraceLevel="Info" />
    </Tracing>
    <Statistics MetricsTableWriteInterval="30s" PerfCounterWriteInterval="30s" LogWriteInterval="300s" WriteLogStatisticsToTable="true" StatisticsCollectionLevel="Info" />
  </Defaults>
  <Override Node="node1">
    <Networking Address="localhost" Port="22222" />
    <ProxyingGateway Address="localhost" Port="40000" />
  </Override>
</OrleansConfiguration>

最终的配置文件效果如下,我们先运行看一下效果再回头来看配置文件。

首先启动Server.exe,输入“OrleansConfiguration”
2.png

在输入节点名称:“node1”,等待启动完成。

2.png

接着我们运行一个客户端程序。

3.png

进行测试。


4.png


Server.exe会出现我处理了一个请求,代表逻辑正常。接着我们在运行一个Server.exe。
配置文件名称为:“OrleansConfiguration”,节点名称为:“node2”。
等待启动完成。

5.png

继续切换到client进行测试,将会出现如下结果:

6.png

代表请求被分发了,分发的规则由orleans控制,如果你愿意可以再启动多个Server.exe进行集群。可以是node3、node4、nodeN。
接着我们关闭node2(按回车关闭,不要直接关闭窗口)继续切换到Client进行测试。

7.png

发现还是可以正常请求。

这样一个简单的集群就完成了。
这种集群是利用了orleans提供的“MembershipTable”方式,这在正式环境是不推荐使用的只推荐在开发时使用,为了简单演示功能本次将以这个方式为主进行简单的说明。
Demo源码:https://github.com/majian159/Samples/tree/master/OrleansSamples/Clusters
配置文件

下面我们再回头看看配置文件。

8.png


重点在我圈出的3行代码。

SeedNode

silo(筒仓)地址这个地址是配置了我属于某一个silo,某个silo可以使用我。

集群者需要连接到一个指定的silo中标识为我可以为这个silo提供计算请求。

Networking

silo终结点,供各个宿主(Server)连接到此,一个集群有且只有一个。

也就是集群者需要连接到一个地方的地址。

ProxyingGateway

代理网关,客户端调用的通道。

客户端发送请求的接收通道。

Override

重写,可以根据节点名称进行特定的配置,比如我们的node1才会对Networking和ProxyingGateway配置生效,node2,node3其它节点则不会不会。

关系

可以看出我们第一个运行的node1节点承担了部分计算请求、silo管理的作用,node2则承担了部分计算请求。实际的角色有3个。

1.计算者1

2.计算者2

3.silo管理者

而node1承载着1、3这两个角色,这时候肯定会会有人提出疑问:“这样压力不还没有分担掉吗?如果我的node1挂了就全完了?”

答案是肯定的,这也是为什么orleans不推荐在正式环境使用这种集群方式,后续章节中我们会介绍其它的集群方案,让silo管理者的角色分配给应该负责的人。

请求结构

9.png


本文转载至:http://www.cnblogs.com/ants/p/5126723.html