在这篇文章中,我们将快速了解什么是服务发现,使用consul实现一个基本的服务基础设施;使用asp.net核心mvc框架,并使用dns client.net实现基于dns的客户端服务发现。

Service Discovery

在现代微服务体系结构中,服务可以在容器中运行,并且可以动态地启动、停止和扩展。这将导致一个非常动态的托管环境,其中可能有数百个实际的端点,无法手动配置或找到正确的端点。

尽管如此,我相信服务发现不仅仅是针对生活在容器中的细粒度微服务。它可以被任何需要访问其他资源的应用程序使用。资源可以是数据库、其他web服务,也可以只是托管在其他地方的网站的一部分。服务发现有助于删除特定于环境的配置文件!

服务发现可以用来解决这个问题,但是和往常一样,有很多不同的方法来实现它

客户端服务发现

一种解决方案是有一个中心服务注册中心,所有服务实例都在这里注册。客户机必须实现逻辑来查询他们需要的服务,最终验证端点是否仍然存在,并可能将请求分发到多个端点。

服务器端/负载平衡

所有流量都通过一个负载平衡器,它知道所有实际的、动态变化的端点,并相应地重定向所有请求

Consul Setup

consul是一个服务注册中心,可用于实现客户端服务发现。

除了使用这种方法的许多优点和特性外,它的缺点是每个客户机应用程序都需要实现一些逻辑来使用这个中央注册表。这种逻辑可能会变得非常具体,因为consun和任何其他技术都有自定义的api和工作原理。

负载平衡也可能不会自动完成。客户机可以查询服务的所有可用/注册的端点,然后决定选择哪个端点。

好消息是consul不仅提供了一个rest api来查询服务注册中心。它还提供返回标准srv和txt记录的dns端点。

DNS终结点确实关心服务运行状况,因为它不会返回不正常的服务实例。它还通过以交替顺序返回记录来实现负载平衡!此外,它可能会赋予服务更高的优先级,使其更接近客户端。

consul是hashicorp开发的一个软件,它不仅进行服务发现(如上所述),还进行“健康检查”,并提供一个分布式的“密钥值存储”。

consul应该在一个集群中运行,其中至少有三个实例处理集群和宿主环境中每个节点上的“代理”的协调。应用程序总是只与本地代理通信,这使得通信速度非常快,并将网络延迟降至最低。

不过,对于本地开发,您可以在--dev模式下运行consul,而不是设置完整的集群。但请记住,为了生产使用,需要做一些工作来正确设置consul。

Download and Run Consul

下载地址:https://www.consul.io/downloads.html

用代理——dev参数运行consul。这将在本地服务模式下引导consul,无需配置文件,并且只能在本地主机上访问。

http://localhost:8500,打开consul ui。

下面开始

目标

通过appsettings.json配置服务名

主机和端口不应硬编码

使用microsoft.extensions.configuration和options正确配置所有需要的内容

将注册设置为启动管道的一部分

集成Identity Server4到Identity api

添加Ocelot网关并集成identity server4认证

.Ocelot集成Consul服务发现

新建项目User.Api

添加UserController

