大多数使用 REST 的系统间通信组件都以 JSON 格式序列化其有效载荷。目前,JSON 缺乏广泛使用的模式验证标准: JSON 模式尚未普及。标准模式验证允许将验证工作委托给第三方库,然后就可以完成验证。如果没有标准,我们就必须在代码中进行手动验证。更糟糕的是,我们必须让验证代码与模式保持同步。XML 具有开箱即用的模式验证功能:XML 文档可以声明其必须符合的语法。基于 XML 的 SOAP 也能从中受益。
其他序列化替代方案也有模式验证选项:如 AvroKryoProtocol Buffers。有趣的是,gRPC 使用 Protobuf 跨分布式组件提供 RPC:

    gRPC 是一个可在任何环境中运行的现代开源高性能远程过程调用(RPC)框架。它可以高效地连接数据中心内和数据中心间的服务,并为负载平衡、跟踪、健康检查和身份验证提供可插拔的支持。它还适用于分布式计算的最后一英里,将设备、移动应用程序和浏览器连接到后端服务。

- 为什么选择 gRPC?

     此外,协议是一种二进制序列化机制,可节省大量带宽。因此,gRPC 是系统间通信的绝佳选择。但是,如果你的所有组件都使用 gRPC,那么简单的客户端如何调用它们呢?在本篇文章中,我们将构建一个 gRPC 服务,并展示如何从 cURL 调用它。

image

简单的 gRPC 服务
gRPC 文档详尽无遗,因此在此做一个总结:

gRPC 是一个远程过程调用框架。
它适用于多种语言。
它依赖于Protocol Buffers

image

Protocol Buffers是谷歌语言中立、平台中立、可扩展的结构化数据序列化机制--想想 XML,但更小、更快、更简单。您只需定义一次数据的结构化方式,然后就可以使用特殊生成的源代码,使用各种语言轻松地将结构化数据写入各种数据流或从各种数据流中读取结构化数据。

- Protocol Buffers

image

它是 CNCF 产品组合的一部分,目前处于孵化阶段。
      让我们建立我们的 gRPC 服务。我们将使用 Java、Kotlin、Spring Boot 和一个专用的 gRPC Spring Boot 集成项目。
项目结构包含两个项目:一个是模型项目,另一个是代码项目。让我们从模型项目开始。

重复使用一个简单的例子就足够了:请求发送一个字符串,响应以 Hello 作为前缀。我们在一个专门的 Protobuf 模式文件中设计这个模型:

syntax = "proto3";                                        //1

package ch.frankel.blog.grpc.model;                       //2

option java_multiple_files = true;                        //3
option java_package = "ch.frankel.blog.grpc.model";       //3
option java_outer_classname = "HelloProtos";              //3

service HelloService {                                    //4
     rpc SayHello (HelloRequest) returns (HelloResponse) {
     }
}

message HelloRequest {                                    //5
     string name = 1;                                      //6
}

message HelloResponse {                                   //7
     string message = 1;                                   //6
}

  1. Protobuf 定义版本
  2. 软件包
  3. Java 特定配置
  4. 服务定义
  5. 请求定义
  6. 字段定义: 首先是类型,然后是名称,最后是顺序。
  7. 响应定义

是对上说明。

我们将使用 Maven 生成 Java 模板代码:

<project>
   <dependencies>
     <dependency>
       <groupId>io.grpc</groupId>                         <!--1-->
       <artifactId>grpc-stub</artifactId>
       <version>${grpc.version}</version>
     </dependency>
     <dependency>
       <groupId>io.grpc</groupId>                         <!--1-->
       <artifactId>grpc-protobuf</artifactId>
       <version>${grpc.version}</version>
     </dependency>
     <dependency>
       <groupId>jakarta.annotation</groupId>              <!--1-->
       <artifactId>jakarta.annotation-api</artifactId>
       <version>1.3.5</version>
       <optional>true</optional>
     </dependency>
   </dependencies>
   <build>
     <extensions>
       <extension>
         <groupId>kr.motd.maven</groupId>                 <!--2-->
         <artifactId>os-maven-plugin</artifactId>
         <version>1.7.1</version>
       </extension>
     </extensions>
     <plugins>
       <plugin>
         <groupId>org.xolstice.maven.plugins</groupId>    <!--3-->
         <artifactId>protobuf-maven-plugin</artifactId>
         <version>${protobuf-plugin.version}</version>
         <configuration>
           <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
           <pluginId>grpc-java</pluginId>
           <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
         </configuration>
         <executions>
           <execution>
             <goals>
               <goal>compile</goal>
               <goal>compile-custom</goal>
             </goals>
           </execution>
         </executions>
       </plugin>
     </plugins>
   </build>
