Basic use of high-performance remote communication framework grpc

1. Understand grpc

Google's open source remote process call framework supports communication between multi-language systems and is suitable for various distributed systems. It uses the http2 protocol and uses protobuf to serialize communication data with high performance. nacos2 has also integrated grpc for communication between nacos server and nacos client. grpc also supports client-server bidirectional streams. The two streams can be independently read and written continuously without blocking.

2. About protobuf

If only protobuf is used as the serialization solution, it is recommended to use protostuff (based on protobuf) without writing a proto file. The official quickstart of grpc uses protobuf directly, so we will not study whether there is a grpc+protostuff solution for the time being. Java has a maven plug-in to generate Java files from proto files, so there is no need to install proto separately, but you still need to understand the basic syntax of proto.

3. Try grpc

The official quickstart is not based on springboot, so here we refer to this boss'sJAVA uses gRPC
. For convenience, here is the server , the clients are all in the same project. If you want to separate, introduce grpc-client-spring-boot-starter and other dependencies into an independent project, and copy the required classes com.example.demo.protogen.UserServiceGrpc,com.example.demo.protogen.User and so on.

3.1 Entire project directory

Insert image description here

3.2 proto file writing

proto releasefile.proto,message.protobunjijoshita

syntax = "proto3";

package protogen;
option java_package = "com.example.demo.protogen";

message File {
  string name = 1;
  int32 size = 2;
}
syntax = "proto3";
package protogen;

import "file.proto";

option java_multiple_files = true;
option java_package = "com.example.demo.protogen";

message User {
  reserved 6 to 7;
  reserved "userId2";
  int32 userId = 1;
  string username = 2;
  oneof msg {
    string error = 3;
    int32 code = 4;
  }
  string name = 8;

  UserType userType = 9;
  repeated int32 roles = 10;

  protogen.File file = 11;
  map<string, string> hobbys = 12;
}

enum UserType {
  UNKNOW = 0;
  ADMIN = 1;
  BUSINESS_USER = 2;
};

service UserService {
  rpc getUser (User) returns (User) {}
  rpc getUsers (User) returns (stream User) {}
  rpc saveUsers (stream User) returns (User) {}
}

service FileService {
  rpc getFile(User) returns(File) {}
}

3.3 Use the maven protobuf plug-in to convert .proto files

The files in protogen are all generated. The plug-in is configured to read this directory and generate it into the protogen folder. By clicking the plug-in button as shown, protobuf:compile actually generates a Java file from the .proto file through proto.exe, except that FileServiceGrpc,UserServiceGrpc is protobuf:compile-customGenerated, the rest are protobuf:compilegenerated.

3.4 Grpc server business implementation class

UserServiceImpl.java

package com.example.demo.grpc.server;

import com.example.demo.protogen.User;
import com.example.demo.protogen.UserServiceGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;

/**
 * @author Kone
 * @date 2022/1/29
 */
@GrpcService
public class UserServiceImpl extends UserServiceGrpc.UserServiceImplBase {
    
    
    @Override
    public void getUser(User request, StreamObserver<User> responseObserver) {
    
    
        System.out.println(request);
        User user = User.newBuilder()
                .setName("response name")
                .build();
        responseObserver.onNext(user);
        responseObserver.onCompleted();
    }

    @Override
    public void getUsers(User request, StreamObserver<User> responseObserver) {
    
    
        System.out.println("get users");
        System.out.println(request);
        User user = User.newBuilder()
                .setName("user1")
                .build();
        User user2 = User.newBuilder()
                .setName("user2")
                .build();
        responseObserver.onNext(user);
        responseObserver.onNext(user2);

        responseObserver.onCompleted();
    }

    @Override
    public StreamObserver<User> saveUsers(StreamObserver<User> responseObserver) {
    
    

        return new StreamObserver<User>() {
    
    
            @Override
            public void onNext(User user) {
    
    
                System.out.println("get saveUsers list ---->");
                System.out.println(user);
            }

            @Override
            public void onError(Throwable throwable) {
    
    
                System.out.println("saveUsers error " + throwable.getMessage());
            }

            @Override
            public void onCompleted() {
    
    
                User user = User.newBuilder()
                        .setName("saveUsers user1")
                        .build();
                responseObserver.onNext(user);
                responseObserver.onCompleted();
            }
        };
    }
}

3.5 pom reference

There is something wrong with springboot3, so I will use springboot version 2.7.9 for the time being.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         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>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.9</version>
        <relativePath/> 
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
        <protobuf.version>3.23.4</protobuf.version>
        <grpc.version>1.26.0</grpc.version>
    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>${protobuf.version}</version>
        </dependency>
        <!-- grpc server和spring-boot集成框架 -->
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-server-spring-boot-starter</artifactId>
            <version>2.14.0.RELEASE</version>
        </dependency>

        <!-- grpc client和spring-boot集成框架 -->
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-client-spring-boot-starter</artifactId>
            <version>2.14.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

    <build>

        <!-- os系统信息插件, protobuf-maven-plugin需要获取系统信息下载相应的protobuf程序 -->
        <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>
                    <!--os.detected.classifier属性是通过如上os-maven-plugin插件获取的-->
                    <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}
                    </protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
                    </pluginArtifact>

                    <!-- proto文件目录 -->
                    <protoSourceRoot>${project.basedir}/src/main/java/com/example/demo/proto</protoSourceRoot>
                    <!-- 生成的Java文件目录,这里指定到这一级就可以咯,proto文件有基于package指定 -->
                    <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
                    <clearOutputDirectory>false</clearOutputDirectory>
                </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>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

3.6 grpc client call

  • Direct call based on URL of grpc server
package com.example.demo;

import com.example.demo.protogen.User;
import com.example.demo.protogen.UserServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;

import java.util.Iterator;

public class FramelessGrpcClient {
    
    

    public static void main(String[] args) {
    
    
        ManagedChannel managedChannel = ManagedChannelBuilder.forAddress("localhost", 9090).usePlaintext().build();
        UserServiceGrpc.UserServiceBlockingStub userService = UserServiceGrpc.newBlockingStub(managedChannel);
        User user = User.newBuilder()
                .setUserId(100)
                .putHobbys("pingpong", "play pingpong")
                .setCode(200)
                .build();
        System.out.println("get response-------->");
        System.out.println(userService.getUser(user));
        Iterator<User> users = userService.getUsers(user);
    }
}
  • Inject beans from spring IOC to call grpc server (URL, etc. are configured in yml). Scheduled tasks are used here to trigger scheduling. Note that the injected "userClient" needs to be consistent with the custom client name in yml (if multiple grpc servers are called, this custom name is used to distinguish which grpc server is called).
package com.example.demo;

import com.example.demo.protogen.User;
import com.example.demo.protogen.UserServiceGrpc;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.Iterator;

/**
 * TODO
 *
 * @author majun
 * @version 1.0
 * @since 2023-08-02 20:19
 */
@Component
@EnableScheduling
public class SpringbootGrpcClient {
    
    
    @GrpcClient("userClient")
    private UserServiceGrpc.UserServiceBlockingStub userService;


    @Scheduled(fixedDelay = 10000)
    public void test() {
    
    
        User user = User.newBuilder()
                .setUserId(100)
                .putHobbys("pingpong", "play pingpong")
                .setCode(200)
                .build();
        System.out.println("get response-------->");
        System.out.println(userService.getUser(user));

        Iterator<User> users = this.userService.getUsers(user);
        while (users.hasNext()) {
    
    
            System.out.println(users.next());
        }
    }
}

Guess you like

Origin blog.csdn.net/qq_39506978/article/details/132070946