|
|
51CTO旗下网站
|
|
移动端

2.3.5.3 服务分区和故障恢复

《Microsoft Azure 管理与开发(下册)平台服务PaaS》本书由世纪互联蓝云Microsoft Azure 开发技术支持团队的资深工程师们编写,主要阐述MicrosoftAzure PaaS 服务的开发应用,涉及计算服务、集成认证服务、数据存储服务、大数据服务等方面的内容。本节为大家介绍服务分区和故障恢复。

作者:世纪互联蓝云公司来源:电子工业出版社|2018-07-12 17:47

2.3.5.3 服务分区和故障恢复

在深入了解Reliable Service 之前,需要了解Reliable Service 分区的概念,这样能够了解Service Fabric 是如何将应用程序服务分布到各个节点中,并实现高可伸缩性的。分区的概念不是Service Fabric 所独有的,基本上所有支持可缩放的服务,都会应用到该概念。分区也可以叫作分片,是一种可以将数据和计算划分为更小的访问单元的一种概念,例如Redis 群集,通过分片将数据分布到不同的实例上。

1. Service Fabric Stateless Service 分区

对于无状态服务,可以将分区视为包含服务的一个或者多个实例,其中五个实例使用一个分区在群集中分布,如图2.3.5-4 所示。在实际使用中,有两种类型的无状态服务解决方案,一种是使用外部存储(Azure SQL Database 或者MySQL)保持其状态;另外一种不包含任何持久化状态,仅包含计算服务(如计算器和图像缩略图)。

无论哪种情况,对无状态服务进行分区都是极罕见的情况,通常都是通过增加实例来实现可伸缩性和高可用性。对于无状态服务,要考虑多个分区的唯一情况就是在需要满足特殊的路由请求时,例如:ID 处于特定范围的用户应该有特殊的服务实例提供服务。这时可以考虑对无状态服务进行分区。但是这种情况下通常都有其他解决方案来进行解决,并一定必须要对服务进行分区。

2. Service Fabric Stateful Service 分区

Service Fabric 提供非常优秀的数据分区服务,从而方便了开发可缩放有状态服务。从概念上讲,可以将有状态服务分区视为应用服务副本在群集节点间进行分布和平衡的高可用的缩放单位,每一个分区都是一个缩放单位。每个分区中都包含一个主副本和多个辅助副本,并且Service Fabric 会将一个分区的多个副本分布到不同的实例上,以保证主副本发生故障时,辅助副本可以升级为主副本持续提供服务。

Service Fabric 有状态服务分区的一大优点就是它将分区置于不同的节点上。这使得它们可以按照节点资源限制来增长。随着数据需求的增长,分区也会增长,Service Fabric 会自动在节点间重新平衡分区,这样可以保证硬件资源的高效利用。

为了更好地说明这一点,可以参考下面这个例子:假设有5 个节点群集,以及一个配置了10 个分区和3 个副本的服务。在这种情况下,Service Fabric 会在节点间分布和平衡副本,每个节点会有两个主副本,如图2.3.5-5 所示,以不同的颜色来表示不同的分区,可以看到当副本被分配到不同的节点时的分布情况。

假如将节点数量增加至10 个节点,则Service Fabric 会在10 个节点间重新平衡副本,这时每个节点都只有一个主副本,如图2.3.5-6 所示。同样,如果再将节点减少至5 个,ServiceFabric 就会在5 个节点再次均衡平衡副本。

当节点数不能够平均分配副本数时,Service Fabric 会在已分配主副本的节点上依次追加多余的主节点,例如:群集中有三个节点,服务设置了4 个分区和3 个副本,这样在分配时,多余的分区会被分布在其中一个节点中,如图2.3.5-7 所示。

3. 如何规划分区

实现需求之前,一定要优先考虑好可缩放的分区策略,并且要依据实现的业务设计分区。在分区之前,要明确分区的目的,按照一定规则将数据平衡分配到各个分区中,这样能够均衡各个分区的压力,并提高应用的整体性能。

所以在分区之前,要对应用服务的数据有清晰的认识,例如,在以下这个简单的实例中,如果构建一个面向全国的服务系统,就可以为中国的每个省份建立一个分区以存储各省份的数据,随后可以将各省份的数据对应地存储在相应的分区中。见表2.3.5-1 的数据。

表2.3.5-1

(1) 按照省份进行分区后的数据结构如图2.3.5-8 所示。

以上例子只是作为参考,这种分区方式并不一定是最好的分区方法,因为各个省份的用户数可能相差很大,有的省份用户非常多,有的省份用户非常少,这样会导致群集某些节点处理的流量和数据相对其他要多很多,就会造成分区不均衡的状况。所以在设计分区时,一定要尽量将数据均匀地分布到各个分区,如果事先不清楚分区的数据量,那么就从各个服务副本收集服务负载报告,这样Service Fabric 就会报告服务消耗的负载,例如内存量和记录数,这样随着时间的迁移,就可以定位到各个分区的资源消耗和各个节点的消耗差异,以随时应对可能出现的不均衡状况。

分区规划的另一个方面是,在设计时就要考虑使用分区方案的最大分区数。因为到目前为止,Service Fabric 是不支持在事后修改分区数的,而大部分应用也不需要在后期扩展分区数,所以建议在一开始时,就要规划最多的分区数。

4. 构建多个分区的有状态服务

接下来,将创建一个分区的有状态服务示例,将相同首字母姓氏的名字存储到相同的分区中。

在编写代码前,需要考虑分区。以名称姓氏的首字母进行分区,所以需要26 个分区,分别以0~25 数字来代表26 个字母。这只是个简单示例,这样分区是有些不均衡的,因为有的首字母对应的姓氏非常少。以这个例子仅仅是为了展示如何在编写服务时设置分区,在实际应用中,分区的设计会考虑更多方面。

