文档章节

去中心化访问HTTP服务集群

泥水佬
 泥水佬
发布于 2018/12/13 16:55
字数 2019
阅读 302
收藏 5

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

场景定义

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

    public class HomeController : Controller
    {
        [HttpPost]
        public int EmployeeAdd([FromBody]List<Employee> items)
        {
            return items == null ? 0 : items.Count;
        }

        [HttpPost]
        public Employee EmployeeEdit(int id, [FromBody]Employee emp)
        {
            Employee record = DataHelper.Employees.Find(e => e.EmployeeID == id);
            if (record != null)
            {
                record.City = emp.City;
                record.Address = emp.Address;
                record.Title = emp.Title;
                record.HomePhone = emp.HomePhone;
                return record;
            }
            return null;
        }

        public object EmployeesGetName()
        {
            return from e in DataHelper.Employees select new { ID = e.EmployeeID, Name = e.FirstName + " " + e.LastName };
        }

        public object Customers(int count)
        {
            List<Customer> result = new List<Customer>();
            if (count > DataHelper.Customers.Count)
                count = DataHelper.Customers.Count;
            for (int i = 0; i < count; i++)
            {
                result.Add(DataHelper.Customers[i]);
            }
            return result;
        }

        public object CustomersGetName()
        {
            return from c in DataHelper.Customers select new { ID = c.CustomerID, Name = c.CompanyName };
        }

        public object Orders(int employeeid, string customerid, int index, int size)
        {
            Func<Order, bool> exp = o => (employeeid == 0 || o.EmployeeID == employeeid)
             && (string.IsNullOrEmpty(customerid) || o.CustomerID == customerid);
            int count = DataHelper.Orders.Count(exp);
            if (size == 0)
                size = 10;
            int pages = count / size;
            if (count % size > 0)
                pages++;
            var items = DataHelper.Orders.Where(exp).Skip(index * size).Take(size);
            return items;

        }
    }

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

引用组件

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

定义访问接口

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

    [JsonFormater]
    [Controller(BaseUrl = "Home")]
    public interface IDataService
    {
        [Get]
        Task<DateTime> GetTime();
        [Get]
        Task<string> Hello(string name);
        [Get]
        Task<bool> Login(string name, string pwd);
        [Get]
        Task<List<Employee>> Employees();
        [Get]
        Task<Employee> EmployeeGet(int id);
        [Post]
        Task<int> EmployeeAdd(params Employee[] items);
        [Post]
        Task<Employee> EmployeeEdit([CQuery]int id, Employee emp);
        [Get]
        Task<List<EmployeeName>> EmployeesGetName();
        [Get]
        Task<List<Customer>> Customers(int count);
        [Get]
        Task<List<CustomerName>> CustomersGetName();
        [Get]
        Task<List<Order>> Orders(int? employeeid, string customerid, int index, int size);
    }

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

集群化访问定义

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

    private static HttpClusterApi HttpClusterApi = new HttpClusterApi();

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

private static IDataService DataService;

DataService = HttpClusterApi.Create<IDataService>();

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

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

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

平均负载

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

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

接下简单地测试一下

            System.Threading.ThreadPool.QueueUserWorkItem(async (o) =>
            {
                while (true)
                {
                    try
                    {
                        await DataService.EmployeeGet(2);
                    }
                    catch (Exception e_)
                    {
                        Console.WriteLine(e_.Message);
                    }
                }
            });

测试结果:

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

权重定义

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

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

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

定义备份节点

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

故障迁移和恢复

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

自定义集群信息源

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

    public class HTTPRemoteSourceHandler : INodeSourceHandler
    {
        private HttpClusterApi mHttpClusterApi = new HttpClusterApi();

        private IHttpSourceApi mRemoteSourceApi;

        public HTTPRemoteSourceHandler(string name, params string[] hosts)
        {
            mHttpClusterApi.AddHost("*", hosts);
            mRemoteSourceApi = mHttpClusterApi.Create<IHttpSourceApi>();
            Name = name;
        }

        public string Name { get; set; }

        public int UpdateTime { get; set; } = 5;

        public Task<ApiClusterInfo> Load()
        {
            return mRemoteSourceApi._GetCluster(Name);
        }
    }

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

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

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

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

© 著作权归作者所有

共有 人打赏支持
泥水佬

泥水佬

粉丝 72
博文 86
码字总数 56002
作品 7
广州
架构师
私信 提问
Spring Cloud(二)Consul 服务治理实现

Spring Cloud Consul 项目是针对Consul的服务治理实现。Consul是一个分布式高可用的系统,具有分布式、高可用、高扩展性。 Consul 简介 Consul 是 HashiCorp 公司推出的开源工具,用于实现分...

whs0668
2018/01/05
0
0
Spring Cloud(二) Consul 服务治理实现

Spring Cloud Consul 项目是针对Consul的服务治理实现。Consul是一个分布式高可用的系统,具有分布式、高可用、高扩展性。 Consul 简介 Consul 是 HashiCorp 公司推出的开源工具,用于实现分...

lemonLove
2018/08/11
0
0
Spring Cloud 2.x系列之断路器集群监控Turbine

前几篇已经实现了对单个服务实例的监控,当然在实际应用中,单个实例的监控数据没有多大的价值,我们其实更需要的是一个集群系统的监控信息,这时就需要引入Turbine。Turbine能够汇集监控信息...

技术小能手
2018/10/08
0
0
Commonrpc 1.0-stable 发布,分布式 RPC 框架

commonrpc 是 一个以netty 传输协议框架为基础。自定义 spring shcema标签的rpc框架,不侵入任何业务代码,插件模式,即插即用;一个高性能分布式rpc框架,支持tcp,http协议,扩展性强。 bu...

liubingsmile
2015/03/29
1K
27
Dubbo详细介绍与安装使用过程

1 Dubbo介绍 1.1 dubbox简介 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进...

monroeCode
2017/10/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

4.57 MariaDB慢查询日志 4.58 Tomcat_JDK部署 4.59 zrlog安装 4.60 Nginx代理Tomcat

4.57 MariaDB慢查询日志 为什么要配置慢查询日志? 目的是为了帮助我们分析MariaDB的瓶颈点。 如何配置? 1)进入MariaDB里面执行:show variables like 'slow%';show variables li...

Champin
今天
3
0
自动机器学习简述(AutoML)

为什么需要自动机器学习 对于机器学习的新用户而言,使用机器学习算法的一个主要的障碍就是算法的性能受许多的设计决策影响。随着深度学习的流行,工程师需要选择相应的神经网络架构,训练过...

naughty
今天
2
0
Android Studio Unable to resolve dependency for错误的排查

记录一次Android Studio Unable to resolve dependency for错误的排查 Android Studio 3.2.1 错误提示 Unable to resolve dependency for... 原因:在gradle中设置的代理并没有gradle 4.6的版......

Gemini-Lin
今天
0
0
java常用设计模式

设计模式; 一个程序员对设计模式的理解: “不懂”为什么要把很简单的东西搞得那么复杂。后来随着软件开发经验的增加才开始明白我所看到的“复杂”恰恰就是设计模式的精髓所在,我所理解的“...

呵呵哒灬
今天
5
0
Kafka入门

1、Kafka使用背景 在我们大量使用分布式数据库、分布式计算集群的时候,是否会遇到这样的一些问题: 我们想分析下用户行为(pageviews),以便我们设计出更好的广告位 我想对用户的搜索关键词...

watermelon11
昨天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部