grpc-java springboot 同步异步调用 demo

本文介绍了使用grpc-java和SpringBoot创建的简单demo,包括proto文件定义、服务端与客户端的实现。服务端通过GrpcServerConfiguration启动,客户端提供了同步、基于Future的异步和基于回调的异步三种调用方式。项目分为grpc-springboot-demo-api、grpc-springboot-demo-server和grpc-springboot-demo-consumer三个部分。

近期学习 grpc-java,从 demo 入手,进行学习

grpc 的基础知识暂不整理,后续有时间再整理一波,这一篇主要是记录一个简单的 grpc 使用 springboot 创建的 demo。

我整个 demo 分成三个 Project :

  • grpc-springboot-demo-api:用于存放 proto 文件,生成Java 代码
  • grpc-springboot-demo-server:服务端代码
  • grpc-springboot-demo-consumer:客户端代码

demo-api(proto)

创建一个最简单的 proto 文件,请求跟相应都只包含一个 String 的对象。

syntax = "proto3";

option java_package = "grpc.springboot.demo.api";
option java_outer_classname = "HelloWorldService";

package helloworld;

// 定义服务
service HelloWorld {
    rpc SayHello (HelloRequest) returns (HelloResponse) {}
}

// 定义请求体
message HelloRequest {
    string message = 1;
}

// 定义相应内容
message HelloResponse {
    string message = 1;
}

文件创建好之后,需要进行编译,pom文件中需要引入相关插件:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.parallelline1996</groupId>
    <artifactId>grpc-springboot-demo-api</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-all</artifactId>
            <version>1.12.0</version>
        </dependency>
    </dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.4.1.Final</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.0</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.0.0:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.0.0:exe:${os.detected.classifier}</pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>6</source>
                    <target>6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

完成后,直接通过 maven 工具的 install 指令,推到本地仓库即可,构建时,便会自动生成我们需要的类。

在这里插入图片描述

demo-server

服务端项目的目录结构:

  • Starter:springboot 的启动类
  • GrpcServerConfiguration:grpc 服务端的启动类
  • HelloServerImpl:提供服务的实现类

在这里插入图片描述
首先 pom 文件,主要是导入 springboot 的启动的依赖,以及 grpc 的依赖,以及我们上面的 api 项目。

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.parallelline1996</groupId>
    <artifactId>grpc-springboot-demo-server</artifactId>
    <version>1.0.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.parallelline1996</groupId>
            <artifactId>grpc-springboot-demo-api</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>
        <!-- spring boot -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-all</artifactId>
            <version>1.28.1</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>27.0-jre</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>1.5.21.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

Starter 启动类:

@SpringBootApplication
public class Starter implements CommandLineRunner {
   
   

    private GrpcServerConfiguration configuration;

    public Starter(GrpcServerConfiguration configuration) {
   
   
        this.configuration = configuration;
    }

	// 启动 grpc 服务端
    @Override
    public void run(String... args) throws Exception {
   
   
        configuration.start();
    }

	// 启动 springboot 容器
    public static void main(String[] args) {
   
   
        SpringApplication.run(Starter.class, args);
    }
}

GrpcServerConfiguration grpc 服务端启动类:
这里注意:如果不在创建 Server 指定线程池,则会使用 grpc-java 中提供的默认线程池响应从客户端发送的请求(后续关于 Server 的创建,会再写博客说明)。也可以直接指定线程池,只需调用 ServerBuilder.executor(executor)
即可。

@Component
public class GrpcServerConfiguration {
   
   

