上一篇文章简单实用Consul试下服务注册,本篇继续学习Consul中的另外特性:服务发现、KV操作 ;以及对上篇文章中存在的问题进行解决
在上一篇文章中,注册服务提示检查失败。
通过排查发现为在docker 中运行的容器中配置的心跳检查api地址配置错误:
“Consul”: {
“Address”: “http://host.docker.internal:8500”,
“HealthCheck”: “/api/healthcheck”,//心跳检查api地址
“Name”: “czapigoods”,
“Ip”: “host.docker.internal”,
“Port”: “5602” //未指定成当前docker运行对于端口
}
“Consul”: {
“Address”: “http://host.docker.internal:8500”,
“HealthCheck”: “/api/healthcheck”,//心跳检查api地址
“Name”: “czapigoods”,
“Ip”: “host.docker.internal”,
“Port”: “5602” //未指定成当前docker运行对于端口
}
解决方法(docker修改配置方式):修改docker中配置文件appsettings.json:
进入docker命令行:docker exec -it 容器id /bin/bash 例如:docker exec -it f38a7a2ddfba /bin/bash
更新软件列表:apt-get update
安装vim命令:apt-get install vim
进入appsettings.json 修改:vim appsettings.json
修改appsettings中Consul.Port节点为对于docker映射端口
按Esc键,并输入:wq命令(退出保存修改)
重启对于容器效果如下
Ps:Doker相关操作后面单独详细
服务注册问题解决了,接下来我们了解下服务如何发现;首先创建一个web项目Consul.Client并添加Consul包引用
Install-Package Consul
Install-Package Consul
1、添加一个服务调用接口ICallService.cs用于调用我们添加的服务
public interface ICallService
{
///
/// 获取 Goods Service 返回数据
///
///
Task
///
/// 获取 Order Service 返回数据
///
///
Task
///
/// 初始化服务
///
void InitServices();
}
public interface ICallService
{
///
/// 获取 Goods Service 返回数据
///
///
Task
///
/// 获取 Order Service 返回数据
///
///
Task
///
/// 初始化服务
///
void InitServices();
}
2、实现该接口CallService.cs调用Goods、Order的api方法
public class CallService : ICallService
{
private readonly IConfiguration _configuration;
private readonly ConsulClient _consulClient;
private ConcurrentBag
private ConcurrentBag
private IHttpClientFactory _httpClient;
public CallService(IConfiguration configuration, IHttpClientFactory httpClient)
{
_cOnfiguration= configuration;
_cOnsulClient= new ConsulClient(optiOns=>
{
options.Address = new Uri(_configuration[“Consul:Address”]);
});
_httpClient = httpClient;
}
public async Task
{
if (_serviceAUrls == null)
return await Task.FromResult(“Goods Service Initializing…”);
using var httpClient = _httpClient.CreateClient();
//随机获取一个服务地址调用
var serviceUrl = _serviceAUrls.ElementAt(new Random().Next(_serviceAUrls.Count()));
Console.WriteLine(“Goods Service:” + serviceUrl);
var result = await httpClient.GetStringAsync($”{serviceUrl}/goods”);
return result;
}
public async Task
{
if (_serviceBUrls == null)
return await Task.FromResult(“Order Service Initializing…”);
using var httpClient = _httpClient.CreateClient();
//随机获取一个服务地址调用
var serviceUrl = _serviceBUrls.ElementAt(new Random().Next(_serviceBUrls.Count()));
Console.WriteLine(“Order Service:” + serviceUrl);
var result = await httpClient.GetStringAsync($”{serviceUrl}/order”);
return result;
}
public void InitServiceList()
{
var serviceNames = new string[] { “czapigoods”, “czapiorder” };
foreach (var item in serviceNames)
{
Task.Run(async () =>
{
var queryOptiOns= new QueryOptions
{
WaitTime = TimeSpan.FromMinutes(5)
};
while (true)
{
await InitServicesAsync(queryOptions, item);
}
});
}
}
private async Task InitServicesAsync(QueryOptions queryOptions, string serviceName)
{
//获取心跳检查服务
var result = await _consulClient.Health.Service(serviceName, null, true, queryOptions);
if (queryOptions.WaitIndex != result.LastIndex)
{
queryOptions.WaitIndex = result.LastIndex;
var services = result.Response.Select(x => $”http://{x.Service.Address}:{x.Service.Port}”);
if (serviceName == “czapigoods”)
{
_serviceAUrls = new ConcurrentBag
}
else if (serviceName == “czapiorder”)
{
_serviceBUrls = new ConcurrentBag
}
}
}
}
public class CallService : ICallService
{
private readonly IConfiguration _configuration;
private readonly ConsulClient _consulClient;
private ConcurrentBag
private ConcurrentBag
private IHttpClientFactory _httpClient;
public CallService(IConfiguration configuration, IHttpClientFactory httpClient)
{
_cOnfiguration= configuration;
_cOnsulClient= new ConsulClient(optiOns=>
{
options.Address = new Uri(_configuration[“Consul:Address”]);
});
_httpClient = httpClient;
}
public async Task
{
if (_serviceAUrls == null)
return await Task.FromResult(“Goods Service Initializing…”);
using var httpClient = _httpClient.CreateClient();
//随机获取一个服务地址调用
var serviceUrl = _serviceAUrls.ElementAt(new Random().Next(_serviceAUrls.Count()));
Console.WriteLine(“Goods Service:” + serviceUrl);
var result = await httpClient.GetStringAsync($”{serviceUrl}/goods”);
return result;
}
public async Task
{
if (_serviceBUrls == null)
return await Task.FromResult(“Order Service Initializing…”);
using var httpClient = _httpClient.CreateClient();
//随机获取一个服务地址调用
var serviceUrl = _serviceBUrls.ElementAt(new Random().Next(_serviceBUrls.Count()));
Console.WriteLine(“Order Service:” + serviceUrl);
var result = await httpClient.GetStringAsync($”{serviceUrl}/order”);
return result;
}
public void InitServiceList()
{
var serviceNames = new string[] { “czapigoods”, “czapiorder” };
foreach (var item in serviceNames)
{
Task.Run(async () =>
{
var queryOptiOns= new QueryOptions
{
WaitTime = TimeSpan.FromMinutes(5)
};
while (true)
{
await InitServicesAsync(queryOptions, item);
}
});
}
}
private async Task InitServicesAsync(QueryOptions queryOptions, string serviceName)
{
//获取心跳检查服务
var result = await _consulClient.Health.Service(serviceName, null, true, queryOptions);
if (queryOptions.WaitIndex != result.LastIndex)
{
queryOptions.WaitIndex = result.LastIndex;
var services = result.Response.Select(x => $”http://{x.Service.Address}:{x.Service.Port}”);
if (serviceName == “czapigoods”)
{
_serviceAUrls = new ConcurrentBag
}
else if (serviceName == “czapiorder”)
{
_serviceBUrls = new ConcurrentBag
}
}
}
}
3、接下来添加接口依赖注入、以及服务初始化调用
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.AddHttpClient();
//依赖注入CallService
services.AddSingleton
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ICallService service)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
//初始化服务列表调用
service.InitServiceList();
}
}
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.AddHttpClient();
//依赖注入CallService
services.AddSingleton
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ICallService service)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
//初始化服务列表调用
service.InitServiceList();
}
}
4、使用Postman测试调用
除了提供服务发现和健康检查的集成.Consul提供了一个易用的键/值存储.这可以用来保持动态配置,协助服务协调,领袖选举,做开发者可以想到的任何事情.
1、创建/修改
命令方式:consul kv put key val 如:consul kv put port 9000 –添加key为port值为9000
Http方式:
2、查询
命令方式:consul kv get key 如:consul kv get port –查询key为port的KV
Http方式:value为baisc
3、删除
命令方式:consul kv delete key 如:consul kv delete port –删除key为port的KV
Http方式:
网上找了下:常用服务发现框架consul、zookeeper及etcd比较:
参考:
consul手册:https://blog.csdn.net/liuzhuchen/article/details/81913562
https://www.consul.io/docs
https://www.consul.io/api/kv.html
github:
https://github.com/cwsheng/Consul.Demo.git