在上一篇 .net core grpc 实现通信(一) 中,我们实现的grpc通信在.net core中的可行性,但要在微服务中真正使用,还缺少 服务注册,服务发现及负载均衡等,本篇我们将在 .net core grpc 通信 的基础上加上 服务注册,服务发现,负载均衡。

如对.net core grpc 通信不太熟悉的,可以看上一篇 .net core grpc 实现通信(一) ,然后再看本篇。

grpc(https://grpc.io/)是google发布的一个开源、高性能、通用RPC(Remote Procedure Call)框架,使用HTTP/2协议,支持多路复用,并用ProtoBuf作为序列化工具,提供跨语言、跨平台支持。

Consul(https://www.consul.io)是一个分布式,高可用、支持多数据中心的服务注册、发现、健康检查和配置共享的服务软件,由 HashiCorp 公司用 Go 语言开发。

本次服务注册、发现 通过 Consul Api 来实现,开发过程中结合.net core 依赖注入,切面管道思想等。

软件版本

.net core:2.0

grpc:1.11.0

Consul:1.1.0

Consul Nuget注册组件:0.7.2.5

项目结构

.net core 代码部分:

Snai.GrpcClient 客户端 .net core 2.0控制台程序

Snai.GrpcService.Hosting 服务端宿主,Api服务注册,asp.net core 2.0网站程序

Snai.GrpcService.Impl 协议方法实现  .net standard 2.0类库

Snai.GrpcService.Protocol 生成协议方法 .net standard 2.0类库

aaarticlea/png;base64,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" alt="" />

Consul:

conf 配置目录,本次用api注册服务,可以删除

data 缓存数据目录,可清空里面内容

dist Consul UI目录,本次用默认的UI,可以删除

consul.exe 注册软件

startup.bat 执行脚本

项目实现

一、服务端

服务端主要包括Grpc服务端,Consul Api服务注册、健康检查等。

新建Snai.GrpcService解决方案,由于这次加入了 Consul Api 服务注册,所以我们先从 Api 服务注册开始。

1、实现 Consul Api 服务注册

新建 Snai.GrpcService.Hosting 基于Asp.net Core 2.0空网站,在 依赖项 右击 管理NuGet程序包 浏览 找到 Consul 版本0.7.2.5安装,用于Api服务注册使用

新建 appsettings.json 配置文件,配置 GrpcService Grpc服务端IP和端口,HealthService健康检测名称、IP和地址,ConsulService Consul的IP和端口,代码如下

{
  "GrpcService": {
    "IP": "localhost",
    "Port": "5031"
  },
  "HealthService": {
    "Name": "GrpcService",
    "IP": "localhost",
    "Port": "5021"
  },
  "ConsulService": {
    "IP": "localhost",
    "Port": "8500"
  }
}

新建Consul目录,用于放Api注册相关代码

在Consul目录下新建Entity目录,在Entity目录下新建HealthService.cs,ConsulService.cs类,分别对应HealthService,ConsulService两个配置项,代码如下

HealthService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; namespace Snai.GrpcService.Hosting.Consul.Entity
{
    public class HealthService
    {
        public string Name { get; set; }
        public string IP { get; set; }
        public int Port { get; set; }
        
    }
}

ConsulService.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; namespace Snai.GrpcService.Hosting.Consul.Entity
{
    public class ConsulService
    {
        public string IP { get; set; }
        public int Port { get; set; }
    }
}

在 Consul 目录下新建 AppRregister.cs 类,添加 IApplicationBuilder 扩展方法 RegisterConsul,来调用 Consul Api 实现服务注册,代码如下

using Consul;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Options;
using Snai.GrpcService.Hosting.Consul.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; namespace Snai.GrpcService.Hosting.Consul
{
    public static class AppRregister
    {
        // 服务注册
        public static IApplicationBuilder RegisterConsul(this IApplicationBuilder app, IApplicationLifetime lifetime, IOptions<HealthService> healthService, IOptions<ConsulService> consulService)
        {
            var consulClient = new ConsulClient(x => x.Address = new Uri($"http://{consulService.Value.IP}:{consulService.Value.Port}"));//请求注册的 Consul 地址
            var httpCheck = new AgentServiceCheck()
            {
                DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(5),//服务启动多久后注册
                Interval = TimeSpan.FromSeconds(10),//健康检查时间间隔,或者称为心跳间隔
                HTTP = $"http://{healthService.Value.IP}:{healthService.Value.Port}/health",//健康检查地址
                Timeout = TimeSpan.FromSeconds(5)
            };             // Register service with consul
            var registration = new AgentServiceRegistration()
            {
                Checks = new[] { httpCheck },
                ID = healthService.Value.Name + "_" + healthService.Value.Port,
                Name = healthService.Value.Name,
                Address = healthService.Value.IP,
                Port = healthService.Value.Port,
                Tags = new[] { $"urlprefix-/{healthService.Value.Name}" }//添加 urlprefix-/servicename 格式的 tag 标签,以便 Fabio 识别
            };             consulClient.Agent.ServiceRegister(registration).Wait();//服务启动时注册,内部实现其实就是使用 Consul API 进行注册(HttpClient发起)
            lifetime.ApplicationStopping.Register(() =>
            {
                consulClient.Agent.ServiceDeregister(registration.ID).Wait();//服务停止时取消注册
            });             return app;
        }
    }
}

修改 Startup.cs 代码

加入 Startup(IConfiguration configuration) 构造函数,实现配置注入,如果建的是Web Api或MVC网站,默认是有的

修改 ConfigureServices(IServiceCollection services)  方法,注册全局配置

修改 Configure() 方法,添加健康检查路由地址 app.Map("/health", HealthMap),调用 RegisterConsul 扩展方法实现服务注册

添加 HealthMap(IApplicationBuilder app) 实现health路由。由于只有一个健康检查地址,所以没有建Web Api网站,只建了个空网站

代码如下,注册配置GrpcService 、 注册Rpc服务、启动Rpc服务 后面用到等下讲

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Snai.GrpcService.Hosting.Consul;
using Snai.GrpcService.Hosting.Consul.Entity;
using Snai.GrpcService.Impl; namespace Snai.GrpcService.Hosting
{
    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.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            //注册全局配置
            services.AddOptions();
            services.Configure<Impl.Entity.GrpcService>(Configuration.GetSection(nameof(Impl.Entity.GrpcService)));
            services.Configure<HealthService>(Configuration.GetSection(nameof(HealthService)));
            services.Configure<ConsulService>(Configuration.GetSection(nameof(ConsulService)));             //注册Rpc服务
            services.AddSingleton<IRpcConfig, RpcConfig>();
        }         // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime, IOptions<HealthService> healthService, IOptions<ConsulService> consulService, IRpcConfig rpc)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }             // 添加健康检查路由地址
            app.Map("/health", HealthMap);             // 服务注册
            app.RegisterConsul(lifetime, healthService, consulService);             // 启动Rpc服务
            rpc.Start();
        }         private static void HealthMap(IApplicationBuilder app)
        {
            app.Run(async context =>
            {
                await context.Response.WriteAsync("OK");
            });
        }
    }
}

