grpc工具生成代码,java、go和python集成grpc demo
# 1、指定限定proto语法
syntax = "proto3";
注意:如果不指定,默认使用proto2。必须是文件的第一个非空、非注释行。
# 2、定义消息类型
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
# 2.1 指定字段类型
2.1.1 标量值类型
2.1.2 枚举类型
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
注意:枚举的第一个常量映射到零,每个枚举定义都必须包含一个映射到零的常量作为其第一个元素。
message MyMessage1 {
enum EnumAllowingAlias {
option allow_alias = true;
UNKNOWN = 0;
STARTED = 1;
RUNNING = 1;
}
}
注意:相同的值分配给不同的枚举常量来定义别名;否则协议编译器将在找到别名时生成错误消息。
2.1.3 其他类型
message SearchResponse {
repeated Result results = 1;
}
message Result {
string url = 1;
string title = 2;
repeated string snippets = 3;
}
# 2.2 分配字段编号
消息定义中的每个字段都有一个唯一的编号。字段编号用于在消息二进制格式 (opens new window)中标识字段。
1-15 范围内的字段编号需要一个字节进行编码,包括字段编号和字段类型。
16-2047 范围内的字段编号占用两个字节。
应该为非常频繁出现的消息元素保留数字 1-15。
1-2^29 - 1,不能使用数字 19000-19999(Protocol Buffers 实现保留)
# 3、注释
/*
* SearchRequest represents a search query, with pagination options to
* indicate which results to include in the response.
*/
message SearchRequest {
string query = 1;
int32 page_number = 2; // Which page number do we want?
int32 result_per_page = 3; // Number of results to return per page.
}
# 4、导入定义
import "myproject/other_protos.proto";
# 5、定义服务
service SearchService {
rpc Search(SearchRequest) returns (SearchResponse);
}
# 6、选项
# 6.1 Java/Kotlin 类的包
option java_package = "com.example.foo";
注意:文件中没有给出明确java_package
的选项,默认使用 proto 包(package关键字指定)。如果不生成 Java 代码,则此选项无效。
# 6.2 Java 类的类名
option java_outer_classname = "Ponycopter";
注意:如果文件中没有明确java_outer_classname
指定,通过将.proto
文件名转换为驼峰式来构造类名(因此foo_bar.proto
变为FooBar.java
)。如果不生成 Java 代码,则此选项无效。
# 6.3 Java多文件
option java_multiple_files = true;
false
(默认)只会生成一个.java
文件,包含所有 Java 类/枚举/等。true
为每个 Java 类/枚举/等生成单独的文件。
注意:如果不生成 Java 代码,则此选项无效。
# 6.4 C++ 和 Java 代码生成器
option optimize_for = CODE_SIZE;
SPEED
(默认):protocol buffer 编译器将生成用于对消息类型进行序列化、解析和执行其他常见操作的代码。这段代码是高度优化的。CODE_SIZE
:协议缓冲区编译器将生成最少的类,并将依赖共享的、基于反射的代码来实现序列化、解析和各种其他操作。因此生成的代码将比SPEED
小得多,但操作会更慢。类仍将实现与SPEED
模式中完全相同的公共 API 。此模式在包含大量.proto
文件且不需要所有文件都非常快的应用程序中最有用。LITE_RUNTIME
:protocol buffer 编译器将生成仅依赖于“lite”运行时库的类(libprotobuf-lite
而不是libprotobuf
)。lite 运行时比完整库小得多(大约小一个数量级),但省略了描述符和反射等某些功能。这对于在手机等受限平台上运行的应用程序特别有用。编译器仍将生成所有方法的快速实现,就像它在SPEED
模式中所做的那样。生成的类只会实现MessageLite
每种语言的接口,它只提供完整Message
接口方法的子集。
# 6.5 Java字段弃用
int32 old_field = 6 [deprecated = true];
true
,则表示该字段已弃用,不应被新代码使用。在 Java 中,这成为@Deprecated
注解。
# 7、生成
protoc --proto_path=IMPORT_PATH \
--cpp_out=DST_DIR \
--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
IMPORT_PATH
:-I
,指定.proto
解析import
指令时在其中查找文件的目录。省略则使用当前目录。--cpp_out
生成 C++ 代码DST_DIR
。有关更多信息,请参阅C++ 生成的代码参考 (opens new window)。--java_out
生成 Java 代码DST_DIR
。有关更多信息,请参阅Java 生成的代码参考 (opens new window)。--kotlin_out
在DST_DIR
. 有关更多信息,请参阅Kotlin 生成的代码参考 (opens new window)。--python_out
生成 Python 代码DST_DIR
。有关更多信息,请参阅Python 生成的代码参考 (opens new window)。--go_out
生成 Go 代码DST_DIR
。有关更多信息,请参阅Go 生成的代码参考 (opens new window)。--ruby_out
生成 Ruby 代码DST_DIR
。有关更多信息,请参阅Ruby 生成的代码参考 (opens new window)。--objc_out
在DST_DIR
. 有关更多信息,请参阅Objective-C 生成的代码参考 (opens new window)。--csharp_out
生成 C# 代码DST_DIR
。有关更多信息,请参阅C# 生成的代码参考 (opens new window)。--php_out
生成 PHP 代码DST_DIR
。有关更多信息,请参阅PHP 生成的代码参考 (opens new window)。
# 8、go配置
go get -u "google.golang.org/grpc"
go get -u "google.golang.org/grpc/reflection"
# $GOPATH/bin 加入到PATH环境中
syntax = "proto3";
package auth;
option java_package = "cn.pascall.blog.grpc.java.auth";
option java_outer_classname = "AuthenticationService";
option java_multiple_files = true;
option go_package = "./pkg/protos/auth";
message AuthRequestDto {
string name = 1;
string password = 2;
}
message AuthResponseDto {
int32 code = 1;
string message = 2;
}
service AuthorizationService {
rpc login(AuthRequestDto) returns (AuthResponseDto);
}
# 上面的.proto文件应放在./目录下
protoc --go_out=plugins=grpc:. ./auth-service.proto
// 创建服务端实现类 pkg.server
package server
import (
"golang.org/x/net/context"
"pascall.cn/grpc-demo/grpc-go/pkg/protos/auth"
)
type AuthServer struct {
}
func (s *AuthServer) Login(ctx context.Context, req *auth.AuthRequestDto) (resp *auth.AuthResponseDto, err error) {
if "admin" == req.Name && "admin" == req.Password {
return &auth.AuthResponseDto{Code: 200, Message: "go service: login success"}, nil
}
return &auth.AuthResponseDto{Code: 401, Message: "go service: login fail"}, nil
}
// 创建服务端 cmd.server
package main
import (
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"net"
"pascall.cn/grpc-demo/grpc-go/pkg/protos/auth"
"pascall.cn/grpc-demo/grpc-go/pkg/server"
)
var (
port = 50050
)
func main() {
err := serverInit(port)
if err != nil {
fmt.Println("init gRPC server failed", err)
}
}
func serverInit(port int) error {
fmt.Println("gRPC server init")
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
fmt.Println("fail to listen on port 9503")
return err
}
var opts []grpc.ServerOption
s := grpc.NewServer(opts...)
auth.RegisterAuthorizationServiceServer(s, &server.AuthServer{})
reflection.Register(s)
if err := s.Serve(listener); err != nil {
fmt.Println("gRPC server init failed", err)
}
fmt.Println("gRPC server init success")
return err
}
// 创建客户端 cmd.client
package main
import (
"fmt"
"golang.org/x/net/context"
"google.golang.org/grpc"
"log"
"pascall.cn/grpc-demo/grpc-go/pkg/protos/auth"
)
var (
port = 50050
address = fmt.Sprintf("localhost:%d", port)
name = "admin"
pass = "admin"
)
func main() {
var opts []grpc.DialOption
opts = append(opts, grpc.WithInsecure())
conn, err := grpc.Dial(address, opts...)
if err != nil {
log.Fatalf("could not greet: %v", err)
return
}
c := auth.NewAuthorizationServiceClient(conn)
dto := auth.AuthRequestDto{Name: name, Password: pass}
resp, err := c.Login(context.Background(), &dto)
if err != nil {
log.Fatalf("did not connect: %v", err)
return
}
log.Fatalf("response: code = %v, message = %v", resp.Code, resp.Message)
}
# 9、java配置
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.pascall.blog.grpc</groupId>
<artifactId>grpc-demo</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.32.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.32.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<version>1.32.1</version>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.2</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.32.1:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
syntax = "proto3";
package auth;
option java_package = "cn.pascall.blog.grpc.java.auth";
option java_outer_classname = "AuthenticationService";
option java_multiple_files = true;
message AuthRequestDto {
string name = 1;
string password = 2;
}
message AuthResponseDto {
int32 code = 1;
string message = 2;
}
service AuthorizationService {
rpc login(AuthRequestDto) returns (AuthResponseDto);
}
# 上面的.proto文件应放在 src/main/proto 目录下
mvn clean compile
# 将 target/generated-sources/protobuf 下的 grpc-java 和 java 移动到 src/main/java 目录下
// 创建服务端实现类 server.auth
package server.auth;
import cn.pascall.blog.grpc.java.auth.AuthRequestDto;
import cn.pascall.blog.grpc.java.auth.AuthResponseDto;
import cn.pascall.blog.grpc.java.auth.AuthorizationServiceGrpc;
import io.grpc.stub.StreamObserver;
public class AuthorizationServiceImpl extends AuthorizationServiceGrpc.AuthorizationServiceImplBase {
@Override
public void login(AuthRequestDto request, StreamObserver<AuthResponseDto> responseObserver) {
int code = 401;
String message = "java server: login error";
if ("admin".equals(request.getName()) && "admin".equals(request.getPassword())) {
code = 200;
message = "java server: login success";
}
AuthResponseDto response = AuthResponseDto.newBuilder().setCode(code).setMessage(message).build();
responseObserver.onNext(response);
responseObserver.onCompleted();
}
}
// 创建服务端 server
package server;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import server.auth.AuthorizationServiceImpl;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class ServerBootstrap {
private Server server;
public static void main(String[] args) throws IOException, InterruptedException {
ServerBootstrap server = new ServerBootstrap();
server.start();
server.blockUntilShutdown();
}
private void start() throws IOException {
int port = 50051;
server = ServerBuilder.forPort(port).addService(new AuthorizationServiceImpl()).build().start();
System.out.println("Server started, listening on " + port);
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.err.println("*** shutting down gRPC server since JVM is shutting down");
try {
ServerBootstrap.this.stop();
} catch (InterruptedException e) {
e.printStackTrace(System.err);
}
System.err.println("*** server shut down");
}
});
}
private void stop() throws InterruptedException {
if (server != null) {
server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
}
}
private void blockUntilShutdown() throws InterruptedException {
if (server != null) {
server.awaitTermination();
}
}
}
// 创建客户端 client
package client;
import cn.pascall.blog.grpc.java.auth.AuthRequestDto;
import cn.pascall.blog.grpc.java.auth.AuthResponseDto;
import cn.pascall.blog.grpc.java.auth.AuthorizationServiceGrpc;
import io.grpc.Channel;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;
import java.util.concurrent.TimeUnit;
public class ClientBootstrap {
public static void main(String[] args) throws Exception {
String user = "admin";
String pass = "admin";
String target = "localhost:50051";
ManagedChannel channel = ManagedChannelBuilder.forTarget(target).usePlaintext().build();
try {
ClientBootstrap client = new ClientBootstrap(channel);
client.login(user, pass);
} finally {
channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
}
}
private final AuthorizationServiceGrpc.AuthorizationServiceBlockingStub authorizationServiceBlockingStub;
private ClientBootstrap(Channel channel) {
authorizationServiceBlockingStub = AuthorizationServiceGrpc.newBlockingStub(channel);
}
private void login(String name, String pass) {
System.out.println("Will try to Auth: name = " + name + ", pass = " + pass + " ...");
AuthRequestDto request = AuthRequestDto.newBuilder().setName(name).setPassword(pass).build();
AuthResponseDto response;
try {
response = authorizationServiceBlockingStub.login(request);
} catch (StatusRuntimeException e) {
System.out.println("WARNING, RPC failed: Status=" + e.getStatus());
return;
}
System.out.println("response: code = " + response.getCode() + ", message = " + response.getMessage());
}
}
# 10、python配置
# 包管理工具
pip3.9 install pigar
pip3.9 install grpcio
pip3.9 install grpcio-tools
syntax = "proto3";
package auth;
option java_package = "cn.pascall.blog.grpc.java.auth";
option java_outer_classname = "AuthenticationService";
option java_multiple_files = true;
message AuthRequestDto {
string name = 1;
string password = 2;
}
message AuthResponseDto {
int32 code = 1;
string message = 2;
}
service AuthorizationService {
rpc login(AuthRequestDto) returns (AuthResponseDto);
}
python3.9 -m grpc_tools.protoc -I./protos/file/ \
--python_out=protos/auth --grpc_python_out=protos/auth \
protos/file/auth-service.proto
# 修改auth_service_pb2_grpc.py 文件中对 auth_service_pb2 的引用,加上 protos.auth 前缀
# 创建服务端实现类 auth
from protos.auth import auth_service_pb2
from protos.auth import auth_service_pb2_grpc
class AuthServer(auth_service_pb2_grpc.AuthorizationService):
def login(self, request, context):
if 'admin' == request.name and 'admin' == request.password:
return auth_service_pb2.AuthResponseDto(code=200, message="python service: login success")
return auth_service_pb2.AuthResponseDto(code=401, message="python service: login fail")
# 创建服务端
import grpc
from concurrent import futures
from auth import auth_server
from protos.auth import auth_service_pb2_grpc
if __name__ == '__main__':
server = grpc.server(futures.ThreadPoolExecutor(max_workers=2))
auth_service_pb2_grpc.add_AuthorizationServiceServicer_to_server(auth_server.AuthServer(), server)
server.add_insecure_port('[::]:50052')
server.start()
server.wait_for_termination()
# 创建客户端
import grpc
from protos.auth import auth_service_pb2_grpc
from protos.auth import auth_service_pb2
if __name__ == '__main__':
with grpc.insecure_channel('localhost:50052') as channel:
stub = auth_service_pb2_grpc.AuthorizationServiceStub(channel)
resp = stub.login(auth_service_pb2.AuthRequestDto(name='admin', password='admin'))
print("response: [code: " + str(resp.code) + ", message: " + resp.message + "]")