springboot 集成 grpc 和 protobuf(二) | 在实际项目中使用 grpc 和 protobuf


springboot 集成 grpc 和 protobuf(二) | 在实际项目中使用 grpc 和 protobuf


不熟悉grpc和protobuf的请参考第一篇:

springboot 集成 grpc 和 protobuf(一) | grpc和protobuf 的简介及本地使用.


注意:下面的测试代码写在同一个项目中!


一、项目结构:如下图

在这里插入图片描述


二、项目搭建

2.1 引入pom依赖:代码比较长,请耐心

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.45</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.9</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>
        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

        <!--使用grpc和protobuf-->
        <!-- protobuf依赖-->
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.5.1</version>
        </dependency>
        <!--grpc依赖-->
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-all</artifactId>
            <version>1.11.0</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.25.Final</version>
        </dependency>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-tcnative-boringssl-static</artifactId>
            <version>2.0.8.Final</version>
        </dependency>

        <!--grpc客户端-->
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-client-spring-boot-starter</artifactId>
            <version>1.3.0-RELEASE</version>
        </dependency>
        <!--grpc服务端-->
        <dependency>
            <groupId>net.devh</groupId>
            <artifactId>grpc-server-spring-boot-starter</artifactId>
            <version>1.3.0-RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.6.0</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <!--跳过test测试-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>


            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.0</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:3.5.1:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.11.0:exe:${os.detected.classifier}</pluginArtifact>
                    <!--默认值-->
                    <protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot>
                    <!--默认值-->
                    <!--<outputDirectory>${project.build.directory}/generated-sources/protobuf/java</outputDirectory>-->
                    <outputDirectory>${project.basedir}/src/main/java</outputDirectory>
                    <!--设置是否在生成java文件之前清空outputDirectory的文件,默认值为true,设置为false时也会覆盖同名文件-->
                    <clearOutputDirectory>false</clearOutputDirectory>
                </configuration>
                <executions>
                    <execution>
                        <!--在执行mvn compile的时候会执行以下操作-->
                        <phase>compile</phase>
                        <goals>
                            <!--生成OuterClass类-->
                            <goal>compile</goal>
                            <!--生成Grpc类-->
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

2.2 proto 文件介绍,第一篇也有介绍

文件名:data.proto
syntax = "proto3";

package example; //编译出来的文件存放目录

service FormatData {	
  rpc DoFormat(Data) returns (Data){}  //这里后面会用到,注意!!!
  // DoForma 客户端调用服务端使用的方法
  // Data 消息体,跟实体类类似,传参和返回都是一个实体接收
}

message Data {
  string text = 1;  // 1:代表第一个参数
  string flag = 2;  // 2:代表第二个参数
}

2.3 项目搭建好后,使用 maven 编译一下

在这里插入图片描述


2.4 然后会在java目录下生成:以Grpc结尾的文件才是连接的主要

2.4.1 DataOuterClass 文件是生成上面 data.proto 里面的message下面的 text 和 flag 参数的set和get方法,相当于实体Bean
2.4.2 FormatDataGrpc 文件是 grpc 的连接服务,下面会用这个调用

在这里插入图片描述


2.5 编写客户端:

在这里插入图片描述


三、代码编写:下面是先写的客户端代码,再写的服务端代码


客户端