</project>

1)编译时依赖性

2)嗅探操作系统信息;

3)用于下一个插件 从 proto 文件生成 Java 代码。


编译后的结构应如下所示:

image

我们可以将这些类打包到 JAR 中,然后在网络应用项目中使用。后者使用 Kotlin,但这只是因为它是我最喜欢的 JVM 语言。

我们只需要一个特定的 Spring Boot 启动器依赖项,就能将 gRPC 端点与 Spring Boot 集成:

<dependency>
   <groupId>net.devh</groupId>
   <artifactId>grpc-server-spring-boot-starter</artifactId>
   <version>2.14.0.RELEASE</version>
</dependency>

下面是最重要的部分Kotlin:

@GrpcService                                                        //1
class HelloService : HelloServiceImplBase() {                       //2
   override fun sayHello(
       request: HelloRequest,                                        //2
       observer: StreamObserver<HelloResponse>                       //3
   ) {
     with(observer) {
       val reply = HelloResponse.newBuilder()                        //2
                                .setMessage("Hello ${request.name}") //4
                                .build()
       onNext(reply)                                                 //5
       onCompleted()                                                 //5
     }
   }
}

grpc-server-spring-boot-starter 会检测到注释并开始工作。 上述项目中生成的参考类 方法签名允许使用 StreamObserver 参数。该类来自 grpc-stub.jar。
获取请求并添加前缀以生成响应信息。 播放事件。
现在我们可以使用 ./mvnw spring-boot:run 启动网络应用程序:


测试 gRPC 服务

这篇文章的主旨是,使用常规工具访问 gRPC 服务是不可能的。为了测试,我们还是需要一个专用工具。我找到了 grpcurl。让我们安装它并用它列出可用的服务:

grpcurl --plaintext localhost:9090 list   #1-2

列出所有无需 TLS 验证的可用 gRPC 服务。
为避免 gRPC 与其他通道(如 REST)发生冲突,Spring Boot 使用了另一个端口。

输出

ch.frankel.blog.grpc.model.HelloService   #1
grpc.health.v1.Health                            #2
grpc.reflection.v1alpha.ServerReflection  #2

我们还可以深入研究服务的结构:

grpcurl --plaintext localhost:9090 describe ch.frankel.blog.grpc.model.HelloService

输出

service HelloService {
   rpc SayHello ( .ch.frankel.blog.grpc.model.HelloRequest ) returns ( .ch.frankel.blog.grpc.model.HelloResponse );
}

最后,我们可以使用数据调用服务:

grpcurl --plaintext -d '{"name": "John"}' localhost:9090 ch.frankel.blog.grpc.model.HelloService/SayHello

输出

{
   "message": "Hello John"
}

使用常规工具访问 gRPC 服务 想象一下,我们有一个常规的 JavaScript 客户端应用程序需要访问 gRPC 服务。有什么其他方法吗?

image

一般的方法是通过 grpc-web

用于浏览器客户端的 gRPC JavaScript 实现。更多信息(包括快速入门),请参阅 gRPC-web 文档。

gRPC-web 客户端通过一个特殊的代理连接到 gRPC 服务;默认情况下,gRPC-web 使用 Envoy。

未来,我们希望 gRPC-web 能在 Python、Java 和 Node 等语言的特定网络框架中得到支持。有关详情,请参阅路线图。

- grpc-web

说明中只提到了一个限制:它只适用于 JavaScript(截至目前)。不过,还有另一个限制。它非常麻烦。你需要获取 proto 文件,生成模板代码,然后让你的代码调用它。你必须为每一种客户类型都这样做。更糟糕的是,如果原型文件发生变化,你还需要重新生成每种类型的客户端代码。

不过,如果你使用的是 API Gateway,还有一种替代方法。grpc-transcode 是一个可以将 REST 调用转码为 gRPC 并返回的插件:

image

第一步是在 Apache APISIX 中注册 proto 文件:

curl http://localhost:9180/apisix/admin/protos/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d "{ \"content\": \"$(sed 's/"/\\"/g' ../model/src/main/proto/model.proto)\" }

第二步是使用上述插件创建路由:

