• 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());
    		}
    	}
    
    }
  • 相关阅读:
    (原)Lazarus 异构平台下多层架构思路、DataSet转换核心代码
    (学)新版动态表单研发,阶段成果3
    (学) 如何将 Oracle 序列 重置 清零 How to reset an Oracle sequence
    (学)XtraReport WebService Print 报错
    (原)三星 i6410 刷机 短信 无法 保存 解决 办法
    (原) Devexpress 汉化包 制作工具、测试程序
    linux下网络配置
    apache自带ab.exe小工具使用小结
    Yii::app()用法小结
    PDO使用小结
  • 原文地址:https://www.cnblogs.com/xiondun/p/16393987.html
Copyright © 2020-2023  润新知