SGwanmin

在学习C语言和数据结构,,, 日了,上学时候没感觉啥的,学的越多发现得调头翻大学教材了,啊,西八!

导航

微服务间通信-gRPC

gRPC的搭建和使用

前言

微服务/进程间通信,主要是数据交换,在几个项目中使用过他,理解的不深,因为没有遇到过bug,,,emmm

下文就是主要介绍一下gRPC的一些基本概念和搭建使用,文末会有Demo Code。

RPC

RPC 的全称是 Remote Procedure Call 是一种进程间通信方式,主要功能目标是让构建分布式应用更容易,在提供强大的远程调用能力时不损失本地调用的语义简洁性。为实现该目标,RPC 框架提供一种透明调用机制让使用者不必显式的区分本地调用和远程调用。

简单的说:

  • RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服务器)上的一个函数或方法(可以统称为服务)并得到返回的结果。
  • RPC 会隐藏底层的通讯细节(不需要直接处理Socket通讯或Http通讯) RPC 是一个请求响应模型。
  • 客户端发起请求,服务器返回响应(类似于Http的工作方式) RPC 在使用形式上像调用本地函数(或方法)一样去调用远程的函数(或方法)。

rpc的通信过程:默认socket通信。本地机器的RPC框架反序列化出执行结果,函数return这个结果

 

RPC使用的是Http请求,那么我们思考一个问题,既然有Http了那为什么要用RPC调用呢~

很简单 效率! 通用定义的http1.1协议的tcp报文包含了太多废信息,比如这样

 

HTTP/1.0 200 OK 
Content-Type: text/plain
Content-Length: 137582
Expires: Thu, 05 Dec 1997 16:00:00 GMT
Last-Modified: Wed, 5 August 1996 15:55:28 GMT
Server: Apache 0.84

<html>
  <body>Hello World</body>
</html>

 
请求的body是使用的二进制编码协议,而报文的元数据也就死header头的键值对却用了文本编码,非常占字节数。而使用自定义的tcp协议的报文会极大的精简了传输的内容。良好的rpc调用是面向服务的封装,针对服务的可用性和效率等都做了优化。单纯使用http调用则缺少了这些特性。

RPC就介绍这么多,毕竟我们的主题是gRPC,更多详细解释,以及常见的restful对比可以参考:

 RPC简介及框架选择

 HTTP请求和RPC调用

上面有一个问题哈!为什么会考虑RPC和restful API比较,因为他们都是server/client模型!而且他们都是使用http作为底层的传输协议(严格地说, gRPC使用的http2.0,RPC使用的是http1.1,而restful api则不一定)。

restfule 可以理解为主体对象是资源,而RPC是动作,一个动词,表示我要做什么!

 gRPC

 gRPC是由Google主导开发的RPC框架,使用HTTP/2协议并用ProtoBuf作为序列化工具. gRPC是一个现代的开源高性能RPC框架,可以在任何环境中运行。它可以有效地连接数据中心内和跨数据中心的服务,并支持可插拔的负载平衡、跟踪、健康检查和身份验证。