修改 Program.cs 代码,调置网站地址为 .UseUrls("http://localhost:5021"),代码如下

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging; namespace Snai.GrpcService.Hosting
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }         public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseUrls("http://localhost:5021")
                .UseStartup<Startup>()
                .Build();
    }
}

到此 Consul Api 服务注册 已完成,最终项目结构如下:

aaarticlea/png;base64,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" alt="" />

2、协议编写,将协议生成C#代码

由于在上一篇 .net core grpc 实现通信(一) 有过介绍,这里就简单说下

新建 Snai.GrpcService.Protocol协议类库项目,在 依赖项 右击 管理NuGet程序包 浏览 找到 Grpc.Core 版本1.11.0,Google.Protobuf 版本3.5.1 包下载安装

在根目录下新建msg.proto 文件,编写基于proto3语言的协议代码,用于生成各语言协议,msg.proto 代码如下

syntax = "proto3";

package Snai.GrpcService.Protocol;

service MsgService{
  rpc GetSum(GetMsgNumRequest) returns (GetMsgSumReply){}
} message GetMsgNumRequest {
  int32 Num1 = 1;
  int32 Num2 = 2;
} message GetMsgSumReply {
  int32 Sum = 1;
}

新建.net framework 项目类库,引用安装 Grpc.Tools、Google.Protobuf.Tools 组件程序包,分别得到 grpc_csharp_plugin.exe、protoc.exe 工具

到package目录下,找到与系统相应的grpc_csharp_plugin.exe、protoc.exe工具,拷到 Snai.GrpcService.Protocol 项目下

在Snai.GrpcService.Protocol根目录下新建 ProtocGenerate.cmd 文件,在其中输入以下指令

protoc -I . --csharp_out . --grpc_out . --plugin=protoc-gen-grpc=grpc_csharp_plugin.exe msg.proto

然后直接双击运行,项目下生成了“Msg.cs”和“MsgGrpc.cs”两个文件,这样协议部分的所有工作就完成了,最终项目结构如下:

3、编写协议实现代码

新建 Snai.GrpcService.Impl 实现类库项目,在 依赖项 下载安装Grpc.Core 包,项目引用 Snai.GrpcService.Protocol

新建 Entity 目录,在Entity目录下新建 GrpcService.cs 类,对应 Snai.GrpcService.Hosting 项目下 appsettings.json 配置文件的 GrpcService 配置项,代码如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; namespace Snai.GrpcService.Impl.Entity
{
    public class GrpcService
    {
        public string IP { get; set; }
        public int Port { get; set; }
    }
}

在根目录下新建 RpcService 目录,在 RpcService 目录下新建 MsgServiceImpl.cs 类,继承 MsgService.MsgServiceBase 协议类,实现服务方法,代码如下

