tnblog
首页
视频
资源
登录

NetCore加Consul实现简单服务实例负载

6963人阅读 2020/6/5 14:53 总访问:82587 评论:0 收藏:1 手机
分类: 工具

今天,总结一下之前学习的Consul基础(后续如果有时间的话,再加上Ocelot部分):

  1. 新建一个WebApi项目:模拟服务

  2. 创建一个Web应用程序:模拟客户端

  3. 下载好Consul客户端

    准备工作做好了,开始撸码

  4. 首先,在两个项目中分别引入Consul包

  5. 第二步:准备一个测试接口(UserController)和一个健康检查的接口(HealthController)


    1. using Microsoft.AspNetCore.Authorization;
    2. using Microsoft.AspNetCore.Mvc;
    3. namespace Tnblog.MicroService.WebApiDemo.Controllers
    4. {
    5.     /// <summary>
    6.     /// 用户信息服务
    7.     /// </summary>
    8.     [Route("api/[controller]/[action]")]
    9.     [ApiController]
    10.     public class UserController : ControllerBase
    11.     {
    12.         /// <summary>
    13.         /// AllowAnonymous:允许匿名访问接口
    14.         /// </summary>
    15.         /// <param name="id"></param>
    16.         /// <returns></returns>
    17.         [HttpGet]
    18.         [AllowAnonymous]
    19.         public Users Get()
    20.         {
    21.             Users user = new Users() {UserName="Asa",Email="12345@qq.com"};
    22.             // 允许跨域(这一句不需要)
    23.             base.HttpContext.Response.Headers.Add("Access-Control-Allow-Origin""*");
    24.             return user;
    25.         }
    26.     }
    27.     public class Users
    28.     {
    29.         public int UserID { getset; }
    30.         public string UserName { getset; }
    31.         public string Email { getset; }
    32.     }
    33. }
    1. using Microsoft.AspNetCore.Mvc;
    2. using Microsoft.Extensions.Configuration;
    3. using Microsoft.Extensions.Logging;
    4. namespace Asa.AspNetCore31WebApiDemo.Controllers
    5. {
    6.     /// <summary>
    7.     /// (服务)心跳检查/健康检查
    8.     /// </summary>
    9.     [Route("api/[controller]")]
    10.     [ApiController]
    11.     public class HealthController : ControllerBase
    12.     {
    13.         private readonly ILogger<HealthController> _logger;
    14.         private readonly IConfiguration _iConfiguration;
    15.         public HealthController(ILogger<HealthController> logger, IConfiguration configuration)
    16.         {
    17.             _logger = logger;
    18.             this._iConfiguration = configuration;
    19.         }
    20.         /// <summary>
    21.         /// 用于做健康检查的服务
    22.         /// [Route("Index")]:拼接到控制器上的route
    23.         /// </summary>
    24.         /// <returns></returns>
    25.         [HttpGet]
    26.         [Route("Index")]
    27.         public IActionResult Index()
    28.         {
    29.             this._logger.LogWarning($"This is HealthController {this._iConfiguration["Port"]}");
    30.             //HttpStatusCode--200:代表调用成功,服务正常运行
    31.             return Ok();
    32.         }
    33.     }
    34. }

  8.第三步:准备一个服务注册帮助类

  1. using Consul;
  2. using Microsoft.Extensions.Configuration;
  3. using System;
  4. namespace Asa.AspNetCore31WebApiDemo.Utility
  5. {
  6.     /// <summary>
  7.     /// 自己封装的Consul服务注册类
  8.     /// </summary>
  9.     public static class ConsulHelper
  10.     {
  11.         /// <summary>
  12.         /// Consul服务注册
  13.         /// </summary>
  14.         /// <param name="configuration"></param>
  15.         public static void ConsulRegist(this IConfiguration configuration)
  16.         {
  17.             #region 获取Consul客户端
  18.             // 获取Consul客户端:用于将服务注册进Consul
  19.             ConsulClient client = new ConsulClient(c =>
  20.             {
  21.                 // Consul客户端地址
  22.                 c.Address = new Uri("http://localhost:8500/");
  23.                 // 数据中心
  24.                 c.Datacenter = "dc1";
  25.             });
  26.             #endregion
  27.             #region 获取命令行参数
  28.             // Program-Main中配置了AddCommandLine就能支持命令行获取
  29.             // 服务实例IP
  30.             string ip = configuration["ip"];
  31.             // 服务实例端口号:命令行参数必须传入
  32.             int port = int.Parse(configuration["port"]);
  33.             // 命令行参数必须传入:权重模式(根据配置的权重来随机调用服务实例)
  34.             int weight = string.IsNullOrWhiteSpace(configuration["weight"]) ? 1 : int.Parse(configuration["weight"]);
  35.             #endregion
  36.             #region 注册服务
  37.             // 注册服务
  38.             client.Agent.ServiceRegister(new AgentServiceRegistration()
  39.             {
  40.                 // 在consul中服务的id:唯一的
  41.                 ID = "service" + Guid.NewGuid(),
  42.                 // 一组服务的组名称
  43.                 Name = "AsaUserService",
  44.                 // 其实应该写ip地址
  45.                 Address = ip,
  46.                 // 不同实例:例如(8085/8086)
  47.                 Port = port,
  48.                 // 标签:权重模式使用
  49.                 Tags = new string[] { weight.ToString() },
  50.                 #region 健康检查
  51.                 // 配置心跳检查的
  52.                 Check = new AgentServiceCheck()
  53.                 {
  54.                     // 每隔12秒检查一次
  55.                     Interval = TimeSpan.FromSeconds(12),
  56.                     // 心跳检查的IP地址
  57.                     HTTP = $"http://{ip}:{port}/Api/Health/Index",
  58.                     // 超时时间:如果五秒内服务实例没有响应,那么就判定为服务实例已经挂掉
  59.                     // 测试后发现这个设置延迟生效:实际时间不止五秒
  60.                     Timeout = TimeSpan.FromSeconds(5),
  61.                     // 在多久之后去除注册
  62.                     DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5)
  63.                 }
  64.                 #endregion
  65.             });
  66.             #endregion
  67.             Console.WriteLine($"http://{ip}:{port}完成注册");
  68.         }
  69.     }
  70. }

