gRPC quickly integrates SpringCloud

gRPC is a high-performance, general-purpose open source RPC framework developed by Google. It is mainly designed for mobile application development and based on the HTTP/2 protocol standard, and supports most popular programming languages. It is a language- and platform-independent, extensible serialized structured data. Its positioning is similar to JSON and XML, but smaller, faster and simpler than them.

Advantage

gRPC is based on HTTP/2 protocol transmission, and HTTP/2 still has certain advantages compared with HTTP1.x:

  • HTTP/2 uses a binary format transmission protocol instead of the text format of HTTP1.x.

  • Multiplexing

HTTP/2 supports sending multiple concurrent requests over a single connection.

  • Server Push

Server push is a mechanism for sending data ahead of a client request. In HTTP/2, a server can send multiple responses to a client request. Unlike HTTP/1.X, only the client can initiate a request, and the server can generate a corresponding response.

  • Reduce header compression for network traffic

HTTP/2 compresses and transmits message headers, which can save network traffic occupied by message headers.

How it works

As can be seen from the above figure, let’s briefly understand the working mode of grpc. To use gRPC for remote call service, the client (client) only needs gRPC Stub to initiate a service call to gRPC Server through Proto Request, and then gRPC Server returns the call result to the calling client through Proto Response(s).

Usage scenario

  • Interface Constraints: It is necessary to strictly control the interface. For example, when providing an interface to the outside, we do not want the client to pass data at will. This is why we can use gRPC to restrict the interface.
  • Performance requirements: There are high requirements for transmission performance. If the message body we transmit is too large, or the scheduling is too frequent and we do not want to affect system performance, we can consider using gRPC, whose message body is much smaller than JSON or text transmission.

Protobuf syntax

Basic specification

  • The file is suffixed with .proto, and statements other than structure definitions end with a semicolon
  • Structure definition can contain: message, service, enum
  • The semicolon at the end of the rpc method definition is optional
  • Message names are named in camel case, and field names are separated by lowercase letters and underscores
  • Enums type names are named in camel case, and field names are separated by uppercase letters and underscores
  • Service and rpc method names are uniformly named in camel case
message SongServerRequest {
    required string song_name = 1;
}

enum Foo {
  FIRST_VALUE = 1;
  SECOND_VALUE = 2;
}
Copy Code

Limit modifier

  • Required: Indicates that it is a required field. It must be relative to the sender. The value of this field must be set before sending the message. For the receiver, it must be able to recognize the meaning of this field. If the required field is not set or the required field cannot be recognized before sending, a codec exception will be thrown, resulting in the message being discarded.
  • Optional: Indicates that it is an optional field. Optional For the sender, when sending a message, you can selectively set or not set the value of this field. For the receiver, if the optional field can be recognized, it will be processed accordingly; if it cannot be recognized, the field will be ignored, and other fields in the message will be processed normally.
  • Repeated: Indicates that the field can contain 0~N elements. Its characteristics are the same as optional, but each time can contain multiple values. Can be seen as passing an array of values.

Data type

.proto C++ Java< /strong> Python Go Ruby C#
double double double float float64 Float double
float float float float float32 float float
int32 int32 int int int32 Fixnum or Bignum int
int64 int64 long ing /long[3] int64 Bignum long
uint32 uint32 int[1] int/long[3] uint32 Fixnum or Bignum uint
uint64 uint64 long[1] int/long[3] uint64 Bignum ulong
sint32 int32 int intj int32 Fixnum or Bignum int
sint64 int64 long int/long[3] int64 Bignum long
fixed32 uint32 int[1] int uint32 Fixnum or Bignum uint
fixed64 uint64 long[1] int/long[3] uint64 Bignum ulong
sfixed32 int32 int int int32 Fixnum or Bignum int
sfixed64 int64 long int/long[3] int64 Bignum long
bool bool boolean boolean bool TrueClass/FalseClass bool
string string String str/unicode[4] string String(UTF-8) string
bytes string ByteString str []byte String(ASCII-8BIT) ByteString

gRPC integrates SpringCloud & amp; Nacos

In fact, the first time I learned about gRPC was when Nacos2.0 was upgraded. Compared with 1.X, Nacos2.0 added a gRPC communication method.

Port Offset from main port Description
9848 1000 client gRPC request server port , for the client to initiate a connection and request to the server
9849 1001 The server gRPC requests the server port, Used for synchronization between services, etc.