using Grpc.Core;
using Snai.GrpcService.Protocol;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks; namespace Snai.GrpcService.Impl.RpcService
{
    public class MsgServiceImpl : MsgService.MsgServiceBase
    {
        public override async Task<GetMsgSumReply> GetSum(GetMsgNumRequest request, ServerCallContext context)
        {
            var result = new GetMsgSumReply();             result.Sum = request.Num1 + request.Num2;             Console.WriteLine(request.Num1 + "+" + request.Num2 + "=" + result.Sum);             return result;
        }
    }
}

在根目录下新建IRpcConfig.cs接口,定义 Start() 用于Rpc启动基方法,代码如下

using System;
using System.Collections.Generic;
using System.Text; namespace Snai.GrpcService.Impl
{
    public interface IRpcConfig
    {
        void Start();
    }
}

在根目录下新建 RpcConfig.cs 类,用于实现 IRpcConfig.cs 接口,启动Rpc服务,代码如下

using Grpc.Core;
using Microsoft.Extensions.Options;
using Snai.GrpcService.Impl.RpcService;
using Snai.GrpcService.Protocol;
using System;
using System.Collections.Generic;
using System.Text; namespace Snai.GrpcService.Impl
{
    public class RpcConfig: IRpcConfig
    {
        private static Server _server;
        static IOptions<Entity.GrpcService> GrpcSettings;         public RpcConfig(IOptions<Entity.GrpcService> grpcSettings)
        {
            GrpcSettings = grpcSettings;
        }         public void Start()
        {
            _server = new Server
            {
                Services = { MsgService.BindService(new MsgServiceImpl()) },
                Ports = { new ServerPort(GrpcSettings.Value.IP, GrpcSettings.Value.Port, ServerCredentials.Insecure) }
            };
            _server.Start();             Console.WriteLine($"Grpc ServerListening On Port {GrpcSettings.Value.Port}");
        }
    }
}

在回到Snai.GrpcService.Hosting项目中,在 Startup.cs 中 ConfigureServices 中注册 GrpcService 配置、注册Rpc服务,在 Configure 中 启动Rpc服务 就是上面说到的蓝色字体标识的,如图

最终项目结构如下:

到此服务端的代码实现已完成,下面我们启动Consul和服务端,验证 Api 注册和Grpc启动。

二、Consul和服务端启动

启动Consul,启动Grpc服务、注册服务到Consul

1、启动Consul

首先下载Consul:https://www.consul.io/downloads.html,本项目是windows下进行测试,得到consul.exe

由于本次用Api注册,用Consul默认自带UI,所以conf和dist可删除

清除Consul/data 内容,新建startup.bat文件,输入下面代码,双击启动Consul,本项目测试时一台机器,所以把 本机IP 改成 127.0.0.1

consul agent -server -datacenter=grpc-consul -bootstrap -data-dir ./data -ui -node=grpc-consul1 -bind 本机IP -client=0.0.0.0

再在Consul目录下启动另一个cmd命令行窗口,输入命令:consul operator raft list-peers 查看状态查看状态,结果如下

打开Consul UI:http://localhost:8500 查看情况

Consul 启动成功。

.net core Ocelot Consul 实现API网关 服务注册 服务发现 负载均衡 中后面 Consul 部分,有 Consul 集群搭建等其他介绍,可以去参考看下。

2、启动服务端,启动Grpc服务、注册服务到Consul

由于客户端要实现负载,所以把 Snai.GrpcService.Hosting 项目生成两次,启动两个一样的服务端,只是端口不同

服务5021 地址为5021: .UseUrls("http://localhost:5021"),GrpcService:5031,如下图

aaarticlea/png;base64,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" alt="" />

服务5022 修改地址为5022: .UseUrls("http://localhost:5022"),GrpcService:5032,如下图

aaarticlea/png;base64,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" alt="" />

aaarticlea/png;base64,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" alt="" />

启动 服务5021和服务5022两个服务端,如下面

看到 Grpc ServerListening On Port 5031,Grpc ServerListening On Port 5032 说明 Grpc 服务端启动成功

看到 Request starting HTTP/1.1 GET http://localhost:5021/health 说明 Consul 健康检查成功

打开Consul服务查看地址 http://localhost:8500/ui/#/grpc-consul/services/GrpcService 查看,两个GrpcService注册成功,健康检查状态正常

到此,Grpc启动正常,Consul Api服务注册、健康检查都正常,下面开始实现Grpc客户端

三、客户端

客户端主要包括Grpc客户端,Consul Api服务发现、负载均衡等。

新建Snai.GrpcClient 控制台程序,在 依赖项 下载安装Grpc.Core 包,项目引用Snai.GrpcService.Protocol,在依赖项下载安装下面工具组件包

用于读取 json配置:Microsoft.Extensions.Configuration,Microsoft.Extensions.Configuration.Json

用于依赖注入:Microsoft.Extensions.DependencyInjection