3.1 application.yml文件配置(注意,yml文件中最下面url和端口根据启动项目打印的来,服务端有启动时有介绍

server:
  port: 8078
  servlet:
    context-path: /huangtu-timer

# mysql
spring:
  # 环境 dev|test|prod
  profiles:
    active: test

logging:
  level: debug
  level.com.huangtu: debug
  path: logs/
  file: timer.log

# Grpc配置
grpc:
  client:
    local-grpc-server:
      #测试,大写转小写
      host: 0.0.0.0 		  #grpc服务端的 url
      port: 9090		      #grpc服务端的端口
      enableKeepAlive: true
      keepAliveWithoutCalls: true

3.2 客户端代码如下:一边参考 data.proto 文件一边理解更好

使用自动注入只需要配置yml中的即可,手动配置url和端口的话就注释上面的代码,放开下面代码的注释,然后直接用构造方法调用即可

import example.DataOuterClass;
import example.FormatDataGrpc;
import io.grpc.Channel;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import net.devh.springboot.autoconfigure.grpc.client.GrpcClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import types.Common;
import types.Unlock;
import types.WalletServiceGrpc;

import java.util.concurrent.TimeUnit;

/**
 * CreateBy: huangtu
 */
@Component
public class GrpcClientService {
    private Logger logger = LoggerFactory.getLogger(GrpcClientService.class);

    /** yml文件配置自动注入grpc的url和端口 */
//    @GrpcClient("local-grpc-server")
//    private Channel serverChannel;


    /**
     * 测试方法
     * @param letter        字母大小写转换
     */
//    public void sendMessage(String letter) {
        //将yml文件中的url和端口封装进连接服务 -- 
        //这里大部分都是一样的,proto文件里面的server是什么方法,这里就会自带 xxxxxBlockingStub
//        FormatDataGrpc.FormatDataBlockingStub stub = FormatDataGrpc.newBlockingStub(serverChannel);
        //调用连接方法,封装参数,进行传输和接收
        //自己自定义的 proto 文件直接参考替换类和方法就行
 //       DataOuterClass.Data data = stub.doFormat(DataOuterClass.Data.newBuilder().setText(letter).build());
 //       logger.info(data.getText()+"=======");
 //   }


    /** 手动配置grpc的url和端口 */
    private final ManagedChannel channel;
    private final FormatDataGrpc.FormatDataBlockingStub blockingStub;
    public GrpcClientService(String host, int port) {
        ManagedChannelBuilder<?> channelBuilder = ManagedChannelBuilder.forAddress(host, port).usePlaintext(true);
        channel = channelBuilder.build();
        blockingStub = FormatDataGrpc.newBlockingStub(channel);
    }

	/**
     * 停止连接---遇到异常可关闭连接----一般不用,因为连接是一直在的,报错看日志即可,关闭则需要重启服务端
     */
    public void shutdown() throws InterruptedException {
        channel.shutdown().awaitTermination(5, TimeUnit.SECONDS);
    }
    
    /**
     * 测试方法
     * @param letter        字母:大小写转换
     */
    public void sendMessage(String letter) {
        //将yml文件中的url和端口封装进连接服务 -- 
        //这里大部分都是一样的,proto文件里面的server是什么方法,这里就会自带 xxxxxBlockingStub
        FormatDataGrpc.FormatDataBlockingStub stub = FormatDataGrpc.newBlockingStub(serverChannel);
        //调用连接方法,封装参数,进行传输和接收
        //自己自定义的 proto 文件直接参考替换类和方法就行
        DataOuterClass.Data data = stub.doFormat(DataOuterClass.Data.newBuilder().setText(letter).build());
        logger.info(data.getText()+"=======");
    }

}

服务端----


3.3 java服务端代码:

在这里插入图片描述

3.3.1 加入pom依赖:这里的服务端依赖已经在上面加了,所以可以不用重复加
<!--grpc服务端-->
<dependency>
    <groupId>net.devh</groupId>
    <artifactId>grpc-server-spring-boot-starter</artifactId>
    <version>1.3.0-RELEASE</version>
</dependency>

3.3.2 服务端代码
import example.DataOuterClass;
import example.FormatDataGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.springboot.autoconfigure.grpc.server.GrpcService;

/**
 * CreateBy: huangtu
 */
@GrpcService(DataOuterClass.class)
public class GrpcServerService extends FormatDataGrpc.FormatDataImplBase {

    //注意,这里的 doFormat 方法,要跟dat.proto文件的server代码块里面的方法一样,注意大小写
    //因为我这里message 代码块 是Data。我自己既用它做传参也用它做返回
    //request是客户端传过来的参数,responseObserver是返回给客户端响应的参数
    public void doFormat(DataOuterClass.Data request, StreamObserver<DataOuterClass.Data> responseObserver) {
        String text = request.getText();
        StringBuffer stringBuffer = new StringBuffer();
        //大小写转换
        for(int i=0;i<text.length();i++) {
            //如果是小写
            if (text.substring(i, i + 1).equals(text.substring(i, i + 1).toLowerCase())) {
                stringBuffer.append(text.substring(i, i + 1).toUpperCase());
                System.out.print(text.substring(i, i + 1).toUpperCase());
            } else {
                stringBuffer.append(text.substring(i, i + 1).toLowerCase());
                System.out.print(text.substring(i, i + 1).toLowerCase());
            }
        }
        DataOuterClass.Data.Builder abc = DataOuterClass.Data.newBuilder().setText(stringBuffer.toString());
        //客户端每传一个消息过来都会调用一次onNext方法,当客户端发送完毕后,会执行onCompleted来返回一个对象给客户端
        responseObserver.onNext(abc.build());
        responseObserver.onCompleted();
    }
}


3.4 启动项目:

在用一个项目中即在控制台可以看到客户端需要访问服务端的URL和端口,如果是两个单独的项目,根据自己的配置来。

在这里插入图片描述


3.5 方法调用

注意:自己写个测试类,用main方法的话直接仿照我的即可。使用yml配置的方式的话就放开客户端 GrpcClientService 代码上面的注释,把下面的代码注释
main方法测试:
public static void main(String[] args) {
		//同一个项目启动,这里的端口和url跟日志上的一样。不同项目自己配置
        GrpcClientService client = new GrpcClientService ("0.0.0.0", 9090);
        try {
            client.sendMessage("aaBBcc======");
        } catch (Exception e) {
            client.shutdown();
        }
}
    
使用yml方式如下:
@Autowired
private GrpcClientService grpcClientService;

public void test(){
	grpcClientService.sendMessage("aaBBcc======");
}

3.6 返回结果

AAbbCC=============

四、扩展,遇到的bug

1. springboot集成grpc报错:io.grpc.StatusRuntimeException: UNAVAILABLE.
2. springboot集成grpc报错:io.grpc.StatusRuntimeException: INTERNAL: Connection closed with unknown cause.

欢迎关注公众号:慌途L
后面会慢慢将文章迁移至公众号,也是方便在没有电脑的情况下可以进行翻阅,更新的话会两边同时更新,大家不用担心!
在这里插入图片描述


  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值