gRPC实战(一、快速开始,Java、go实现)

412 阅读3分钟

一、快速开始

1、定义一个服务-useinfo.proto

syntax = "proto3";//标识 proto版本 建议使用proto3
package userinfoservice;//proto包名 避免命名冲突,也可以作为引入其他proto文件时使用
option java_package = "com.example.userinfoservice" ;//生成的类将带有此包名,不指定则使用package
option cc_generic_services = true;
option go_package = "./pb";
option java_outer_classname = "UserInfoEntity";//指定生成后的类名,里面会包含req/res,不指定则使用文件名

message GetUserInfoReq{
  string id = 1;
}
message GetUserInfoRes{
  string id = 1;
  string name = 2;
  int32 age = 3;
}

service UserInfoService {
  rpc getUserInfo(GetUserInfoReq) returns (GetUserInfoRes);
}

2、通过proto文件生成代码

1) Go

  • 安装protoc命令
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
$ export PATH="$PATH:$(go env GOPATH)/bin"
  • 生成userinfo.pb.go文件和userinfo_grpc.pb.go
protoc --go_out=. --go_opt=paths=source_relative \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    proto/userinfo.proto

2) Java

  • 将你的proto文件放到src/main/proto和src/test/proto目录,并配置生成代码的插件

  • Gradle生成代码插件:

    /*
     * This file was generated by the Gradle 'init' task.
     *
     * This generated file contains a sample Java application project to get you started.
     * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
     * User Manual available at https://docs.gradle.org/7.2/userguide/building_java_projects.html
     */
    plugins {
        // Apply the application plugin to add support for building a CLI application in Java.
        id 'application'
        // ASSUMES GRADLE 5.6 OR HIGHER. Use plugin version 0.8.10 with earlier gradle versions
        id 'com.google.protobuf' version '0.8.17'
        // Generate IntelliJ IDEA's .idea & .iml project files
        id 'idea'
        id 'java'
    }
    
    sourceCompatibility = 1.8
    targetCompatibility = 1.8
    
    repositories {
        // Use Maven Central for resolving dependencies.
        mavenCentral()
        repositories {
            maven { url 'https://maven.aliyun.com/repository/google/' }
            maven { url 'https://maven.aliyun.com/repository/jcenter/'}
        }
    }
    def grpcVersion = '1.48.0' // CURRENT_GRPC_VERSION
    dependencies {
        // Use JUnit test framework.
        testImplementation 'junit:junit:4.13.2'
    
        // This dependency is used by the application.
        implementation 'com.google.guava:guava:30.1.1-jre'
        implementation "io.grpc:grpc-netty:${grpcVersion}"
        implementation "io.grpc:grpc-protobuf:${grpcVersion}"
        implementation "io.grpc:grpc-services:${grpcVersion}"
        implementation "io.grpc:grpc-stub:${grpcVersion}"
    }
    
    application {
        // Define the main class for the application.
        mainClass = 'client.MainApplication'
    }
    protobuf {
        protoc {
            artifact = "com.google.protobuf:protoc:3.2.0"
        }
        plugins {
            grpc {
                artifact = 'io.grpc:protoc-gen-grpc-java:1.4.0'
            }
        }
        generateProtoTasks {
            all()*.plugins {
                grpc {}
            }
        }
    }
    
  • 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.xolstice.maven.plugins</groupId>
          <artifactId>protobuf-maven-plugin</artifactId>
          <version>0.5.0</version>
          <configuration>
            <protocArtifact>com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}</protocArtifact>
            <pluginId>grpc-java</pluginId>
            <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.4.0:exe:${os.detected.classifier}</pluginArtifact>
          </configuration>
          <executions>
            <execution>
              <goals>
                <goal>compile</goal>
                <goal>compile-custom</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
      </plugins>
    </build>
    

    3、对应生成服务端和客户端

    (1 )Java 服务端

    package com.example.userinfoservice.impl;
    
    import com.example.userinfoservice.UserInfoEntity;
    import com.example.userinfoservice.UserInfoServiceGrpc;
    import com.example.userinfoservice.entity.UserInfoData;
    import com.google.common.collect.Maps;
    import io.grpc.stub.StreamObserver;
    import org.apache.commons.lang3.StringUtils;
    
    import java.util.HashMap;
    
    public class UserInfoServiceImpl extends UserInfoServiceGrpc.UserInfoServiceImplBase {
    
        private HashMap<String, UserInfoData> userinfoHashMap = Maps.newHashMap();
    
        @Override
        public void getUserInfo(UserInfoEntity.GetUserInfoReq request, StreamObserver<UserInfoEntity.GetUserInfoRes> responseObserver) {
    
            System.out.println("java服务收到请求");
            if (StringUtils.isEmpty(request.getId())){
                responseObserver.onError(new Exception("id不能为空"));
                responseObserver.onCompleted();
                return;
            }
    
            if (userinfoHashMap.isEmpty()){
                initUserInfoHashMap();
            }
    
            UserInfoData userInfoData = userinfoHashMap.get(request.getId());
            if (userInfoData == null){
                responseObserver.onError(new Exception("id不能为空"));
                responseObserver.onCompleted();
                return;
            }
            responseObserver.onNext(UserInfoEntity.GetUserInfoRes.newBuilder()
                    .setId(userInfoData.getId())
                    .setName(userInfoData.getName())
                    .setAge(userInfoData.getAge())
                    .build());
            responseObserver.onCompleted();
        }
    
        private void initUserInfoHashMap() {
            userinfoHashMap.put("1",new UserInfoData("1","小美",24));
            userinfoHashMap.put("2",new UserInfoData("2","小强",28));
            userinfoHashMap.put("3",new UserInfoData("3","小刚",30));
        }
    }
    
    • 实体 UserInfoData
    package com.example.userinfoservice.entity;
    
    public class UserInfoData{
        private String id;
        private String name;
        private int age;
    
        public UserInfoData() {
        }
    
        public UserInfoData(String id, String name, int age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    • 服务主类 App
    /*
     * This Java source file was generated by the Gradle 'init' task.
     */
    package com.example.userinfoservice;
    
    import com.example.userinfoservice.impl.UserInfoServiceImpl;
    import io.grpc.Server;
    import io.grpc.ServerBuilder;
    
    import java.io.IOException;
    import java.util.logging.Logger;
    
    public class App {
        public String getGreeting() {
            return "Hello World!";
        }
    
        private static final Logger logger = Logger.getLogger(App.class.getName());
    
        private Server server;
    
        private void start() throws IOException {
            int port = 10080;
            server = ServerBuilder.forPort(port)
                    .addService(new UserInfoServiceImpl())
                    .build()
                    .start();
            logger.info("Server started, listening on " + port);
            Runtime.getRuntime().addShutdownHook(new Thread(() -> {
                // Use stderr here since the logger may have been reset by its JVM shutdown hook.
                logger.info("*** shutting down gRPC server since JVM is shutting down");
                App.this.stop();
                logger.info("*** server shut down");
            }));
        }
    
        private void stop() {
            if (server != null) {
                server.shutdown();
            }
        }
    
        /**
         * Await termination on the main thread since the grpc library uses daemon threads.
         */
        private void blockUntilShutdown() throws InterruptedException {
            if (server != null) {
                server.awaitTermination();
            }
        }
    
        /**
         * Main launches the server from the command line.
         */
        public static void main(String[] args) throws IOException, InterruptedException {
            final App server = new App();
            server.start();
            server.blockUntilShutdown();
        }
    }
    

    (2)Java 客户端

    package com.example.userinfoservice;
    
    import io.grpc.ManagedChannel;
    import io.grpc.ManagedChannelBuilder;
    
    import java.util.logging.Logger;
    
    public class MainApplication {
    
        private static final Logger logger = Logger.getLogger(MainApplication.class.getName());
    
        public static void main(String[] args) throws InterruptedException {
            ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 10080)
                    .usePlaintext()
                    .build();
    
            UserInfoServiceGrpc.UserInfoServiceBlockingStub stub =
                    UserInfoServiceGrpc.newBlockingStub(channel);
    
            Userinfo.GetUserInfoRes res = stub.getUserInfo(
                    Userinfo.GetUserInfoReq.newBuilder()
                            .setId("1")
                            .build());
            logger.info("用户id: " + res.getId() + "\t用户姓名:"+ res.getName()
            +"\t用户年龄:"+ res.getAge());
            channel.shutdown();
        }
    }
    

    (3)go 服务端

    • 实现用户服务
    package serviceimpl
    
    import (
    	"context"
    	"errors"
    	"grpc-in-action/part1/go/server/pb"
    	"log"
    )
    
    type UserInfo struct {
    	Id string
    	Name string
    	Age int32
    }
    type UserInfoServiceImpl struct {
    	UserInfoData map[string]*UserInfo
    	pb.UnimplementedUserInfoServiceServer
    }
    
    func (server *UserInfoServiceImpl) GetUserInfo(ctx context.Context, in *pb.GetUserInfoReq) (*pb.GetUserInfoRes, error)  {
    
    	log.Printf("go server收到请求")
    	//初始化一个map
    	if server.UserInfoData == nil{
    		server.initUserInfoData()
    	}
    	data:= server.UserInfoData[in.Id]
    	if data == nil{
    		return nil,errors.New("该id不存在")
    	}
    	res := &pb.GetUserInfoRes{
    		Id:   data.Id,
    		Name: data.Name,
    		Age:  data.Age,
    	}
    
    	return res,nil
    }
    
    func (server *UserInfoServiceImpl) initUserInfoData (){
    	server.UserInfoData = make(map[string]*UserInfo)
    
    	server.UserInfoData["1"] = &UserInfo{
    		Id:   "1",
    		Name: "小美",
    		Age:  18,
    	}
    	server.UserInfoData["2"] = &UserInfo{
    		Id:   "2",
    		Name: "小刚",
    		Age:  28,
    	}
    	server.UserInfoData["3"] = &UserInfo{
    		Id:   "3",
    		Name: "小王",
    		Age:  20,
    	}
    }
    
    • 服务主类
    package main
    
    import (
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/reflection"
    	"grpc-in-action/part1/go/server/pb"
    	"grpc-in-action/part1/go/server/serviceimpl"
    	"log"
    	"net"
    )
    
    func main() {
    	lis, err := net.Listen("tcp", "localhost:10080")
    	if err != nil {
    		log.Fatalf("failed to listen: %v", err)
    	}
    	s := grpc.NewServer()
    	ser := &serviceimpl.UserInfoServiceImpl{}
    	pb.RegisterUserInfoServiceServer(s,ser)
    	log.Printf("start")
    	reflection.Register(s)
    	if err := s.Serve(lis); err != nil {
    		log.Fatalf("failed to serve: %v", err)
    	}
    }
    

    (4)go 客户端

    package main
    
    import (
    	"context"
    	"google.golang.org/grpc/credentials/insecure"
    	"google.golang.org/grpc"
    	"grpc-in-action/part1/go/client/pb"
    	"log"
    )
    
    func main() {
    
    	// Set up a connection to the server.
    		conn, err := grpc.Dial("localhost:10080",grpc.WithTransportCredentials(insecure.NewCredentials()))
    	if err != nil {
    		log.Fatalf("did not connect: %v", err)
    	}
    	defer conn.Close()
    	c := pb.NewUserInfoServiceClient(conn)
    	req:= &pb.GetUserInfoReq{Id: "1"}
    	res,err :=c.GetUserInfo(context.Background(),req)
    	if err != nil {
    		log.Fatalf("err:%+v",err)
    	}else {
    		log.Printf("res:%+v",res)
    	}
    
    }