用于注入全局配置:Microsoft.Extensions.Options,Microsoft.Extensions.Options.ConfigurationExtensions

在项目根目录下新建 Utils 目录,在 Utils 目录下新建 HttpHelper.cs 类,用于程序内发送http请求,代码如下

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
/*
 * 参考 pudefu https://www.cnblogs.com/pudefu/p/7581956.html ,在此表示感谢
 */
namespace Snai.GrpcClient.Utils
{
    public class HttpHelper
    {
        /// <summary>
        /// 同步GET请求
        /// </summary>
        /// <param name="url"></param>
        /// <param name="headers"></param>
        /// <param name="timeout">请求响应超时时间,单位/s(默认100秒)</param>
        /// <returns></returns>
        public static string HttpGet(string url, Dictionary<string, string> headers = null, int timeout = 0)
        {
            using (HttpClient client = new HttpClient())
            {
                if (headers != null)
                {
                    foreach (KeyValuePair<string, string> header in headers)
                    {
                        client.DefaultRequestHeaders.Add(header.Key, header.Value);
                    }
                }
                if (timeout > 0)
                {
                    client.Timeout = new TimeSpan(0, 0, timeout);
                }
                Byte[] resultBytes = client.GetByteArrayAsync(url).Result;
                return Encoding.UTF8.GetString(resultBytes);
            }
        }         /// <summary>
        /// 异步GET请求
        /// </summary>
        /// <param name="url"></param>
        /// <param name="headers"></param>
        /// <param name="timeout">请求响应超时时间,单位/s(默认100秒)</param>
        /// <returns></returns>
        public static async Task<string> HttpGetAsync(string url, Dictionary<string, string> headers = null, int timeout = 0)
        {
            using (HttpClient client = new HttpClient())
            {
                if (headers != null)
                {
                    foreach (KeyValuePair<string, string> header in headers)
                    {
                        client.DefaultRequestHeaders.Add(header.Key, header.Value);
                    }
                }
                if (timeout > 0)
                {
                    client.Timeout = new TimeSpan(0, 0, timeout);
                }
                Byte[] resultBytes = await client.GetByteArrayAsync(url);
                return Encoding.Default.GetString(resultBytes);
            }
        }         /// <summary>
        /// 同步POST请求
        /// </summary>
        /// <param name="url"></param>
        /// <param name="postData"></param>
        /// <param name="headers"></param>
        /// <param name="contentType"></param>
        /// <param name="timeout">请求响应超时时间,单位/s(默认100秒)</param>
        /// <param name="encoding">默认UTF8</param>
        /// <returns></returns>
        public static string HttpPost(string url, string postData, Dictionary<string, string> headers = null, string contentType = null, int timeout = 0, Encoding encoding = null)
        {
            using (HttpClient client = new HttpClient())
            {
                if (headers != null)
                {
                    foreach (KeyValuePair<string, string> header in headers)
                    {
                        client.DefaultRequestHeaders.Add(header.Key, header.Value);
                    }
                }
                if (timeout > 0)
                {
                    client.Timeout = new TimeSpan(0, 0, timeout);
                }
                using (HttpContent content = new StringContent(postData ?? "", encoding ?? Encoding.UTF8))
                {
                    if (contentType != null)
                    {
                        content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);
                    }
                    using (HttpResponseMessage responseMessage = client.PostAsync(url, content).Result)
                    {
                        Byte[] resultBytes = responseMessage.Content.ReadAsByteArrayAsync().Result;
                        return Encoding.UTF8.GetString(resultBytes);
                    }
                }
            }
        }         /// <summary>
        /// 异步POST请求
        /// </summary>
        /// <param name="url"></param>
        /// <param name="postData"></param>
        /// <param name="headers"></param>
        /// <param name="contentType"></param>
        /// <param name="timeout">请求响应超时时间,单位/s(默认100秒)</param>
        /// <param name="encoding">默认UTF8</param>
        /// <returns></returns>
        public static async Task<string> HttpPostAsync(string url, string postData, Dictionary<string, string> headers = null, string contentType = null, int timeout = 0, Encoding encoding = null)
        {
            using (HttpClient client = new HttpClient())
            {
                if (headers != null)
                {
                    foreach (KeyValuePair<string, string> header in headers)
                    {
                        client.DefaultRequestHeaders.Add(header.Key, header.Value);
                    }
                }
                if (timeout > 0)
                {
                    client.Timeout = new TimeSpan(0, 0, timeout);
                }
                using (HttpContent content = new StringContent(postData ?? "", encoding ?? Encoding.UTF8))
                {
                    if (contentType != null)
                    {
                        content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(contentType);
                    }
                    using (HttpResponseMessage responseMessage = await client.PostAsync(url, content))
                    {
                        Byte[] resultBytes = await responseMessage.Content.ReadAsByteArrayAsync();
                        return Encoding.UTF8.GetString(resultBytes);
                    }
                }
            }
        }
    }
}