9.第四步:配置服务注册,在服务启动时注册

10.第五步:在客户端添加一个用于测试的控制器(TestController)

  1. using Asa.AspNetCore31.Demo.Utility.WebApiHelper;
  2. using Consul;
  3. using Microsoft.AspNetCore.Mvc;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. namespace Tnblog.MicroService.ClientDemo.Controllers
  8. {
  9.     public class TestController : Controller
  10.     {
  11.         // (随机标识)没考虑溢出问题,到达一定长度应该重置
  12.         private static int iSeed = 0;
  13.         /// <summary>
  14.         /// 模拟客户端调用用户服务
  15.         /// </summary>
  16.         /// <returns></returns>
  17.         public IActionResult Index()
  18.         {
  19.             Users user = new Users();
  20.             string resultUrl = null;
  21.             #region 通过Consul去发现所有服务地址
  22.             {
  23.                 // 指定要调用的服务实例:用Consul服务组名称替换IP和端口号:AsaUserService:服务实例注册时指定的组名称
  24.                 //string url = "http://localhost:5728/api/user/get";
  25.                 string url = "http://AsaUserService/api/user/get";
  26.                 Uri uri = new Uri(url);
  27.                 // 获取服务实例组名称
  28.                 string groupName = uri.Host;
  29.                 // 获取Consul客户端
  30.                 using (ConsulClient client = new ConsulClient(c =>
  31.                 {
  32.                     // Consul地址:8500 端口基于 HTTP 协议,用于 API 接口或 WEB UI 访问
  33.                     c.Address = new Uri("http://localhost:8500/");
  34.                     // 数据中心:默认名称为dc1
  35.                     c.Datacenter = "dc1";
  36.                 }))
  37.                 {
  38.                     // 获取所有注册的服务实例
  39.                     var dictionary = client.Agent.Services().Result.Response;
  40.                     // 根据组名称筛选服务实例:得到名称为AsaUserService的一组服务
  41.                     var list = dictionary.Where(k => k.Value.Service.Equals(groupName, StringComparison.OrdinalIgnoreCase));
  42.                     KeyValuePair<string, AgentService> keyValuePair = new KeyValuePair<string, AgentService>();
  43.                     #region 负载策略
  44.                     //{
  45.                     //    // 获取第一个服务实例
  46.                     //    keyValuePair = list.First();
  47.                     //    //url = url.Replace(groupName, $"{keyValuePair.Value.Address}:{keyValuePair.Value.Port}");
  48.                     //}
  49.                     //// 随机策略/平均策略
  50.                     //{
  51.                     //    var array = list.ToArray();
  52.                     //    // 随机策略/平均策略
  53.                     //    keyValuePair = array[new Random(iSeed++).Next(0, array.Length)];
  54.                     //}
  55.                     //// 轮巡策略 / 平均策略
  56.                     //{
  57.                     //    var array = list.ToArray();
  58.                     //    keyValuePair = array[iSeed++ % array.Length];// 取余数:就能按照0 1 2 的顺序调用
  59.                     //}
  60.                     // 权重模式
  61.                     {
  62.                         List<KeyValuePair<string, AgentService>> serviceList = new List<KeyValuePair<string, AgentService>>();
  63.                         foreach (KeyValuePair<string, AgentService> agentService in list)
  64.                         {
  65.                             int count = int.Parse(agentService.Value.Tags[0]);
  66.                             for (int i = 0; i < count; i++)
  67.                             {
  68.                                 serviceList.Add(agentService);
  69.                             }
  70.                         }
  71.                         keyValuePair = serviceList[new Random(iSeed++).Next(0, serviceList.Count())];
  72.                     }
  73.                     #endregion
  74.                     resultUrl = $"{uri.Scheme}://{keyValuePair.Value.Address}:{keyValuePair.Value.Port}{uri.PathAndQuery}";
  75.                     // 调用服务实例中的接口(WebApiHelperExtend:接口调用拓展类)
  76.                     string result = WebApiHelperExtend.InvokeApi(resultUrl);
  77.                     user = Newtonsoft.Json.JsonConvert.DeserializeObject<Users>(result);
  78.                 }
  79.             }
  80.             #endregion
  81.             this.ViewBag.User = user;
  82.             this.ViewBag.Url = resultUrl;
  83.             return View();
  84.         }
  85.     }
  86.     /// <summary>
  87.     /// 用于测试
  88.     /// </summary>
  89.     public class Users
  90.     {
  91.         public int UserID { getset; }
  92.         public string UserName { getset; }
  93.         public string Email { getset; }
  94.     }
  95. }