1) 在Visual Studio 中创建有状态服务项目

项目名称:AlphabetPartitions,服务名称:Alphabet.Processing,如图2.3.5-9 所示。

(2) 按照之前的设计,来设置分区数。

打开AlphabetPartitions 项ApplicationPackageRoot 文件夹中的 Applicationmanifest.xml文件,然后将参数 Processing_PartitionCount 更新为26,代码如下:

  1. <Parameter Name="Processing_PartitionCount" DefaultValue="26" /> 

还需要更新 ApplicationManifest.xml 中 StatefulService 元素的 LowKey 和HighKey属性,代码如下:

  1. <Service Name="Processing"> 
  2. <StatefulService ServiceTypeName="ProcessingType"  
  3. TargetReplicaSetSize="[Processing_TargetReplicaSetSize]" MinReplicaSetSize= "  
  4. [Processing_MinReplicaSetSize]"> 
  5. <UniformInt64Partition PartitionCount="[Processing_PartitionCount]"  
  6. LowKey="0" HighKey="25" /> 
  7. </StatefulService> 
  8. </Service> 

(3) 接下来要是服务可以被访问,需要在Alphabet.Processing 服务的ServiceManifest.xml添加Endpoint 元素,在某个端口上打开终结点,代码如下:

  1. <Endpoint Name="ProcessingServiceEndpoint" Port="8089" Protocol="http"  
  2. Type="Internal" /> 

现在,服务已为侦听配置具有26 个分区的内部终结点。

(4) 接下来,需要重写 Processing 类的 CreateServiceReplicaListeners()方法。实现一个简单的HttpCommunicationListener。副本推荐的侦听地址模式建议如下:

  1. {scheme}://{nodeIp}:{port}/{partitionid}/{replicaid}/{guid} 

因为有状态服务可以在一台实例上运行该服务的多个副本,因此侦听地址必须是副本独有的,所以在地址上加上分区ID 和副本ID,以确保地址唯一。额外的GUID 是为了支持使用辅助副本提供只读请求侦听器的高级情况。完整代码如下:

  1. protected override IEnumerable<ServiceReplicaListener> CreateService  
  2. ReplicaListeners()  
  3. {  
  4. return new[] { new ServiceReplicaListener(context => this.  
  5. CreateInternalListener(context))};  
  6. }  
  7. private ICommunicationListener CreateInternalListener(ServiceContext  
  8. context)  
  9. {  
  10. EndpointResourceDescription internalEndpoint = context.  
  11. CodePackageActivationContext.GetEndpoint("ProcessingServiceEndpoint");  
  12. string uriPrefix = String.Format(  
  13. "{0}://+:{1}/{2}/{3}-{4}/",  
  14. internalEndpoint.Protocol,  
  15. internalEndpoint.Port,  
  16. context.PartitionId,  
  17. context.ReplicaOrInstanceId,  
  18. Guid.NewGuid());  
  19. string nodeIP = FabricRuntime.GetNodeContext().IPAddressOrFQDN;  
  20. string uriPublished = uriPrefix.Replace("+", nodeIP);  
  21. return new HttpCommunicationListener(uriPrefix, uriPublished,  
  22. this.ProcessInternalRequest);  

在发布服务时,使用节点IP 作为访问地址,因为将该服务作为后端服务调用,所以只需要在后台将地址注册到Naming Service,以便让其他服务可以发现此服务,并获取其调用地址。

(5) 最后,将处理逻辑添加到服务,实现将名称存储到Reliable Collections 中。完整代码如下:

  1. private async Task ProcessInternalRequest(HttpListenerContext context,  
  2. CancellationToken cancelRequest)  
  3. {  
  4. string output = null;  
  5. string user = context.Request.QueryString["lastname"].ToString();  
  6. try  
  7. {  
  8. output = await this.AddUserAsync(user);  
  9. }  
  10. catch (Exception ex)  
  11. {  
  12. output = ex.Message;  
  13. }  
  14. using (HttpListenerResponse response = context.Response)  
  15. {  
  16. if (output != null)  
  17. {  
  18. byte[] outBytes = Encoding.UTF8.GetBytes(output);  
  19. response.OutputStream.Write(outBytes, 0, outBytes.Length);  
  20. }  
  21. }  
  22. }  
  23. private async Task<string> AddUserAsync(string user)  
  24. {  
  25. IReliableDictionary<String, String> dictionary = await this.  
  26. StateManager.GetOrAddAsync<IReliableDictionary<String, String>>("dictionary");  
  27. using (ITransaction tx = this.StateManager.CreateTransaction())  
  28. {  
  29. bool addResult = await dictionary.TryAddAsync(tx,  
  30. user.ToUpperInvariant(), user);  
  31. await tx.CommitAsync();  
  32. return String.Format(  
  33. "User {0} {1}",  
  34. user,  
  35. addResult ? "sucessfully added" : "already exists");  
  36. }  

该逻辑会将调用传入的参数,保存到IReliableDictionary。这样,就完成了有状态服务的分区服务。后面需要加入一个Stateless Service,来使用Naming Service 获取有状态服务的地址。


喜欢的朋友可以添加我们的微信账号:

51CTO读书频道二维码


51CTO读书频道活动讨论群:365934973

【责任编辑:book TEL:(010)68476606】

回书目   上一节   下一节
点赞 0
分享:
大家都在看
猜你喜欢

读 书 +更多

JavaScript核心技术

它从最简单的地方入手,不仅讲述了JavaScript的基础知识,还讲述了JavsScript如何操作CSS、DOM等Ajax基础技术。而关于跨浏览器兼容问题的解...

订阅51CTO邮刊

点击这里查看样刊

订阅51CTO邮刊