signed

QiShunwang

“诚信为本、客户至上”

Spring Boot 1.5.19.RELEASE 集成 GRPC

2021/6/3 16:12:32   来源:

Spring Boot 1.5.19.RELEASE 集成 GRPC

  • 背景
    • 环境和过程
      • 环境
      • 过程

背景

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;
      }
      
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/202106031535302.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3RhbndlbmthaTE5ODgwOTAwNA==,size_16,color_FFFFFF,t_70)
    
  • 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;
    }
}

调用即可获取到需要到数据。