11.第六步:准备一个接口调用帮助类

  1. using System;
  2. using System.Net.Http;
  3. namespace Asa.AspNetCore31.Demo.Utility.WebApiHelper
  4. {
  5.     /// <summary>
  6.     /// 接口调用帮助类
  7.     /// </summary>
  8.     public static class WebApiHelperExtend
  9.     {
  10.         /// <summary>
  11.         /// 模拟调用接口
  12.         /// </summary>
  13.         /// <param name="url"></param>
  14.         /// <returns></returns>
  15.         public static string InvokeApi(string url)
  16.         {
  17.             // 提供基类,用于从由URI标识的资源发送HTTP请求和接收HTTP响应。
  18.             using (HttpClient httpClient = new HttpClient())
  19.             {
  20.                 // Http请求消息
  21.                 HttpRequestMessage message = new HttpRequestMessage();
  22.                 // 请求方式
  23.                 message.Method = HttpMethod.Get;
  24.                 // 请求地址
  25.                 message.RequestUri = new Uri(url);
  26.                 // 以异步操作的形式发送HTTP请求。并接收响应信息
  27.                 var result = httpClient.SendAsync(message).Result;
  28.                 // 将HTTP内容序列化为字符串
  29.                 string content = result.Content.ReadAsStringAsync().Result;
  30.                 return content;
  31.             }
  32.         }
  33.     }
  34. }

12.第七步:为TestController添加对应视图,用于观察负载的效果

  1. @{
  2.     ViewData["Title"] = "Index";
  3. }
  4. <h1>Index</h1>
  5. <h3>@this.ViewBag.User.UserName</h3>
  6. <h3>@this.ViewBag.Url</h3>

13.第八步:由于我是使用的命令提示符窗口运行,所以要配置为支持从命令行读取配置信息

14.找到Consul所在的文件夹,以cmd的方式打开:

使用命令启动Consul:consul_1.6.2.exe agent –dev  

启动后在浏览器使用以下地址打开:

http://localhost:8500

效果如下:这时只有一个默认的Service

15.打开服务实例项目所在的地址,同样以cmd方式打开,然后用命令启动:

启动命令:dotnet Asa.AspNetCore31WebApiDemo.dll --urls="http://*:5726" --ip="127.0.0.1" --port=5726 --weight=7

