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