.NET Core微服务之基于Consul实现服务治理

Tip: 此篇已加入.NET Core微服务基础系列文章索引html

1、Consul基础介绍

  Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置。与其余分布式服务注册与发现的方案,好比 Airbnb的SmartStack等相比,Consul的方案更“一站式”,内置了服务注册与发现框 架、分布一致性协议实现、健康检查、Key/Value存储、多数据中心方案,再也不须要依赖其余工具(好比ZooKeeper等),使用起来也较 为简单。node

  Consul用Golang实现,所以具备自然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,方便部署,与Docker等轻量级容器可无缝配合linux

  关于Consul的更多介绍,好比优势,这里就再也不赘述了,上网一搜就能够随处找到了。可是,必须贴一个和其余相似软件的对比:web

  此外,关于Consul的架构以及相关的角色,以下图所示:算法

  要想利用Consul提供的服务实现服务的注册与发现,咱们须要创建Consul Cluster。在Consul方案中,每一个提供服务的节点上都要部署和运行Consul的Client Agent,全部运行Consul Agent节点的集合构成Consul Cluster。Consul Agent有两种运行模式:ServerClient。这里的Server和Client只是Consul集群层面的区分,与搭建在Cluster之上的应用服务无关。以Server模式运行的Consul Agent节点用于维护Consul集群的状态,官方建议每一个Consul Cluster至少有3个或以上的运行在Server Mode的Agent,Client节点不限。spring

  Consul支持多数据中心,每一个数据中心的Consul Cluster都会在运行于Server模式下的Agent节点中选出一个Leader节点,这个选举过程经过Consul实现的raft协议保证,多个 Server节点上的Consul数据信息是强一致的。处于Client Mode的Consul Agent节点比较简单,无状态,仅仅负责将请求转发给Server Agent节点。docker

2、Consul集群搭建

2.1 环境准备

  这里我准备了三台Linux(CentOS)虚拟机和一台Windows Server 2008 R2虚拟机,借助VMware Workstation搭建,以下图所示。json

  其中,192.168.80.100会做为leader角色,其他两台192.168.80.101和192.168.80.102会做为follower角色。固然,实际环境中leader角色不会是一个固定的,会随着环境的变化(好比Leader宕机或失联)由算法选出新的leader。在进行下面的操做会前,请确保三台节点可以相互ping通,并可以和宿主机也ping通。另外,192.168.80.71会做为client角色,而且和其他三台虚拟机互相ping通。bootstrap

2.2 下载Consul

  Consul的下载很简单,直接去:https://www.consul.io/downloads.html 选择对应的平台便可。ubuntu

  这里咱们的linux虚拟机选择的是Linux版本:

  

  下载以后是一个zip文件,咱们经过XFtp等工具将其传送到咱们的linux节点中便可。

  而Windows Server虚拟机选择的是Windows版本,再也不赘述。

2.3 安装与配置Consul

  1.解压Consul.zip:

  分别在三台节点中解压,解压命令:

> unzip consul_1.1.0_linux_386.zip   

  解压以后将consul复制到咱们的自定义文件目录中,好比:/usr/local/consul

> cp consul /usr/local/consul

  2.设置环境变量

  分别在三台节点中设置环境变量:

> vim /etc/profile  

  在profile中增长一行CONSUL_HOME并更改PATH:

# Consul

export CONSUL_HOME=/usr/local/consul

export PATH=$PATH:$JAVA_HOME/bin:$CONSUL_HOME;  

  使得配置生效

> source /etc/profile

  测试是否生效,在三个节点测试输入consul

> consul

  看到下图所示的命令提示,就表明OK了。

  

  3.启动Server(s)

  分别在三台节点上执行如下命令便可启动Consul

192.168.80.100>consul agent -server -ui -bootstrap-expect=3 -data-dir=/tmp/consul -node=consul-1 -client=0.0.0.0 -bind=192.168.80.100 -datacenter=dc1

192.168.80.101>consul agent -server -ui -bootstrap-expect=3 -data-dir=/tmp/consul -node=consul-2 -client=0.0.0.0 -bind=192.168.80.101 -datacenter=dc1 -join 192.168.80.100

192.168.80.102>consul agent -server -ui -bootstrap-expect=3 -data-dir=/tmp/consul -node=consul-3 -client=0.0.0.0 -bind=192.168.80.102 -datacenter=dc1 -join 192.168.80.100

  注意101和102的启动命令中,有一句 -join 192.168.80.100 => 有了这一句,就把101和102加入到了100所在的集群中。

  启动以后,集群就开始了Vote(投票选Leader)的过程,经过下面的命令能够看到集群的状况:

  

  在Windows Server虚拟机上启动:

> consul agent -bind 0.0.0.0 -client 192.168.80.71 -data-dir=C:\Counsul\tempdata -node EDC.DEV.WebServer -join 192.168.80.100

  启动后会有以下提示:

  

  4.经过UI查看集群

  Consul不只提供了丰富的命令查看集群状况,还提供了一个WebUI,默认端口8500,咱们能够经过访问这个URL(eg. http://192.168.80.100:8500)获得以下图所示的WebUI:

  能够看到三个节点都正常启动,下面咱们就来试试向Consul注册一下咱们基于ASP.NET Core的WebAPI服务。

  5.模拟Leader挂掉,查看Consul集群的新选举Leader

  这里我暴力一点直接将Leader节点关机:shutdown -h now,能够看到咱们的80.100已经挂了。

  

  查看其他两个节点的日志能够发现,consul-3 (80.102)被选为了新的leader:

  

  固然,也能够经过80.101或102的WebUI查看:

  也能够经过如下命令查看目前的各个Server的角色状态:

> consul operator raft list-peers

  

  虽然这里80.100这个原leader节点挂掉了,可是只要超过一半的Server(这里是2/3还活着)还活着,集群是能够正常工做的,这也是为何像Consul、ZooKeeper这样的分布式管理组件推荐咱们使用3个或5个节点来部署的缘由。

3、ASP.NET Core WebAPI服务注册

3.1 准备一个ASP.NET Core WebAPI程序

  Step1.建立一个ASP.NET Core WebAPI程序

  

  Step2.建立一个HealthController用于Consul的健康检查

    [Produces("application/json")]
    [Route("api/Health")]
    public class HealthController : Controller
    {
        [HttpGet]
        public IActionResult Get() => Ok("ok");
    }

  *.Consul会经过call这个API来确认Service的健康状态

  Step3.改写启动代码,调用Consul API注册服务

  (1)经过Nuget安装Consul的.NET客户端

PM> install-package Consul

  (2)基于IApplicationBuilder写一个扩展方法,用于调用Consul API

    public static class AppBuilderExtensions
    {
        public static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, IApplicationLifetime lifetime, ServiceEntity serviceEntity)
        {
            var consulClient = new ConsulClient(x => x.Address = new Uri($"http://{serviceEntity.ConsulIP}:{serviceEntity.ConsulPort}"));//请求注册的 Consul 地址
            var httpCheck = new AgentServiceCheck()
            {
                DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
                Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔,或者称为心跳间隔
                HTTP = $"http://{serviceEntity.IP}:{serviceEntity.Port}/api/health",//健康检查地址
                Timeout = TimeSpan.FromSeconds(5)
            };

            // Register service with consul
            var registration = new AgentServiceRegistration()
            {
                Checks = new[] { httpCheck },
                ID = Guid.NewGuid().ToString(),
                Name = serviceEntity.ServiceName,
                Address = serviceEntity.IP,
                Port = serviceEntity.Port,
                Tags = new[] { $"urlprefix-/{serviceEntity.ServiceName}" }//添加 urlprefix-/servicename 格式的 tag 标签,以便 Fabio 识别
            };

            consulClient.Agent.ServiceRegister(registration).Wait();//服务启动时注册,内部实现其实就是使用 Consul API 进行注册(HttpClient发起)
            lifetime.ApplicationStopping.Register(() =>
            {
                consulClient.Agent.ServiceDeregister(registration.ID).Wait();//服务中止时取消注册
            });

            return app;
        }

  *.这里主要是参考的田园的蟋蟀的Code,连接见参考资料部分

  (3)在Starup类的Configure方法中,调用此扩展方法

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            app.UseMvc();
            // register this service
            ServiceEntity serviceEntity = new ServiceEntity
            {
                IP = NetworkHelper.LocalIPAddress,
                Port = Convert.ToInt32(Configuration["Service:Port"]),
                ServiceName = Configuration["Service:Name"],
                ConsulIP = Configuration["Consul:IP"],
                ConsulPort = Convert.ToInt32(Configuration["Consul:Port"])
            };
            app.RegisterConsul(lifetime, serviceEntity);
        }

  其中ServiceEntity类定义以下:

    public class ServiceEntity
    {
        public string IP { get; set; }
        public int Port { get; set; }
        public string ServiceName { get; set; }
        public string ConsulIP { get; set; }
        public int ConsulPort { get; set; }
    }
View Code

  其中用到了appSettings.json配置文件,其定义以下:

{
  "Service": {
    "Name": "CAS.NB.ClientService",
    "Port": "8810"
  },
  "Consul": {
    "IP": "192.168.80.101",
    "Port": "8500"
  },
  "Logging": {
    "IncludeScopes": false,
    "Debug": {
      "LogLevel": {
        "Default": "Warning"
      }
    },
    "Console": {
      "LogLevel": {
        "Default": "Warning"
      }
    }
  }
}
View Code

  *.这段代码再也不解释,一眼就能看懂。另外,除了调用Consul API以外,还能够经过配置文件的方式,例如如下配置文件格式,这里再也不演示。

{
  "service": {
    "name": "hwapp_web",
    "tags": ["master"],
    "address": "10.9.10.173",
    "port": 5000,
    "checks": [
      {
        "http": "http://10.9.10.173:5000/health",
        "interval": "10s"
      }
    ]
  }
}
View Code

  Step4.保留默认建立的ValuesController,其他再也不建立任何API,不是本次实验的重点。固然,你能够集成一下Swagger,这样有个界面文档能够看。

  这里我默认跳转到healthcontroller:

  

