前言

系统分布式已经成为程序员的家常,将大型单体划分为相对简单的小模块,分散系统能力,提升系统扩展性、功能模块复用性等;各功能模块之间肯定会有很多数据共享和交互的应用场景,那就避免不了各模块之间的通信;目前用的比较多的方式是HTTP(Restful API)接口、消息队列等,而HTTP(Restful API)接口应该是目前应用比较广泛的,相对之前的webservice和WCF都显得比较轻量级,而且实用;

随着微服务的盛行,对服务间的通信要求也越来越高,比如传输方式、传输速率、传输内容大小等,而HTTP(Restful API)方式有较重的头信息、无状态、不能复用连接等缺点,所以优化是早晚的事;那么gRPC的出现就显得情有可原啦(gRPC不局限于服务间通信,只要符合Server/Client场景就可以),所以接下来咱们一起来探究学习一下。

正文

1. 认识一下gRPC

gRPC 由 google 开发,最前面的g就代表google。进入gRPC官网,一句话描述了gRPC的主要特色,先上张图:

A high performance, open source universal RPC framework

翻译:一个高性能开源通用RPC框架

  • 高性能:gRPC遵循HTTP/2协议,解决并优化了HTTP1.1的一些缺陷;默认使用谷歌开源的 protocol buffers(类似于XML、JSON的数据序列化结构协议),传输速率、解析速度都很快、压缩率高,性能整体都比XML和JSON好(后续专门写个程序来比较比较);
  • 开源:源码地址请进传送门
  • 通用:各种流行语言(C++、C#、Java、Go、Python等)都能用,轻松实现跨语言通信;本身不限于任何平台。
  • RPC:远程过程调用(Remote Procedure Call),通俗一点理解,就是分布式中各服务之间调用的一种技术。

总而言之,gRPC是一个现代的开源高性能远程过程调用(RPC)框架,可以在任何环境中运行。它可以有效地连接数据中心内和跨数据中心的服务,并支持可插拔的负载均衡、跟踪、健康检查和身份认证(后续会一一举例演示)。

算啦,估计到这有些小伙伴还是有点懵圈(语言组织能力还有待提高),那就先将其理解为一个类似于WebAPI的调用框架,只是性能更高,使用更简单,就像调用本地方法一样;它使客户端和服务器应用程序能够透明地通信,隐藏了远程调用的细节,大概过程如下:

上图简析:各语言之间可以互相调用,只要客户端按照约定(Protocol Buffer)传递对应的请求参数,调用服务端对应的方法,最后就会返回约定好(Protocol Buffer)的响应数据。

不说那么多啦,直接开干吧,一边撸码一边说理论。

2. 初体验 gRPC

2.1 从0开始写服务端
  • 创建一个空的Web项目(基于.NetCore3.1),引入包Grpc.AspNetCore

  • 编写proto文件(重点),因为gRPC是使用protocol Buffer作为接口定义语言,内容包含以下两部分:

    传递的消息:请求和响应时的数据信息,类似于现在用的DTO类。

    gRPC服务的定义:定义gRPC服务方法,可以理解为现在写的Restful 接口。

    这里模拟用户维护的场景,包含增、删、改、查方法,这里新建protos目录专门用来存放proto文件,user.proto内容如下:

    // 使用的是proto3版本
    syntax = "proto3";
    // 定义命名空间,后续生成代码时就会生成对应的命名空间
    option csharp_namespace = "gRPC.Demo.Server.protos"; /*
    每一句需要用分号结尾
    message 用来定义请求和返回数据格式
    tag message后面的值数字代表是字段的标识(tag),不是赋值,
    */ // 新增用户时需要传递数据消息, 可理解为一个类
    message AddUserReuqest{
    string name=1;
    int32 age=2;
    bool isBoy=3;
    }
    // 新增时返回的消息格式
    message ResultResponse {
    int32 code=1;
    string msg =2;
    }
    //传递的查询条件信息格式,可理解为平时传入的查询条件对象
    message QueryUserReuqest{
    string name=1;
    }
    //查询返回的用户信息格式,可理解为返回的类
    message UserInfoResponse {
    string name=1;
    int32 age=2;
    string gender=3;
    } // service 用标识定义服务的,里面写对应的方法
    service UserService{
    // 新增用户
    rpc AddUser(AddUserReuqest) returns (ResultResponse);
    // 查询用户
    rpc GetAllUser(QueryUserReuqest) returns (UserInfoResponse);
    }

    proto文件,各种编程语言是通用的,都可以根据定义好的proto文件生成对应编程语言的代码,重写业务即可。对于自动生成代码,其他编程语言需要借助protoc 工具(点这里去下载),而.Net微软已经封装好了,只要引入了Grpc相关的包,直接编译即可。先简单设置一下proto文件属性,大概步骤如下:

    注:如果不想按照上面步骤设置文件属性,也可以直接编辑项目文件也行,如下增加红框部分:

    最后编译程序就可以自动生成对应的服务端代码了,如下路径:

  • 重写生成的方法,增加业务逻辑

    proto文件生成的代码只是一个约定,不包含业务逻辑,关于具体的业务需要自己处理,如下:

  • 在Startup.cs文件中开启gRPC的功能,如下:

    到这服务端就完成了,现在就去写一个客户端连接一下;

2.2 搞一个客户端访问服务
  • 创建一个控制台程序

    引入Google.Protobuf、Grpc.Net.Client、Grpc.Tools三个包,然后将服务端写好的proto文件拷贝到项目中,按照上面说的步骤设置一下proto文件属性为Client only,编译即可,项目结构如下:

    如果不想通过设置属性的方式,也可以直接修改项目文件,然后编译就可自动生成代码:

  • 现在可以写业务啦

    新增用户调用远程服务逻辑:

    查询用户调用远程服务逻辑:

    从上面代码来看,使用是不是比较简单,只需要知道远程地址即可,远程调用就像调用本地方法一样,也不用判断状态码,不用自己去解析内容,而是按照约定直接返回结果。

  • 先运行服务端,再运行客户端,效果如下:

    调试时注意:

    由于gRPC使用SSL/TLS保护服务,在调试的时候尽管指定为https连接也会报错,这是因为系统没有信任微软开发证书,执行以下命令即可:

    dotnet dev-certs https --trust

    然后就可以正常调试了;当然在生产环境中,还是需要配置真实证书的,如果不想用证书也是可以滴,这个后续单独会说到。

2.3 使用.NetCore模板快速生成服务端

.NetCore已经将gRPC服务端封装成一个模板了,可以通过模板快速创建一个gRPC项目,如下:

创建出来的服务端项目基本和刚刚创建的一样,编译运行就能启动;小伙伴后续可以直接用模板创建,在里面添加对应的业务代码即可。

本来打算把gRPC的四种模式接着演示的,但考虑到proto文件的编写是一个重点,所以总结了一些常用的写法,先熟悉熟悉proto文件的编写,后续再通过案例演示加深印象就更好啦。

3. Protocol Buffer 是重点

通过上面示例演示,proto文件用于约定服务接口,各编程语言用其可以生成对应的代码,然后进行业务逻辑的编写,可见proto文件扮演了很重要的角色。在性能方便是以二进制格式进行解析,内容小,传输效率高,适合传递大量数据的场景。

proto文件中每一个message代表了一类结构化的数据,message 里面定义了每一个属性的类型和名字,并指定一个Tag(每个属性后面的数字)。在gRPC传输过程中是通过Tag这个数字进行标识属性的,不是用属性名

3.1 数据类型

由于protocol Buffer 不限于编程语言,所以在编写proto文件时,指定的类型和不同编程语言的类型是一一对应的,这样在根据proto文件生成对应代码时,约束力就比较强,大概的类型对照表如下(只整理了比较常用的语言,详细请进官网):

每一种类型在没有指定值时都对应有默认值:

  • string: 空字符串
  • bytes:空byte数组
  • bool:false
  • 数值型:0
  • 枚举enum: 默认第一个枚举值,第一个值必须是0

默认值其实基本上和对应的编程语言默认值差不多一样。

3.2 常用编写方式
  • 一般形式

    通过message定义一类结构化数据,如下:

    使用message定义一类数据时,其实就可以理解为在编写一个类,里面的字段就是类里面的属性。需要注意的是message中指定字段的后面数值不是字段值,而是字段名称对应的一个标识(Tag)。

  • 嵌套自定义类型

    经常会遇到这种嵌套自定义类型的场景,比如一个班级有多个学生,班级类型就需要嵌套学生,如下:

    这里用到repeated来表示一个班级有多个学生,可以理解为List.

  • 使用枚举

    枚举在编程过程中肯定少不了,这里把上面的性别改为枚举,如下:

    枚举类型需要注意的是第一项映射的常量值必须为0,方便设置其作为默认项,同时也是为了兼容proto2。

    如果想要一个数字对应多个枚举项,可以开启允许别名的方式进行设置,如下:

  • 文件引入

    在实际开发场景中,定义的数据类型很多,放在一个文件肯定不太合适,可读性差,所以都会将其放在多个proto文件,然后通过引用多个文件即可;接下来把学生单独出来作为一个proto文件,如下:

    注:在创建proto文件时,按照2.1那个步骤设置文件属性,否则不会自动生成类,如果有文件相互引用,还会编译报错。

  • 引用第三方proto

    在平时开发的时候,有些机构、社区或是大佬将常用的类进行封装,然后打包好, 我们只需要下载引用就可以愉快的使用啦;同样,proto也可以这样,如下在Student中加一个入学时间,如下:

    其他类型进传送门,需要哪个,就照着上面方式使用即可。

3.3 参数字段变更注意

需求变动对于开发来说是家常便饭,针对接口增加字段或修改字段都是常有的事,如果打算在原有方法修改字段,那就需要注意啦; 在通过protocol Buffer进行序列化时,是通过类似于字典的形式约定好的,其中字段后面的数字Tag至关重要,用其进行属性字段标识,学生举例如下:

// 在解析时,都是用后面的数字tag进行约定解析的
message Student{
string name=1; // 1 就代表 姓名
int32 age=2; // 2 就代表 年龄
Gender gender=3; // 3 就代表 性别
string addr=4; // 4 就代表 地址
google.protobuf.Timestamp enrollmentDate=5; //5 就代表 入学时间
}

只要已经上线互相调用,如果有参数字段变动,最好是通过新增的方式,因为这样会避免双方调用因为没有同时更改参数字段造成对业务的影响。比如上面学生字段要变动,需求是:新增一个分数字段,删除年龄字段,通常会有以下方式:

  • 方式1:将年龄字段直接改成分数字段即可

    如果服务端将proto文件改成这样,客户端还没来的及改,就会造成如下情况:

    2这个tag标识在服务端代表分数,在传递数据的时候就将分数赋值给该字段;但对于客户端来说,还是认为2这个tag标识为年龄,接收的时候还是以年龄字段进行处理,最终就会影响原有解析逻辑,并影响到业务。

  • 方式2:删除年龄字段并将其对应tag标识设置为保留字段,然后新增一个分数字段

    通过reserved将原有参数字段和对应的tag标识设为保留值,这样不允许用作其他业务,就会避免业务逻辑处理异常的情况,最多是就接受方没有收到值,按默认值处理即可。

关于字段的更新注意先暂时说这么多,类型之间互相兼容的细节小伙伴去官网好好看看。 这里记住一个重点:tag 标识不能随便进行重用,不然proto文件没有及时同步就有可能导致业务逻辑处理有问题。

总结

.NetCore对于gRPC已经是封装得比较方便啦,引入对应的包,编译自动根据.proto文件生成对应代码,然后直接写业务即可;下一篇说说关于gRPC四种模式及gRPC服务的认证、授权。

一个被程序搞丑的帅小伙,关注"Code综艺圈",和我一起学~~~

gRPC趁现在还没大火,抢先了解一下的更多相关文章

  1. 在各方面还没准备好的时候,大家一定要慎用border-box样式!!!!

    这几天,我被一个js问题困扰到癫狂了! 事情是这样的,我之前写了个功能非常复杂的纯jquery代码的前端gridview控件,实现了大量的功能和效果,在一些项目里也用得很好. 最近有个项目,样式做了调 ...

  2. 成功熬了四年还没死?一个IT屌丝创业者的深刻反思

    三个IT屌丝创业的故事 从前有三个屌丝,聚在一起做网络.提供免费的网络服务,砸锅卖铁,通宵达旦,除了卖肾,啥都做了.3年后终于做到了五百万用户.对于年轻人来说,能把五百万人玩弄于鼓掌之间,已经是很牛逼 ...

  3. php大力力 [013节]mySQL数据库乱码问题我还没解决

    <?php echo"测试<br>"; $sql_connection = mysql_connect("localhost","e ...

  4. 你好,C++(21)只要天还没黑,就一直在工地干活-4.3.1 while循环:只要…就一直…

    4.3  循环控制语句 在现实世界中,有这样一类现象: 只要油箱中的当前油量小于油箱容量100升,就一直往油箱中加油: 一直不断地为祖国辛勤工作,只要我还活着: 公司100000位员工,每个人的工资都 ...

  5. 谁还没遇上过NoClassDefFoundError咋地——浅谈字节码生成与热部署

    谁还没遇上过NoClassDefFoundError咋地--浅谈字节码生成与热部署 前言 在Java程序员的世界里,NoClassDefFoundError是一类相当令人厌恶的错误,因为这类错误通常非 ...

  6. noi2018还没想好记

    前面说点什么.. 没想到吧 嘴上说着不写的彩笔博主最后还是写了这篇东西.. Day -inf 在雅礼集训,打了四场模拟赛.. 真正说打得好的.. 也就那么一场 身体很差 心态很差 状态很差 虽然有书读 ...

  7. C-Lodop提示“网页还没下载完毕,请稍等一下再操作.”

    该提示在Lodop旧版本中是: 提示"WebSocket没准备好,点确定继续",提示“C-Lodop没准备好”,新版本修改了该提示的描述“网页还没下载完毕,请稍等一下再操作.”,让 ...

  8. web工程启动时,在一个类中延迟加载Bean,因为该Bean类可能还没被JVM加载

     问题描述: (1)javaWeb项目启动中,还没启动完成,在下面这个类加载另一个Bean类, (2)通过getBean方法获取到该Bean,可以获取到,不为null (3)但是,调用该Bean的方法 ...

  9. 趁webpack5还没出,先升级成webpack4吧

    上一次将webpack1升级到3,也仅是 半年前,前端工具发展变化太快了,如今webpack4已经灰常稳定,传说性能提升非常高,值得升级. 一直用着的webpack3越来越慢,一分多钟的编译时间简直不 ...

随机推荐

  1. c#RSA 私钥加签公钥解签

    /// RSA签名 /// </summary> /// <param name="data">待签名数据</param> /// <pa ...

  2. SE_Work1_阅读构建之法&项目管理实践

    项目 内容 课程:北航-2020-春-软件工程 博客园班级博客 要求:阅读<构建之法>并回答问题 个人博客作业 我在这个课程的目标是 提升团队管理及合作能力,开发一项满意的工程项目 这个作 ...

  3. .Net平台的GC垃圾回收

    一.先了解下必备的知识前提 内存中的托管与非托管,可简单理解为: 托管:可借助GC从内存中释放的数据对象(以下要描述的内容点) 非托管:必须手工借助Dispose释放资源(实现自IDisposable ...

  4. Linux工程师必备的88个监控工具

    Linux工程师必备的88个监控工具 https://learn-linux.readthedocs.io/zh_CN/latest/maintenance/monitor/tools/80-linu ...

  5. linux 磁盘管理命令之df-(转自 Howie的专栏)

    linux中df命令的功能是用来检查linux服务器的文件系统的磁盘空间占用情况.可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息. 1.命令格式: df [选项] [文件] 2.命 ...

  6. 90%的人都不知道的Node.js 依赖关系管理(上)

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文参考:https://dzone.com/articles/nodejs-dependency-mana ...

  7. xshell中登录服务器图形化界面

    安装全套的xmanager程序 打开xshell工具程序 点击新建 输入ip等必要信息 点击隧道,英文版为tunnel 勾选红色的选项1和2 填写一些用户名和密码信息(图就略了O(∩_∩)O) 敲入指 ...

  8. 更短且不失高效的UUID生成算法

    Java原生的UUID长度为36位,嫌长 这里自己实现了一套自己的算法,来生成较短的UUID 由雪花算法启发而来, 大致原理是利用时间戳+随机值做值,然后转换成62进制(当然这个进制数你也可以搞成更多 ...

  9. win10家庭中文版CUDA+CUDNN+显卡GPU使用tensorflow-gpu训练模型安装过程(精华帖汇总+重新修改多次复现)

    查看安装包 pip list 本帖提供操作过程,具体操作网上有好多了,不赘述.红色字体为后来复现出现的问题以及批注 题外话: (1)python 的环境尽量保持干净,尽量单一,否则容易把自己搞晕,不知 ...

  10. NNVM AI框架编译器

    NNVM AI框架编译器 深度学习已变得无处不在且不可或缺.看到对在多种平台(例如手机,GPU,IoT设备和专用加速器)上部署深度学习工作负载的需求不断增长.TVM堆栈弥合深度学习框架与面向性能或效率 ...