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 .