想要观察多个服务实例负载的效果的话,这里可以多用cmd启动几个窗口,指定不同的端口号,例如:

dotnet Asa.AspNetCore31WebApiDemo.dll --urls="http://*:5200" --ip="127.0.0.1" --port=5200 --weight=5

dotnet Asa.AspNetCore31WebApiDemo.dll --urls="http://*:5201" --ip="127.0.0.1" --port=5201 --weight=3

dotnet Asa.AspNetCore31WebApiDemo.dll --urls="http://*:5202" --ip="127.0.0.1" --port=5202 --weight=2




最后,运行客户端测试,效果如下:

评价

.netcore 使用缓存

1.Startup.csservices.AddMemoryCache(); services.AddMvc();2.封装Logger类[Route(&quot;api/todo&quot;)] [Authorize] ...

.netcore 使用分布式缓存(docker+redis)

1. 通过docker创建 Redis执行命令:dockerrun--nameasp-redis-p6379:6379-dredis查看redis状态:dockerps可以发现alreadywo...

.netcore3.0 Ocelot 制作简单负载均衡 (一)

开发环境环境版本:vs2019框架版本:.netCore 3.0注意:.netCore 3.1 Ocelot 暂时不支持我这种方式!1.项目创建创建的项目...

.netcore3.1 Abp+vue 实战 (一)

ABP前言一个应用程序框架和起点的ASP.NET MVC web应用程序与最佳实践和最流行的工具!(它是这样说的)1.项目的创建创建项...

.netcore3.1 Consul服务集群

前言Consul是一种服务网络解决方案,可跨任何运行时平台以及公共或私有云连接和保护服务简而言之:集群下载地址环境版本:v...

.netcore3.1 Ocelot 与 Consul 熔断限流与缓存

目录与前言目录链接:.net core Ocelot 简单网关集群熔断架构整合目录基于上一篇文章展开 Ocelot 与 Consul 进行合并创建一...

.netcore 3.x 与 grpc

目录 本篇文章主要讲解如下内容 一。grpc在 .Net Core 3.x 中的应用 二。grpc如何分段上传图片 代码与实现过程比较简单,...

.netcore运用IServiceScope实现全局服务持续化注入

IServiceScope实现全局服务持续化注入[TOC] 创建 GloablePullWorkFlow.cs 类 public class GloablePullWorkFlow { ...

.netcore 通过Flurl验证k8s内部访问与获取环境变量

.netcore验证k8s内部访问与获取环境变量[TOC] 创建k8s-name项目 项目结构如下 实验过程 st=>start: k8s-demo项...

.netcore配置MarkDown的学习之路 (一)

前言MarkDown讲述 (开始使用)现在很多人都自己开发一个博客平台分享技术,老程序员都说干程序员如果没有开发出来一个...

.netcore 通过Flurl验证k8s内部访问与获取环境变量 (二)

.netcore验证k8s内部访问与获取环境变量 (二)[TOC] 修改k8s-name项目 NameController.cs [ApiController] ...

netcore项目时,Debug文件夹下多一个netcoreapp文件夹的解决办法

解决办法:编辑.csproj文件,在 &lt;TargetFramework&gt;netcoreapp2.1&lt;/TargetFramework&gt; 后加入“是否追加目标框...

.netcore 3.1 运用 Json Patch 时的常见问题

.netcore 3.1 运用Json Patch[TOC] JSON Patch是一种使用API显式更新文档的方法。它本身是一种契约,用于描述如何修改文...

.netcore 解析JWT中间Base64报错

.netcore 解析JWT中间Base64报错[TOC] 解析时改为var jsonPayload = Base64UrlEncoder.Decode(basepayload); var claim...
a genius is the person who repeats the most times
排名
35
文章
15
粉丝
5
评论
5
vue.js 学习日记第五章-v-if和v-for指令的使用方式
饰心 : 吃惊!博客更新小王子?
vue.js 学习日记第三章-vue中的简单事件及事件修饰符
修心 : 一个专栏可以的!说一下前端的mvc就更好了
通过PlUpload控件配合WebApi接口完成压缩上传文件
剑轩 : 厉害了,这个很实用。这个可以实现图片压缩?
C#+selenium实现自动登录
剑轩 : 坐标不能是线性的,因为人拖动的时候不是线性的。可以试下这个思路...
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术