ASP.NET Core微服务之基于Consul实现服务治理(3)

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 在去年的.NET Core微服务系列文章中,初步学习了一下Consul服务发现,总结了两篇文章。本次基于Docker部署的方式,以一个Demo示例来搭建一个Consul的示例集群,最后给出一个HA的架构示范,也会更加贴近于实际应用环境。

一、示例整体架构

  此示例会由一个API Gateway, 一个Consul Client以及三个Consul Server组成,有关Consul的Client和Server这两种模式的Agent的背景知识,请移步我之前的文章加以了解:《.NET Core微服务之基于Consul实现服务治理》。其中,Consul的Client和Server节点共同构成一个Data Center,而API Gateway则从Consul中获取到服务的IP和端口号,并返回给服务消费者。这里的API Gateway是基于Ocelot来实现的,它不是这里的重点,也就不过多说明了,不了解的朋友请移步我的另一篇:《.NET Core微服务之基于Ocelot实现API网关服务》。

二、Consul集群搭建

2.1 Consul镜像拉取

docker pull consul:1.4.4

  验证:docker images

2.2 Consul Server实例创建

  以下我的实践是在一台机器上(CentOS 7)操作的,因此将三个实例分别使用了不同的端口号(区别于默认端口号8500)。实际环境中,建议多台机器部署。

  (1)Consul实例1

docker run -d -p 8510:8500 --restart=always -v /XiLife/consul/data/server1:/consul/data -v /XiLife/consul/conf/server1:/consul/config -e CONSUL_BIND_INTERFACE='eth0' --privileged=true --name=consul_server_1 consul:1.4.4 agent -server -bootstrap-expect=3 -ui -node=consul_server_1 -client='0.0.0.0' -data-dir /consul/data -config-dir /consul/config -datacenter=xdp_dc;

  (2)Consul实例2

  为了让Consul实例2加入集群,首先获取一下Consul实例1的IP地址:

  JOIN_IP="$(docker inspect -f '{{.NetworkSettings.IPAddress}}' consul_server_1)";

docker run -d -p 8520:8500 --restart=always -v /XiLife/consul/data/server2:/consul/data -v /XiLife/consul/conf/server2:/consul/config -e CONSUL_BIND_INTERFACE='eth0' --privileged=true --name=consul_server_2 consul:1.4.4 agent -server -ui -node=consul_server_2 -client='0.0.0.0' -datacenter=xdp_dc -data-dir /consul/data -config-dir /consul/config -join=$JOIN_IP;  

  (3)Consul实例3

docker run -d -p 8530:8500 --restart=always -v /XiLife/consul/data/server3:/consul/data -v /XiLife/consul/conf/server3:/consul/config -e CONSUL_BIND_INTERFACE='eth0' --privileged=true --name=consul_server_3 consul:1.4.4 agent -server -ui -node=consul_server_3 -client='0.0.0.0' -datacenter=xdp_dc -data-dir /consul/data -config-dir /consul/config -join=$JOIN_IP;

  验证1:docker exec consul_server_1 consul operator raft list-peers

  验证2:http://192.168.16.170:8500/

2.3 Consul Client实例创建

  (1)准备services.json配置文件,向Consul注册两个同样的Product API服务

    {
      "services": [
        {
          "id": "core.product-/192.168.16.170:8000",
          "name": "core.product",
          "tags": [ "xdp-/core.product" ],
          "address": "192.168.16.170",
          "port": 8000,
          "checks": [
            {
              "name": "core.product.check",
              "http": "http://192.168.16.170:8000/api/health",
              "interval": "10s",
              "timeout": "5s"
            }
          ]
        },
        {
          "id": "core.product-/192.168.16.170:8001",
          "name": "core.product",
          "tags": [ "xdp-/core.product" ],
          "address": "192.168.16.170",
          "port": 8001,
          "checks": [
            {
              "name": "core.product.check",
              "http": "http://192.168.16.170:8001/api/health",
              "interval": "10s",
              "timeout": "5s"
            }
          ]
        }
      ]
    }

  有关配置文件的细节,请移步另一篇文章:《.NET Core微服务之基于Consul实现服务治理(续)

  (2)Consul Client实例

docker run -d -p 8550:8500 --restart=always -v /XiLife/consul/conf/client1:/consul/config -e CONSUL_BIND_INTERFACE='eth0' --name=consul_client_1 consul:1.4.4 agent -node=consul_client_1 -join=$JOIN_IP -client='0.0.0.0' -datacenter=xdp_dc -config-dir /consul/config

  (3)验证

