分类:
工具
今天,总结一下之前学习的Consul基础(后续如果有时间的话,再加上Ocelot部分):
新建一个WebApi项目:模拟服务


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

下载好Consul客户端

准备工作做好了,开始撸码
首先,在两个项目中分别引入Consul包

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

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; } } }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

启动后在浏览器使用以下地址打开:
效果如下:这时只有一个默认的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




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



50010702506256