在项目根目录下新建 Consul 目录,在 Consul 目录下新建 Entity 目录,在 Entity 目录下新建 HealthCheck.cs 类,用于接收 Consul Api发现的信息实体,代码如下

using System;
using System.Collections.Generic;
using System.Text; namespace Snai.GrpcClient.Consul.Entity
{
    public class HealthCheck
    {
        public string Node { get; set; }
        public string CheckID { get; set; }
        public string Name { get; set; }
        public string Status { get; set; }
        public string Notes { get; set; }
        public string Output { get; set; }
        public string ServiceID { get; set; }
        public string ServiceName { get; set; }
        public string[] ServiceTags { get; set; }
        public dynamic Definition { get; set; }
        public int CreateIndex { get; set; }
        public int ModifyIndex { get; set; }
    }
}

在 Consul 目录下新建 IAppFind.cs 接口,定义 FindConsul() 用于 Consul 服务发现基方法,代码如下

using System;
using System.Collections.Generic;
using System.Text; namespace Snai.GrpcClient.Consul
{
    public interface IAppFind
    {
        IEnumerable<string> FindConsul(string ServiceName);
    }
}

在 Consul 目录下新建 AppFind.cs 类,用于实现 IAppFind.cs 接口,实现 Consul 服务发现方法,代码如下

using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Snai.GrpcClient.Consul.Entity;
using Snai.GrpcClient.Framework.Entity;
using Snai.GrpcClient.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace Snai.GrpcClient.Consul
{
    /*
     * 服务发现
     * (服务和健康信息)http://localhost:8500/v1/health/service/GrpcService
     * (健康信息)http://localhost:8500/v1/health/checks/GrpcService
     */
    public class AppFind: IAppFind
    {
        static IOptions<GrpcServiceSettings> GrpcSettings;
        static IOptions<ConsulService> ConsulSettings;         public AppFind(IOptions<GrpcServiceSettings> grpcSettings, IOptions<ConsulService> consulSettings)
        {
            GrpcSettings = grpcSettings;
            ConsulSettings = consulSettings;
        }
        
        public IEnumerable<string> FindConsul(string ServiceName)
        {
            Dictionary<string, string> headers = new Dictionary<string, string>();             var consul = ConsulSettings.Value;
            string findUrl = $"http://{consul.IP}:{consul.Port}/v1/health/checks/{ServiceName}";             string findResult = HttpHelper.HttpGet(findUrl, headers, 5);
            if (findResult.Equals(""))
            {
                var grpcServices = GrpcSettings.Value.GrpcServices;
                return grpcServices.Where(g=>g.ServiceName.Equals(ServiceName,StringComparison.CurrentCultureIgnoreCase)).Select(s => s.ServiceID);
            }             var findCheck = JsonConvert.DeserializeObject<List<HealthCheck>>(findResult);             return findCheck.Where(g => g.Status.Equals("passing", StringComparison.CurrentCultureIgnoreCase)).Select(g => g.ServiceID);
        }
    }
}

在项目根目录下新建 LoadBalance 目录,在 LoadBalance 目录下新建 ILoadBalance.cs 接口,定义 GetGrpcService() 用于负载均衡基方法,代码如下

using Snai.GrpcClient.Framework.Entity;
using System;
using System.Collections.Generic;
using System.Text; namespace Snai.GrpcClient.LoadBalance
{
    /*
     * 负载均衡接口
     */
    public interface ILoadBalance
    {
        string GetGrpcService(string ServiceName);
    }
}

在 LoadBalance 目录下新建 WeightRoundBalance.cs 类,用于实现 ILoadBalance.cs 接口,实现 GetGrpcService() 负载均衡方法,本次负载均衡实现权重轮询算法,代码如下

using Snai.GrpcClient.Consul;
using Snai.GrpcClient.Utils;
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using Snai.GrpcClient.Framework.Entity;
using Microsoft.Extensions.Options; namespace Snai.GrpcClient.LoadBalance
{
    /*
     * 权重轮询
     */
    public class WeightRoundBalance : ILoadBalance
    {
        int Balance;
        IOptions<GrpcServiceSettings> GrpcSettings;
        IAppFind AppFind;         public WeightRoundBalance(IOptions<GrpcServiceSettings> grpcSettings, IAppFind appFind)
        {
            Balance = 0;
            GrpcSettings = grpcSettings;
            AppFind = appFind;
        }         public string GetGrpcService(string ServiceName)
        {
            var grpcServices = GrpcSettings.Value.GrpcServices;             var healthServiceID = AppFind.FindConsul(ServiceName);             if (grpcServices == null || grpcServices.Count() == 0 || healthServiceID == null || healthServiceID.Count() == 0)
            {
                return "";
            }             //健康的服务
            var healthServices = new List<Framework.Entity.GrpcService>();             foreach (var service in grpcServices)
            {
                foreach (var health in healthServiceID)
                {
                    if (service.ServiceID.Equals(health, StringComparison.CurrentCultureIgnoreCase))
                    {
                        healthServices.Add(service);
                        break;
                    }
                }
            }             if (healthServices == null || healthServices.Count() == 0)
            {
                return "";
            }             //加权轮询
            var services = new List<string>();             foreach (var service in healthServices)
            {
                services.AddRange(Enumerable.Repeat(service.IP + ":" + service.Port, service.Weight));
            }
            
            var servicesArray = services.ToArray();             Balance = Balance % servicesArray.Length;
            var grpcUrl = servicesArray[Balance];
            Balance = Balance + 1;             return grpcUrl;
        }
    }
}

