一般应用服务都会部署到多台服务器之上,一、可以通过硬件得到更多的并发处理能力;二、可以避免单点太故障的出现,从而确保服务7X24有效运作。当访问这些HTTP服务的情况一般都是经过反向代理服务进行统一处理,这样的好处就访问透明化,统一管理和控制。但存在的问题就是服务处理延时加大,还有就是对小团或公司来说可能没有专门的技术人来规划和管理这些代理服务。接下一来讲一下在.net core下更轻更的一种处理方案,这种方案通过Client自身的功能实现集群化的HTTP服务访问,通过故障迁移和权重分配达到一个无中心化灵活的HTTP集群服务访问(通过它还能访问所有非.net core的HTTP服务)

场景定义

首先有以下一个asp.net core mvc服务

  1. public class HomeController : Controller
  2. {
  3. [HttpPost]
  4. public int EmployeeAdd([FromBody]List<Employee> items)
  5. {
  6. return items == null ? : items.Count;
  7. }
  8.  
  9. [HttpPost]
  10. public Employee EmployeeEdit(int id, [FromBody]Employee emp)
  11. {
  12. Employee record = DataHelper.Employees.Find(e => e.EmployeeID == id);
  13. if (record != null)
  14. {
  15. record.City = emp.City;
  16. record.Address = emp.Address;
  17. record.Title = emp.Title;
  18. record.HomePhone = emp.HomePhone;
  19. return record;
  20. }
  21. return null;
  22. }
  23.  
  24. public object EmployeesGetName()
  25. {
  26. return from e in DataHelper.Employees select new { ID = e.EmployeeID, Name = e.FirstName + " " + e.LastName };
  27. }
  28.  
  29. public object Customers(int count)
  30. {
  31. List<Customer> result = new List<Customer>();
  32. if (count > DataHelper.Customers.Count)
  33. count = DataHelper.Customers.Count;
  34. for (int i = ; i < count; i++)
  35. {
  36. result.Add(DataHelper.Customers[i]);
  37. }
  38. return result;
  39. }
  40.  
  41. public object CustomersGetName()
  42. {
  43. return from c in DataHelper.Customers select new { ID = c.CustomerID, Name = c.CompanyName };
  44. }
  45.  
  46. }

代码简化了一下,正常API的服务都部署在多台服务器构建应用集群,一般情况都是通过nginx或其他反向代理服务器接管Client的请求,然后针对负载配置进行转发处理。但接下来需要讲解的是通过开源组件实现无中心化的集群负载调用。

引用组件

首先net core的http client组件并不具这一功能,所以需要引用第三方的一个开源组件BeetleX(组件暂只支持net core 2.1或更高版本) 

定义访问接口

组件支持通过接口的方式来描述HTTP接口服务,接口的访问方式对使用和维护都具有着极大的便利性,以下是针对以上服务描述的接口

  1. [JsonFormater]
  2. [Controller(BaseUrl = "Home")]
  3. public interface IDataService
  4. {
  5. [Get]
  6. Task<DateTime> GetTime();
  7. [Get]
  8. Task<string> Hello(string name);
  9. [Get]
  10. Task<bool> Login(string name, string pwd);
  11. [Get]
  12. Task<List<Employee>> Employees();
  13. [Get]
  14. Task<Employee> EmployeeGet(int id);
  15. [Post]
  16. Task<int> EmployeeAdd(params Employee[] items);
  17. [Post]
  18. Task<Employee> EmployeeEdit([CQuery]int id, Employee emp);
  19. [Get]
  20. Task<List<EmployeeName>> EmployeesGetName();
  21. [Get]
  22. Task<List<Customer>> Customers(int count);
  23. [Get]
  24. Task<List<CustomerName>> CustomersGetName();
  25. [Get]
  26. Task<List<Order>> Orders(int? employeeid, string customerid, int index, int size);
  27. }

组件支持Task和非Task返回值的方法定义,由于基础网络访问是基于异步,所以最好还是定义Task返回值的方法。如果定义了非Task返回值访问组件内部会同步等待完成返回,在并发服务下这不利于线程资源的利用。

集群化访问定义

接口定义好之后,就可以通过接口来调用并把请求调用负载到不同服务器的服务上。这样做首先需要定义一个集群化服务访问对象:

  1. private static HttpClusterApi HttpClusterApi = new HttpClusterApi();

HttpClusterApi对象是线程安全的,所以定义成静态即可;也可以根据服务分类来定义不同的HttpClusterApi(之于内部的工作原理这里就不详细解说了,可以到GitHub上过一步了解)。创建了集群接口对象之后就可以用它来创建接口实例。

  1. private static IDataService DataService;
  2.  
  3. DataService = HttpClusterApi.Create<IDataService>();

同样接口实例也是线程安的,只需要创建一个即可在不同线程和方法里同时调用。其实这样创建接口后还没能正常使用,因为没有定义相应服务地址,可以通过HttpClusterApi添加不同服务地址:

  1. HttpClusterApi.AddHost("*", Host25, Host29);

