tnblog
首页
登录

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

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

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

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

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

  3. 下载好Consul客户端

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

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

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


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

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

using Consul;
using Microsoft.Extensions.Configuration;
using System;

namespace Asa.AspNetCore31WebApiDemo.Utility
{
    /// <summary>
    /// 自己封装的Consul服务注册类
    /// </summary>
    public static class ConsulHelper
    {
        /// <summary>
        /// Consul服务注册
        /// </summary>
        /// <param name="configuration"></param>
        public static void ConsulRegist(this IConfiguration configuration)
        {
            #region 获取Consul客户端

            // 获取Consul客户端:用于将服务注册进Consul
            ConsulClient client = new ConsulClient(c =>
            {
                // Consul客户端地址
                c.Address = new Uri("http://localhost:8500/");
                // 数据中心
                c.Datacenter = "dc1";
            });

            #endregion

            #region 获取命令行参数

            // Program-Main中配置了AddCommandLine就能支持命令行获取
            // 服务实例IP
            string ip = configuration["ip"];
            // 服务实例端口号:命令行参数必须传入
            int port = int.Parse(configuration["port"]);
            // 命令行参数必须传入:权重模式(根据配置的权重来随机调用服务实例)
            int weight = string.IsNullOrWhiteSpace(configuration["weight"]) ? 1 : int.Parse(configuration["weight"]);

            #endregion

            #region 注册服务

            // 注册服务
            client.Agent.ServiceRegister(new AgentServiceRegistration()
            {
                // 在consul中服务的id:唯一的
                ID = "service" + Guid.NewGuid(),
                // 一组服务的组名称
                Name = "AsaUserService",
                // 其实应该写ip地址
                Address = ip,
                // 不同实例:例如(8085/8086)
                Port = port,
                // 标签:权重模式使用
                Tags = new string[] { weight.ToString() },

                #region 健康检查

                // 配置心跳检查的
                Check = new AgentServiceCheck()
                {
                    // 每隔12秒检查一次
                    Interval = TimeSpan.FromSeconds(12),
                    // 心跳检查的IP地址
                    HTTP = $"http://{ip}:{port}/Api/Health/Index",
                    // 超时时间:如果五秒内服务实例没有响应,那么就判定为服务实例已经挂掉
                    // 测试后发现这个设置延迟生效:实际时间不止五秒
                    Timeout = TimeSpan.FromSeconds(5),
                    // 在多久之后去除注册
                    DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5)
                }

                #endregion
            });

            #endregion

            Console.WriteLine($"http://{ip}:{port}完成注册");
        }
    }
}

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

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

using Asa.AspNetCore31.Demo.Utility.WebApiHelper;
using Consul;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Tnblog.MicroService.ClientDemo.Controllers
{
    public class TestController : Controller
    {
        // (随机标识)没考虑溢出问题,到达一定长度应该重置
        private static int iSeed = 0;

        /// <summary>
        /// 模拟客户端调用用户服务
        /// </summary>
        /// <returns></returns>
        public IActionResult Index()
        {
            Users user = new Users();
            string resultUrl = null;

            #region 通过Consul去发现所有服务地址

            {
                // 指定要调用的服务实例:用Consul服务组名称替换IP和端口号:AsaUserService:服务实例注册时指定的组名称
                //string url = "http://localhost:5728/api/user/get";
                string url = "http://AsaUserService/api/user/get";
                Uri uri = new Uri(url);
                // 获取服务实例组名称
                string groupName = uri.Host;
                // 获取Consul客户端
                using (ConsulClient client = new ConsulClient(c =>
                {
                    // Consul地址:8500 端口基于 HTTP 协议,用于 API 接口或 WEB UI 访问
                    c.Address = new Uri("http://localhost:8500/");
                    // 数据中心:默认名称为dc1
                    c.Datacenter = "dc1";
                }))
                {
                    // 获取所有注册的服务实例
                    var dictionary = client.Agent.Services().Result.Response;
                    // 根据组名称筛选服务实例:得到名称为AsaUserService的一组服务
                    var list = dictionary.Where(k => k.Value.Service.Equals(groupName, StringComparison.OrdinalIgnoreCase));
                    KeyValuePair<string, AgentService> keyValuePair = new KeyValuePair<string, AgentService>();

                    #region 负载策略

                    //{
                    //    // 获取第一个服务实例
                    //    keyValuePair = list.First();
                    //    //url = url.Replace(groupName, $"{keyValuePair.Value.Address}:{keyValuePair.Value.Port}");
                    //}

                    //// 随机策略/平均策略
                    //{
                    //    var array = list.ToArray();
                    //    // 随机策略/平均策略
                    //    keyValuePair = array[new Random(iSeed++).Next(0, array.Length)];
                    //}

                    //// 轮巡策略 / 平均策略
                    //{
                    //    var array = list.ToArray();
                    //    keyValuePair = array[iSeed++ % array.Length];// 取余数:就能按照0 1 2 的顺序调用
                    //}

                    // 权重模式
                    {
                        List<KeyValuePair<string, AgentService>> serviceList = new List<KeyValuePair<string, AgentService>>();

                        foreach (KeyValuePair<string, AgentService> agentService in list)
                        {
                            int count = int.Parse(agentService.Value.Tags[0]);
                            for (int i = 0; i < count; i++)
                            {
                                serviceList.Add(agentService);
                            }
                        }

                        keyValuePair = serviceList[new Random(iSeed++).Next(0, serviceList.Count())];
                    }

                    #endregion

                    resultUrl = $"{uri.Scheme}://{keyValuePair.Value.Address}:{keyValuePair.Value.Port}{uri.PathAndQuery}";
                    // 调用服务实例中的接口(WebApiHelperExtend:接口调用拓展类)
                    string result = WebApiHelperExtend.InvokeApi(resultUrl);
                    user = Newtonsoft.Json.JsonConvert.DeserializeObject<Users>(result);
                }
            }

            #endregion

            this.ViewBag.User = user;
            this.ViewBag.Url = resultUrl;

            return View();
        }
    }

    /// <summary>
    /// 用于测试
    /// </summary>
    public class Users
    {
        public int UserID { get; set; }
        public string UserName { get; set; }
        public string Email { get; set; }
    }
}

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

using System;
using System.Net.Http;

namespace Asa.AspNetCore31.Demo.Utility.WebApiHelper
{
    /// <summary>
    /// 接口调用帮助类
    /// </summary>
    public static class WebApiHelperExtend
    {
        /// <summary>
        /// 模拟调用接口
        /// </summary>
        /// <param name="url"></param>
        /// <returns></returns>
        public static string InvokeApi(string url)
        {
            // 提供基类,用于从由URI标识的资源发送HTTP请求和接收HTTP响应。
            using (HttpClient httpClient = new HttpClient())
            {
                // Http请求消息
                HttpRequestMessage message = new HttpRequestMessage();
                // 请求方式
                message.Method = HttpMethod.Get;
                // 请求地址
                message.RequestUri = new Uri(url);
                // 以异步操作的形式发送HTTP请求。并接收响应信息
                var result = httpClient.SendAsync(message).Result;
                // 将HTTP内容序列化为字符串
                string content = result.Content.ReadAsStringAsync().Result;

                return content;
            }
        }
    }
}

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

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<h3>@this.ViewBag.User.UserName</h3>

<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




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

评价
a genius is the person who repeats the most times
排名
6
文章
6
粉丝
16
评论
8
{{item.articleTitle}}
{{item.blogName}} : {{item.content}}
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2020TNBLOG.NET
技术交流:群号677373950
欢迎加群交流技术