在项目根目录下新建 RpcClient 目录,在 RpcClient 目录下新建 IMsgClient.cs 接口,定义 GetSum() 用于Grpc客户端调用基方法,代码如下

using System;
using System.Collections.Generic;
using System.Text; namespace Snai.GrpcClient.RpcClient
{
    public interface IMsgClient
    {
        void GetSum(int num1, int num2);
    }
}

在 RpcClient 目录下新建 MsgClient.cs 类,用于实现 IMsgClient.cs 接口,实现 GetSum() 方法用于Grpc客户端调用,代码如下

using Grpc.Core;
using Microsoft.Extensions.DependencyInjection;
using Snai.GrpcClient.LoadBalance;
using Snai.GrpcService.Protocol;
using System;
using System.Collections.Generic;
using System.Text; namespace Snai.GrpcClient.RpcClient
{
    public class MsgClient: IMsgClient
    {
        ILoadBalance LoadBalance;
        Channel GrpcChannel;
        MsgService.MsgServiceClient GrpcClient;         public MsgClient(ILoadBalance loadBalance)
        {
            LoadBalance = loadBalance;             var grpcUrl = LoadBalance.GetGrpcService("GrpcService");             if (!grpcUrl.Equals(""))
            {
                Console.WriteLine($"Grpc Service:{grpcUrl}");                 GrpcChannel = new Channel(grpcUrl, ChannelCredentials.Insecure);
                GrpcClient = new MsgService.MsgServiceClient(GrpcChannel);
            }
        }         public void GetSum(int num1, int num2)
        {
            if (GrpcClient != null)
            {
                GetMsgSumReply msgSum = GrpcClient.GetSum(new GetMsgNumRequest
                {
                    Num1 = num1,
                    Num2 = num2
                });                 Console.WriteLine("Grpc Client Call GetSum():" + msgSum.Sum);
            }
            else
            {
                Console.WriteLine("所有负载都挂掉了!");
            }
        }
    }
}

在项目根目录下新建 Framework 目录,在 Framework 目录下新建 Entity 目录,在 Entity 目录下新建 ConsulService.cs 和 GrpcServiceSettings.cs 类,分别对应配置appsettings.json的 ConsulService,GrpcServiceSettings 两个配置项,代码如下

ConsulService.cs

using System;
using System.Collections.Generic;
using System.Text; namespace Snai.GrpcClient.Framework.Entity
{
    public class ConsulService
    {
        public string IP { get; set; }
        public int Port { get; set; }
    }
}

GrpcServiceSettings.cs

using System;
using System.Collections.Generic;
using System.Text; namespace Snai.GrpcClient.Framework.Entity
{
    public class GrpcServiceSettings
    {
        public List<GrpcService> GrpcServices { get; set; }
    }     public class GrpcService
    {
        public string ServiceName { get; set; }
        public string ServiceID { get; set; }
        public string IP { get; set; }
        public int Port { get; set; }
        public int Weight { get; set; }
    }
}

在 Framework 目录下新建 DependencyInitialize.cs 类,定义 AddImplement() 方法用于注册全局配置和类到容器,实现依赖注入,代码如下

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Snai.GrpcClient.Consul;
using Snai.GrpcClient.Framework.Entity;
using Snai.GrpcClient.LoadBalance;
using Snai.GrpcClient.RpcClient;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text; namespace Snai.GrpcClient.Framework
{
    /*
     *  IServiceCollection 依赖注入生命周期
     *  AddTransient 每次都是全新的
     *  AddScoped    在一个范围之内只有同一个实例(同一个线程,同一个浏览器请求只有一个实例)
     *  AddSingleton 单例
     */
    public static class DependencyInitialize
    {
        /// <summary>
        /// 注册对象
        /// </summary>
        /// <param name="services">The services.</param>
        /*
         * IAppFind AppFind;
         * 构造函数注入使用 IAppFind appFind
         * AppFind = appFind;
         */
        public static void AddImplement(this IServiceCollection services)
        {
            //添加 json 文件路径
            var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json");
            //创建配置根对象
            var configurationRoot = builder.Build();             //注册全局配置
            services.AddConfigImplement(configurationRoot);             //注册服务发现
            services.AddScoped<IAppFind, AppFind>();             //注册负载均衡
            if (configurationRoot["LoadBalancer"].Equals("WeightRound", StringComparison.CurrentCultureIgnoreCase))
            {
                services.AddSingleton<ILoadBalance, WeightRoundBalance>();
            }             //注册Rpc客户端
            services.AddTransient<IMsgClient, MsgClient>();
        }         /// <summary>
        /// 注册全局配置
        /// </summary>
        /// <param name="services">The services.</param>
        /// <param name="configurationRoot">The configurationRoot.</param>
        /*  
         *  IOptions<GrpcServiceSettings> GrpcSettings;
         *  构造函数注入使用 IOptions<GrpcServiceSettings> grpcSettings
         *  GrpcSettings = grpcSettings;
         */
        public static void AddConfigImplement(this IServiceCollection services, IConfigurationRoot configurationRoot)
        {
            //注册配置对象
            services.AddOptions();
            services.Configure<GrpcServiceSettings>(configurationRoot.GetSection(nameof(GrpcServiceSettings)));
            services.Configure<ConsulService>(configurationRoot.GetSection(nameof(ConsulService)));
        }
    }
}

