SpringBoot 之整合gRPC
父工程中引入基本的依赖:
<modules>
<module>api</module>
<module>client</module>
<module>service</module>
</modules>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.7.3</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
API模块(定义proto文件):
<artifactId>grpc-api</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<grpc.version>1.6.1</grpc.version>
<protobuf.version>3.3.0</protobuf.version>
</properties>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>${grpc.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>${protobuf.version}</version>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<!-- 引入一些插件来帮助我们将.proto文件编译为java的类 -->
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.0</version>
<configuration>
<!--使用的protoc版本,os.detected.classifier表示检测到的操作系统,这里检测到的是windows-x86_64-->
<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>src/main/proto</protoSourceRoot>
<!--生成的文件的存放位置 -->
<outputDirectory>src/main/java</outputDirectory>
<!--在调用插件去生成java类时,是否清空输出文件夹,这个要设置为false,否则运行compile-custom时会把compile的结果给删了-->
<clearOutputDirectory>false</clearOutputDirectory>
</configuration>
<!-- 做的一个扩展,在执行maven的compile的时候,顺便也执行protobuf-maven-plugin插件的compile和compile-custom-->
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
在src/main目录下创建proto
文件夹。
创建一个HelloWorldService.proto
文件。
//使用proto3语法
syntax = "proto3";
//生成多个java文件
option java_multiple_files = true;
//把生成的文件放到哪个包下
option java_package = "com.gotion.grpc.api";
//输出的类名
option java_outer_classname = "HelloWorldServiceProto";
//定义一个类
service HelloWorldService {
//定义一个gRPC方法,参数为HelloRequest,返回结果为HelloResponse
rpc helloWorld(HelloRequest) returns(HelloResponse) {};
}
//定义的一个请求参数对象
message HelloRequest {
//msg参数,编号为1,这是编号不是赋值
string msg = 1;
//code参数,编号为2
int32 code = 2;
}
//定义的一个返回结果对象
message HelloResponse {
//resut参数,编号为1
string result = 1;
}
将proto文件编译成需要的JAVA文件(使用maven去compile)了。
server模块:
<dependencies>
<!-- 引入api模块-->
<dependency>
<groupId>com.gotion</groupId>
<artifactId>grpc-api</artifactId>
<version>1.0.0</version>
</dependency>
<!-- 引入gRPC服务提供端依赖-->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>2.14.0.RELEASE</version>
</dependency>
</dependencies>
application.yml
文件:
server:
port: 8081 #正常的SpringBoot应用监听的端口号
grpc:
server:
port: 9081 #gRPC服务监听的端口号
编写启动类。
实现在API模块中定义的gRPC服务。
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.server.service.GrpcService;
/**
* gRPC服务提供类,继承api模块中的proto文件编译生成的java文件,重写所定义的gRPC方法
*/
@GrpcService
@Slf4j
public class HelloWorldService extends HelloWorldServiceGrpc.HelloWorldServiceImplBase {
/**
* 定义的gRPC方法
*
* @param request 请求对象
* @param responseObserver
*/
@Override
public void helloWorld(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
//解析请求,获取其中的参数,这些都是我们之前在proto文件中定义的
String msg = request.getMsg();
int code = request.getCode();
log.info("请求中的参数为msg:{},code:{}", msg, code);
//创建一个响应对象
HelloResponse helloResponse = HelloResponse.newBuilder().setResult("我是server服务端,我收到了你的请求~").build();
//将该响应对象返回给调用者
responseObserver.onNext(helloResponse);
//这次调用结束了
responseObserver.onCompleted();
}
}
client端:
<dependencies>
<!-- spring boot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入api模块-->
<dependency>
<groupId>com.gotion</groupId>
<artifactId>grpc-api</artifactId>
<version>1.0.0</version>
</dependency>
<!-- 引入gRPC客户端依赖-->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-starter</artifactId>
<version>2.14.0.RELEASE</version>
</dependency>
</dependencies>
application.yml
文件:
server:
port: 8080 #springboot应用监听的端口号
grpc:
client:
# 自定义服务名(不同服务名可对应不同配置)GrpcClient注解会用到
grpc-server:
# 服务端地址,9081是服务提供方的gRPC监听的端口号
address: localhost:9081
# 是否开启保持连接(长连接)
enable-keep-alive: true
# 使用明文传输
negotiation-type: plaintext
编写启动类。
测试服务调用(阻塞方式和异步方式)。
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 测试gRPC服务调用
*/
@RestController
@Slf4j
@RequestMapping("/client")
public class TestController {
//注入阻塞型的gRPC调用对象,服务调用的地址在application.yml中设置了
@GrpcClient("grpc-server")
private HelloWorldServiceGrpc.HelloWorldServiceBlockingStub blockingStub;
//注入异步调用的gRPC调用对象
@GrpcClient("grpc-server")
private HelloWorldServiceGrpc.HelloWorldServiceFutureStub futureStub;
//创建一个线程池来进行异步调用
private ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20,
0, TimeUnit.SECONDS, new ArrayBlockingQueue<>(50), new ThreadPoolExecutor.CallerRunsPolicy());
//用来接收异步调用结果
private String result;
/**
* 测试阻塞调用
*
* @return 调用结果
*/
@GetMapping("/block")
public String block() {
//构造请求对象
HelloRequest helloRequest = HelloRequest.newBuilder().setMsg("block").setCode(100).build();
//进行阻塞式地调用
HelloResponse helloResponse = blockingStub.helloWorld(helloRequest);
return helloResponse.getResult();
}
/**
* 测试异步调用
*
* @return 调用结果
*/
@GetMapping("/future")
public String future() throws InterruptedException {
//构造请求对象
HelloRequest helloRequest = HelloRequest.newBuilder().setMsg("block").setCode(100).build();
//进行异步调用,看到这里返回的是一个ListenableFuture,大家应该都知道要怎么做了哈哈哈
ListenableFuture<HelloResponse> helloResponseListenableFuture = futureStub.helloWorld(helloRequest);
//创建一个CountDownLatch,来等待所有的异步任务完成(如果要执行多个异步任务的话,这里只是用一下)
//参数为要等待执行的异步任务数,这里是1,其实就是一个计算器
CountDownLatch countDownLatch = new CountDownLatch(1);
//设置回调
Futures.addCallback(helloResponseListenableFuture,
new FutureCallback<HelloResponse>() {
@Override
public void onSuccess(HelloResponse helloResponse) {
log.info("异步调用成功了,结果为{}", helloResponse.getResult());
result = helloResponse.getResult();
//计数器减1,表示该异步任务执行完成
countDownLatch.countDown();
}
@Override
public void onFailure(Throwable throwable) {
log.error("异步调用失败,原因是{}", throwable.getMessage());
}
},
executor);
//为了更直观地表现出异步任务,这里打印一个日志
log.info("这里是主线程");
//等待所有异步任务执行完成
countDownLatch.await();
return result;
}
}
原文地址:https://blog.csdn.net/const_/article/details/143861618
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!