    private static final Logger logger = LoggerFactory.getLogger(GrpcServerConfiguration
"C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\bin\java.exe" "-javaagent:D:\IntelliJ IDEA Community Edition 2025.1.4.1\lib\idea_rt.jar=57570" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\charsets.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\ext\dnsns.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\ext\jaccess.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\ext\localedata.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\ext\nashorn.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\ext\sunec.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\ext\zipfs.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\jce.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\jfr.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\jfxswt.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\jsse.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\management-agent.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\resources.jar;C:\Program Files\Java\openlogic-openjdk-8u462-b08-windows-x64\jre\lib\rt.jar;D:\r\basic-spring-boot\target\classes;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot-starter-validation\2.1.14.RELEASE\spring-boot-starter-validation-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot-starter\2.1.14.RELEASE\spring-boot-starter-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot\2.1.14.RELEASE\spring-boot-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.1.14.RELEASE\spring-boot-autoconfigure-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.1.14.RELEASE\spring-boot-starter-logging-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\admin\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\admin\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.11.2\log4j-to-slf4j-2.11.2.jar;C:\Users\admin\.m2\repository\org\apache\logging\log4j\log4j-api\2.11.2\log4j-api-2.11.2.jar;C:\Users\admin\.m2\repository\org\slf4j\jul-to-slf4j\1.7.30\jul-to-slf4j-1.7.30.jar;C:\Users\admin\.m2\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;C:\Users\admin\.m2\repository\org\yaml\snakeyaml\1.23\snakeyaml-1.23.jar;C:\Users\admin\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.34\tomcat-embed-el-9.0.34.jar;C:\Users\admin\.m2\repository\org\hibernate\validator\hibernate-validator\6.0.19.Final\hibernate-validator-6.0.19.Final.jar;C:\Users\admin\.m2\repository\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;C:\Users\admin\.m2\repository\org\jboss\logging\jboss-logging\3.3.3.Final\jboss-logging-3.3.3.Final.jar;C:\Users\admin\.m2\repository\com\fasterxml\classmate\1.4.0\classmate-1.4.0.jar;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot-starter-data-jpa\2.1.14.RELEASE\spring-boot-starter-data-jpa-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot-starter-jdbc\2.1.14.RELEASE\spring-boot-starter-jdbc-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\com\zaxxer\HikariCP\3.2.0\HikariCP-3.2.0.jar;C:\Users\admin\.m2\repository\org\springframework\spring-jdbc\5.1.15.RELEASE\spring-jdbc-5.1.15.RELEASE.jar;C:\Users\admin\.m2\repository\javax\transaction\javax.transaction-api\1.3\javax.transaction-api-1.3.jar;C:\Users\admin\.m2\repository\javax\xml\bind\jaxb-api\2.3.1\jaxb-api-2.3.1.jar;C:\Users\admin\.m2\repository\javax\activation\javax.activation-api\1.2.0\javax.activation-api-1.2.0.jar;C:\Users\admin\.m2\repository\org\hibernate\hibernate-core\5.3.17.Final\hibernate-core-5.3.17.Final.jar;C:\Users\admin\.m2\repository\javax\persistence\javax.persistence-api\2.2\javax.persistence-api-2.2.jar;C:\Users\admin\.m2\repository\org\javassist\javassist\3.23.2-GA\javassist-3.23.2-GA.jar;C:\Users\admin\.m2\repository\net\bytebuddy\byte-buddy\1.9.16\byte-buddy-1.9.16.jar;C:\Users\admin\.m2\repository\antlr\antlr\2.7.7\antlr-2.7.7.jar;C:\Users\admin\.m2\repository\org\jboss\jandex\2.0.5.Final\jandex-2.0.5.Final.jar;C:\Users\admin\.m2\repository\org\dom4j\dom4j\2.1.3\dom4j-2.1.3.jar;C:\Users\admin\.m2\repository\org\hibernate\common\hibernate-commons-annotations\5.0.4.Final\hibernate-commons-annotations-5.0.4.Final.jar;C:\Users\admin\.m2\repository\org\glassfish\jaxb\jaxb-runtime\2.3.1\jaxb-runtime-2.3.1.jar;C:\Users\admin\.m2\repository\org\glassfish\jaxb\txw2\2.3.1\txw2-2.3.1.jar;C:\Users\admin\.m2\repository\com\sun\istack\istack-commons-runtime\3.0.7\istack-commons-runtime-3.0.7.jar;C:\Users\admin\.m2\repository\org\jvnet\staxex\stax-ex\1.8\stax-ex-1.8.jar;C:\Users\admin\.m2\repository\com\sun\xml\fastinfoset\FastInfoset\1.2.15\FastInfoset-1.2.15.jar;C:\Users\admin\.m2\repository\org\springframework\data\spring-data-jpa\2.1.17.RELEASE\spring-data-jpa-2.1.17.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\data\spring-data-commons\2.1.17.RELEASE\spring-data-commons-2.1.17.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\spring-orm\5.1.15.RELEASE\spring-orm-5.1.15.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\spring-context\5.1.15.RELEASE\spring-context-5.1.15.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\spring-tx\5.1.15.RELEASE\spring-tx-5.1.15.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\spring-beans\5.1.15.RELEASE\spring-beans-5.1.15.RELEASE.jar;C:\Users\admin\.m2\repository\org\slf4j\slf4j-api\1.7.30\slf4j-api-1.7.30.jar;C:\Users\admin\.m2\repository\org\springframework\spring-aspects\5.1.15.RELEASE\spring-aspects-5.1.15.RELEASE.jar;C:\Users\admin\.m2\repository\com\mysql\mysql-connector-j\8.2.0\mysql-connector-j-8.2.0.jar;C:\Users\admin\.m2\repository\com\google\protobuf\protobuf-java\3.21.9\protobuf-java-3.21.9.jar;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot-starter-web\2.1.14.RELEASE\spring-boot-starter-web-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot-starter-json\2.1.14.RELEASE\spring-boot-starter-json-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.9.10.4\jackson-databind-2.9.10.4.jar;C:\Users\admin\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.9.10\jackson-annotations-2.9.10.jar;C:\Users\admin\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.9.10\jackson-core-2.9.10.jar;C:\Users\admin\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.10\jackson-datatype-jdk8-2.9.10.jar;C:\Users\admin\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.10\jackson-datatype-jsr310-2.9.10.jar;C:\Users\admin\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.10\jackson-module-parameter-names-2.9.10.jar;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\2.1.14.RELEASE\spring-boot-starter-tomcat-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.34\tomcat-embed-core-9.0.34.jar;C:\Users\admin\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.34\tomcat-embed-websocket-9.0.34.jar;C:\Users\admin\.m2\repository\org\springframework\spring-web\5.1.15.RELEASE\spring-web-5.1.15.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\spring-webmvc\5.1.15.RELEASE\spring-webmvc-5.1.15.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\spring-expression\5.1.15.RELEASE\spring-expression-5.1.15.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot-starter-aop\2.1.14.RELEASE\spring-boot-starter-aop-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\spring-aop\5.1.15.RELEASE\spring-aop-5.1.15.RELEASE.jar;C:\Users\admin\.m2\repository\org\aspectj\aspectjweaver\1.9.5\aspectjweaver-1.9.5.jar;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot-starter-actuator\2.1.14.RELEASE\spring-boot-starter-actuator-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot-actuator-autoconfigure\2.1.14.RELEASE\spring-boot-actuator-autoconfigure-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\boot\spring-boot-actuator\2.1.14.RELEASE\spring-boot-actuator-2.1.14.RELEASE.jar;C:\Users\admin\.m2\repository\org\projectlombok\lombok\1.18.12\lombok-1.18.12.jar;C:\Users\admin\.m2\repository\org\springframework\spring-core\5.1.15.RELEASE\spring-core-5.1.15.RELEASE.jar;C:\Users\admin\.m2\repository\org\springframework\spring-jcl\5.1.15.RELEASE\spring-jcl-5.1.15.RELEASE.jar;C:\Users\admin\.m2\repository\com\alibaba\fastjson2\fastjson2\2.0.23\fastjson2-2.0.23.jar;C:\Users\admin\.m2\repository\io\grpc\grpc-netty-shaded\1.56.0\grpc-netty-shaded-1.56.0.jar;C:\Users\admin\.m2\repository\com\google\guava\guava\31.1-android\guava-31.1-android.jar;C:\Users\admin\.m2\repository\com\google\guava\failureaccess\1.0.1\failureaccess-1.0.1.jar;C:\Users\admin\.m2\repository\com\google\guava\listenablefuture\9999.0-empty-to-avoid-conflict-with-guava\listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar;C:\Users\admin\.m2\repository\org\checkerframework\checker-qual\3.12.0\checker-qual-3.12.0.jar;C:\Users\admin\.m2\repository\com\google\j2objc\j2objc-annotations\1.3\j2objc-annotations-1.3.jar;C:\Users\admin\.m2\repository\com\google\errorprone\error_prone_annotations\2.18.0\error_prone_annotations-2.18.0.jar;C:\Users\admin\.m2\repository\io\perfmark\perfmark-api\0.26.0\perfmark-api-0.26.0.jar;C:\Users\admin\.m2\repository\io\grpc\grpc-protobuf\1.56.0\grpc-protobuf-1.56.0.jar;C:\Users\admin\.m2\repository\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;C:\Users\admin\.m2\repository\com\google\api\grpc\proto-google-common-protos\2.17.0\proto-google-common-protos-2.17.0.jar;C:\Users\admin\.m2\repository\io\grpc\grpc-protobuf-lite\1.56.0\grpc-protobuf-lite-1.56.0.jar;C:\Users\admin\.m2\repository\io\grpc\grpc-stub\1.56.0\grpc-stub-1.56.0.jar;C:\Users\admin\.m2\repository\net\devh\grpc-spring-boot-starter\2.14.0.RELEASE\grpc-spring-boot-starter-2.14.0.RELEASE.jar;C:\Users\admin\.m2\repository\net\devh\grpc-server-spring-boot-starter\2.14.0.RELEASE\grpc-server-spring-boot-starter-2.14.0.RELEASE.jar;C:\Users\admin\.m2\repository\net\devh\grpc-server-spring-boot-autoconfigure\2.14.0.RELEASE\grpc-server-spring-boot-autoconfigure-2.14.0.RELEASE.jar;C:\Users\admin\.m2\repository\net\devh\grpc-common-spring-boot\2.14.0.RELEASE\grpc-common-spring-boot-2.14.0.RELEASE.jar;C:\Users\admin\.m2\repository\io\grpc\grpc-core\1.51.0\grpc-core-1.51.0.jar;C:\Users\admin\.m2\repository\com\google\code\gson\gson\2.8.6\gson-2.8.6.jar;C:\Users\admin\.m2\repository\com\google\android\annotations\4.1.1.4\annotations-4.1.1.4.jar;C:\Users\admin\.m2\repository\org\codehaus\mojo\animal-sniffer-annotations\1.21\animal-sniffer-annotations-1.21.jar;C:\Users\admin\.m2\repository\io\grpc\grpc-services\1.51.0\grpc-services-1.51.0.jar;C:\Users\admin\.m2\repository\com\google\protobuf\protobuf-java-util\3.21.7\protobuf-java-util-3.21.7.jar;C:\Users\admin\.m2\repository\io\grpc\grpc-api\1.51.0\grpc-api-1.51.0.jar;C:\Users\admin\.m2\repository\io\grpc\grpc-context\1.51.0\grpc-context-1.51.0.jar;C:\Users\admin\.m2\repository\net\devh\grpc-client-spring-boot-starter\2.14.0.RELEASE\grpc-client-spring-boot-starter-2.14.0.RELEASE.jar;C:\Users\admin\.m2\repository\net\devh\grpc-client-spring-boot-autoconfigure\2.14.0.RELEASE\grpc-client-spring-boot-autoconfigure-2.14.0.RELEASE.jar;C:\Users\admin\.m2\repository\io\micrometer\micrometer-core\1.11.4\micrometer-core-1.11.4.jar;C:\Users\admin\.m2\repository\io\micrometer\micrometer-commons\1.11.4\micrometer-commons-1.11.4.jar;C:\Users\admin\.m2\repository\io\micrometer\micrometer-observation\1.11.4\micrometer-observation-1.11.4.jar;C:\Users\admin\.m2\repository\org\hdrhistogram\HdrHistogram\2.1.12\HdrHistogram-2.1.12.jar;C:\Users\admin\.m2\repository\org\latencyutils\LatencyUtils\2.0.3\LatencyUtils-2.0.3.jar" com.tplink.nbu.demo.basicspringboot.GrpcClient 11:38:31.760 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.logging.InternalLoggerFactory - Using SLF4J as the default logging framework 11:38:31.764 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent0 - -Dio.netty.noUnsafe: false 11:38:31.764 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent0 - Java version: 8 11:38:31.764 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.theUnsafe: available 11:38:31.765 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.copyMemory: available 11:38:31.765 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent0 - sun.misc.Unsafe.storeFence: available 11:38:31.765 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent0 - java.nio.Buffer.address: available 11:38:31.765 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent0 - direct buffer constructor: available 11:38:31.765 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent0 - java.nio.Bits.unaligned: available, true 11:38:31.765 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent0 - jdk.internal.misc.Unsafe.allocateUninitializedArray(int): unavailable prior to Java9 11:38:31.765 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent0 - java.nio.DirectByteBuffer.<init>(long, int): available 11:38:31.765 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent - sun.misc.Unsafe: available 11:38:31.766 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent - -Dio.netty.tmpdir: C:\Users\admin\AppData\Local\Temp (java.io.tmpdir) 11:38:31.766 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent - -Dio.netty.bitMode: 64 (sun.arch.data.model) 11:38:31.766 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent - Platform: Windows 11:38:31.767 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent - -Dio.netty.maxDirectMemory: 7570718720 bytes 11:38:31.767 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent - -Dio.netty.uninitializedArrayAllocationThreshold: -1 11:38:31.767 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.CleanerJava6 - java.nio.ByteBuffer.cleaner(): available 11:38:31.767 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent - -Dio.netty.noPreferDirect: false 11:38:31.905 [main] DEBUG io.grpc.netty.shaded.io.netty.channel.MultithreadEventLoopGroup - -Dio.netty.eventLoopThreads: 48 11:38:31.912 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.InternalThreadLocalMap - -Dio.netty.threadLocalMap.stringBuilder.initialSize: 1024 11:38:31.912 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.InternalThreadLocalMap - -Dio.netty.threadLocalMap.stringBuilder.maxSize: 4096 11:38:31.915 [main] DEBUG io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop - -Dio.netty.noKeySetOptimization: false 11:38:31.915 [main] DEBUG io.grpc.netty.shaded.io.netty.channel.nio.NioEventLoop - -Dio.netty.selectorAutoRebuildThreshold: 512 11:38:31.918 [main] DEBUG io.grpc.netty.shaded.io.netty.util.internal.PlatformDependent - org.jctools-core.MpscChunkedArrayQueue: available 11:38:32.087 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.util.ResourceLeakDetector - -Dio.grpc.netty.shaded.io.netty.leakDetection.level: simple 11:38:32.087 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.util.ResourceLeakDetector - -Dio.grpc.netty.shaded.io.netty.leakDetection.targetRecords: 4 11:38:32.090 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.AbstractByteBuf - -Dio.grpc.netty.shaded.io.netty.buffer.checkAccessible: true 11:38:32.090 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.AbstractByteBuf - -Dio.grpc.netty.shaded.io.netty.buffer.checkBounds: true 11:38:32.090 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.util.ResourceLeakDetectorFactory - Loaded default ResourceLeakDetector: io.grpc.netty.shaded.io.netty.util.ResourceLeakDetector@7f0df18 11:38:32.117 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.numHeapArenas: 48 11:38:32.117 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.numDirectArenas: 48 11:38:32.117 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.pageSize: 8192 11:38:32.117 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxOrder: 9 11:38:32.117 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.chunkSize: 4194304 11:38:32.117 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.smallCacheSize: 256 11:38:32.117 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.normalCacheSize: 64 11:38:32.117 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxCachedBufferCapacity: 32768 11:38:32.117 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.cacheTrimInterval: 8192 11:38:32.117 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.cacheTrimIntervalMillis: 0 11:38:32.117 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.useCacheForAllThreads: false 11:38:32.117 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator - -Dio.netty.allocator.maxCachedByteBuffersPerChunk: 1023 11:38:32.135 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.channel.DefaultChannelId - -Dio.netty.processId: 8460 (auto-detected) 11:38:32.136 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.util.NetUtil - -Djava.net.preferIPv4Stack: false 11:38:32.136 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.util.NetUtil - -Djava.net.preferIPv6Addresses: false 11:38:32.176 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.util.NetUtilInitializations - Loopback interface: lo (Software Loopback Interface 1, 127.0.0.1) 11:38:32.176 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.util.NetUtil - Failed to get SOMAXCONN from sysctl and file \proc\sys\net\core\somaxconn. Default: 200 11:38:32.179 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.channel.DefaultChannelId - -Dio.netty.machineId: a0:ad:9f:ff:fe:1d:8c:93 (auto-detected) 11:38:32.189 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.ByteBufUtil - -Dio.netty.allocator.type: pooled 11:38:32.189 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.ByteBufUtil - -Dio.netty.threadLocalDirectBufferSize: 0 11:38:32.189 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.buffer.ByteBufUtil - -Dio.netty.maxThreadLocalCharBufferSize: 16384 11:38:32.196 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.util.Recycler - -Dio.netty.recycler.maxCapacityPerThread: 4096 11:38:32.196 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.util.Recycler - -Dio.netty.recycler.ratio: 8 11:38:32.196 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.util.Recycler - -Dio.netty.recycler.chunkSize: 32 11:38:32.196 [grpc-default-executor-0] DEBUG io.grpc.netty.shaded.io.netty.util.Recycler - -Dio.netty.recycler.blocking: false 11:38:32.207 [grpc-nio-worker-ELG-1-2] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler - [id: 0xf85c2e3d, L:/127.0.0.1:57623 - R:localhost/127.0.0.1:9090] OUTBOUND SETTINGS: ack=false settings={ENABLE_PUSH=0, MAX_CONCURRENT_STREAMS=0, INITIAL_WINDOW_SIZE=1048576, MAX_HEADER_LIST_SIZE=8192} 11:38:32.211 [grpc-nio-worker-ELG-1-2] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler - [id: 0xf85c2e3d, L:/127.0.0.1:57623 - R:localhost/127.0.0.1:9090] OUTBOUND WINDOW_UPDATE: streamId=0 windowSizeIncrement=983041 11:38:32.256 [grpc-nio-worker-ELG-1-2] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler - [id: 0xf85c2e3d, L:/127.0.0.1:57623 - R:localhost/127.0.0.1:9090] INBOUND SETTINGS: ack=false settings={MAX_CONCURRENT_STREAMS=2147483647, INITIAL_WINDOW_SIZE=1048576, MAX_HEADER_LIST_SIZE=8192} 11:38:32.257 [grpc-nio-worker-ELG-1-2] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler - [id: 0xf85c2e3d, L:/127.0.0.1:57623 - R:localhost/127.0.0.1:9090] OUTBOUND SETTINGS: ack=true 11:38:32.265 [grpc-nio-worker-ELG-1-2] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler - [id: 0xf85c2e3d, L:/127.0.0.1:57623 - R:localhost/127.0.0.1:9090] INBOUND WINDOW_UPDATE: streamId=0 windowSizeIncrement=983041 11:38:32.266 [grpc-nio-worker-ELG-1-2] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler - [id: 0xf85c2e3d, L:/127.0.0.1:57623 - R:localhost/127.0.0.1:9090] INBOUND SETTINGS: ack=true 11:38:32.271 [grpc-nio-worker-ELG-1-2] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler - [id: 0xf85c2e3d, L:/127.0.0.1:57623 - R:localhost/127.0.0.1:9090] OUTBOUND HEADERS: streamId=3 headers=GrpcHttp2OutboundHeaders[:authority: localhost:9090, :path: /UserGrpcService/Register, :method: POST, :scheme: http, content-type: application/grpc, te: trailers, user-agent: grpc-java-netty/1.51.0, grpc-accept-encoding: gzip] streamDependency=0 weight=16 exclusive=false padding=0 endStream=false 11:38:32.277 [grpc-nio-worker-ELG-1-2] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler - [id: 0xf85c2e3d, L:/127.0.0.1:57623 - R:localhost/127.0.0.1:9090] OUTBOUND DATA: streamId=3 padding=0 endStream=true length=49 bytes=000000002c0a08746573745573657212063132333435361a1074657374406578616d706c652e636f6d2206e58c97e4baac 11:38:32.302 [grpc-nio-worker-ELG-1-2] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler - [id: 0xf85c2e3d, L:/127.0.0.1:57623 - R:localhost/127.0.0.1:9090] INBOUND PING: ack=false bytes=1234 11:38:32.302 [grpc-nio-worker-ELG-1-2] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler - [id: 0xf85c2e3d, L:/127.0.0.1:57623 - R:localhost/127.0.0.1:9090] OUTBOUND PING: ack=true bytes=1234 11:38:32.308 [grpc-nio-worker-ELG-1-2] DEBUG io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler - [id: 0xf85c2e3d, L:/127.0.0.1:57623 - R:localhost/127.0.0.1:9090] INBOUND HEADERS: streamId=3 headers=GrpcHttp2ResponseHeaders[:status: 200, content-type: application/grpc, grpc-status: 12, grpc-message: Method not found: UserGrpcService/Register] padding=0 endStream=true 【注册失败】原因:Method not found: UserGrpcService/Register,请分析并修改,源代码如下public class GrpcClient { private final ManagedChannel channel; private final UserGrpcServiceGrpc.UserGrpcServiceFutureStub futureStub; private final CountDownLatch latch = new CountDownLatch(3); public GrpcClient(String host, int port) { this.channel = ManagedChannelBuilder.forAddress(host, port) .usePlaintext() .build(); this.futureStub = UserGrpcServiceGrpc.newFutureStub(channel); } // 异步注册(触发后续登录) public void asyncRegister(String username, String password, String email, String address) { UserRegisterRequest request = UserRegisterRequest.newBuilder() .setUsername(username) .setPassword(password) .setEmail(email) .setAddress(address) .build(); ListenableFuture<UserRegisterResponse> responseFuture = futureStub.register(request); responseFuture.addListener(() -> { try { UserRegisterResponse response = responseFuture.get(); System.out.println("【注册结果】成功:" + response.getSuccess() + ",消息:" + response.getMessage()); if (response.getSuccess()) { // 注册成功后触发登录(使用相同邮箱和密码) asyncLogin(email, password); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println("【注册中断】原因:" + e.getMessage()); } catch (ExecutionException e) { Status status = Status.fromThrowable(e.getCause()); System.err.println("【注册失败】原因:" + status.getDescription()); } finally { latch.countDown(); // 注册操作完成 } }, MoreExecutors.directExecutor()); } // 异步登录(触发后续登出) private void asyncLogin(String email, String password) { UserLoginRequest request = UserLoginRequest.newBuilder() .setEmail(email) .setPassword(password) .build(); ListenableFuture<UserLoginResponse> responseFuture = futureStub.login(request); responseFuture.addListener(() -> { try { UserLoginResponse response = responseFuture.get(); System.out.println("【登录结果】token:" + response.getToken() + ",用户ID:" + response.getUserId()); if (!response.getToken().isEmpty()) { // 登录成功后触发登出 asyncLogout(); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println("【登录中断】原因:" + e.getMessage()); } catch (ExecutionException e) { Status status = Status.fromThrowable(e.getCause()); System.err.println("【登录失败】原因:" + status.getDescription()); } finally { latch.countDown(); // 登录操作完成 } }, MoreExecutors.directExecutor()); } // 异步登出 private void asyncLogout() { UserLogoutRequest request = UserLogoutRequest.newBuilder().build(); ListenableFuture<UserLogoutResponse> responseFuture = futureStub.logout(request); responseFuture.addListener(() -> { try { UserLogoutResponse response = responseFuture.get(); System.out.println("【登出结果】成功:" + response.getSuccess() + ",消息:" + response.getMessage()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); System.err.println("【登出中断】原因:" + e.getMessage()); } catch (ExecutionException e) { Status status = Status.fromThrowable(e.getCause()); System.err.println("【登出失败】原因:" + status.getDescription()); } finally { latch.countDown(); // 登出操作完成 } }, MoreExecutors.directExecutor()); } // 等待所有操作完成后关闭通道 public void shutdown() throws InterruptedException { latch.await(); // 阻塞直到所有操作完成 channel.shutdown(); } public static void main(String[] args) throws InterruptedException { GrpcClient client = new GrpcClient("localhost", 9090); // 启动注册流程(触发后续登录、登出) client.asyncRegister("testUser", "123456", "test@example.com", "北京"); // 等待所有操作完成后关闭客户端 client.shutdown(); } }
08-26
要将现有基于 HTTP 的接口转换为 gRPC 接口,并使用异步非阻塞方式调用,你需要按照以下步骤进行操作: ### 步骤 1: 定义 gRPC 接口(proto 文件) 1. 创建一个 `user.proto` 文件,定义 gRPC 服务和消息格式。 2. 文件位置:`src/main/proto/com/tplink/nbu/demo/basicspringboot/grpc/` ```protobuf syntax = "proto3"; package user; option java_package = "com.tplink.nbu.demo.basicspringboot.grpc"; option java_outer_classname = "UserProto"; service UserService { rpc Login (UserLoginRequest) returns (UserLoginSuccessResponse); rpc Register (UserRegisterRequest) returns (RegisterResponse); rpc Logout (LogoutRequest) returns (LogoutResponse); } message UserLoginRequest { string usernameOrEmail = 1; string password = 2; } message UserLoginSuccessResponse { string token = 1; } message UserRegisterRequest { string username = 1; string password = 2; string email = 3; string address = 4; } message RegisterResponse { string message = 1; } message LogoutRequest { string token = 1; } message LogoutResponse { string username = 1; } ``` ### 步骤 2: 生成 gRPC 代码 1. 使用 `protoc` 生成 gRPC 代码。 2. 运行命令: ```bash protoc --java_out=src/main/java --grpc-java_out=src/main/java src/main/proto/com/tplink/nbu/demo/basicspringboot/grpc/user.proto ``` 3. 生成的代码将位于 `com.tplink.nbu.demo.basicspringboot.grpc` 包中。 ### 步骤 3: 实现 gRPC 服务 1. 创建 `UserGrpcServiceImpl` 类,实现 gRPC 服务。 2. 文件位置:`src/main/java/com/tplink/nbu/demo/basicspringboot/grpc/UserGrpcServiceImpl.java` ```java package com.tplink.nbu.demo.basicspringboot.grpc; import io.grpc.stub.StreamObserver; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.tplink.nbu.demo.basicspringboot.dto.UserLoginDTO; import com.tplink.nbu.demo.basicspringboot.dto.UserLoginSuccessDTO; import com.tplink.nbu.demo.basicspringboot.dto.UserRegisterDTO; import com.tplink.nbu.demo.basicspringboot.exception.EmailExistsException; import com.tplink.nbu.demo.basicspringboot.exception.MaxRegisterExceededException; import com.tplink.nbu.demo.basicspringboot.exception.UnauthorizedException; import com.tplink.nbu.demo.basicspringboot.service.UserService; @Service public class UserGrpcServiceImpl extends UserServiceGrpc.UserServiceImplBase { @Autowired private UserService userService; @Override public void login(UserLoginRequest request, StreamObserver<UserLoginSuccessResponse> responseObserver) { try { boolean auth = userService.auth(new UserLoginDTO(request.getUsernameOrEmail(), request.getPassword())); if (!auth) { throw new UnauthorizedException(); } String token = request.getUsernameOrEmail(); responseObserver.onNext(UserLoginSuccessResponse.newBuilder().setToken(token).build()); responseObserver.onCompleted(); } catch (Exception e) { responseObserver.onError(e); } } @Override public void register(UserRegisterRequest request, StreamObserver<RegisterResponse> responseObserver) { try { if (userService.existsByEmail(request.getEmail())) { throw new EmailExistsException(); } if (userService.count() >= maxRegister) { throw new MaxRegisterExceededException(); } userService.register(new UserRegisterDTO(request.getUsername(), request.getPassword(), request.getEmail(), request.getAddress())); responseObserver.onNext(RegisterResponse.newBuilder().setMessage("REGISTERED SUCCESSFULLY").build()); responseObserver.onCompleted(); } catch (Exception e) { responseObserver.onError(e); } } @Override public void logout(LogoutRequest request, StreamObserver<LogoutResponse> responseObserver) { // 逻辑处理 responseObserver.onNext(LogoutResponse.newBuilder().setUsername("username").build()); responseObserver.onCompleted(); } } ``` ### 步骤 4: 配置 gRPC 服务器 1. 创建 `GrpcServerConfig` 类。 2. 文件位置:`src/main/java/com/tplink/nbu/demo/basicspringboot/grpc/GrpcServerConfig.java` ```java package com.tplink.nbu.demo.basicspringboot.grpc; import io.grpc.Server; import io.grpc.ServerBuilder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class GrpcServerConfig { @Autowired private UserGrpcServiceImpl userGrpcService; @Bean public Server grpcServer() throws Exception { return ServerBuilder.forPort(8080) .addService(userGrpcService) .build() .start(); } } ``` ### 步骤 5: 创建 gRPC 客户端 1. 创建 `UserGrpcClient` 类。 2. 文件位置:`src/main/java/com/tplink/nbu/demo/basicspringboot/grpc/UserGrpcClient.java` ```java package com.tplink.nbu.demo.basicspringboot.grpc; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import org.springframework.stereotype.Component; @Component public class UserGrpcClient { private final UserServiceGrpc.UserServiceFutureStub stub; public UserGrpcClient() { ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8080) .usePlaintext() .build(); stub = UserServiceGrpc.newFutureStub(channel); } public void login(String usernameOrEmail, String password) { UserLoginRequest request = UserLoginRequest.newBuilder() .setUsernameOrEmail(usernameOrEmail) .setPassword(password) .build(); stub.login(request).addListener(() -> { // 处理响应 }, Runnable::run); } public void register(String username, String password, String email, String address) { UserRegisterRequest request = UserRegisterRequest.newBuilder() .setUsername(username) .setPassword(password) .setEmail(email) .setAddress(address) .build(); stub.register(request).addListener(() -> { // 处理响应 }, Runnable::run); } public void logout(String token) { LogoutRequest request = LogoutRequest.newBuilder() .setToken(token) .build(); stub.logout(request).addListener(() -> { // 处理响应 }, Runnable::run); } } ``` ### 步骤 6: 启动和测试 1. 启动 Spring Boot 应用。 2. 使用 gRPC 客户端发送请求并验证响应。 3. 测试逻辑: - 调用 `login` 方法,传入用户名或邮箱和密码。 - 调用 `register` 方法,传入用户名、密码、邮箱和地址。 - 调用 `logout` 方法,传入 token。 启动springboot应用后,http接口还能用的吧?那如何测试grpc呢?grpc客户端是什么,怎么发送和验证响应?怎么调用login之类的方法,说明白清楚点
最新发布
08-27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值