在根目录下新建 appsettings.json 配置文件,配置 GrpcServiceSettings 的 GrpcServices 为服务端发布的两个服务5021和5022,LoadBalancer 负载均衡为 WeightRound 权重轮询(如实现其他负载方法可做相应配置,注册负载均衡时也做相应修改),ConsulService Consul的IP和端口,代码如下

{
  "GrpcServiceSettings": {
    "GrpcServices": [
      {
        "ServiceName": "GrpcService",
        "ServiceID": "GrpcService_5021",
        "IP": "localhost",
        "Port": "5031",
        "Weight": "2"
      },
      {
        "ServiceName": "GrpcService",
        "ServiceID": "GrpcService_5022",
        "IP": "localhost",
        "Port": "5032",
        "Weight": "1"
      }
    ]
  },
  "LoadBalancer": "WeightRound",
  "ConsulService": {
    "IP": "localhost",
    "Port": "8500"
  }
}

GrpcServices Grpc服务列表

  ServiceName:服务名称,负载同一服务名称相同

  ServiceID:服务ID,保持唯一

  IP:服务IP

  Port:端口

  Weight:服务权重

修改 Program.cs 的 Main() 方法,调用 AddImplement(),注册全局配置和类到容器,注入使用 MsgClient 类的 GetSum() 方法,实现 Grpc 调用,代码如下

using Microsoft.Extensions.DependencyInjection;
using Snai.GrpcClient.Framework;
using Snai.GrpcClient.RpcClient;
using System; namespace Snai.GrpcClient
{
    class Program
    {
        static void Main(string[] args)
        {
            IServiceCollection service = new ServiceCollection();             //注册对象
            service.AddImplement();             //注入使用对象
            var provider = service.BuildServiceProvider();             string exeArg = string.Empty;
            Console.WriteLine("Grpc调用!");
            Console.WriteLine("-c\t调用Grpc服务;");
            Console.WriteLine("-q\t退出服务;");             while (true)
            {
                exeArg = Console.ReadKey().KeyChar.ToString();
                Console.WriteLine();                 if (exeArg.ToLower().Equals("c", StringComparison.CurrentCultureIgnoreCase))
                {
                    //调用服务
                    var rpcClient = provider.GetService<IMsgClient>();
                    rpcClient.GetSum(10, 2);
                }
                else if (exeArg.ToLower().Equals("q", StringComparison.CurrentCultureIgnoreCase))
                {
                    break;
                }
                else
                {
                    Console.WriteLine("参数异常!");
                }
            }
        }
    }
}

右击项目生成,最终项目结构如下:

aaarticlea/png;base64,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" alt="" />

到此客户端的代码实现已完成,下面运行测试 Grpc+Consul 服务注册、服务发现和负载均衡。

四、运行测试 Grpc+Consul 服务注册、服务发现和负载均衡

双击 startup.bat 启动 Consul,再启动服务5021和5022,启动成功打开 http://localhost:8500/ui/#/grpc-consul/services/GrpcService 查看服务情况

启动 Snai.GrpcClient 客户端

输入 c 调用Grpc服务,调用3次,5031调用2次,5032调用1次,成功实现负载均衡

关掉服务5022,等10秒左右(因为设置健康检查时间间隔10秒),再输入 c 调用Grpc服务,只调用5031

打开 http://localhost:8500/ui/#/grpc-consul/services/GrpcService 查看,5022 状态失败,或消失

Grpc+Consul实现服务注册、服务发现、健康检查和负载均衡已完成

Github源码地址:https://github.com/Liu-Alan/Grpc-Consul

博客地址:http://www.snaill.net/post/2

