背景
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。
Grpc 由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。
项目是物联网平台和连接模块获取数据服务,以前都是使用http接口模式做数据传输,一直觉得http有点麻烦,并且rpc是我一直想用的。于是花了点时间查看RPC框架,并且决定以后项目中都使用GRPC去解决不同项目之间的通信问题。
网上Grpc和SpringBoot集成的文章不是很多,大部分都是采用 springboot.grpc.starter的项目,看了一下,版本,不是适合我们目前的项目,项目的springboot版本又不能随意升级,于是尝试SpringBoot集成,并且把步骤分享下。
环境和过程
环境
Spring Boot 1.5.19.RELEASE
Grpc 1.38.0
过程
-
1: maven 引入:
<dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>1.38.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.38.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.38.0</version> </dependency>
-
2: maven引入编译插件:
kr.motd.maven os-maven-plugin 1.7.0<plugins> <!-- proto 打包信息 --> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.15.0:exe:${os.detected.classifier}</pluginArtifact> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> </plugins>
- 3: 编写 proto 文件并且放入项目根路径下
syntax = "proto3"; option java_multiple_files = true; option java_package = "com.yuyuan.internet.modules.rpc"; option java_outer_classname = "EcloudConstructionProtobuf"; option objc_class_prefix = "Grpc"; service GrpcService { rpc GetDevicesSensorInfoList (GetDevicesInfoRequest) returns (SensorInfoList) {} } message GetDevicesInfoRequest { string devicesId = 1; string imei = 2; } message SensorInfo { string id = 1; string name = 2; string addr = 3; string time = 4; string value = 5; int32 type = 6; } message SensorInfoList { repeated SensorInfo sensorInfo = 1; }