3.2 发布到IIS

  Step1.在.NET Core程序中进行发布很简单,既能够采用原来在VS里边建立配置文件进行发布,也可使用命令行(例如:dotnet publish),这里我仍是在VS里面发布,获得Release文件

  Step2.经过Ftp工具copy到Windows Server虚拟机中

  Step3.这里个人Windows Server虚拟机是2008 R2,须要装一个Windows Server Hosting,下载地址:点我点我。若是不安装这个,你的IIS是跑不起来.NET Core程序的。

  Step4.按照你熟悉的方式在IIS中添加一个网站(服务):

  

  Step5.更改默认应用程序池的.net framework版本为“无托管代码”。

  

  Step6.照理说,到这里就OK了,点击浏览访问,TMD,给我报了个502.5的错误

  

  因而乎网上一阵搜索,发现须要打个补丁(Windows6.1-KB2533623-x64.msu),下载地址:点我点我

  Step7.安装补丁以后,重启IIS,能够成功访问了=>确保Consul可以call到咱们的服务的health API。

3.3 查看Consul集群状态

  Step1.访问Consul的WebUI查看服务是否注册成功:能够看到咱们的ClientService已成功注册

  Step2.Consul不只仅提供了服务注册,还提供了服务发现,咱们能够经过调用其提供的API来发现服务的IP和Port。

Url>http://192.168.80.100:8500/v1/catalog/service/CAS.NB.ClientService  

  *.咱们能够看到返回了ClientService的ServiceAddress和ServicePort,就能够经过其组成URL进行服务调用了。固然,咱们可能会对一个服务部署多个实例,以组成集群来实现负载均衡。咱们能够设置一些负载均衡的策略,假设经过取模运算随机选择一个服务地址返回给服务消费者

  Step3.这里咱们将发布的Release文件在Windows Server虚拟机上copy一份,并改一下配置文件,让其ServiceName变为CAS.NB.ProductService,其Port变为8820,在IIS上再添加一个网站,启动起来,再经过WebUI查看Consul集群状态:

  Step4.这时咱们再经过如下API进行服务发现:

Url>http://192.168.80.100:8500/v1/catalog/service/CAS.NB.ProductService

  

4、小结与后续工做

  本篇主要基于一个最小化的集群搭建了一个Consul服务治理组件,并将ASP.NET Core API程序注册到了Consul,并尝试经过Consul进行服务发现(虽然没有模拟具体的服务消费)。本篇没有仔细讲述Consul的介绍、优势、缺点,由于本人也没有啥实际的经验,所以只能是站在其余园友的肩膀上作个小实验。ASP.NET Core是一个天生适合微服务的技术,也但愿能在咱们的学习和推进下,让公司把.NET Core应用起来,未来可以跑在Linux和Docker上,这是我目前的目标,与你们共勉。

  后续我会继续尝试基于Ocelot构建API网关,到时会结合Consul进行进一步的集成。另外,还会尝试Polly进行熔断降级、Identity Server进行验证,Exceptionless做分布式日志,基本上会遵循.NET Core大队长张善友的微服务示例项目NanoFabric用到的(或者说是给咱们安利的技术框架)那些开源技术来搭建一个初步的微服务架构,以便于之后在公司内部推广和应用。此外,考虑到公司目前的微服务架构是基于Java的(Spring Cloud),那么也会考虑经过 Steeltoe 来和Spring Cloud进行兼容,让Java和.Net Core微服务可以共存(能够参考资料里的第7个,田园里的蟋蟀出品)。这里,也安利一下正在学习微服务的.NET Coder们,能够看看张善友老师的NanoFabric,或者是杨中科老师的.NET Core微服务课程。

  

  最近不少朋友问我为何再也不写技术文了,总是写一些观后感、读后感之类的,一来由于读那些书其实也是我今年的我的OKR,二来这半年来的确不想看技术书和资料,最后由于身体缘由无法过多地熬夜。如今学习.NET Core以来被其深深吸引,就像是找到了一个那些年咱们追过的女孩儿,以为它样样都美,很想跟它继续走下去,发现它更多的美,所以也会趁着微服务学习的劲跟你们分享更多的文章。

  

附件下载

  示例代码:点我下载

参考资料

(1)田园里的蟋蟀,《Docker & Consul & Fabio & ASP.NET Core 2.0 微服务跨平台实践

(2)不小下,《服务发现 - consul 的介绍、部署和使用

(3)二胡槽子,《我是服务的执政官-服务发现和注册工具consul简介

(4)大副,《consul分布式集群搭建&简单功能测试&故障恢复

(5)94cool,《win2008server R2 x64 部署.net core到IIS

(6)杨中科《.NET Core微服务课件》

(7)田园里的蟋蟀,《.NET Core 微服务架构 Steeltoe 使用(基于 Spring Cloud)

 

相关文章
相关标签/搜索