以上是所有请求都负载到Host25和Host29,一般情况都不会直这样定义;*的优先级是最低的,只有没有匹配到其他url描述的情况才会匹配*

平均负载

平均负载是一种最常用的方式,主要是把并发请求平均到不同的服务器上;以下是针对employee相关请求的地址负载到5个服务上.

  1. HttpClusterApi.AddHost("employee.*", Host26, Host27, Host28, Host29, Host30);

接下简单地测试一下

  1. System.Threading.ThreadPool.QueueUserWorkItem(async (o) =>
  2. {
  3. while (true)
  4. {
  5. try
  6. {
  7. await DataService.EmployeeGet();
  8. }
  9. catch (Exception e_)
  10. {
  11. Console.WriteLine(e_.Message);
  12. }
  13. }
  14. });

测试结果:

从测试打印信息来看,基本上把请求平均到不同的服务器上,5台服务器的权重值都是10.

权重定义

现实中由于服务器配置不同,服务器运行的服务也有所差异,所以一般情况都不会平均化负载;这时候权重定义就起到一个非常重要的作用,针对不同地址的服务制定不同的重权值这样可以让服务资源得到更好地利用!接下来还是针对以上测试代码,针对不同的服务器设置不同的权重

  1. HttpClusterApi.GetUrlNode("employee.*").Add(new string[] { Host26, Host27, Host28 }).Add(Host29,).Add(Host30,);

以上代码分别把Host29和Host30的权重设置为5其他均为10.

定义备份节点

所谓的备份节点,其实就是不参与负载处理,但所有可用点都故障的情况,备份节点就生效。其实在组件中没有所谓的备份节点,不过可以通过0权重这个值来实现。当存在其他重权的节点可用时,0权重的节点是不会存在于重权表中的,只有当所有节点不可用的情况下,0权重的节点才会移入到权重表;当有效节点恢复后0权重的节点也会被移出权重表。

故障迁移和恢复

组件会自动把存在N次连接错的服务地址在权重表中排除出去,所以当服务不可用的情况会短暂有部分请求会引发访问异常;接下来组件会每隔一段时间去检测有问题的服务地址,直到服务连接可用的情况才会把服务地址重新添加回重权表里接受负载轮循处理。

自定义集群信息源

如果在代码中把负载设置写死了,那到增加节点的时候就非常麻烦需要重新发布程序;组件为了解决这一问题制定了一个接口方便使用者制定自己的集群信息来源配置。通过实现INodeSourceHandler并设置到HttpClusterApi即可以动态更新节点信息便于更改和管理。以下是针对负载源实现的一个HTTP获取负载源信息实现

  1. public class HTTPRemoteSourceHandler : INodeSourceHandler
  2. {
  3. private HttpClusterApi mHttpClusterApi = new HttpClusterApi();
  4.  
  5. private IHttpSourceApi mRemoteSourceApi;
  6.  
  7. public HTTPRemoteSourceHandler(string name, params string[] hosts)
  8. {
  9. mHttpClusterApi.AddHost("*", hosts);
  10. mRemoteSourceApi = mHttpClusterApi.Create<IHttpSourceApi>();
  11. Name = name;
  12. }
  13.  
  14. public string Name { get; set; }
  15.  
  16. public int UpdateTime { get; set; } = ;
  17.  
  18. public Task<ApiClusterInfo> Load()
  19. {
  20. return mRemoteSourceApi._GetCluster(Name);
  21. }
  22. }

实现是每5秒钟检则一下信息源,集成到HttpClusterApi如下:

  1. HTTPRemoteSourceHandler remoteSourceHandler = new HTTPRemoteSourceHandler("default", "http://localhost:8080");
  2. HttpClusterApi.NodeSourceHandler = remoteSourceHandler;
  3. var result = await HttpClusterApi.LoadNodeSource();

设置INodeSourceHandler后组件就会检配置信息,检测错误或版本没有变化的情况就不会更新配置。以下是针对测编写的一个HTTP配置服务:

如果想得到更详细的代码可以访问 https://github.com/IKende/FastHttpApi