curl http://localhost:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
   "uri": "/helloservice/sayhello",                           #1
   "plugins": {
     "grpc-transcode": {
       "proto_id": "1",                                       #2
       "service": "ch.frankel.blog.grpc.model.HelloService",  #3
       "method": "SayHello"                                   #4
     }
   },
   "upstream": {
     "scheme": "grpc",
     "nodes": {
       "server:9090": 1
     }
   }
}'

1) 定义细粒度路由。
2) 参考上一条命令中定义的 proto 文件
3) gRPC 服务
4) gRPC 方法

此时,任何客户端都可以向定义的端点发出 HTTP 请求。Apache APISIX 会将调用转码为 gRPC,将其转发到定义的服务,获取响应,然后再次转码:

curl localhost:9080/helloservice/sayhello?name=John

输出

{"message":"Hello John"}

与 grpc-web 相比,API Gateway 方法只允许与一个组件(即 Gateway 本身)共享 proto 文件。

转码的好处

在这一点上,我们可以利用 API Gateway 的功能。试想一下,如果没有传递名称,我们需要一个默认值,例如 "World"。开发人员会很乐意在代码中设置它,但对该值的任何更改都需要完整的构建和部署。如果我们将默认值放在网关的路由处理链中,则几乎可以立即更改。让我们对路由进行相应的更改:

curl http://localhost:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '
{
   "uri": "/helloservice/sayhello",
   "plugins": {
     "grpc-transcode": {
       ...
     },
     "serverless-pre-function": {                    #1
       "phase": "rewrite",                           #2
       "functions" : [
         "return function(conf, ctx)                 #3
           local core = require(\"apisix.core\")
           if not ngx.var.arg_name then
             local uri_args = core.request.get_uri_args(ctx)
             uri_args.name = \"World\"
             ngx.req.set_uri_args(uri_args)
           end
         end"
       ]
     }
   },
   "upstream": {
       ...
   }
}'

1) 当没有合适的通用插件时,重写请求。

2) 神奇的 Lua 代码可以做到这一点

3)现在,我们可以用空参数来执行请求,并得到预期的结果:

curl localhost:9080/helloservice/sayhello?name

输出

{"message":"Hello World"}

结论

      在本篇博文中,我们简要介绍了 gRPC 及其对服务间通信的益处。我们使用 Spring Bootgrpc-server-spring-boot-starter 开发了一个简单的 gRPC 服务。但这是有代价的:普通客户端无法访问该服务。我们不得不使用 grpcurl 进行测试。基于 JavaScript 或浏览器的客户端也是如此。绕过这一限制,我们可以利用 API 网关。演示了如何使用 grpc-transcode 插件配置 Apache APISIX,以达到预期效果。APISIX本身是基于OpenResty扩展,可以支持Lua脚步

更多



今天先到这儿,希望对云原生,技术领导力, 企业管理,系统架构设计与评估,团队管理, 项目管理, 产品管管,团队建设 有参考作用 , 您可能感兴趣的文章:
领导人怎样带领好团队
构建创业公司突击小团队
国际化环境下系统架构演化
微服务架构设计
视频直播平台的系统架构演化
微服务与Docker介绍
Docker与CI持续集成/CD
互联网电商购物车架构演变案例
互联网业务场景下消息队列架构
互联网高效研发团队管理演进之一
消息系统架构设计演进
互联网电商搜索架构演化之一
企业信息化与软件工程的迷思
企业项目化管理介绍
软件项目成功之要素
人际沟通风格介绍一
精益IT组织与分享式领导
学习型组织与企业
企业创新文化与等级观念
组织目标与个人目标
初创公司人才招聘与管理
人才公司环境与企业文化
企业文化、团队文化与知识共享
高效能的团队建设
项目管理沟通计划
构建高效的研发与自动化运维
某大型电商云平台实践
互联网数据库架构设计思路
IT基础架构规划方案一(网络系统规划)
餐饮行业解决方案之客户分析流程
餐饮行业解决方案之采购战略制定与实施流程
餐饮行业解决方案之业务设计流程
供应链需求调研CheckList
企业应用之性能实时度量系统演变

如有想了解更多软件设计与架构, 系统IT,企业信息化, 团队管理 资讯,请关注我的微信订阅号:

MegadotnetMicroMsg_thumb1_thumb1_thu[2]

作者:Petter Liu
出处:http://www.cnblogs.com/wintersun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 该文章也同时发布在我的独立博客中-Petter Liu Blog。

posted on 2023-12-02 14:47  PetterLiu  阅读(98)  评论(0编辑  收藏  举报