赞
踩
gRPC 基于服务的思想:定义一个服务,描述这个服务的方法以及入参出参,服务器端有这个服务的具体实现,客户端保有一个存根,提供与服务端相同的服务。
gRPC 默认采用 protocol buffer 作为 IDL (Interface Description Lanage) 接口描述语言,服务之间通信的数据序列化和反序列化也是基于 protocol buffer 的,因为 protocol buffer 的特殊性,所以 gRPC 框架是跨语言的通信框架(与编程语言无关性),也就是说用 Java 开发的基于 gRPC 的服务,可以用 GoLang 编程语言调用。
gRPC 同时支持同步调用和异步调用,同步 RPC 调用时会一直阻塞直到服务端处理完成返回结果, 异步 RPC 是客户端调用服务端时不等待服务段处理完成返回,而是服务端处理完成后主动回调客户端告诉客户端处理完成。
gRPC 是基于 http2 协议实现的,http2 协议提供了很多新的特性,并且在性能上也比 http1 提搞了许多,所以 gRPC 的性能是非常好的。
gRPC 并没有直接实现负载均衡和服务发现的功能,但是已经提供了自己的设计思路。已经为命名解析和负载均衡提供了接口。
基于 http2 协议的特性:gRPC 允许定义如下四类服务方法:
单项 RPC:客户端发送一次请求,等待服务端响应结构,会话结束,就像一次普通的函数调用这样简单
服务端流式 RPC:客户端发起一起请求,服务端会返回一个流,客户端会从流中读取一系列消息,直到没有结果为止
客户端流式 RPC:客户端提供一个数据流并写入消息发给服务端,一旦客户端发送完毕,就等待服务器读取这些消息并返回应答
双向流式 RPC:客户端和服务端都一个数据流,都可以通过各自的流进行读写数据,这两个流是相互独立的,客户端和服务端都可以按其希望的任意顺序独写
gRPC 优点
gRPC 缺点
todo
proto buffer是一种轻便高效的结构化数据存储格式,可以用于结构化数据序列化。适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。gRPC默认使用 protobuf 作为IDL来定义数据结构和服务。 可以定义数据结构,也可以定义rpc 接口。 然后用proto编译器生成对应语言的框架代码。
proto buffer定义:
user.proto
syntax = "proto3"; import "google/protobuf/any.proto"; //package user; option go_package = "protos_golang/user"; message User { int32 id = 1; string name = 2; uint32 age = 3; enum Flag { NORMAL = 0; VIP = 1; SVIP = 2; } repeated int32 friends_ids = 5; reserved 6, 7, 8; message Command { int32 id = 1; oneof cmd_value { string name = 2; int32 age = 3; } } Command cmd = 9; map<int32, string> tags = 10; google.protobuf.Any details = 11; }
具体参数含义参考文章:gRPC 特性、原理、实践、生态:https://zhuanlan.zhihu.com/p/403336804
1.添加POM依赖
//gRPC依赖包 <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-all</artifactId> <version>1.12.0</version> </dependency> ... //proto buffer编译器maven插件 <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.4.1.Final</version> </extension> </extensions> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.0</version> <configuration> <pluginId>grpc-java</pluginId> <protocArtifact>com.google.protobuf:protoc:3.0.2:exe:${os.detected.classifier}</protocArtifact> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.2.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>
2.编写proto buffer文件
syntax = "proto3"; // 协议版本 // 选项配置 option java_package = "com.yangnk.springboottestexample.grpcExample.api"; option java_outer_classname = "RPCDateServiceApi"; option java_multiple_files = true; // 定义包名 package com.yangnk.springboottestexample.grpcExample; // 服务接口.定义请求参数和相应结果 service RPCDateService { rpc getDate (RPCDateRequest) returns (RPCDateResponse) { } } // 定义请求体 message RPCDateRequest { string userName = 1; } // 定义相应内容 message RPCDateResponse { string serverDate = 1; }
其中option java_package参数规定了proto buffer编译后java代码位置,编译会在target目录下生成,后续需要自己移动到对于的src/main目录中。
在maven的插件选项中,第一步生成的bean实例,第二步生成服务接口。
3.服务端实现
编写接口实现逻辑
import com.yangnk.springboottestexample.grpcExample.api.RPCDateRequest; import com.yangnk.springboottestexample.grpcExample.api.RPCDateResponse; import com.yangnk.springboottestexample.grpcExample.api.RPCDateServiceGrpc; import io.grpc.stub.StreamObserver; import java.time.LocalDate; import java.time.format.DateTimeFormatter; // RPCDateServiceGrpc.RPCDateServiceImplBase 这个就是接口. // RPCDateServiceImpl 我们需要继承他的,实现方法回调 public class RPCDateServiceImpl extends RPCDateServiceGrpc.RPCDateServiceImplBase { @Override public void getDate(RPCDateRequest request, StreamObserver<RPCDateResponse> responseObserver) { // 请求结果,我们定义的 RPCDateResponse rpcDateResponse = null; String userName = request.getUserName(); String response = String.format("你好: %s, 今天是%s.", userName, LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))); try { // 定义响应,是一个builder构造器. rpcDateResponse = RPCDateResponse .newBuilder() .setServerDate(response) .build(); } catch (Exception e) { responseObserver.onError(e); } finally { // 这种写法是observer,异步写法,老外喜欢用这个框架. responseObserver.onNext(rpcDateResponse); } responseObserver.onCompleted(); }
编写服务端启动类
import io.grpc.Server; import io.grpc.ServerBuilder; public class GRPCServer { private static final int port = 9999; public static void main(String[] args) throws Exception { // 设置service接口. Server server = ServerBuilder. forPort(port) .addService(new RPCDateServiceImpl()) .build().start(); System.out.println(String.format("GRpc服务端启动成功, 端口号: %d.", port)); server.awaitTermination(); } }
4.客户端实现
import com.yangnk.springboottestexample.grpcExample.api.RPCDateRequest; import com.yangnk.springboottestexample.grpcExample.api.RPCDateResponse; import com.yangnk.springboottestexample.grpcExample.api.RPCDateServiceGrpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; public class GRPCClient { private static final String host = "localhost"; private static final int serverPort = 9999; public static void main(String[] args) throws Exception { // 1. 拿到一个通信的channel ManagedChannel managedChannel = ManagedChannelBuilder.forAddress(host, serverPort).usePlaintext().build(); try { // 2.拿到道理对象 RPCDateServiceGrpc.RPCDateServiceBlockingStub rpcDateService = RPCDateServiceGrpc.newBlockingStub(managedChannel); RPCDateRequest rpcDateRequest = RPCDateRequest .newBuilder() .setUserName("yangnk") .build(); // 3. 请求 RPCDateResponse rpcDateResponse = rpcDateService.getDate(rpcDateRequest); // 4. 输出结果 System.out.println(rpcDateResponse.getServerDate()); } finally { // 5.关闭channel, 释放资源. managedChannel.shutdown(); } } }
5.结果验证
说明
为gRPC结合springboot,使用的是开源库net.devh:grpc-server-spring-boot-starter,在调用其他gRPC服务时用的是net.devh:grpc-client-spring-boot-starter。具体使用方法如下:
@GrpcService
自动配置并运行一个嵌入式的 gRPC 服务。@GrpcClient
自动创建和管理您的 gRPC Channels 和 stubs具体创建流程如下:
1.创建父工程
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.6</version> <relativePath/> <!-- lookup parent from repository --> </parent> <modelVersion>4.0.0</modelVersion> <groupId>com.yangnk</groupId> <artifactId>gRPCDemo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>pom</packaging> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <modules> <module>GRPCApi</module> <module>GRPCServer</module> <module>GRPCClient</module> </modules>
指定该项目的POM文件中<packaging>pom</packaging>
即为父工程。
2.创建API子工程
在src/proto目录下出创建helloworld.proto文件,同上面 :grpc的简单实现-编写proto buffer文件 部分生成对于的bean文件和接口文件。
syntax = "proto3"; option java_multiple_files = true; option java_package = "com.yangnk.grpcapi.api"; option java_outer_classname = "HelloWorldProto"; // The greeting service definition. service Simple { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) { } } // The request message containing the user's name. message HelloRequest { string name = 1; } // The response message containing the greetings message HelloReply { string message = 1; }
3.创建服务端子工程
引入pom文件
... <!--父工程依赖位置--> <parent> <groupId>com.yangnk</groupId> <artifactId>GRPCDemo</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>com.yangnk</groupId> <artifactId>GRPCServer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>GRPCServer</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!--grpc服务端依赖包--> <dependency> <groupId>net.devh</groupId> <artifactId>grpc-server-spring-boot-starter</artifactId> <version>2.12.0.RELEASE</version> </dependency> <dependency> <groupId>com.yangnk</groupId> <artifactId>GRPCApi</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!--Lombok引入--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> ...
创建配置文件 application.yaml
spring:
application:
name: spring-boot-grpc-server
# gRPC有关的配置,这里只需要配置服务端口号
grpc:
server:
port: 9898
server:
port: 8080
配置接口实现类
import com.yangnk.grpcapi.api.HelloReply; import com.yangnk.grpcapi.api.HelloRequest; import com.yangnk.grpcapi.api.SimpleGrpc; import io.grpc.stub.StreamObserver; import net.devh.boot.grpc.server.service.GrpcService; import java.util.Date; @GrpcService public class GrpcServerService extends SimpleGrpc.SimpleImplBase { @Override public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) { HelloReply reply = HelloReply.newBuilder().setMessage("你好, " + request.getName() + ", " + new Date()).build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } }
4.创建客户端子工程
... <parent> <groupId>com.yangnk</groupId> <artifactId>GRPCDemo</artifactId> <version>0.0.1-SNAPSHOT</version> </parent> <groupId>com.yangnk</groupId> <artifactId>GRPCClient</artifactId> <version>0.0.1-SNAPSHOT</version> <name>GRPCClient</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>net.devh</groupId> <artifactId>grpc-client-spring-boot-starter</artifactId> <version>2.12.0.RELEASE</version> </dependency> <dependency> <groupId>com.yangnk</groupId> <artifactId>GRPCApi</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency> <!--Lombok引入--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> ...
配置文件application.yaml
server:
port: 8088
spring:
application:
name: local-client
grpc:
client:
# gRPC配置的名字,GrpcClient注解会用到
local-grpc-server:
# gRPC服务端地址
address: 'static://127.0.0.1:9898'
enableKeepAlive: true
keepAliveWithoutCalls: true
negotiationType: plaintext
调用服务端接口
import com.yangnk.grpcapi.api.HelloReply; import com.yangnk.grpcapi.api.HelloRequest; import com.yangnk.grpcapi.api.SimpleGrpc; import io.grpc.StatusRuntimeException; import net.devh.boot.grpc.client.inject.GrpcClient; import org.springframework.stereotype.Service; @Service public class GrpcClientService { @GrpcClient("local-grpc-server") private SimpleGrpc.SimpleBlockingStub simpleStub; public String sendMessage(final String name) { try { final HelloReply response = this.simpleStub.sayHello(HelloRequest.newBuilder().setName(name).build()); return response.getMessage(); } catch (final StatusRuntimeException e) { return "FAILED with " + e.getStatus().getCode().name(); } } }
gRPC是一种基于CS模式的架构设计,解决了服务端和客户端编程语言或架构设计不一致但需要通信的问题,HTTP也是CS模式的架构设计,常用来和gRPC比较,相比于HTTP通信,gRPC具有以下特征:(1)gRPC可以自定义消息格式,HTTP的格式是固定的,可能存在性能不足的问题;(2)gRPC通过proto buffer进行数据交换,相比于json和XML,他的序列号和反序列号的效率都更高。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。