JAVA使用gRPC

目录
收起
gRPC介绍
protobuf插件安装
协议编写
maven插件
生成代码
代码实现
方式一
方式二
 
https://zhuanlan.zhihu.com/p/464658805

gRPC介绍

gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特性。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

gRPC有四中服务方法:

  • Unary RPCs,一元RPC。客户端发送一个请求到服务端,服务端响应一个请求。
  • rpc getUser (User) returns (User) {}
  • Server streaming RPCs,服务端流RPC。客户端发送一个请求到服务端,获取到一个流去连续读取返回的消息,直到消息全部获取。gRPC保证单个请求的消息顺序。
  • rpc getUsers (User) returns (stream User) {}
  • Client streaming RPCs,客户端流RPC。客户端给服务器通过流写入连续的消息,一旦客户端完成了消息写入,就等待服务端读取完成然后返回一个响应。同时gRPC也会保证单个请求的消息顺序。
  • rpc saveUsers (stream User) returns (User) {}
  • Bidirectional streaming RPCs,双向流。客户端和服务端都可以通过 read-write流发送一个连续的消息。两个流之间的操作是相互独立的。所以,客户端和服务端可以同时进行流的读写。
  • rpc saveUsers (stream User) returns (stream User) {}

protobuf插件安装

协议编写

message.proto

syntax = "proto3";
package protocol;

import "file.proto";

option go_package = "protocol";
option java_multiple_files = true;
option java_package = "com.kone.pbdemo.protocol";

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;

  protocol.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) {}
}

 file.proto

syntax = "proto3";

package protocol;

option go_package = "protocol";
option java_package = "com.kone.pbdemo.protocol";

message File {
  string name = 1;
  int32 size = 2;
}

 

 

maven插件

           <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.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>

			<plugin>
				<groupId>org.xolstice.maven.plugins</groupId>
				<artifactId>protobuf-maven-plugin</artifactId>
				<version>0.6.1</version>

				<configuration>
					<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/proto</protoSourceRoot>
					<!-- 生成的Java文件目录 -->
					<outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
					<clearOutputDirectory>false</clearOutputDirectory>
					<!--<outputDirectory>${project.build.directory}/generated-sources/protobuf</outputDirectory>-->
				</configuration>
				<executions>
					<execution>
						<goals>
							<goal>compile</goal>
							<goal>compile-custom</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

注意添加os系统信息插件,否则会提示如下错误。

添加os系统信息插件

                <extensions>
			<extension>
				<groupId>kr.motd.maven</groupId>
				<artifactId>os-maven-plugin</artifactId>
				<version>1.6.2</version>
			</extension>
		</extensions>

生成代码

双击插件中的compile和compile-custom,分别编译bean和service。

也可以执行mvn clean compile生成文件。

生成的代码:

代码实现

方式一

不和springboot进行集成,自己手动通过ServerBuilder将服务进行启动。

添加需要的maven依赖

		<dependency>
			<groupId>com.google.protobuf</groupId>
			<artifactId>protobuf-java</artifactId>
			<version>${protobuf.version}</version>
		</dependency>

		<dependency>
			<groupId>io.grpc</groupId>
			<artifactId>grpc-all</artifactId>
			<version>${grpc.version}</version>
		</dependency>

完整的maven依赖

<?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 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.6.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.kone</groupId>
	<artifactId>pb-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>pb-demo</name>
	<description>Demo project for Protocbuf</description>
	<properties>
		<java.version>1.8</java.version>
		<protobuf.version>3.19.4</protobuf.version>
		<grpc.version>1.26.0</grpc.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>com.google.protobuf</groupId>
			<artifactId>protobuf-java</artifactId>
			<version>${protobuf.version}</version>
		</dependency>

		<dependency>
			<groupId>io.grpc</groupId>
			<artifactId>grpc-all</artifactId>
			<version>${grpc.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</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.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>

			<plugin>
				<groupId>org.xolstice.maven.plugins</groupId>
				<artifactId>protobuf-maven-plugin</artifactId>
				<version>0.6.1</version>

				<configuration>
					<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/proto</protoSourceRoot>
					<!-- 生成的Java文件目录 -->
					<outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
					<clearOutputDirectory>false</clearOutputDirectory>
					<!--<outputDirectory>${project.build.directory}/generated-sources/protobuf</outputDirectory>-->
				</configuration>
				<executions>
					<execution>
						<goals>
							<goal>compile</goal>
							<goal>compile-custom</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

</project>
  • 实现服务端

实现UserService

package com.kone.pbdemo.service;

import com.kone.pbdemo.protocol.User;
import com.kone.pbdemo.protocol.UserServiceGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;

/**
 * @author Kone
 * @date 2022/1/29
 */
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();
            }
        };
    }
}

添加服务并启动:

package com.kone.pbdemo.test;

import com.kone.pbdemo.service.FileServiceImpl;
import com.kone.pbdemo.service.MessageServiceImpl;
import com.kone.pbdemo.service.UserServiceImpl;
import io.grpc.Server;
import io.grpc.ServerBuilder;

/**
 * @author Kone
 * @date 2022/2/7
 */
public class PbServer {
    public static void main(String[] args) throws Exception {
        int port = 9091;
        Server server = ServerBuilder
                .forPort(port)
                .addService(new UserServiceImpl())
                .build()
                .start();
        System.out.println("server started, port : " + port);
        server.awaitTermination();
    }
}
  • 客户端发起请求
package com.kone.pbdemo.test;

