目录
什么是服务发现
服务发现的两种方式
1.客户端发现
2.服务端发现
代码演示
手动实现服务发现
使用Consul进行服务发现
使用Consul进行服务发现 -BlockingQueries
网关Ocelot
网关Ocelot+服务发现Consul
什么是服务发现
在传统的系统部署中,服务运行在一个固定的已知的 IP 和端口上,如果一个服务需要调用另外一个服务,可以通过地址直接调用,但是,在虚拟化或容器话的环境中,服务实例的启动和销毁是很频繁的,服务地址在动态的变化,如果需要将请求发送到动态变化的服务实例上,至少需要两个步骤:
服务注册 — 存储服务的主机和端口信息
服务发现 — 允许其他用户发现服务注册阶段存储的信息
服务发现的主要优点是可以无需了解架构的部署拓扑环境,只通过服务的名字就能够使用服务,提供了一种服务发布与查找的协调机制。服务发现除了提供服务注册、目录和查找三大关键特性,还需要能够提供健康监控、多种查询、实时更新和高可用性等。
服务发现的两种方式
1.客户端发现
在使用客户端发现方式时,客户端通过查询服务注册中心,获取可用的服务的实际网络地址(IP 和端口)。然后通过负载均衡算法来选择一个可用的服务实例,并将请求发送至该服务。
优点:架构简单,扩展灵活,方便实现负载均衡功能。
缺点:强耦合,有一定开发成本。
2.服务端发现
客户端向load balancer 发送请求。load balancer 查询服务注册中心找到可用的服务,然后转发请求到该服务上。和客户端发现一样,服务都要到注册中心进行服务注册和注销。 优点:服务的发现逻辑对客户端是透明的。 缺点:需要额外部署和维护高可用的负载均衡器。
以上都是网络上可以搜索到的一些概念具体可以看一下,下面的链接
https://segmentfault.com/a/1190000004960668
下面我们假设一个业务环境,我们开发了一个库存的模块作为服务,然后有个盘点的界面会调用库存的服务,还有其他的模块,比如下单会间接的调用库存服务里的一些方法(我们假设调用模式是以api的形式调用),那么我们得到如下的一张库存服务的调用关系图:
大家注意一下:网关可以看做是一个负载均衡器,服务注册中心会返回服务的所有健康的真实的机器地址,然后网关就根据算法从对应服务下的n个地址中拿到一个真实地址进行链路的转发(客户端访问的是网关,他并不知道具体是访问了哪个api站点,网关会链路转发并且将http response返回给客户端):
举个例子:客户端访问一个请求 http://192.168.0.1:8090/StoreAPI/api/Store/Add (网关转发的配置 /StoreAPI/{url} 指向 StoreService下所有的服务地址)
我们的库存API服务下有个新增库存的接口地址为: http://192.168.10:8091/api/Store/Add , http://192.168.11:8091/api/Store/Add , http://192.168.12:8091/api/Store/Add 三个服务都是健康可用的 那么网关就挑里面的一个访问,并且返回结果给客户端。
如果 此时第三台服务器宕机了,那么服务注册中心获取不到心跳,就会把 192.168.0.12这台机器从可用服务列表中移除,网关就从前两个地址中挑一个访问。
下面我们通过代码更直观的循序渐进的理解一下:
代码演示
手动实现服务发现
我们先看一下项目目录结构:
ServiceAPI项目中建一个ServiceController.cs 里面做一个最简单的方法输出配置文件的 Msg,代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
namespace ServiceAPI.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class ServiceController : ControllerBase
{
public IConfiguration Configuration { get; }
public ServiceController(IConfiguration configuration)
{
Configuration = configuration;
}
[HttpGet]
public ActionResult<string> Show()
{
return Configuration["Msg"];
}
}
}
然后我们在IIS下部署两个相同的站点,修改配置文件中的Msg值分别为api1和api2
服务端部署好了 我们开发一下客户端我们用asp.net core mvc项目代替,WebClient项目中我们修改 首页中的OnGet方法,让他输出 当前时间+ 服务端api/Service/Show 中返回的Msg值
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Consul;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace WebClient.Pages
{
public class IndexModel : PageModel
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IConfiguration _configuration;
public IndexModel(IHttpClientFactory httpClientFactory, IConfiguration configuration)
{
_httpClientFactory = httpClientFactory;
_configuration = configuration;
}
public string Message { get; private set; }
public void OnGet()
{
Message = $"{DateTime.Now} {GetService()}";
}
/// <summary>
/// 客户端 手动发现
/// </summary>
/// <returns></returns>
public string GetService()
{
try
{
string[] serviceUrls = { "http://localhost:9050", "http://localhost:9051" };
string serviceUrl = serviceUrls[new Random().Next(0, 2)];
var client = _httpClientFactory.CreateClient();
client.BaseAddress = new Uri(serviceUrl + "/api/Service/Show");
return client.GetStringAsync(serviceUrl + "/api/Service/Show").Result;
}
catch
{
return "bad service";
}
}
}
}
我们新建了一个变量 string[] serviceUrls 保存了 "http://localhost:9050", "http://localhost:9051" 两个地址,并且用了随机访问的模式随机一个地址进行访问:
我们看一下运行效果:
启动后访问到 http://localhost:9050/api/Service/Show 输出 api1
再刷新一下,访问到 http://localhost:9051/api/Service/Show 输出 api2
此时我们把api2的服务停一下,然后多刷新几次你会发现有时候会出现以下情况:
随机访问到了 被我们关闭的api站点,所以返回bad service了,原因是我们手写的 变量 string[] serviceUrls 中没有移除关闭的站点地址。
那真实业务中当一个机器宕机的时候我们不应该再去访问这个服务,那么怎么办呢?就有了我们下面的改进
使用Consul进行服务发现
为了让各种原因导致的不可用的站点地址从 string[] serviceUrls 移除走,我们就需要实时去监控serviceUrls里面的所有站点是否健康。
首先我们要在api中添加一个 健康检查 api,注册中心会调用这个api检查站点是否可用,可能1s监测一次,也可能n秒去监测一次
然后当注册中心发现这个心跳 好几次都不通了,那么就要标记一下这个站点地址不健康了, 客户端通过注册中心查询服务地址列表时只会拿到健康的服务地址,那么自然就访问不到那个宕机的api了。
最后要注意了,当这个宕机的api修复后,服务注册中心又能监听到这个api的心跳了,又会再标记一下这个服务也是健康的并且返回给客户端。
这时候如果还是通过手撸代码的模式 实现服务注册中心的功能,已经有一些难度了,有兴趣的同学可以自己去实现一下,我们这里引用第三方工具 Consul 来实现。
我们先介绍一下 Consul 需要安装部署一些什么 并且consul做了些什么事情:
官方文档 https://www.consul.io/
我还找一篇博客 https://www.jianshu.com/p/32dc52f28a14
首先我们在服务器上安装一下 consul服务端,consul客户端的实现是写在netcore的客户端和 服务端里的
注册大致流程图如下:
当然我们也可以 不使用 Consul NuGet包 进行向Consul服务端的注册,只要服务器上启用了Consul服务端,我们可以用api去直接访问操作服务端,只不过第三方包已经帮我们封装好了直接调用就可以了。
接下来我们上代码,改造一下 API服务的代码,再startup中 向consul服务端注册,并且关闭程序的时候移除注册信息。
ServiceAPI 项目中 Startup代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
namespace ServiceAPI
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,IHostApplicationLifetime lifetime)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
#region 注册服务
var consulClient = new ConsulClient(c =>
{
c.Address = new Uri(Configuration["ConsulSetting:ConsulAddress"]);
});
var registration = new AgentServiceRegistration()
{
ID = Guid.NewGuid().ToString(),//服务实例唯一标识
Name = Configuration["ConsulSetting:ServiceName"],//服务名
Address = Configuration["ConsulSetting:ServiceIP"], //服务IP
Port = int.Parse(Configuration["ConsulSetting:ServicePort"]),//服务端口
Check = new AgentServiceCheck()
{
DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔
HTTP = $"http://{Configuration["ConsulSetting:ServiceIP"]}:{Configuration["ConsulSetting:ServicePort"]}{Configuration["ConsulSetting:ServiceHealthCheck"]}",//健康检查地址
Timeout = TimeSpan.FromSeconds(5)//超时时间
}
};
//服务注册
consulClient.Agent.ServiceRegister(registration).Wait();
//应用程序终止时,取消注册
lifetime.ApplicationStopping.Register(() =>
{
consulClient.Agent.ServiceDeregister(registration.ID).Wait();
});
#endregion
}
}
}
配置文件 appsetting.json 如下:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"Msg": "{msg}",
"ConsulSetting": {
"ServiceName": "APIService",
"ServiceIP": "127.0.0.1",
"ServicePort": "9050",
"ServiceHealthCheck": "/api/ApiMonitor/HealthCheck",
"ConsulAddress": "http://localhost:8500"
}
}
我们重新发布一下两个api站点,然后启用看一下Consul的UI界面。
我推荐启用命令 使用: consul.exe agent - dev 后面再跟上 -client 0.0.0.0 -ui 这样可以开启ip地址的访问 否则只能通过localhost:8500进行访问
咦,怎么api没有注册进来,大家不要担心,代码没有问题只不过 .netcore 在IIS中发布的时候,走的是进程外挂载,只有第一个请求到达的时候 api站点程序才会运行起来,大家看一下这个博客跟着设置一下,设置完之后只要发布完api 注册中心就会有 注册进去的服务了
https://www.cnblogs.com/lxz-blog/p/12870950.html
正常的Consul UI界面如下:
WebClient的客户端我们也需要做一些修改,我们不在是在变量中随机获取服务的地址,而是从Consul服务端中找到 注册服务的地址列表:
WebClient项目的 Index.cshtml.cs 改动如下:
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Consul;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace WebClient.Pages
{
public class IndexModel : PageModel
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IConfiguration _configuration;
private readonly ConsulClient _consulClient;
public IndexModel(IHttpClientFactory httpClientFactory, IConfiguration configuration)
{
_httpClientFactory = httpClientFactory;
_configuration = configuration;
#region
_consulClient = new ConsulClient(c =>
{
//consul地址
c.Address = new Uri(_configuration["ConsulSetting:ConsulAddress"]);
});
#endregion
}
public string Message { get; private set; }
public void OnGet()
{
#region 通过consul发现服务
Message = $"{DateTime.Now} {GetServiceByConsul()}";
#endregion
}
/// <summary>
/// 使用Consul实现服务发现
/// </summary>
/// <returns></returns>
public string GetServiceByConsul()
{
var consulClient = new ConsulClient(c =>
{
//consul地址
c.Address = new Uri(_configuration["ConsulSetting:ConsulAddress"]);
});
var services = consulClient.Health.Service("APIService", null, true, null).Result.Response;//健康的服务
string[] serviceUrls = services.Select(p => $"http://{p.Service.Address + ":" + p.Service.Port}").ToArray();//服务地址列表
if (!serviceUrls.Any())
{
return "服务列表为空!" ;
}
//每次随机访问一个服务实例
string serviceUrl = serviceUrls[new Random().Next(0, serviceUrls.Length)];
var client = _httpClientFactory.CreateClient();
client.BaseAddress = new Uri(serviceUrl + "/api/Service/Show");
return client.GetStringAsync(serviceUrl + "/api/Service/Show").Result;
}
}
}
配置文件如下:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConsulSetting": {
"ConsulAddress": "http://localhost:8500"
}
}
这时候就完美的解决了 ,不访问不健康的api了。
大家有没有注意到一点,其实这样写法效率上还有点问题,当客户端需要调用 APIService这个服务名称下的服务地址的时候,每次都要去 Consul服务端获取一遍健康的api地址,那应该怎么去优化一下呢?我们看一下一章
使用Consul进行服务发现 -BlockingQueries
大家看一下consul官网对BlockingQueries的解释,其实就是用一个版本号去控制,我们获取Consul服务列表的同时获取一个版本号存于程序本地,当服务列表内的地址有所改变后会有个新的版本号,此时在调用服务发现查询列表时两个版本对不上才去Consul服务端在请求一次服务列表的查询(这个版本号的用法 大家有没有觉得很像 数据库里处理并发的乐观锁)
那么我们在 WebClient项目的 Startup中加入一端程序去维护服务列表并落地,当版本发生改变的时候维护这个落地数据,这样调用的时候 落地的服务列表永远就是最新的健康的服务列表:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace WebClient
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient();
services.AddRazorPages();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
#region
Task.Run(() =>
{
ConsulClient _consulClient = new ConsulClient(c =>
{
//consul地址
c.Address = new Uri(Configuration["ConsulSetting:ConsulAddress"]);
});
//WaitTime默认为5分钟
var queryOptions = new QueryOptions { WaitTime = TimeSpan.FromMinutes(10) };
while (true)
{
var res = _consulClient.Health.Service("APIService", null, true, queryOptions).Result;
//版本号不一致 说明服务列表发生了变化
if (queryOptions.WaitIndex != res.LastIndex)
{
//控制台打印一下获取服务列表的响应时间等信息
Console.WriteLine($"{DateTime.Now}获取 APIService :queryOptions.WaitIndex:{queryOptions.WaitIndex} LastIndex:{res.LastIndex}");
queryOptions.WaitIndex = res.LastIndex;
//控制台打印一下获取服务列表的响应时间等信息
Console.WriteLine($"{DateTime.Now}更新 APIService :queryOptions.WaitIndex:{queryOptions.WaitIndex} LastIndex:{res.LastIndex}");
//服务地址列表
var serviceUrls = res.Response.Select(p => $"http://{p.Service.Address + ":" + p.Service.Port}").ToList();
ServicesList.Urls = serviceUrls;
}
}
});
#endregion
}
}
}
WebClient项目的 Index.cshtml.cs 改动如下:
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Consul;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace WebClient.Pages
{
public class IndexModel : PageModel
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IConfiguration _configuration;
public IndexModel(IHttpClientFactory httpClientFactory, IConfiguration configuration)
{
_httpClientFactory = httpClientFactory;
_configuration = configuration;
}
public string Message { get; private set; }
public void OnGet()
{
Message = $"{DateTime.Now} {GetServiceByConsulBlockingQueries()}";
}
/// <summary>
/// 客户端 Consul发现 优化
/// </summary>
/// <returns></returns>
public string GetServiceByConsulBlockingQueries()
{
var serviceUrls = ServicesList.Urls;
if (!serviceUrls.Any())
{
return "服务列表为空!";
}
//每次随机访问一个服务实例
string serviceUrl = serviceUrls[new Random().Next(0, serviceUrls.Count)];
var client = _httpClientFactory.CreateClient();
client.BaseAddress = new Uri(serviceUrl + "/api/Service/Show");
return client.GetStringAsync(serviceUrl + "/api/Service/Show").Result;
}
}
}
现在看看最新的代码 越来越简单,并且执行效率也快了。但是有没有发现还有一个美中不足的地方就是
//每次随机访问一个服务实例
string serviceUrl = serviceUrls[new Random().Next(0, serviceUrls.Count)];
我们先在用的时客户端自己去实现 负载均衡寻找一个服务地址,现在的客户端时asp.net core mvc 可能还有客户端是 ios程序,也可能是web程序,那开发的成本就有点高了,这时候我们引入网关,把负载均衡这块的功能移除到客户端外,客户端访问网关,网关再去和服务注册中心进行交互,就有了我们下面的改动。
网关Ocelot
大家再拉到文章的开始 看一下那张 库存服务的调用关系图。是不是就豁然开朗了,其实现在要写的代码才是最终解决方案,之前的代码只是告诉大家最后的方案是怎么演变过来的。
对于网关 我们选用netcore下的第三方开源包 Ocelot,功能其实和Nginx差不多,只是我觉得Ocelot是一款更适合编程的网关,Nginx如果需要加一些定制化的东西进去开发成本会比较高。
如果有同学还不知道网关,不知道Nginx的话,可以自行网上搜索一下。
这个时候 我们demo的目录结构发生了变化,新加了一个网关的项目:
我们先做一个网关手动指定的服务ip地址的例子:
Ocelot.json 内容如下:
{
"Routes": [
{
"DownstreamPathTemplate": "/{url}",
"DownstreamScheme": "http",
"DownstreamHostAndPorts": [
{
"Host": "localhost",
"Port": 9050
},
{
"Host": "localhost",
"Port": 9051
}
],
"UpstreamPathTemplate": "/GateWay/{url}",
"UpstreamHttpMethod": [
"Get", "Post", "Put", "Delete"
],
"LoadBalancerOptions": {
"Type": "RoundRobin"
}
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:9070"
}
}
大致的意思是 上游访问网关的时候根据 /GateWay/{url} 进行分发 ,将请求分发到 localhost:9050/{url} 或者 localhost:9051/{url}下,请求的类型限定为 Get,Post,Put和 Delete,负载均衡的分发策略为:RoundRobin(轮询)
然后网关的 Startup.cs 注册一下 ocelot中间件 并且使用一下ocelot管道即可代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Provider.Consul;
namespace ApiGateWay
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddOcelot(new ConfigurationBuilder()
.AddJsonFile("Ocelot.json")
.Build());//注意!!
//services.AddOcelot(new ConfigurationBuilder()
//.AddJsonFile("Ocelot.json")
//.Build()).AddConsul();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseOcelot().Wait();//注意!!
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "api/{controller}/{action}");
});
}
}
}
我们发布一下网关项目:
再修改一下 WebClient项目,代码变得更为精简,只需要访问网关9070地址:
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Consul;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace WebClient.Pages
{
public class IndexModel : PageModel
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IConfiguration _configuration;
public IndexModel(IHttpClientFactory httpClientFactory, IConfiguration configuration)
{
_httpClientFactory = httpClientFactory;
_configuration = configuration;
}
public string Message { get; private set; }
public void OnGet()
{
Message = $"{DateTime.Now} {GetServiceByOcelot()}";
}
/// <summary>
/// 网关
/// </summary>
/// <returns></returns>
public string GetServiceByOcelot()
{
try
{
var gatewayUrl = "http://localhost:9070";
var client = _httpClientFactory.CreateClient();
client.BaseAddress = new Uri(gatewayUrl + "/GateWay/api/Service/Show");
return client.GetStringAsync(gatewayUrl + "/GateWay/api/Service/Show").Result;
}
catch
{
return "bad service";
}
}
}
}
马上就要大功告成了,我们只需要在网关中集成Consul服务发现,就能去除掉刚才网关配置文件中那些服务器地址的硬编码了。
网关Ocelot+服务发现Consul
这里已经没有什么代码需要改了,netcore这个框架 包括Ocelot这个网关,我是真心觉得很方便,我们改一下网关Ocelot的配置文件:
{
"Routes": [
{
"DownstreamPathTemplate": "/{url}",
"DownstreamScheme": "http",
"ServiceName": "APIService",
"UpstreamPathTemplate": "/GateWay/{url}",
"UpstreamHttpMethod": [
"Get",
"Post",
"Put",
"Delete"
],
"LoadBalancerOptions": {
"Type": "RoundRobin"
}
}
],
"GlobalConfiguration": {
"BaseUrl": "http://localhost:9070",
"ServiceDiscoveryProvider": {
"Scheme": "http",
"Host": "localhost",
"Port": 8500,
"Type": "Consul"
}
}
}
已经没有了服务的硬编码 ip地址,剩下的只是 Consul中服务注册的名字 APIService。
网关项目中我们引入一下 Ocelot.Provider.Consul 包
Startup文件中我们 在注册网关的代码后再顺便 添加一下Consul这个服务发现。
services.AddOcelot(new ConfigurationBuilder()
.AddJsonFile("Ocelot.json")
.Build()).AddConsul();
今天分享的内容全部结束了
示例代码已上传github https://github.com/Benjamin0626/ServiceDiscoveryDemo
但是如果要做好分布式的架构,我们需要考虑的因素其实还有很多,比如Consul的集群,服务的治理,服务的熔断等等,包括还有 网关的高可用,以及我们可以发现IIS其实已经不是一个很好的选择了,我们更倾向将Netcore的程序发布在Linux中,或者发布在Docker中,那么程序CI/CD 等等等等 又是新的话题。
如果您认为这篇文章还不错或者有所收获,您可以点击左下角的【点赞】【收藏】,或请我喝杯咖啡【赞赏】,这将是我继续写作,分享的最大动力!