.net core grpc consul 实现服务注册 服务发现 负载均衡(二)的更多相关文章

  1. Spring Cloud Eureka 分布式开发之服务注册中心、负载均衡、声明式服务调用实现

    介绍 本示例主要介绍 Spring Cloud 系列中的 Eureka,使你能快速上手负载均衡.声明式服务.服务注册中心等 Eureka Server Eureka 是 Netflix 的子模块,它是 ...

  2. .Net Core Grpc Consul 实现服务注册 服务发现 负载均衡

    本文是基于..net core grpc consul 实现服务注册 服务发现 负载均衡(二)的,很多内容是直接复制过来的,..net core grpc consul 实现服务注册 服务发现 负载均 ...

  3. Spring Cloud Consul 实现服务注册和发现

    Spring Cloud 是一个基于 Spring Boot 实现的云应用开发工具,它为基于 JVM 的云应用开发中涉及的配置管理.服务发现.断路器.智能路由.微代理.控制总线.全局锁.决策竞选.分布 ...

  4. 基于 Consul 实现 MagicOnion(GRpc) 服务注册与发现

    0.简介 0.1 什么是 Consul Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置. 这里所谓的服务,不仅仅包括常用的 Api 这些服务,也包括软件开发过程 ...

  5. 微服务学习笔记(2)——使用Consul 实现 MagicOnion(GRpc) 服务注册和发现

    原文:微服务学习笔记(2)--使用Consul 实现 MagicOnion(GRpc) 服务注册和发现 1.下载打开Consul 笔者是windows下面开发的(也可以使用Docker). 官网下载w ...

  6. .net core Ocelot Consul 实现API网关 服务注册 服务发现 负载均衡

    大神张善友 分享过一篇 <.NET Core 在腾讯财付通的企业级应用开发实践>里面就是用.net core 和 Ocelot搭建的可扩展的高性能Api网关. Ocelot(http:// ...

  7. 温故知新,.Net Core遇见Consul(HashiCorp),实践分布式服务注册与发现

    什么是Consul 参考 https://www.consul.io https://www.hashicorp.com 使用Consul做服务发现的若干姿势 ASP.NET Core 发布之后通过命 ...

  8. .Net Core 商城微服务项目系列(二):使用Ocelot + Consul构建具备服务注册和发现功能的网关

    1.服务注册 在上一篇的鉴权和登录服务中分别通过NuGet引用Consul这个包,同时新增AppBuilderExtensions类: public static class AppBuilderEx ...

  9. Docker+Consul+Registrator 实现服务注册与发现

    Docker+Consul+Registrator实现服务注册与发现 逻辑图 实现nginx节点自动化加入容器IP代理 1.三台Consul agent server作为高可用通过Consul Tem ...

随机推荐

  1. 支付宝SDK ios快捷支付

    配置PartnerConfig.h的参数 //合作身份者id,以2088开头的16位纯数字 #define PartnerID @"" //收款支付宝账号 #define Sell ...

  2. models渲染字典&form表单上传文件&ajax上传文件

    {# {% for u in teacher_d.keys %}#} {# {% for u in teacher_d.values %}#} {% for k,u in teacher_d.item ...

  3. Python中类的定义与使用

    目标: 1.类的定义 2.父类,子类定义,以及子类调用父类 3.类的组合使用 4.内置功能 1.类的定义 代码如下: #!/usr/bin/env python #coding:utf8 class ...

  4. Java中spring读取配置文件的几种方法

    Spring读取配置XML文件分三步: 一.新建一个Java Bean: package springdemo; public class HelloBean { private String hel ...

  5. MQ java 基础编程

    MQ java 基础编程 编写人:邬文俊 编写时间 : 2006-2-16 联系邮件 : wenjunwu430@gmail.com 前言 通过 2 个多星期对 MQ 学习,在 partner 丁 & ...

  6. php5.3 php-fpm 开启 关闭 重启

    自php5.3开始,php源码中包含了php-fpm,不需要单独通过补丁的方式安装php-fpm,在源码安装的时候直接 configure 中增加参数 –enable-fpm 即可.   所以启动.关 ...

  7. Newtonsoft.Json自动升级版本号,导致dll冲突

    不知道怎么回事,vs偶尔会自动升级Newtonsoft.Json.dll的版本号,但是又不升级dll,仅仅是版本号变了,实际引用的dll还是原来的. 我用的是6.0.0的,然后版本号升级成了7.0.0 ...

  8. How to add libraries to “External Libraries” in WebStorm/PhpStorm/Intellij

    Stack Overflow Questions Developer Jobs Tags Users   Log In Sign Up Join Stack Overflow to learn, sh ...

  9. 2018.10.19 bzoj1057: [ZJOI2007]棋盘制作(悬线法)

    传送门 悬线法板题. 如果只求最大矩形面积那么跟玉蟾宫是一道题. 现在要求最大正方形面积. 所以每次更新最大矩形面积时用矩形宽的平方更新一下正方形答案就行了. 代码: #include<bits ...

  10. yii2 控制器的生命周期

    控制器生命周期 http://www.yii-china.com/doc/guide/structure_controllers.html 处理一个请求时,应用主体 会根据请求路由创建一个控制器,控制 ...