2.4 服务检查监控邮件提箱

  (1)为Client添加watches.json

       {
          "watches": [
            {
              "type": "checks",
              "handler_type": "http",
              "state": "critical",
              "http_handler_config": {
                "path": "http://192.168.16.170:6030/api/Notifications/consul",
                "method": "POST",
                "timeout": "10s",
                "header": { "Authorization": [ "token" ] }
              }
            }
          ]
        }

  *.这里的api接口 http://192.168.16.170:6030/api/Notifications/consul是我的一个通知服务接口,下面是实现的代码

        /// <summary>
        /// 发送Consul服务中心健康检查Email
        /// </summary>
        [HttpPost("consul")]
        public async Task SendConsulHealthCheckEmail()
        {
            using (var stream = new MemoryStream())
            {
                HttpContext.Request.Body.CopyTo(stream);
                var ary = stream.ToArray();
                var str = Encoding.UTF8.GetString(ary);

                dynamic notifications = JsonConvert.DeserializeObject(str);
                if (notifications == null || notifications.Count == 0)
                {
                    return;
                }

                var title = "XDP服务中心健康检查通知";
                var emailBody = new StringBuilder($"<span style='font-weight:bold; color:red;'>{title}</span> : <br/>");
                foreach (var notification in notifications)
                {
                    emailBody.AppendLine($"---------------------------------------------------------<br/>");
                    emailBody.AppendLine($"<span style='font-weight:bold;'>节点</span>:{notification.Node}<br/>");
                    emailBody.AppendLine($"<span style='font-weight:bold;'>服务ID</span>:{notification.ServiceID}<br/>");
                    emailBody.AppendLine($"<span style='font-weight:bold;'>服务名称</span>:{notification.ServiceName}<br/>");
                    emailBody.AppendLine($"<span style='font-weight:bold;'>检查ID</span>:{notification.CheckID}<br/>");
                    emailBody.AppendLine($"<span style='font-weight:bold;'>检查名称</span>:{notification.Name}<br/>");
                    emailBody.AppendLine($"<span style='font-weight:bold;'>检查状态</span>:{notification.Status}<br/>");
                    emailBody.AppendLine($"<span style='font-weight:bold;'>检查输出</span>:{notification.Output}<br/>");
                    emailBody.AppendLine($"---------------------------------------------------------<br/>");
                }

                var email = new Email()
                {
                    Username = _configuration["EmailSettings:Username"],
                    Password = _configuration["EmailSettings:Password"],
                    SmtpServerAddress = _configuration["EmailSettings:SmtpServerAddress"],
                    SmtpPort = Convert.ToInt32(_configuration["EmailSettings:SmtpPort"]),
                    Subject = title,
                    Body = emailBody.ToString(),
                    Recipients = _configuration["EmailSettings:Recipients"]
                };

                email.Send();
            }
        }

            /// <summary>
            /// 使用同步发送邮件
            /// </summary>
            public void Send()
            {
                using (SmtpClient smtpClient = GetSmtpClient)
                {
                    using (MailMessage mailMessage = GetClient)
                    {
                        if (smtpClient == null || mailMessage == null) return;
                        Subject = Subject;
                        Body = Body;
                        //EnableSsl = false;
                        smtpClient.Send(mailMessage); //异步发送邮件,如果回调方法中参数不为"true"则表示发送失败
                    }
                }
            }    

  (2)验证

三、Ocelot网关配置

3.1 为Ocelot增加Consul支持

  (1)增加Nuget包:Ocelot.Provider.Consul

Nuget>> Install-Package Ocelot.Provider.Consul

  (2)修改StartUp.cs,增加Consul支持

s.AddOcelot() .AddConsul();

  更多内容,请移步:Ocelot官方文档-服务发现

3.2 修改Ocelot配置文件增加Consul配置

    "GlobalConfiguration": {
        "BaseUrl": "http://api.xique.com",
        "ServiceDiscoveryProvider": {
             "Host": "192.168.16.170",
             "Port": 8550,
             "Type": "Consul"
        }
    }

  *.这里指向的是Consul Client实例的地址

  此外,Ocelot默认策略是每次请求都去Consul中获取服务地址列表,如果想要提高性能,也可以使用PollConsul的策略,即Ocelot自己维护一份列表,然后定期从Consul中获取刷新,就不再是每次请求都去Consul中拿一趟了。例如下面的配置,它告诉Ocelot每2秒钟去Consul中拿一次。

    "Type": "PollConsul",
    "PollingInterval": 2000

3.3 Service配置

    // -- Service
    {
      "UseServiceDiscovery": true,
      "DownstreamPathTemplate": "/api/{url}",
      "DownstreamScheme": "http",
      "ServiceName": "core.product",
      "LoadBalancerOptions": {
        "Type": "RoundRobin"
      },
      "UpstreamPathTemplate": "/product/{url}",
      "UpstreamHttpMethod": [ "Get", "Post", "Put", "Delete" ]
    }

  这里配置了在Consul中配置的服务名(ServiceName),以及告诉Ocelot我们使用轮询策略(RoundRobin)做负载均衡。

3.4 验证

  第一次访问:

  第二次访问:

四、HA示例整体架构

  对于实际应用中,我们往往会考虑单点问题,因此会借助一些负载均衡技术来做高可用的架构,这里给出一个建议的HA示例的整体架构:

  对于一个API请求,首先会经历一个Load Balancer才会到达API Gateway,这个Load Balancer可以是基于硬件的F5,也可以是基于软件的Nginx或LVS再搭配Keepalived,一般来说大部分团队都会选择Nginx。然后API Gateway通过部署多个,来解决单点问题,也达到负载均衡的效果。而对于API Gateway和Consul Client之间的连接,我们往往也会增加一个Load Balancer来实现服务发现的高可用,这个Load Balancer也一般会基于Nginx/LVS搭配Keepalived,API Gateway只需要访问一个Virtual IP即可。而在Consul Data Center中,Consul Server会选择3或5个,Consul Client也会部署多个,刚刚提到的Virtual IP则会指向多个Consul Client,从而防止了Consul Client的单点问题。

目录
相关文章
|
1月前
|
存储 开发框架 JSON
ASP.NET Core OData 9 正式发布
【10月更文挑战第8天】Microsoft 在 2024 年 8 月 30 日宣布推出 ASP.NET Core OData 9,此版本与 .NET 8 的 OData 库保持一致,改进了数据编码以符合 OData 规范,并放弃了对旧版 .NET Framework 的支持,仅支持 .NET 8 及更高版本。新版本引入了更快的 JSON 编写器 `System.Text.UTF8JsonWriter`,优化了内存使用和序列化速度。
|
2月前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。
|
2月前
|
开发框架 .NET 中间件
ASP.NET Core Web 开发浅谈
本文介绍ASP.NET Core,一个轻量级、开源的跨平台框架,专为构建高性能Web应用设计。通过简单步骤,你将学会创建首个Web应用。文章还深入探讨了路由配置、依赖注入及安全性配置等常见问题,并提供了实用示例代码以助于理解与避免错误,帮助开发者更好地掌握ASP.NET Core的核心概念。
92 3
|
1月前
|
开发框架 JavaScript 前端开发
一个适用于 ASP.NET Core 的轻量级插件框架
一个适用于 ASP.NET Core 的轻量级插件框架
|
2月前
|
开发框架 NoSQL .NET
利用分布式锁在ASP.NET Core中实现防抖
【9月更文挑战第5天】在 ASP.NET Core 中,可通过分布式锁实现防抖功能,仅处理连续相同请求中的首个请求,其余请求返回 204 No Content,直至锁释放。具体步骤包括:安装分布式锁库如 `StackExchange.Redis`;创建分布式锁服务接口及其实现;构建防抖中间件;并在 `Startup.cs` 中注册相关服务和中间件。这一机制有效避免了短时间内重复操作的问题。
|
3月前
|
开发框架 监控 .NET
开发者的革新利器:ASP.NET Core实战指南,构建未来Web应用的高效之道
【8月更文挑战第28天】本文探讨了如何利用ASP.NET Core构建高效、可扩展的Web应用。ASP.NET Core是一个开源、跨平台的框架,具有依赖注入、配置管理等特性。文章详细介绍了项目结构规划、依赖注入配置、中间件使用及性能优化方法,并讨论了安全性、可扩展性以及容器化的重要性。通过这些技术要点,开发者能够快速构建出符合现代Web应用需求的应用程序。
56 0
|
3月前
|
缓存 数据库连接 API
Entity Framework Core——.NET 领域的 ORM 利器,深度剖析其最佳实践之路
【8月更文挑战第28天】在软件开发领域,高效的数据访问与管理至关重要。Entity Framework Core(EF Core)作为一款强大的对象关系映射(ORM)工具,在 .NET 开发中扮演着重要角色。本文通过在线书店应用案例,展示了 EF Core 的核心特性和优势。我们定义了 `Book` 实体类及其属性,并通过 `BookStoreContext` 数据库上下文配置了数据库连接。EF Core 提供了简洁的 API,支持数据的查询、插入、更新和删除操作。
115 0
|
6天前
|
设计模式 Java API
微服务架构演变与架构设计深度解析
【11月更文挑战第14天】在当今的IT行业中,微服务架构已经成为构建大型、复杂系统的重要范式。本文将从微服务架构的背景、业务场景、功能点、底层原理、实战、设计模式等多个方面进行深度解析,并结合京东电商的案例,探讨微服务架构在实际应用中的实施与效果。
36 6
|
6天前
|
设计模式 Java API
微服务架构演变与架构设计深度解析
【11月更文挑战第14天】在当今的IT行业中,微服务架构已经成为构建大型、复杂系统的重要范式。本文将从微服务架构的背景、业务场景、功能点、底层原理、实战、设计模式等多个方面进行深度解析,并结合京东电商的案例,探讨微服务架构在实际应用中的实施与效果。
20 1
|
2月前
|
安全 应用服务中间件 API
微服务分布式系统架构之zookeeper与dubbo-2
微服务分布式系统架构之zookeeper与dubbo-2