Core dependencies

 <properties>
        <java.version>8</java.version>
        <nacos.version>2.2.5.RELEASE</nacos.version>
        <mapstruct.version>1.3.1.Final</mapstruct.version>
        <grpc.starter.version>2.10.1.RELEASE</grpc.starter.version>
        <grpc.client.version>2.10.1.RELEASE</grpc.client.version>
        <lombok.version>1.18.12</lombok.version>
        <fastjson.version>1.2.76</fastjson.version>
        <freemarker.verson>2.3.28</freemarker.verson>
        <nacos.client>2.0.0</nacos.client>
    </properties>
    <dependency>
       <groupId>net.devh</groupId>
       <artifactId>grpc-client-spring-boot-starter</artifactId>
       <version>${grpc.client.version}</version>
    </dependency>
    <dependency>
        <groupId>net.devh</groupId>
        <artifactId>grpc-server-spring-boot-starter</artifactId>
        <version>${grpc.starter.version}</version>
    </dependency>
Copy Code

Project Structure

API

  • Write pom configuration.
<dependencies>
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-server-spring-boot-starter</artifactId>
        </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.34.1:exe:${os.detected.classifier}</pluginArtifact>
                    <!--Set grpc to generate code to the specified path -->
                    <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
                    <!--Whether to clear the directory before generating code -->
                    <clearOutputDirectory>false</clearOutputDirectory>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <!-- Set multiple source folders -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <!-- Add main source directory -->
                    <execution>
                        <id>add-source</id>
                        <phase>generate-sources</phase>
                        <goals>
                            <goal>add-source</goal>
                        </goals>
                        <configuration>
                            <sources>
                                <source>${project.basedir}/src/main/gen</source>
                                <source>${project.basedir}/src/main/java</source>
                            </sources>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>
Copy Code
  • Create a new src\main\proto directory and write user.ptoto.
syntax = "proto3";

option java_multiple_files = true;
option java_package = "com.yx.grpc.user";

service UserService {
  rpc queryUser(UserRequest) returns (UserReply) {}
}
message UserRequest {
  int64 id = 2;
}
message UserReply {
  int32 code = 1;
  string msg = 2;
  bool success = 3;
  message Data {
    UserPb userPb = 1;
  }
  Data data = 4;
}

message UserPb {
  int64 id = 1;
  string name = 2;
  string sex = 3;
}
Copy Code
  • Execute mvn compile to generate code.

Server

  • Core service implementation class.
@GrpcService
public class UserServiceImpl extends UserServiceGrpc. UserServiceImplBase{

    @Override
    public void queryUser(UserRequest request, StreamObserver<UserReply> responseObserver) {
        UserReply.Builder userReply = UserReply.newBuilder();
        TblUser tblUser = new TblUser(11L, "syx", "nan");
        userReply.setCode(200).setMsg("SUCCESS").setSuccess(true);
        userReply.setData(UserReply.Data.newBuilder()
                .setUserPb(UserPb.newBuilder()
                        .setId(tblUser.getId())
                .setName(tblUser.getName())
                .setSex(tblUser.getSex())));
        responseObserver.onNext(userReply.build());
        responseObserver.onCompleted();
        super.queryUser(request, responseObserver);
    }
}
Copy Code

client

  • gRPC configuration.
grpc:
  client:
    GLOBAL:
      negotiation-type: plaintext
      enable-keep-alive: true
      keep-alive-without-calls: true
Copy Code
  • Custom request converter.
@Configuration
public class MessageConverter {

    @Bean
    public HttpMessageConverters protobufHttpMessageConverter() {
        ProtobufHttpMessageConverter protobufHttpMessageConverter = new ProtobufHttpMessageConverter();
        protobufHttpMessageConverter.setSupportedMediaTypes(Lists.newArrayList(MediaType.APPLICATION_JSON, MediaType.parseMediaType(MediaType.TEXT_PLAIN_VALUE + ";charset=ISO-8859-1")));
        return new HttpMessageConverters(protobufHttpMessageConverter);
    }

}
Copy Code
  • Request a test class.
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @GrpcClient("yx-grpc-service")
    UserServiceGrpc. UserServiceFutureStub futureStub;

    @RequestMapping(value="/queryUser/{id}")
    public UserReply queryUser(@PathVariable Integer id) {
        UserReply userReply = null;
        try {
            userReply = futureStub.queryUser(UserRequest.newBuilder().setId(id).build()).get();
            return userReply;
        } catch (Exception e) {

        }
        return userReply;
    }
}
Copy Code

Test

Start the server and client, visit http://localhost:8002/user/queryUser/1 .