一、protobuf
gRPC传输用这个挺好的,跨语言调用
- 足够简单
- 序列化后体积小
- 解析速度比XML块
- 多语言支持
- 兼容性好
proto3
这里就先不提proto2了
文件后缀名是 .proto
1.开始部分
syntax = "proto3"; // compile is proto3
// 生成的文件是处在哪个目录哪个包中,.代表在当前目录生成,service代表文件生成的包名是service
option java_package = ".;service";
2.Message
可以理解为需要传输的数据格式的定义,类似于Java中的类class。使用 protobuf 编译器将 proto 编译成 java 代码后,每个 message 都会生成一个名字与之对应的类。
字段规则:
- optional:可选字段,默认为可选字段
- repeate:可重复字段 => 数组
消息号:
每个字段必须有一个唯一的标识号,不能重复,范围[2,2&29-1]
message SearchRequest {
string query = 1;// 每个字段必须有一个唯一的标识号,不能重复,范围:[2,2^29-1]
int32 page_number = 2;
int32 result_per_page = 3;
}
1.Type
double, float, int32, int64, uint32, uint64, sint32, sint64,(s* is more effcient in negative number)
fixed32, fixed64, sfixed32, sfixed64, bool, string, bytes.
repeated //by index
Timestamp
Duration
Struct
ListValue
默认值:
string ""
bytes ""
bool false
numeric 0
enums 0
message fields are language-dependent.
2.enum
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAEGS = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
/////////////////////////////////////
message MyMessage1 {
enum EnumAllowingAlias {
option Allow_alias = true; // same variable in different enums, need this options
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
}
message MyMessage2 {
enum EnumNotAllowingAlias {
UNKNOWN = 0;
STARTED = 1;
// RUNNING = 1; // Uncommenting this line will cause a compile error inside Google and a warning message outside.
}
}
3.using other message
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
4.Importing Definitions
JAVA is not suitable
5.Nested Types
as deeply as U like
// 1 *************************
// staight Nested another message
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snipeets = 3;
}
// 2 ***************************
// nest partial message
message SomeOtherMessage {
SearchResponse.Result result = 1;
}
6.any
7.one of
have many fields but use ao most one field once.
a oneof cannot be repeated.
message SampleMessage{
oneof test_oneof {
string name = 4;
SubMessage submessage = 9;
}
}
SampleMessage message;
message.set_name("name");
CHECK(message.has_name());
message.mutable_sub_message(); // Will clear name filed;
CHECK(!message.has_name());
8.Maps
map<string, Project> projects = 3;
9.Define Services
service SearchService{
rpc Search(SearchRequest) return (SearchResponse);
rpc Search1(SearchRequest) return (stream SearchResponse); // 服务端流式 gRPC 响应
rpc Search2(stream SearchRequest) return (SearchResponse); // 客户端流式 gRPc 通信
}
10.Generating Classes
protoc --proto_path=IMPORT_PATH --cpp_out --java_out=DST_DIR --python_out=DST_DIR --go_out=DST_DIR --ruby_out=DST_DIR --objc_out=DST_DIR --csharp_out=DST_DIR path/to/file.proto
# *_out point the DST_DIR of the output
# proto_path points the import_PATH?
# path/to/file.proto is the file be compiling.
protoc -I=$SRC_DIR --cpp_out=$DST_DIR xxx.proto
$SRC_DIR
:proto
所在的源目录--cpp_out
:生成 cpp 代码$DST_DIR
:生成代码的目标目录xxx.proto
:要针对哪个proto
文件生成接口,例如 tutorial.person.proto
编译完成后,将生成 2 个文件,tutorial.pb.h
和 tutorial.pb.c
,pb 是 protobuf 的缩写。
此外,protocol buffer 编译器为.proto文件中定义的消息的每个字段生成一套存取器方法:
对于 message Person 中的 required int32 id = 2,编译器将生成下列存取器方法:
bool has_id() const: 用于判断字段id是否存在。如果字段被设置,返回true。
**int32 id() const: **返回字段id的当前值,如果字段没有被设置,返回缺省值。
void set_id(int32 value) : 设置字段id的值。调用此方法后,has_id() 将返回true以及id() 将返回value。
void clear_id():清除字段的值。调用此方法后,has_id()将返回false 以及id()将返回缺省值。
当然,对于其他类型的字段,编译器也会生成不同的存取方法,这里就不一一列举了。
二、rpc 通信模式
stub-存根,客户端使用
ImplBase-骨架类,服务端使用
1.一元 RPC 通信模式
使用newBlockingStub
方法,同步,
2.服务端流式 RPC 通信模式
使用newBlockingStub
,支持服务器端推流
3.客户端流式 RPC 通信模式
使用 newStub,异步通信
4.双向流式 RPC 通信模式
跟 3 差不多,使用 newStub,异步通信
三、gRPC 集成到 springboot 与 Eureka
1.集成 springboot
使用下面的 starter ,结合注解与配置文件使用
pom 文件(c 端与 s 端只是使用的具体 starter 不同,s 为grpc-server-spring-boot-starter,c 为grpc-client-spring-boot-starter):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-boot-demo</artifactId>
<groupId>com.zhangyu</groupId>
<version>0.0.1</version>
</parent>
<artifactId>grpc-server-spriongboot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>grpc-server-spriongboot</name>
<description>grpc-server-spriongboot</description>
<properties>
<java.version>11</java.version>
<protoc.version>3.21.5</protoc.version>
<protobuf.version>1.51.0</protobuf.version>
<protobuf-maven-plugin.version>0.6.1</protobuf-maven-plugin.version>
</properties>
<dependencies>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- grpc-server -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>2.14.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<extensions>
<!-- 为了识别不同的操作系统,这样插件可以根据不同的平台加载不同protoc编译器文件 -->
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>${protobuf-maven-plugin.version}</version>
<configuration>
<pluginId>grpc-java</pluginId>
<!-- proto文件放置的目录 -->
<!-- <protoSourceRoot>${basedir}/src/main/proto</protoSourceRoot> -->
<!-- 生成文件的目录, 可以让其在源码目录生成 -->
<!-- <outputDirectory>${project.basedir}/src/main/java</outputDirectory>-->
<!-- 生成文件前是否把目标目录清空,这个最好设置为false,以免误删项目文件 -->
<!-- <clearOutputDirectory>false</clearOutputDirectory>-->
<!-- 寻找本机 protoc 执行命令,需配置环境变量 -->
<protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}
</protocArtifact>
<!-- 生成 grpc 的通信类 -->
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${protobuf.version}:exe:${os.detected.classifier}
</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<!-- 生成消息代码 -->
<goal>compile</goal>
<!-- 生成 grpc 的通信文件 -->
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
application.yml:
- s 端
server:
port: 8080
grpc:
server:
port: 9090
- c 端
server:
port: 8081
grpc:
client:
# 自定义名称,结合@GrpcClient("grpc-server")使用,得到存根bean
grpc-server:
# static 是使用静态地址
address: 'static://127.0.0.1:9090'
# 文本传输
negotiation-type: plaintext
业务代码层:
- s 端
@GrpcService // 需要加这个注解
public class NewService extends NewsServiceGrpc.NewsServiceImplBase {
@Override
public void list(NewsProto.NewsRequest request, StreamObserver<NewsProto.NewsResponse> responseObserver) {
String date = request.getDate();
NewsProto.NewsResponse response = NewsProto.NewsResponse.newBuilder().setTitle("新闻-" + date).build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
- c 端
@RestController
@RequestMapping("/grpc")
public class NewsController {
// 指明使用的配置文件的自定义名称,来获取存根类 bean
@GrpcClient("grpc-server")
private NewsServiceGrpc.NewsServiceBlockingStub blockingStub;
@GetMapping("/news/list")
public String listNews(@RequestParam(value = "date") String date) {
NewsProto.NewsResponse response = blockingStub.list(NewsProto.NewsRequest.newBuilder().setDate(date).build());
return response.getTitle();
}
}
2.接入 Eureka
微服务体系(不过这好像是 cloud 一代目的东西?不是很清楚)
ps:二代目是这样:
1.Eureka 服务端
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka 记得声明 colud 的版本-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
server:
# 服务开在 8761 端口
port: 8761
eureka:
server:
# 是否开启自我保护,默认true
enable-self-preservation: false
instance:
appname: eureka-server
hostname: localhost
client:
service-url:
# 默认服务注册中心地址
defaultZone:
http://localhost:8761/eureka/
register-with-eureka: false
fetch-registry: false
@SpringBootApplication
// 开启 Eureka 服务器端
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
2.Eureka 客户端 - grpc 服务端
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- grpc-server -->
<!-- client 的话把server 换成 client 就行 -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>2.14.0.RELEASE</version>
</dependency>
<!-- Eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
//---------
<build>
<extensions>
<!-- 为了识别不同的操作系统,这样插件可以根据不同的平台加载不同protoc编译器文件 -->
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>${protobuf-maven-plugin.version}</version>
<configuration>
<pluginId>grpc-java</pluginId>
<!-- proto文件放置的目录 -->
<protoSourceRoot>${basedir}/src/main/proto</protoSourceRoot>
<!-- 生成文件的目录, 可以让其在源码目录生成 -->
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<!-- 生成文件前是否把目标目录清空,这个最好设置为false,以免误删项目文件 -->
<clearOutputDirectory>false</clearOutputDirectory>
<!-- 寻找本机 protoc 执行命令,需配置环境变量 -->
<protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}
</protocArtifact>
<!-- 生成 grpc 的通信类 -->
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${protobuf.version}:exe:${os.detected.classifier}
</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<!-- 生成消息代码 -->
<goal>compile</goal>
<!-- 生成 grpc 的通信文件 -->
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
server:
port: 8080
spring:
application:
# 此名字会向 Eureka 注册
name: spb-server-eureka
eureka:
client:
service-url:
# 具体哪个地址提供了 Eureka 服务
defaultZone:
http://localhost:8761/eureka/
grpc:
server:
# 采用一个未占用的随机端口号来提供 gRPC 服务
port: 0
syntax = "proto3";
option java_multiple_files = false;
option java_package = "com.zhangyu.news.proto";
option java_outer_classname = "NewProto";
package news;
service NewService {
rpc listNews(NewsRequest) returns(NewsResponse){};
}
message NewsRequest {
string name = 1;
}
message NewsResponse {
string result = 1;
}
@SpringBootApplication
// 开启 Eureka 客户端类
@EnableEurekaClient
public class SpbServerEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(SpbServerEurekaApplication.class, args);
}
}
// 开启 grpc 注解
@GrpcService
public class NewsService extends NewServiceGrpc.NewServiceImplBase {
@Override
public void listNews(NewProto.NewsRequest request, StreamObserver<NewProto.NewsResponse> responseObserver) {
String name = request.getName();
responseObserver.onNext(NewProto.NewsResponse.newBuilder().setResult("News name is " + name).build());
responseObserver.onCompleted();
}
}
3.Eureka 客户端 - grpc 客户端
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-starter</artifactId>
<version>2.14.0.RELEASE</version>
</dependency>
把服务端的 gprc 服务端依赖改成对应的客户端依赖就行
server:
port: 8081
spring:
application:
# 会把这个名字注册到 Eureka 注册中心
name: spb-client-eureka
eureka:
client:
service-url:
# 具体哪个地址提供了 Eureka 服务
defaultZone:
http://localhost:8761/eureka/
grpc:
client:
# 自定义名称,且需服务端的 application 名(即注册到 eureka 的服务名)相同,不需要再显式的写明地址端口号
spb-server-eureka:
# 声明传输格式
negotiation-type: plaintext
@SpringBootApplication
// 开启 Eureka 客户端
@EnableEurekaClient
public class SpbClientEurekaApplication {
public static void main(String[] args) {
SpringApplication.run(SpbClientEurekaApplication.class, args);
}
}
@RestController
@RequestMapping("/grpc")
public class NewsController {
// 需要与配置文件里面自定义名称相关联,来获取相应的存根类
@GrpcClient("spb-server-eureka")
private NewServiceGrpc.NewServiceBlockingStub newServiceBlockingStub;
@GetMapping("/list")
public void list() {
NewProto.NewsResponse newsResponse = newServiceBlockingStub.listNews(NewProto.NewsRequest.newBuilder().setName("震惊!现在居然还有人在用 Eureka!").build());
System.out.println(newsResponse.getResult());
}
}
标签:proto,gRPC,boot,server,grpc,message,eureka
From: https://www.cnblogs.com/zerozayu/p/gRPC.html