[Route("api/[controller]")]
[ApiController]
public class UserController : BaseController
{
private readonly UserDbContext _userContext;
private readonly ILogger<UserController> _logger;
public UserController(UserDbContext userContext, ILogger<UserController> logger)
{
_userContext = userContext;
_logger = logger; }
/// <summary>
/// 检查或者创建用户 但其那手机号码不存在的时候创建
/// </summary>
/// <returns></returns>
[HttpPost("check-or-create")]
public async Task<ActionResult> CheckOrCreate([FromForm]string phone)
{ var user = await _userContext.Users.SingleOrDefaultAsync(s => s.Phone == phone);
if (user == null)
{
user = new Users() { Phone = phone };
await _userContext.Users.AddAsync(user);
await _userContext.SaveChangesAsync();
}
return Ok(user.Id); } [HttpGet]
public async Task<IActionResult> Get()
{
var user = await _userContext.Users.AsNoTracking().
Include(u => u.Properties).
SingleOrDefaultAsync(s => s.Id == UserIdentity.UserId); if (user == null)
throw new UserOperationException($"错误的用户上下文id:{UserIdentity.UserId}");
return Json(user);
} [HttpPatch]
public async Task<IActionResult> Patch(JsonPatchDocument<Users> patch)
{
var user = await _userContext.Users.SingleOrDefaultAsync(u => u.Id == UserIdentity.UserId);
if (user == null)
throw new UserOperationException($"错误的用户上下文id:{UserIdentity.UserId}"); patch.ApplyTo(user); var originProperties = await _userContext.UserProperty.AsNoTracking().Where(s => s.UserId == user.Id).ToListAsync();
var allProperties = originProperties.Distinct();
if (user.Properties != null)
{
allProperties = originProperties.Union(user.Properties).Distinct();
} var removeProperties = allProperties;
var newProperties = allProperties.Except(originProperties);
if (removeProperties != null)
{
_userContext.UserProperty.RemoveRange(removeProperties);
}
if (newProperties != null)
{
foreach (var item in newProperties)
{
item.UserId = user.Id;
}
await _userContext.AddRangeAsync(newProperties);
}
_userContext.Users.Update(user);
await _userContext.SaveChangesAsync();
return Json(user);
} }

  appsettings.json 添加下面配置

"ServiceDiscovery": {
"ServiceName": "userapi",
"Consul": {
"HttpEndpoint": "http://127.0.0.1:8500",
"DnsEndpoint": {
"Address": "127.0.0.1",
"Port": 8600
}
}

  添加poco,映射类

public class ServiceDisvoveryOptions
{
public string ServiceName { get; set; } public ConsulOptions Consul { get; set; }
} public class ConsulOptions
{
public string HttpEndpoint { get; set; } public DnsEndpoint DnsEndpoint { get; set; }
} public class DnsEndpoint
{
public string Address { get; set; } public int Port { get; set; } public IPEndPoint ToIPEndPoint()
{
return new IPEndPoint(IPAddress.Parse(Address), Port);
}
}

  然后在Startup对其进行配置。ConfigureServices

services.Configure<ServiceDisvoveryOptions>(Configuration.GetSection("ServiceDiscovery"));

//使用此配置设置consul客户端:

services.AddSingleton<IConsulClient>(p => new ConsulClient(cfg =>
{
var serviceConfiguration = p.GetRequiredService<IOptions<ServiceDisvoveryOptions>>().Value;

if (!string.IsNullOrEmpty(serviceConfiguration.Consul.HttpEndpoint))
{
// 如果未配置,客户端将使用默认值“127.0.0.1:8500”
cfg.Address = new Uri(serviceConfiguration.Consul.HttpEndpoint);
}
}));

//consulclient不一定需要配置,如果没有指定任何内容,它将返回到默认值(localhost:8500)。

动态服务注册

只要kestrel用于在某个端口上托管服务,app.properties[“server.features”]就可以用来确定服务的托管位置。如上所述,如果使用了iis集成或任何其他反向代理,则此解决方案将不再工作,并且必须使用服务可访问的实际端点在consul中注册服务。但在启动过程中无法获取这些信息。

如果要将IIS集成与服务发现结合使用,请不要使用以下代码。相反,可以通过配置配置端点,或者手动注册服务。

无论如何,对于红隼,我们可以执行以下操作:获取承载服务的uri红隼(这不适用于useurls(“*:5000”)之类的通配符,然后遍历地址以在consul中注册所有地址:这里默认使用   UseUrls("http://localhost:92")

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime applicationLifetime,
IOptions<ServiceDisvoveryOptions> serviceOptions, IConsulClient consul)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}

app.UseSwagger();
//启用中间件服务对swagger-ui,指定Swagger JSON终结点
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
//app.UseHttpsRedirection();

//启动的时候注册服务
applicationLifetime.ApplicationStarted.Register(() =>
{
RegisterService(app, serviceOptions, consul);
});

