Consul 学习笔记—服务发现

前言:

  上一篇文章简单实用Consul试下服务注册,本篇继续学习Consul中的另外特性:服务发现、KV操作 ;以及对上篇文章中存在的问题进行解决

问题解决

 在上一篇文章中,注册服务提示检查失败。

  通过排查发现为在docker 中运行的容器中配置的心跳检查api地址配置错误:

  "Consul": {
    "Address": "//host.docker.internal:8500",
    "HealthCheck": "/api/healthcheck",//心跳检查api地址
    "Name": "czapigoods",
    "Ip": "host.docker.internal",
    "Port": "5602" //未指定成当前docker运行对于端口
  }

 解决方法(docker修改配置方式):修改docker中配置文件appsettings.json:

  1. 进入docker命令行:docker exec -it 容器id /bin/bash  例如:docker exec -it f38a7a2ddfba /bin/bash
  2. 更新软件列表:apt-get update 
  3. 安装vim命令:apt-get install vim
  4. 进入appsettings.json 修改:vim appsettings.json
  5. 修改appsettings中Consul.Port节点为对于docker映射端口
  6. 按Esc键,并输入:wq命令(退出保存修改)
  7. 重启对于容器效果如下

  

  Ps:Doker相关操作后面单独详细

服务发现

  服务注册问题解决了,接下来我们了解下服务如何发现;首先创建一个web项目Consul.Client并添加Consul包引用

Install-Package Consul

 1、添加一个服务调用接口ICallService.cs用于调用我们添加的服务

public interface ICallService
{
    /// <summary>
    /// 获取 Goods Service 返回数据
    /// </summary>
    /// <returns></returns>
    Task<string> GetGoodsService();

    /// <summary>
    /// 获取 Order Service 返回数据
    /// </summary>
    /// <returns></returns>
    Task<string> GetOrderService();

    /// <summary>
    /// 初始化服务
    /// </summary>
    void InitServices();
}

 

 2、实现该接口CallService.cs调用Goods、Order的api方法  

public class CallService : ICallService
{
private readonly IConfiguration _configuration;
private readonly ConsulClient _consulClient;
private ConcurrentBag<string> _serviceAUrls;
private ConcurrentBag<string> _serviceBUrls;
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<string> GetGoodsService()
{
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<string> GetOrderService()
{
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 => $"//{x.Service.Address}:{x.Service.Port}");
if (serviceName == "czapigoods")
{
_serviceAUrls = new ConcurrentBag<string>(services);
}
else if (serviceName == "czapiorder")
{
_serviceBUrls = new ConcurrentBag<string>(services);
}
}
}
}

 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<ICallService, CallService>();
}
// 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测试调用

  

KV操作

 除了提供服务发现和健康检查的集成.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手册://blog.csdn.net/liuzhuchen/article/details/81913562 

//www.consul.io/docs

//www.consul.io/api/kv.html

github:

//github.com/cwsheng/Consul.Demo.git