grpc使用

2022/5/6 javagrpcgopython

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
# 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 + "]")