//停止的时候移除服务
applicationLifetime.ApplicationStopped.Register(() =>
{
RegisterService(app, serviceOptions, consul);
});
app.UseMvc();

UserContextSeed.SeedAsync(app, loggerFactory).Wait();
// InitDataBase(app);

}

private void RegisterService(IApplicationBuilder app, IOptions<ServiceDisvoveryOptions> serviceOptions, IConsulClient consul)
{
var features = app.Properties["server.Features"] as FeatureCollection;
var addresses = features.Get<IServerAddressesFeature>()
.Addresses
.Select(p => new Uri(p));

foreach (var address in addresses)
{
var serviceId = $"{serviceOptions.Value.ServiceName}_{address.Host}:{address.Port}";

//serviceid必须是唯一的,以便以后再次找到服务的特定实例,以便取消注册。这里使用主机和端口以及实际的服务名

var httpCheck = new AgentServiceCheck()
{
DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(1),
Interval = TimeSpan.FromSeconds(30),
HTTP = new Uri(address, "HealthCheck").OriginalString
};

var registration = new AgentServiceRegistration()
{
Checks = new[] { httpCheck },
Address =address.Host,
ID = serviceId,
Name = serviceOptions.Value.ServiceName,
Port = address.Port
};
consul.Agent.ServiceRegister(registration).GetAwaiter().GetResult();
}
}

private void DeRegisterSWervice(IApplicationBuilder app, IOptions<ServiceDisvoveryOptions> serviceOptions, IConsulClient consul)
{
var features = app.Properties["server.Features"] as FeatureCollection;
var addresses = features.Get<IServerAddressesFeature>().
Addresses.Select(p => new Uri(p));
foreach (var address in addresses)
{

var serviceId = $"{serviceOptions.Value.ServiceName}_{address.Host}:{address.Port}";
consul.Agent.ServiceDeregister(serviceId).GetAwaiter().GetResult();
}
}

添加健康检查接口

[Route("HealthCheck")]
[ApiController]
public class HealthCheckController : ControllerBase
{ [HttpGet]
[HttpHead]
public IActionResult Get()
{
return Ok();
}
}

  

添加项目Cateway.Api:添加端口为91

添加Ocelot.json

{
"ReRoutes": [
{
//暴露出去的地址
"UpstreamPathTemplate": "/{controller}",
"UpstreamHttpMethod": [ "Get" ], //转发到下面这个地址
"DownstreamPathTemplate": "/api/{controller}",
"DownstreamScheme": "http",
//资源服务器列表
"DownstreamHostAndPorts": [
{
"host": "localhost",
"port": 92
}
],
"AuthenticationOptions": {
"AuthenticationProviderKey": "finbook",
"AllowedScopes": []
}
},
{
//暴露出去的地址
"UpstreamPathTemplate": "/connect/token",
"UpstreamHttpMethod": [ "Post" ], //转发到下面这个地址
"DownstreamPathTemplate": "/connect/token",
"DownstreamScheme": "http",
//资源服务器列表
"DownstreamHostAndPorts": [
{
"host": "localhost",
"port": 93
}
] }
],
//对外暴露的访问地址 也就是Ocelot所在的服务器地址
"GlobalConfiguration": {
"BaseUrl": "http://localhost:91"
}
}

  program 修改

 public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args).
ConfigureAppConfiguration((hostingContext, builder) =>
{
builder
.SetBasePath(hostingContext.HostingEnvironment.ContentRootPath)
.AddJsonFile("Ocelot.json");
})
.UseUrls("http://+:91")
.UseStartup<Startup>();
}

  Startup

 public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
var authenticationProviderKey = "finbook";
services.AddAuthentication(). AddIdentityServerAuthentication(authenticationProviderKey, options =>
{
options.Authority = "http://localhost:93";//.Identity服务 配置
options.ApiName = "gateway_api";
options.SupportedTokens = SupportedTokens.Both;
options.ApiSecret = "secret";
options.RequireHttpsMetadata = false;
});
services.AddOcelot();
} // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseOcelot().Wait();
app.UseMvc(); }
}

  添加项目User.Identity idneity代码省略