import com.google.protobuf.InvalidProtocolBufferException;
import com.kone.pbdemo.protocol.*;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;

import java.util.Iterator;

/**
 * @author Kone
 * @date 2022/1/29
 */
public class PbTest {
    public static void main(String[] args) throws InterruptedException {
        FileOuterClass.File file = FileOuterClass.File.newBuilder()
                .setName("fileName")
                .setSize(200)
                .build();
        User user = User.newBuilder()
                .setUsername("zhangsan")
                .setUserId(100)
                .putHobbys("pingpong", "play pingpong")
                .setCode(200)
                .setFile(file)
//                .setError("error string")
                .build();

        System.out.println(user);
        System.out.println(user.getMsgCase().getNumber());


//        测试反序列化
        try {
            FileOuterClass.File fileNew = FileOuterClass.File.parseFrom(file.toByteArray());
            System.out.println(fileNew);
        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        }

        String host = "127.0.0.1";
        int port = 9091;
        ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();

//        client接收一个对象
        UserServiceGrpc.UserServiceBlockingStub userServiceBlockingStub = UserServiceGrpc.newBlockingStub(channel);
        User responseUser = userServiceBlockingStub.getUser(user);
        System.out.println(responseUser);

//        client接收一个列表
        Iterator<User> users = userServiceBlockingStub.getUsers(user);
        while (users.hasNext()) {
            System.out.println(users.next());
        }


//         client发送一个列表到Server端
        UserServiceGrpc.UserServiceStub userServiceStub = UserServiceGrpc.newStub(channel);

        StreamObserver<User> responseObserver = new StreamObserver<User>() {
            @Override
            public void onNext(User user) {
                System.out.println("responseObserver -------> ");
                System.out.println(user);
            }

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

            @Override
            public void onCompleted() {
                System.out.println("responseObserver onCompleted");
            }
        };
        StreamObserver<User> requestObserver = userServiceStub.saveUsers(responseObserver);

        for (int i = 0; i < 3; i++) {
            requestObserver.onNext(user);
        }

        requestObserver.onCompleted();

        Thread.sleep(2000);
        channel.shutdown();
    }
}

 

方式二

通过和springboot进行集成,让spring管理服务,自动启动服务。

需要的依赖

                <!-- grpc server和spring-boot集成框架 -->
		<dependency>
			<groupId>net.devh</groupId>
			<artifactId>grpc-server-spring-boot-starter</artifactId>
			<version>2.13.0.RELEASE</version>
		</dependency>

		<!-- grpc client和spring-boot集成框架 -->
		<dependency>
			<groupId>net.devh</groupId>
			<artifactId>grpc-client-spring-boot-starter</artifactId>
			<version>2.13.0.RELEASE</version>
		</dependency>

完整的maven依赖

<?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 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.6.3</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.kone</groupId>
	<artifactId>pb-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>pb-demo</name>
	<description>Demo project for Protocbuf</description>
	<properties>
		<java.version>1.8</java.version>
		<protobuf.version>3.19.4</protobuf.version>
		<grpc.version>1.26.0</grpc.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<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.13.0.RELEASE</version>
		</dependency>

		<!-- grpc client和spring-boot集成框架 -->
		<dependency>
			<groupId>net.devh</groupId>
			<artifactId>grpc-client-spring-boot-starter</artifactId>
			<version>2.13.0.RELEASE</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</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.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>

			<plugin>
				<groupId>org.xolstice.maven.plugins</groupId>
				<artifactId>protobuf-maven-plugin</artifactId>
				<version>0.6.1</version>

				<configuration>
					<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/proto</protoSourceRoot>
					<!-- 生成的Java文件目录 -->
					<outputDirectory>${project.basedir}/src/main/java/</outputDirectory>
					<clearOutputDirectory>false</clearOutputDirectory>
					<!--<outputDirectory>${project.build.directory}/generated-sources/protobuf</outputDirectory>-->
				</configuration>
				<executions>
					<execution>
						<goals>
							<goal>compile</goal>
							<goal>compile-custom</goal>
						</goals>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>

</project>
  • 服务端

实现服务端的接口,和方式一差不多,唯一不同的地方是添加@GrpcService,让spring进行管理。

package com.kone.pbdemo.service;

import com.kone.pbdemo.protocol.User;
import com.kone.pbdemo.protocol.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();
            }
        };
    }
}
  • 在application.yml中添加gRPC端口配置
grpc:
  client:
    userClient:
      negotiationType: PLAINTEXT
      address: static://localhost:9090
  server:
    port: 9090
server:
  port: 8080

grpc.client下的address是gRPC客户端调用远程的端口,项目自己启动调用自己,所以后下面的gRPC服务端一致。

grpc.server.port是gRPC服务启动的端口。

server.port是spring web自己启动的端口,即平时请求的http接口端口。

 

  • 客户端使用

通过@GrpcClient("userClient"),将spring管理的服务注入,然后调用。

package com.kone.pbdemo;

import com.kone.pbdemo.protocol.User;
import com.kone.pbdemo.protocol.UserServiceGrpc;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Iterator;

@SpringBootTest(classes = PbDemoApplication.class)
class PbDemoApplicationTests {

	@GrpcClient("userClient")
	private UserServiceGrpc.UserServiceBlockingStub userService;

	@Test
	void contextLoads() {
		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());
		}
	}

}

 

 

posted @ 2022-06-20 17:20  xiondun  阅读(3096)  评论(0编辑  收藏  举报