• ASP.NET Core gRPC 使用 Consul 服务注册发现


    原文网址:https://www.cnblogs.com/stulzq/p/11942691.html

    一. 前言

    gRPC 在当前最常见的应用就是在微服务场景中,所以不可避免的会有服务注册与发现问题,我们使用gRPC实现的服务可以使用 Consul 或者 etcd 作为服务注册与发现中心,本文主要介绍Consul。

    二. Consul 介绍

    Consul是一种服务网络解决方案,可跨任何运行平台以及公共或私有云来连接和保护服务。它可以让你发现服务并保护网络流量。它可以在Kubernetes中使用,实现服务发现和服务网格功能(k8s默认etcd)。 提供安全服务通讯,保护和观察服务之间的通信,而无需修改其代码。 提供动态负载平衡, 使用Consul和HAProxy,Nginx或F5自动执行负载均衡器配置。 Consul 可以用于服务发现和服务网格。

    翻译自官网

    三. Consul 安装配置

    安装

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

    根据自己的系统来选择,我这里选择的是 Windows 版本的,直接解压即可运行。

    image-20191122160153106

    启动

    consul agent -dev -ui
    

    image-20191127133742306

    本文不详细介绍Consul使用,如需请自行查看相关资料

    四. .NET Core Consul 客户端的选择

    Consul 提供了 HTTP API 的方式来进行通讯,我们可以直接调用API或者是使用第三方封装好的客户端组件,通过Nuget搜索可以发现许多。

    image-20191127134428569

    这里面我没有一一测试,但是目前使用量最多的 Consul 组件是不支持设置 GRPC 健康检查的,而且 github 也停止了更新。

    image-20191127134541731

    所以我 Fork 了这个仓库,然后添加了 GRPC 的健康检查支持,本文也将使用这个库,欢迎大家使用:

    因为原仓库已经 Archived 了,所以我才 Fork 了自己改一下,改动很小,不影响原来的稳定性。

    Nuget: https://www.nuget.org/packages/NConsul/

    Github:https://github.com/stulzq/NConsul

    求个star img

    关于支持 GPRC 健康检查的好处:

    偷个懒,不翻译了,摘自GRPC官方文档

    五. 注册GRPC服务与健康检查

    基于前文(ASP.NET Core 使用gRPC)的Demo

    1.为服务端项目安装 NConsul.AspNetCore ( https://www.nuget.org/packages/NConsul.AspNetCore )

    这里面对 AspNetCore 做了适配,使用简单。

    2.在 Startup 的 ConfigureServices方法内进行配置

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddGrpc();
    
        services.AddConsul("http://localhost:8500")
            .AddGRPCHealthCheck("localhost:5000")
            .RegisterService("grpctest","localhost",5000,new []{"xc/grpc/test"});
    }
    

    AddConsul 添加 Consul Server 地址。

    AddGRPCHealthCheck 添加 GRPC 健康检查,即健康检查走的是 GRPC 协议,该值为 GRPC 服务的地址,不需要path不需要提供 http/https

    RegisterService 注册服务

    到这步,还不能启动运行,如果运行健康检查是会失败的。

    3.编写 Health Check 服务 **

    对于 GRPC 的健康检查,官方有标准的定义,新建一个 proto 文件,命名为 HealthCheck.proto

    syntax = "proto3";
    
    package grpc.health.v1;
    
    message HealthCheckRequest {
    	string service = 1;
    }
    
    message HealthCheckResponse {
    	enum ServingStatus {
    		UNKNOWN = 0;
    		SERVING = 1;
    		NOT_SERVING = 2;
    	}
    	ServingStatus status = 1;
    }
    
    service Health {
    	rpc Check(HealthCheckRequest) returns (HealthCheckResponse);
    
    	rpc Watch(HealthCheckRequest) returns (stream HealthCheckResponse);
    }
    

    这里面的内容不得更改,是官方标准,资料见后文

    这里编译一下项目,以便自动生成代码。

    然后,添加一个服务的实现类 HealthCheckService

    public class HealthCheckService:Health.HealthBase
    {
        public override Task<HealthCheckResponse> Check(HealthCheckRequest request, ServerCallContext context)
        {
            //TODO:检查逻辑
            return Task.FromResult(new HealthCheckResponse(){Status = HealthCheckResponse.Types.ServingStatus.Serving});
        }
    
        public override async Task Watch(HealthCheckRequest request, IServerStreamWriter<HealthCheckResponse> responseStream, ServerCallContext context)
        {
            //TODO:检查逻辑
            await responseStream.WriteAsync(new HealthCheckResponse()
                {Status = HealthCheckResponse.Types.ServingStatus.Serving});
        }
    }
    

    示例代码直接返回了检查结果,实际使用中应该在这里编写检查逻辑,然后根据情况返回相应的检查结果。检查结果有3种情况:

    结果类型说明
    Unknown 未知状态
    Serving 正常
    NotServing 异常,不能提供服务

    最后别忘了注册服务:

    image-20191127151059008

    4.测试运行

    启动 GRPC 服务

    然后访问 http://localhost:8500/ui 访问 Consul 控制台

    image-20191127151652840

    可以看到服务成功注册,并且健康检查也是通过了的。通过控制台日志,还可以看到健康检查的请求:

    image-20191127151727397

    六. 客户端使用服务发现

    客户端项目安装 Consul 组件,然后改造下代码:

    static async Task Main(string[] args)
    {
        var serviceName = "grpctest";
        var consulClient = new ConsulClient(c => c.Address = new Uri("http://localhost:8500"));
        var services = await consulClient.Catalog.Service(serviceName);
        if (services.Response.Length == 0)
        {
            throw new Exception($"未发现服务 {serviceName}");
        }
    
        var service = services.Response[0];
        var address = $"http://{service.ServiceAddress}:{service.ServicePort}";
    
        Console.WriteLine($"获取服务地址成功:{address}");
    
        //启用通过http使用http2.0
        AppContext.SetSwitch(
            "System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
        var channel = GrpcChannel.ForAddress(address);
        var catClient = new LuCat.LuCatClient(channel);
        var catReply = await catClient.SuckingCatAsync(new Empty());
        Console.WriteLine("调用撸猫服务:"+ catReply.Message);
        Console.ReadKey();
    }
    

    通过服务名称获取服务地址,然后来进行访问。

    运行测试:

    image-20191127152941168

    可以看到,成功的从Consul获取了我们的服务地址,然后调用。

    六. 参考资料

  • 相关阅读:
    lnmp下如何建立svn版本库
    解决更新本地svn版本库,提示:工作副本已锁定 问题
    请不要在意
    ecshop在lbi库文件中添加广告位的方法(转载,试过了确实可以添加成功)
    Jquery AjaxUpload实现文件上传
    js提交表单错误:document.form.submit() is not a function
    kindeditor的使用方法
    phpcmsv9整合ucenter经验分享
    替换字符串sql语句
    初生牛犊之spring(二)
  • 原文地址:https://www.cnblogs.com/bruce1992/p/16831212.html
Copyright © 2020-2023  润新知