使用dnsclient

appsettings.json 添加配置

"ServiceDiscovery": {
"UserServiceName": "userapi",
"Consul": {
"HttpEndpoint": "http://127.0.0.1:8500",
"DnsEndpoint": {
"Address": "127.0.0.1",
"Port": 8600
}
}

  注册dnslookup客户端:

          services.Configure<ServiceDisvoveryOptions>(Configuration.GetSection("ServiceDiscovery"));
//di中注册dns lookup客户端
services.AddSingleton<IDnsQuery>(p =>
{
var serviceConfiguration = p.GetRequiredService<IOptions<ServiceDisvoveryOptions>>().Value;
return new LookupClient(serviceConfiguration.Consul.DnsEndpoint.ToIPEndPoint());
});

  

private readonly IDnsQuery _dns;
private readonly IOptions<ServiceDisvoveryOptions> _options; public SomeController(IDnsQuery dns, IOptions<ServiceDisvoveryOptions> options)
{
_dns = dns ?? throw new ArgumentNullException(nameof(dns));
_options = options ?? throw new ArgumentNullException(nameof(options));
} [HttpGet("")]
[HttpHead("")]
public async Task<IActionResult> DoSomething()
{
var result = await _dns.ResolveServiceAsync("service.consul", _options.Value.ServiceName);
...
}

  

dnsclient.net的resolveserviceasync执行dns srv查找,匹配cname记录,并为每个包含主机名和端口(以及地址(如果使用)的条目返回一个对象。

现在,我们可以用一个简单的httpclient调用(或生成的客户端)来调用服务:

var address = result.First().AddressList.FirstOrDefault();
var port = result.First().Port; using (var client = new HttpClient())
{
var serviceResult = await client.GetStringAsync($"http://{address}:{port}/Values");
}

  启动项目

打开http://localhost:8500,查看服务运行

测试identity 服务

请求userapi -api/user

git:https://gitee.com/LIAOKUI/user.api

参考:http://michaco.net/blog/ServiceDiscoveryAndHealthChecksInAspNetCoreWithConsul?tag=ASP.NET%20Core#service-discovery

consul在netcore中发现服务和运行状况检查的更多相关文章

  1. ASP.NET Core中的运行状况检查

    由卢克·莱瑟姆和格伦Condron ASP.NET Core提供了运行状况检查中间件和库,用于报告应用程序基础结构组件的运行状况. 运行状况检查由应用程序公开为HTTP终结点.可以为各种实时监视方案配 ...

  2. 微服务中的健康监测以及其在ASP.NET Core服务中实现运行状况检查

    1 .什么是健康检查? 健康检查几乎就是名称暗示的.它是一种检查您的应用程序是否健康的方法.随着越来越多的应用程序转向微服务式架构,健康检查变得尤其重要(Health Check).虽然微服务架构有很 ...

  3. .netcore 以widnows服务方式运行

    应用需要 Microsoft.AspNetCore.Hosting.WindowsServices 的包引用. 生成主机时会调用 IHostBuilder.UseWindowsService. 若应用 ...

  4. Consul+Ocelot+Polly在.NetCore中使用(.NET5)-Consul服务注册,服务发现

    环境 .NET5,Consul_v1.10.2 一.简介 在微服务中利用Consul可以实现服务的注册,服务发现,治理,健康检查等. Web调站点需要调用多个服务,如果没有Consul,可能就是Web ...

  5. 在Windows环境中使用Nginx, Consul, Consul Template搭建负载均衡和服务发现服务

    搭建负载均衡和服务发现服务的目的 随着网站业务的不断提升,单个服务器的性能越来越难满足客户的业务需求,所以很多情况下,需要使用多服务器实例和负载均衡器来满足业务需要. Nginx 什么是Nginx N ...

  6. Consul+Ocelot+Polly在.NetCore中使用(.NET5)-Ocelot+Polly缓存、限流、熔断、降级

    相关文章 Consul+Ocelot+Polly在.NetCore中使用(.NET5)-Consul服务注册,服务发现 Consul+Ocelot+Polly在.NetCore中使用(.NET5)-网 ...

  7. 使用docker+consul+nginx集成分布式的服务发现与注册架构

    一.环境说明: 1.一台虚拟机,该系统已经装好了docker: ip 192.168.10.224 虚拟网卡,与主机互通 操作系统rhel6 内核 2.6.32  64位 docker版本 1.7.1 ...

  8. Chris Richardson微服务翻译:微服务架构中的服务发现

    Chris Richardson 微服务系列翻译全7篇链接: 微服务介绍 构建微服务之使用API网关 构建微服务之微服务架构的进程通讯 微服务架构中的服务发现(本文) 微服务之事件驱动的数据管理 微服 ...

  9. Ocelot + Consul + Registrator 基于Docker 实现服务发现、服务自动注册

    目录 1. Consul集群搭建 1.1 F&Q Consul官方推荐的host网络模式运行 2. Registrator服务注册工具 2.1 F&Q Registrator悬挂服务 ...

  10. 实战中的asp.net core结合Consul集群&Docker实现服务治理

    0.目录 整体架构目录:ASP.NET Core分布式项目实战-目录 一.前言 在写这篇文章之前,我看了很多关于consul的服务治理,但发现基本上都是直接在powershell或者以命令工具的方式在 ...

随机推荐

  1. Angular CLI 源码分析

    准备: 安装 Node.js https://nodejs.org/: 安装 VS Code https://code.visualstudio.com/: 创建文件夹 angular-cli-sou ...

  2. C#/.NET/.NET Core优秀项目和框架2025年2月简报

    前言 公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的详细介绍.功能特点.使用方式以及部分功能 ...

  3. WSL2 - Ubuntu 22.04使用记录

    1 安装 搭配Windows Terminal使用为佳,在微软商店可下载: 然后依照官网描述即可. 命令行中运行wsl --install即可.不过由于想自行指定发行版,于是: wsl --list ...

  4. FastAPI 核心机制:分页参数的实现与最佳实践

    title: FastAPI 核心机制:分页参数的实现与最佳实践 date: 2025/3/13 updated: 2025/3/13 author: cmdragon excerpt: 在构建现代W ...

  5. 最新版 Proteus 8.15 Professional 图文安装教程(附安装包)

    前言 大家好,我是梁国庆. Proteus 是世界上唯一将电路仿真软件.PCB设计软件和虚拟模型仿真软件三合一的设计平台. 本篇博主将手把手带领大家安装最新版 Proteus 8.15. 若图片加载超 ...

  6. docker Get "https://registry-1.docker.io/v2/": x509: certificate is valid for

    前言 docker 在进行 build 时,报错:Get "https://registry-1.docker.io/v2/": x509: certificate is vali ...

  7. 搭建自己的OCR服务,第一步:选择合适的开源OCR项目

    一.OCR是什么? 光学字符识别(Optical Character Recognition, OCR)是指对文本资料的图像文件进行分析识别处理,获取文字及版面信息的过程. 亦即将图像中的文字进行识别 ...

  8. 证件用蓝底、红底、白底照片RGB颜色值

    蓝色: R:67 G:142 B:219: 红色: R:255 G:0 B:0: 白色: R:255 G:255 B:255. * PS:仅供参考,如有特殊要求,请按照相应要求调整色值.

  9. 【Spring】事务操作

    事务概念 1.什么事务 事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败 典型场景:银行转账 lucy 转账 100 元 给 mary lucy 少 100,mar ...

  10. Electron 开发:获取当前客户端 IP

    Electron 开发:获取当前客户端 IP 一.背景与需求 1. 项目背景 客户端会自启动一个服务,Web/后端服务通过 IP + port 请求以操作客户端接口 2. 初始方案与问题 2.1. 初 ...