java-grpc
1. 导入依赖
<properties>
<!-- grpc版本 -->
<io.grpc.version>1.23.0</io.grpc.version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- grpc -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${io.grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${io.grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-core</artifactId>
<version>${io.grpc.version}</version>
</dependency>
<!-- gRPC服务端依赖 -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-server-spring-boot-starter</artifactId>
<version>2.5.1.RELEASE</version>
</dependency>
2. protobuf编译插件
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<extensions>true</extensions>
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.8.0:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${io.grpc.version}:exe:${os.detected.classifier}
</pluginArtifact>
<!--默认值-->
<protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
<!--默认值-->
<outputDirectory>${project.basedir}/src/main/java</outputDirectory>
<!--设置是否在生成java文件之前清空outputDirectory的文件,默认值为true,设置为false时也会覆盖同名文件-->
<clearOutputDirectory>false</clearOutputDirectory>
<!--默认值-->
<temporaryProtoFileDirectory>${project.build.directory}/protoc-dependencies
</temporaryProtoFileDirectory>
<!--更多配置信息可以查看https://www.xolstice.org/protobuf-maven-plugin/compile-mojo.html-->
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
3. proto协议定义
- 注意:proto协议客户端与服务端必须一模一样,否则客户端是调不通服务端的。
(1)header.proto
syntax = "proto3";
package headerProto;
option java_multiple_files = true;
option java_package = "com.example.demo.proto3.header";
message Header{
uint32 nEnType=1; //内容数据是否使用了加密 0- 无加密1-AES加密 2-SM4加密
uint32 nErrCode=2; //错误码---如果是请求包,则该字段=0 如果是响应数据 则=对应的错误码
uint32 nPort=3; //服务端grpc端口 用来定位问题
string nIp=4; //服务器ip
string strVersion=5; //本条grpc通信协议版本“3.0.0”
string strFunName=6; //函数名称 请求时=请求者的函数名 响应时=服务端的函数名
string strUser=7; //进程名称 请求时=请求者的进程名称 响应时=服务端的进程名称--用来定位问题
string strTime=8; //时间戳 "2021-12-29 09:08:23:332"
string strKey=9; //加密的密钥
string strUuid=10; //每次请求的唯一ID 用于异步回调
}
(2)device.proto
syntax = "proto3";
package DV;
option java_multiple_files = true;
option java_package = "com.example.demo.proto3.device";
import "header.proto";
message MountRequest {
headerProto.Header header = 1;
uint32 optype = 2;
uint32 nDeviceType = 3;
string strBoxIp = 4;
string authentication = 5;
string random= 6;
string platformip= 7;
int32 direction = 8;
}
message MountResponse {
headerProto.Header header = 1;
int32 errorCode = 2;
string errorMessage = 3;
string deviceSn = 4;
string deviceType = 5;
string deviceModel = 6;
int32 deviceEnabledState = 7;
string mac = 8;
string deviceName = 9;
string softwareVersion = 10;
}
message queryDeviceSnRequest {
headerProto.Header header = 1;
uint32 nDeviceType = 2;
}
message queryDeviceSnResponse {
headerProto.Header header = 1;
string deviceSn = 2;
int32 errorCode = 3;
string errorMessage = 4;
}
message Response {
headerProto.Header header = 1;
int32 errorCode = 2;
string errorMessage = 3;
}
message OpenDoorRequest {
enum openDoorWay {
singleOpenDoor = 0;
longOpenDoor = 1;
closeDoor = 2;
longCloseDoor = 3;
}
headerProto.Header header = 1;
openDoorWay openWay = 2;
}
message RestartRequest {
headerProto.Header header = 1;
}
message UpdatePwdIpNameRequest {
enum updateType {
password = 0;
ip = 1;
name = 2;
}
headerProto.Header header = 1;
updateType type = 2;
string param = 3;
}
message HeartBeatRequest {
headerProto.Header header = 1;
string deviceSn = 2;
string deviceModel = 3;
string cpuUtilization = 4;
string gpuUtilization = 5;
string memory = 6;
string disk = 7;
}
message SoftStateRequest {
headerProto.Header header = 1;
string deviceSn = 2;
string node = 3;
string softName = 4;
string status = 5;
}
message DeviceAlarmRequest {
headerProto.Header header = 1;
string deviceSn = 2;
string alarmType = 3;
string alarmTime = 4;
string alarmDescription = 5;
string alarmId = 6;
}
message DeviceEnabledStateRequest {
enum DeviceEnabledState {
enable = 0;
disable = 1;
}
headerProto.Header header = 1;
DeviceEnabledState enabledState = 2;
}
message clearDeviceDatabaseRequest {
headerProto.Header header = 1;
string tempParam1 = 2;
string tempParam2 = 3;
}
message clearDeviceDatabaseResponse {
headerProto.Header header = 1;
int32 errorCode = 2;
string errorMessage = 3;
}
service S
{
rpc OpenDoor (OpenDoorRequest) returns (Response) {}
rpc RestartDevice (RestartRequest) returns (Response) {}
rpc DeviceMount (MountRequest) returns (MountResponse) {}
rpc UpdatePwdIpName (UpdatePwdIpNameRequest) returns (Response) {}
rpc updateEnabledStatus (DeviceEnabledStateRequest) returns (Response) {}
rpc queryDeviceSn (queryDeviceSnRequest) returns (queryDeviceSnResponse) {}
rpc clearDeviceDatabase (clearDeviceDatabaseRequest) returns (clearDeviceDatabaseResponse) {}
rpc HeartBeat (HeartBeatRequest) returns (Response) {}
rpc DeviceSoftState (SoftStateRequest) returns (Response) {}
rpc DeviceAlarm (DeviceAlarmRequest) returns (Response) {}
}
4.客户端
@Component
public class DeviceGrpcClient {
private SGrpc.SBlockingStub stub = null;
private ManagedChannel channel = null;
/**
* 开门grpc接口
*/
public DeviceResponse openDoor(String deviceIp, Integer throughWay) {
DeviceResponse deviceResponse = new DeviceResponse();
deviceResponse.setErrCode(500);
deviceResponse.setMsg("开门失败");
try {
//初始化stub
initStub(deviceIp, padPort);
OpenDoorRequest.Builder builder = OpenDoorRequest.newBuilder();
//配置请求头
//Header header = GrpcUtil.setHeader(0, 0, 9090, "192.168.66.233", "3.3.0", "deviceMount", "strUser", "key", "uuid").build();
//设置参数
//builder.setHeader(header);
builder.setOpenWayValue(throughWay);
//调用grpc服务器
Response response = stub.withDeadlineAfter(2, TimeUnit.SECONDS).openDoor(builder.build());
if (response.getErrorCode() == 0) {
deviceResponse.setErrCode(200);
deviceResponse.setMsg("开门成功");
return deviceResponse;
}
} catch (Exception e) {
log.info("异常信息:{}", e.getMessage());
deviceResponse.setMsg("连接设备失败,请确认设备信息和网络是否正确");
return deviceResponse;
} finally {
channel.shutdownNow();
log.info("is Shutdown: {}", channel.isShutdown());
}
return deviceResponse;
}
/**
* 初始化stub
*/
private void initStub(String ip, Integer port) {
synchronized (this) {
log.info("mount IP :{}", ip);
channel = GrpcUtil.getChannel(ip, port);
stub = SGrpc.newBlockingStub(channel);
}
}
}
5.服务端
@GrpcService
public class DeviceGrpcService extends SGrpc.SImplBase {
@Override
public void heartBeat(HeartBeatRequest request, StreamObserver<Response> responseObserver) {
//获取header信息
//返回状态码(0/1)和消息(***)
String date = DateUtils.parseDateToStr("yyyy-MM-dd HH:mm:ss", new Date());
Header.Builder builder = Header.newBuilder().setStrTime(date);
Response response = Response.newBuilder().setHeader(builder.build()).setErrorCode(0).setErrorMessage("心跳接收成功").build();
// onNext()方法向客户端返回结果
responseObserver.onNext(response);
// 告诉客户端这次调用已经完成
responseObserver.onCompleted();
HeartInfo heartInfo = new HeartInfo(request.getDeviceSn(), request.getDeviceModel(), request.getMemory(), request.getDisk(), request.getCpuUtilization(), request.getGpuUtilization());
R receiveHeart = remoteDeviceService.receiveHeart(heartInfo, SecurityConstants.INNER);
}
}
6.参数配置
grpc:
server:
port: 9091
#服务端配置心跳,向客户端发送心跳
#enable-keep-alive: true
#发送心跳时间间隔
#keep-alive-time: 60
#收到心跳回复超时时间
#keep-alive-timeout: 20
#允许客户端配置的活跃时间,默认5min,单位s,如果配置的活跃时间大于允许时间,将会关闭连接
permit-keep-alive-time: 300
#允许客户端向服务端发心跳,默认为false
permit-keep-alive-without-calls: true
max-inbound-message-size: 1048576000
#rpc server 参数配置:
enableKeepAlive #服务端是否开启心跳;默认为false ,
keepAliveTime #服务端每隔多少时间向客户端发送心跳检测;默认60s 单位秒
keepAliveTimeout #服务端发送心跳收到回复的超时时间;默认20s 单位秒
permitKeepAliveTime #指定允许客户端配置的最积极的保持活动时间 默认5min 单位秒
permitKeepAliveWithoutCalls #是否允许客户端发送保持活动的HTTP/2 PING 默认false
healthServiceEnabled #是否开启服务健康检查 默认true
maxInboundMessageSize #接收文件大小 单位byte 默认为4MB
port #grpc-server端口 默认9090
#在这里还需要特别注意一个问题, grpc client 的keepalive 的 时间设定 需要在server 允许范围内,否则,server 会认为你是捣蛋的,给你发送一个GOAWAY 消息,把和client 的连接强制关掉