-
开发客户端应用
-
验证
源码下载
- 本篇实战中的完整源码可在GitHub下载到,地址和链接信息如下表所示(https://github.com/zq2599/blog_demos):
| 名称 | 链接 | 备注 |
| :-- | :-- | :-- |
| 项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |
| git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |
| git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
- 这个git项目中有多个文件夹,《java版gRPC实战》系列的源码在grpc-tutorials文件夹下,如下图红框所示:
- grpc-tutorials文件夹下有多个目录,本篇文章对应的服务端代码在double-stream-server-side目录下,客户端代码在double-stream-client-side目录下,如下图:
在proto文件中定义双向流类型的gRPC接口
- 首先要做的就是定义gRPC接口,打开mall.proto,在里面新增方法和相关的数据结构,需要重点关注的是BatchDeduct方法的入参ProductOrder和返回值DeductReply都添加了stream修饰(ProductOrder是上一章定义的),代表该方法是双向流类型:
// gRPC服务,这是个在线商城的库存服务
service StockService {
// 双向流式:批量扣减库存
rpc BatchDeduct (stream ProductOrder) returns (stream DeductReply) {}
}
// 扣减库存返回结果的数据结构
message DeductReply {
// 返回码
int32 code = 1;
// 描述信息
string message = 2;
}
- 双击下图红框中的task即可生成java代码:
- 生成下图红框中的文件,即服务端定义和返回值数据结构:
- 接下来开发服务端;
开发服务端应用
- 在父工程grpc-turtorials下面新建名为double-stream-server-side的模块,其build.gradle内容如下:
// 使用springboot插件
plugins {
id ‘org.springframework.boot’
}
dependencies {
implementation ‘org.projectlombok:lombok’
implementation ‘org.springframework.boot:spring-boot-starter’
// 作为gRPC服务提供方,需要用到此库
implementation ‘net.devh:grpc-server-spring-boot-starter’
// 依赖自动生成源码的工程
implementation project(‘:grpc-lib’)
// annotationProcessor不会传递,使用了lombok生成代码的模块,需要自己声明annotationProcessor
annotationProcessor ‘org.projectlombok:lombok’
}
- 配置文件application.yml:
spring:
application:
name: double-stream-server-side
gRPC有关的配置,这里只需要配置服务端口号
grpc:
server:
port: 9901
-
启动类DoubleStreamServerSideApplication.java的代码就不贴了,普通的springboot启动类而已;
-
重点是提供grpc服务的GrpcServerService.java,咱们要做的就是给上层框架返回一个匿名类,至于里面的onNext、onCompleted方法何时被调用是上层框架决定的,另外还准备了成员变量totalCount,这样就可以记录总数了,由于请求参数是流,因此匿名类的onNext会被多次调用,并且由于返回值是流,因此onNext中调用了responseObserver.onNext方法来响应流中的每个请求,这样客户端就不断收到服务端的响应数据(即客户端的onNext方法会被多次调用):
package grpctutorials;
import com.bolingcavalry.grpctutorials.lib.DeductReply;
import com.bolingcavalry.grpctutorials.lib.ProductOrder;
import com.bolingcavalry.grpctutorials.lib.StockServiceGrpc;
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.server.service.GrpcService;
@GrpcService
@Slf4j
public class GrpcServerService extends StockServiceGrpc.StockServiceImplBase {
@Override
public StreamObserver batchDeduct(StreamObserver responseObserver) {
// 返回匿名类,给上层框架使用
return new StreamObserver() {
private int totalCount = 0;
@Override
public void onNext(ProductOrder value) {
log.info(“正在处理商品[{}],数量为[{}]”,
value.getProductId(),
value.getNumber());
// 增加总量
totalCount += value.getNumber();
int code;
String message;
// 假设单数的都有库存不足的问题
if (0 == value.getNumber() % 2) {
code = 10000;
message = String.format(“商品[%d]扣减库存数[%d]成功”, value.getProductId(), value.getNumber());
} else {
code = 10001;
message = String.format(“商品[%d]扣减库存数[%d]失败”, value.getProductId(), value.getNumber());
}
responseObserver.onNext(DeductReply.newBuilder()
.setCode(code)
.setMessage(message)
.build());
}
@Override
public void onError(Throwable t) {
log.error(“批量减扣库存异常”, t);
}
@Override
public void onCompleted() {
log.info(“批量减扣库存完成,共计[{}]件商品”, totalCount);
responseObserver.onCompleted();
}
};
}
}
开发客户端应用
- 在父工程grpc-turtorials下面新建名为double-stream-server-side的模块,其build.gradle内容如下:
plugins {
id ‘org.springframework.boot’
}
dependencies {
implementation ‘org.projectlombok:lombok’
implementation ‘org.springframework.boot:spring-boot-starter’
implementation ‘org.springframework.boot:spring-boot-starter-web’
implementation ‘net.devh:grpc-client-spring-boot-starter’
implementation project(‘:grpc-lib’)
}
- 配置文件application.yml,设置自己的web端口号和服务端地址:
server:
port: 8082
spring:
application:
name: double-stream-client-side
grpc:
client:
gRPC配置的名字,GrpcClient注解会用到
double-stream-server-side:
gRPC服务端地址
address: ‘static://127.0.0.1:9901’
enableKeepAlive: true
keepAliveWithoutCalls: true
negotiationType: plaintext
-
启动类DoubleStreamClientSideApplication.java的代码就不贴了,普通的springboot启动类而已;
-
正常情况下我们都是用StreamObserver处理服务端响应,这里由于是异步响应,需要额外的方法从StreamObserver中取出业务数据,于是定一个新接口,继承自StreamObserver,新增getExtra方法可以返回String对象,详细的用法稍后会看到:
package com.bolingcavalry.grpctutorials;
import io.grpc.stub.StreamObserver;
public interface ExtendResponseObserver extends StreamObserver {
String getExtra();
}
- 重头戏来了,看看如何远程调用双向流类型的gRPC接口,代码中已经添加详细注释:
package grpctutorials;
import com.bolingcavalry.grpctutorials.lib.DeductReply;
import com.bolingcavalry.grpctutorials.lib.ProductOrder;
import com.bolingcavalry.grpctutorials.lib.StockServiceGrpc;
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Service;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class GrpcClientService {
@GrpcClient(“double-stream-server-side”)
private StockServiceGrpc.StockServiceStub stockServiceStub;
/**
-
批量减库存
-
@param count
-
@return
*/
public String batchDeduct(int count) {
CountDownLatch countDownLatch = new CountDownLatch(1);
// responseObserver的onNext和onCompleted会在另一个线程中被执行,
// ExtendResponseObserver继承自StreamObserver
ExtendResponseObserver responseObserver = new ExtendResponseObserver() {
// 用stringBuilder保存所有来自服务端的响应
private StringBuilder stringBuilder = new StringBuilder();
@Override
public String getExtra() {
return stringBuilder.toString();
}
/**
-
客户端的流式请求期间,每一笔请求都会收到服务端的一个响应,
-
对应每个响应,这里的onNext方法都会被执行一次,入参是响应内容
-
@param value
*/
@Override
public void onNext(DeductReply value) {
log.info(“batch deduct on next”);
// 放入匿名类的成员变量中
stringBuilder.append(String.format(“返回码[%d],返回信息:%s
” , value.getCode(), value.getMessage()));
}
@Override
public void onError(Throwable t) {
log.error(“batch deduct gRPC request error”, t);
stringBuilder.append("batch deduct gRPC error, " + t.getMessage());
countDownLatch.countDown();
}
/**
- 服务端确认响应完成后,这里的onCompleted方法会被调用
*/
@Override
public void onCompleted() {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
总结
总体来说,如果你想转行从事程序员的工作,Java开发一定可以作为你的第一选择。但是不管你选择什么编程语言,提升自己的硬件实力才是拿高薪的唯一手段。
如果你以这份学习路线来学习,你会有一个比较系统化的知识网络,也不至于把知识学习得很零散。我个人是完全不建议刚开始就看《Java编程思想》、《Java核心技术》这些书籍,看完你肯定会放弃学习。建议可以看一些视频来学习,当自己能上手再买这些书看又是非常有收获的事了。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
讲解视频,并且会持续更新!**
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
总结
总体来说,如果你想转行从事程序员的工作,Java开发一定可以作为你的第一选择。但是不管你选择什么编程语言,提升自己的硬件实力才是拿高薪的唯一手段。
如果你以这份学习路线来学习,你会有一个比较系统化的知识网络,也不至于把知识学习得很零散。我个人是完全不建议刚开始就看《Java编程思想》、《Java核心技术》这些书籍,看完你肯定会放弃学习。建议可以看一些视频来学习,当自己能上手再买这些书看又是非常有收获的事了。
[外链图片转存中…(img-iWnhlAcO-1713448478407)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!