gRPC作为RPC的一种升级版本,和restful api相比有一些特有的优势:

  • gRPC可以通过protobuf来定义接口,从而可以有更加严格的接口约束条件。
  • 另外,通过protobuf可以将数据序列化为二进制编码,这会大幅减少需要传输的数据量,从而大幅提高性能。
  • gRPC可以方便地支持流式通信(理论上通过http2.0就可以使用streaming模式, 但是通常web服务的restful api似乎很少这么用,通常的流式数据应用如视频流,一般都会使用专门的协议如HLS,RTMP等,这些就不是我们通常web服务了,而是有专门的服务器应用。
这里有出现一个新的名字 protobuf~

简单点记就可以了 protobuf 是一种无关语言,无关平台的,可扩展的序列化结构数据放法,一般用于通信和存储~
这里我们只介绍一下它的数据结构和一些常用的数据类型即可
// 例1: 在 xxx.proto 文件中定义 Example1 message
message Example1 {
    string stringVal = 1;
    optional bytes bytesVal = 2;
    message EmbeddedMessage {
        int32 int32Val = 1;
        string stringVal = 2;
    }
    EmbeddedMessage embeddedExample1 = 3;
    int32 repeatedInt32Val = 4;
    repeated string repeatedStringVal = 5;
} 
message xxx {
  // 字段规则:required -> 字段只能也必须出现 1 次
  // 字段规则:optional -> 字段可出现 0 次或1次
  // 字段规则:repeated -> 字段可出现任意多次(包括 0)一般用于List
  // 类型:int32、int64、sint32、sint64、string、32-bit ....
  // 字段编号:0 ~ 536870911(除去 19000 到 19999 之间的数字)
  字段规则 类型 名称 = 字段编号;
}
字段的类型都是一些常见类型,不介绍了,就我的经验而言,为了规范接口,对外部开放,我们通常是将参数/返回值字段显示的写好。平时作为项目内部服务间的通信,通常则使用序列化成string,直接传输。

其他的情况,这里就不介绍了,详情可查看这里:protobuf详解深入protobuf-简介

正如我前面提到过的,gRPC可以通过protobuf来对接口进行严格约束,比如提供公共服务时,
而我在项目中使用gRPC时,通常是为了提高性能,通过http2来实现项目中的异步操作。

话不多数下面直接上代码,跨微服务,实现 hello world。

 

 gRPC Hello World 实例详解

 

1.先创建3个工程

两个微服务(一个server端一个client端)和一个class library(为gRPC)

 

 

 2..下载包Package

Grpc.AspNetCore
Grpc.Core

 

3.规范 gRPC method

定义方法

syntax = "proto3";

option csharp_namespace = "Demo.Grpc.Protos";
//引用
import "Helloworld/HelloWorldMsg.proto";

//定义gRPC method
service GHelloWorld {
	rpc GetHelloWorld (HelloWorldMsg) returns (HelloWorldReturnMsg);

} 

 定义参数

syntax = "proto3";

option csharp_namespace = "Demo.Grpc.Protos";

//定义gRPC parameter

message HelloWorldMsg {
     string id=1;
}


message HelloWorldReturnMsg {
     string context = 1;
     bool status = 2;
}

 build gRPC工程后,检查,是否已经添加

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Grpc.AspNetCore" Version="2.44.0" />
    <PackageReference Include="Grpc.Core" Version="2.45.0" />
  </ItemGroup>
	<ItemGroup>
		<None Remove="HelloWorld\HelloWorld.proto" />
		<None Remove="HelloWorld\HelloWorldMsg.proto" />
  </ItemGroup>
  <ItemGroup>
		<Protobuf Include="HelloWorld\HelloWorld.proto" />
	    <Protobuf Include="HelloWorld\HelloWorldMsg.proto" />
  </ItemGroup>
</Project>

4.Server端配置

1.实现HelloWorld方法,注意继承GHelloWorld.GHelloWorldBase,
注意:需要引用gRPC工程,如果依然无法加载,建议重新build gRPC工程。

namespace Server
{
    public class HelloWorld : GHelloWorld.GHelloWorldBase
    {
        public HelloWorld()
        {

        }
        //实现gRPC服务 GetHelloWorld 方法
        public override Task<HelloWorldReturnMsg> GetHelloWorld(HelloWorldMsg request, ServerCallContext context)
        {
            var msg = new HelloWorldReturnMsg() { Context = "Hello World!", Status = true };
            return Task.FromResult(msg);
        }
    }
}

2. 添加gRPC服务

builder.Services.AddGrpc(s =>
{
    s.EnableDetailedErrors = true;
    s.MaxReceiveMessageSize = int.MaxValue;
    s.MaxSendMessageSize = int.MaxValue;
}); 

3.配置program,在Endpoint处添加Service

app.UseEndpoints(endpoints =>
{
    //对外暴露gRPC服务
    endpoints.MapGrpcService<HelloWorld>();
});

5.Client端 调用

1.同上,添加gRPCService

builder.Services.AddGrpc(s =>
{
    s.EnableDetailedErrors = true;
    s.MaxReceiveMessageSize = int.MaxValue;
    s.MaxSendMessageSize = int.MaxValue;
});

2.添加Client 调用配置

//client add gRPC service
builder.Services.AddGrpcClient<GHelloWorld.GHelloWorldClient>(opts =>
{
    //这里必须是https !!!!Server端地址
    opts.Address = new Uri("https://localhost:5267");
    //配置client通道
    opts.ChannelOptionsActions.Add(a =>
    {
        a.MaxReceiveMessageSize = null;
        a.HttpHandler = null;
        a.ThrowOperationCanceledOnCancellation = false;
        a.CompressionProviders = null;
        a.DisableResolverServiceConfig = false;
    });
    //配置监听器   InterceptorScope.Client 可以为每个client端创建一个新的侦听器
}).AddInterceptor<LoggingInterceptor>(/*InterceptorScope.Client*/); 

3.gRPC client 调用

using Client.Service;
using Demo.Grpc.Protos;

namespace Client
{
    public class HelloWorldService: IHelloWorldService
    {
        private readonly GHelloWorld.GHelloWorldClient _gHelloWorldClient;
        public HelloWorldService(GHelloWorld.GHelloWorldClient gHelloWorldClient)
        {
            _gHelloWorldClient = gHelloWorldClient;
        }
        
        public string HelloWorld()
        {         
            try
            {
               return _gHelloWorldClient.GetHelloWorld(new HelloWorldMsg() { Id = "hello world id" }).Context;
            }
            catch (Exception e)
            {
                //logger.Error
                throw;
            }
        }
    }
}

  

代码已同步到GitHub:https://github.com/Seth-Song/gRPC-CAP/tree/main/gRPC-HelloWorld





 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2022-04-17 21:30  GwanMin  阅读(324)  评论(0)    收藏  举报