- 3: 编写 proto 文件并且放入项目根路径下
-
4: 在ecloud-core模块maven运行 clean install 会在根目录生成 protobuf 协议定义的和方法。
grpc-java 和 java 目录中生成java代码。(开发阶段可以把全部java类复制到当前工程包中) -
5 服务端项目编写3个java类。
-
EcloudGrpcService.java 类。实现服务业务代码。
package com.yuyuan.internet.modules.iot.receive.rpc;
import java.util.Set;
import com.yuyuan.internet.modules.iot.receive.task.ReceiveTaskService;
import com.yuyuan.internet.modules.rpc.GetDevicesInfoRequest;
import com.yuyuan.internet.modules.rpc.GrpcServiceGrpc;
import com.yuyuan.internet.modules.rpc.SensorInfo;
import com.yuyuan.internet.modules.rpc.SensorInfoList;
import com.yuyuan.internet.modules.rpc.SensorInfoListOrBuilder;
import io.grpc.stub.StreamObserver;
/***
* ecloud 设备服务
* @author tanwenkai
*/
@GrpcService
public class EcloudGrpcService extends GrpcServiceGrpc.GrpcServiceImplBase {
/***
* 获取设备传感器信息 集合
*/
@Override
public void getDevicesSensorInfoList(GetDevicesInfoRequest request, StreamObserver<SensorInfoList> responseObserver) {
SensorInfoListOrBuilder sensorInfoListOrBuilder = SensorInfoList.newBuilder();
/**
id_ = "";
name_ = "";
addr_ = "";
time_ = "";
value_ = "";
**/
if(ReceiveTaskService.deviceNodeDataMap.size()>=1){
Set<String> keySet = ReceiveTaskService.deviceNodeDataMap.keySet();
for (String key : keySet) {
SensorInfo sensorInfo = ReceiveTaskService.deviceNodeDataMap.get(key);
((SensorInfoList.Builder) sensorInfoListOrBuilder).addSensorInfo(sensorInfo);
}
}
responseObserver.onNext(((SensorInfoList.Builder) sensorInfoListOrBuilder).build());
responseObserver.onCompleted();
}
}
- GrpcLauncher.java 类。定义Grpc Server 服务代码。
package com.yuyuan.internet.modules.iot.receive.rpc;
import java.io.IOException;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import io.grpc.BindableService;
import io.grpc.Server;
import io.grpc.ServerBuilder;
@Component("grpcLauncher")
public class GrpcLauncher {
private static Logger logger = LoggerFactory.getLogger(GrpcLauncher.class);
/**
* 定义Grpc Server
*/
private Server server;
@Value("${grpc.server.port}")
private Integer grpcServerPort;
/**
* GRPC 服务启动方法
* @param grpcServiceBeanMap
*/
public void grpcStart(Map<String, Object> grpcServiceBeanMap) {
try{
grpcServerPort = 4321;
ServerBuilder serverBuilder = ServerBuilder.forPort(grpcServerPort);
for (Object bean : grpcServiceBeanMap.values()){
serverBuilder.addService((BindableService) bean);
logger.info(bean.getClass().getSimpleName() + " is regist in Spring Boot");
}
server = serverBuilder.build().start();
logger.info("grpc server is started at " + grpcServerPort);
server.awaitTermination();
Runtime.getRuntime().addShutdownHook(new Thread(()->{grpcStop();}));
} catch (IOException e){
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* GRPC 服务Stop方法
*/
private void grpcStop(){
if (server != null){
server.shutdownNow();
}
}
}
- GrpcService.java 自定义注解,用于获取Spring扫描到的类。
package com.yuyuan.internet.modules.iot.receive.rpc;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;
/**
* 自定义注解,用于获取Spring扫描到的类
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface GrpcService {
}
- Application 类中增加启动代码。
public static void main(String[] args) throws Exception{
ApplicationContext context = SpringApplication.run(Application.class, args);
//下面为增加启动代码。
Map<String, Object> grpcServiceBeanMap = context.getBeansWithAnnotation(GrpcService.class);
GrpcLauncher grpcLauncher = context.getBean("grpcLauncher",GrpcLauncher.class);
grpcLauncher.grpcStart(grpcServiceBeanMap);
}
- 6: 客户端调用
GrpcClientMananer.java
package com.yuyuan.internet.modules.rpc;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
@Component
public class GrpcClientMananer {
@Value("${grpc.client.host}")
private String host;
@Value("${grpc.client.port}")
private Integer port;
public ManagedChannel getChannel() {
ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().disableRetry().idleTimeout(2, TimeUnit.SECONDS).build();
return channel;
}
}
- EcloudClient.java 使用类。
package com.yuyuan.internet.modules.rpc;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import io.grpc.ManagedChannel;
@Component
public class EcloudClient {
private static Logger logger = LoggerFactory.getLogger(EcloudClient.class);
@Autowired
private GrpcClientMananer grpcClientMananer;
public List<SensorInfo> getSensorInfoList(){
ManagedChannel channel = grpcClientMananer.getChannel();
GetDevicesInfoRequest getDevicesInfoRequest = GetDevicesInfoRequest.newBuilder().setDevicesId("123").setImei("imei").build();
//((HelloWorld.getDevicesInfoRequest.Builder) getDevicesInfoRequestOrBuilder).setName("Geek");
GrpcServiceGrpc.GrpcServiceBlockingStub stub = GrpcServiceGrpc.newBlockingStub(channel);
SensorInfoList sensorInfoListObj = stub.getDevicesSensorInfoList(getDevicesInfoRequest) ;//.welcome(((HelloWorld.getDevicesInfoRequest.Builder) getDevicesInfoRequestOrBuilder).build());
//System.out.println("getSensorInfoCount:"+sensorInfoList.getSensorInfoCount());
logger.info("getSensorInfoCount:"+sensorInfoListObj.getSensorInfoCount());
List<SensorInfo> sensorInfoList = sensorInfoListObj.getSensorInfoList();
for (SensorInfo sensorInfo : sensorInfoList) {
logger.info("sensorInfo,name:"+sensorInfo.getName()+",id:"+sensorInfo.getId()+",addr:"+sensorInfo.getAddr()+",time:"+sensorInfo.getTime()+",value:"+sensorInfo.getValue());
}
channel.shutdown();
return sensorInfoList;
}
}
调用即可获取到需要到数据。