在dotnet core下去中心化访问HTTP服务集群的更多相关文章

  1. .net core下简单构建高可用服务集群

    一说到集群服务相信对普通开发者来说肯定想到很复杂的事情,如zeekeeper ,反向代理服务网关等一系列的搭建和配置等等:总得来说需要有一定经验和规划的团队才能应用起来.在这文章里你能看到在.net ...

  2. .net core 跨平台开发 微服务架构 基于Nginx反向代理 服务集群负载均衡

    1.概述 反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客 ...

  3. Dapr + .NET Core实战(十一)单机Dapr集群

    如何单机部署Dapr集群 第十篇讲过了K8S集群下如何使用Dapr运行程序,但是很多人一直在问如何单机下进行Dapr的负载,这节课我们来聊聊如何单机进行Dapr的负载. 首先要说的是单机下,通过 da ...

  4. Dapr + .NET Core实战(十一)单机Dapr集群负载均衡

    如何单机部署Dapr集群 第十篇讲过了K8S集群下如何使用Dapr运行程序,但是很多人一直在问如何单机下进行Dapr的负载,这节课我们来聊聊如何单机进行Dapr的负载. 首先要说的是单机下,通过 da ...

  5. Dapr + .NET Core实战(十四)虚拟机集群部署 mDNS + Consul

    前面我们说了在单机模式下和K8S集群下的Dapr实战,这次我们来看看如何在不使用K8S的情况下,在一个传统的虚拟机集群里来部署Dapr. 1.环境准备 我们准备两台centos7虚拟机 Dapr1:1 ...

  6. 数据库选型之亿级数据量并发访问(MySQL集群)

    刘 勇  Email:lyssym@sina.com 简介 针对实际应用中并发访问MySQL的场景,本文采用多线程对MySQL进行并发读取访问,其中以返回用户所需的数据并显示在终端为测试结束节点,即将 ...

  7. Spring Cloud - Nacos注册中心入门单机模式及集群模式

    近几年微服务很火,Spring Cloud提供了为服务领域的一整套解决方案.其中Spring Cloud Alibaba是我们SpringCloud的一个子项目,是提供微服务开发的一站式解决方案. 包 ...

  8. SpringCloud(四):服务注册中心Eureka Eureka高可用集群搭建 Eureka自我保护机制

    第四章:服务注册中心 Eureka 4-1. Eureka 注册中心高可用集群概述在微服务架构的这种分布式系统中,我们要充分考虑各个微服务组件的高可用性 问题,不能有单点故障,由于注册中心 eurek ...

  9. 联想企业网盘:SaaS服务集群化持续交付实践

    1      前言 当代信息技术飞速发展,软件和系统的代码规模都变得越来越大,而且组件众多,依赖繁复,每次新版本的发布都仿佛是乘坐一次无座的绿皮车长途夜行,疲惫不堪.软件交付是一个复杂的工程,涉及到软 ...

随机推荐

  1. C++11中list特有版本的算法

    与其他的容器不一样,链表类型的list和forward_list定义了几个成员函数形式的算法,这些函数和前面的所总结的通用算法不同,对于list来说,最好使用自己的特有算法,下面介绍一下主要的几个算法 ...

  2. 关于” 记一次logback传输日志到logstash根据自定义设置动态创建ElasticSearch索引” 这篇博客相关的优化采坑记录

    之前写过一篇博客是关于记录日志的简单方式的   主要就是  应用->redis->logstash->elasticsearch 整个流程的配置方法和过程的 虽然我们部分线上应用使用 ...

  3. github上传文件的几句命令行

    1.首先进入要上传的本地目录,右键打开git命令行. 2.执行指令:git init    初始化本地仓库,这是会看到多了一个.git文件夹(如果没看到那就是电脑隐藏了). 3.执行命令:git ad ...

  4. 记一次产品需求:图片等比缩放和CSS自适应布局16:9

    前言 前阵子,产品跑过来问我现有的模板中没有图片模板,需要添加一个图片模板:然而,他要求图片在展示区最好能够实现随着窗口的变化而自动按图片比例等比缩放,并且居中展示图片.我当时想着,抛开技术实现层面, ...

  5. 腾讯云centos服务器不能登录的解决过程

    在腾讯云上申请了一个centos服务器,最基础的配置,1 核 1 GB 1 Mbps,50G硬盘,主要用来测试程序,练手用.在上面配置了一个mysql数据库,一直使用都没什么问题. 1 问题描述 过了 ...

  6. 为什么我的会话状态在ASP.NET Core中不工作了?

    原文:Why isn't my session state working in ASP.NET Core? Session state, GDPR, and non-essential cookie ...

  7. 《HelloGitHub》第 32 期

    公告 新加入了 2 位机器学期的小伙伴负责机器学习专栏.项目的首页增加合作组织一栏,如有开源组织有意合作可以点击联系我. 我们还在路上,不停地前行. <HelloGitHub>第 32 期 ...

  8. Python调用ansible API系列(五)综合使用

    如何把动态生成资产信息.执行playbook以及自定义结果结合起来用呢? #!/usr/bin/env python # -*- coding: utf-8 -*- """ ...

  9. 用Docker解决坑爹的环境搭建系列——PHP+Apache2

    sudo docker pull eboraas/apache-php sudo docker run -p 9991:80 --name php -v /data/docker/php/www:/v ...

  10. 学习 JavaScript (七) 内存问题

    内存问题是 JavaScript 比较底层的东西,依葫芦画瓢学会了怎么使用变量,但是对于内存的概念依然模糊,今天让我们一起来了解一下内存在这门语言是怎么样的存在. 内存在不同类型的数值面前表现有很大的 ...