Spring Boot 2+gRPC 学习系列2:搭建Spring Cloud +gRPC集群项目

本项目基于Spring Boot 2.0.5+yidongnan/grpc-spring-boot-starter 2.0.1.RELEASE+SpringCloud Finchley.SR1,通过2个grpc-eureka-server模拟Eureka集群,多个可横向弹性扩容的grpc-spring-cloud-provider负责提供gRPC服务,再用grpc-spring-cloud-consumer作为服务消费者来调用gRPC服务,服务提供者和消费者通过grpc-springboot-cloud-lib模块来完成接口约定。

本项目已上传至GitHub:https://github.com/linshenkx/grpc-springboot-cloud

文章目录

    • 一 项目结构
      • 1 项目结构
      • 2 模块功能
    • 二 项目搭建
      • 0 编写根项目pom文件
      • 1 创建grpc-springboot-cloud-lib模块
      • 2 创建grpc-eureka-server模块
      • 3 创建grpc-spring-cloud-provider模块
      • 3 创建grpc-spring-cloud-consumer模块
    • 三 功能实现
      • 1 在grpc-springboot-cloud-lib模块利用proto文件生成java代码
        • (1) 编写proto文件
        • (2) 生成java代码
      • 2 编写grpc-eureka-server
        • (0)添加@EnableEurekaServer支持
        • (1)编写application.yml文件
        • (2)编写application-slave1.yml文件
        • (2)编写application-slave2.yml文件
      • 3 在grpc-spring-cloud-provider模块实现接口
        • (0)添加@EnableDiscoveryClient支持
        • (1) 编写application文件
        • (2) 编写GreeterService类
      • 4 在grpc-spring-cloud-consumer模块调用服务
        • (0)添加@EnableDiscoveryClient支持
        • (1) 编写application文件
        • (2)编写GrpcClientService服务类
        • (3)编写GrpcClientController类
    • 四 项目启动
      • 0 install工程
      • 1 多profiles启动grpc-eureka-server
      • 2 任意实例数量启动grpc-spring-cloud-provider
      • 3 启动grpc-spring-cloud-consumer
    • 五 测试
    • 六 其他
      • 1 相关文章
      • 2 GitHub项目

一 项目结构

1 项目结构

根项目(com.linshen:grpc-springboot-cloud:1.0-SNAPSHOT)下有4个子模块:

  • grpc-eureka-server:Eureka服务器
  • grpc-springboot-cloud-lib:gRPC接口,包含原始proto文件,并负责将其转换为java代码
  • grpc-spring-cloud-provider:Eureka客户端,同时也是gRPC服务提供者
  • grpc-spring-cloud-consumer:Eureka客户端,同时也是gRPC服务消费者

2 模块功能

  • 相互关系
    根项目负责依赖版本管理,通过grpc-eureka-server实现Eureka集群,通过grpc-spring-cloud-provider负责提供gRPC服务,再用grpc-spring-cloud-consumer作为服务消费者来调用gRPC服务,服务提供者和消费者都作为Eureka客户端注册在Eureka集群中,消费者通过服务名发现提供者并通过grpc-springboot-cloud-lib模块来完成接口约定实现RPC。
  • 对应MVC关系
    • grpc-springboot-cloud-lib就是service(接口,为提供实现)
    • grpc-spring-cloud-provider就相当于serviceImpl(service的实现类)
    • grpc-spring-cloud-consumer就相当于controller
  • 框架依赖
    • grpc-eureka-server:除了必要的eureka-server依赖外再依赖actuator方便监控
    • grpc-springboot-cloud-lib需要将proto文件转换成java类,故需依赖于protobuf-maven-plugin插件,另外还需要io.grpc下的grpc-all,否则生成的java类会提示缺少依赖
    • grpc-spring-cloud-provider 要注册到eureka服务中心,需要web依赖,否则会报错
    • grpc-spring-cloud-consumer 基本和provider一样

二 项目搭建

0 编写根项目pom文件


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>

    <groupId>com.linshengroupId>
    <artifactId>grpc-springboot-cloudartifactId>
    <packaging>pompackaging>
    <version>1.0-SNAPSHOTversion>

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.0.5.RELEASEversion>
    parent>

    <modules>
        <module>grpc-springboot-cloud-libmodule>
        <module>grpc-eureka-servermodule>
        <module>grpc-spring-cloud-providermodule>
        <module>grpc-spring-cloud-consumermodule>
    modules>

    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
        <java.version>1.8java.version>
        <spring-boot.version>${parent.version}spring-boot.version>
        <net-devh-grpc.version>2.0.1.RELEASEnet-devh-grpc.version>
        <spring-cloud.version>Finchley.SR1spring-cloud.version>
    properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-dependenciesartifactId>
                <version>${spring-cloud.version}version>
                <type>pomtype>
                <scope>importscope>
            dependency>
            
            <dependency>
                <groupId>com.linshengroupId>
                <artifactId>grpc-springboot-cloud-libartifactId>
                <version>${project.version}version>
            dependency>
            
            <dependency>
                <groupId>net.devhgroupId>
                <artifactId>grpc-client-spring-boot-starterartifactId>
                <version>${net-devh-grpc.version}version>
            dependency>
            <dependency>
                <groupId>net.devhgroupId>
                <artifactId>grpc-server-spring-boot-starterartifactId>
                <version>${net-devh-grpc.version}version>
            dependency>
        dependencies>
    dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-deploy-pluginartifactId>
                <configuration>
                    <skip>trueskip>
                configuration>
            plugin>
        plugins>
    build>

project>

1 创建grpc-springboot-cloud-lib模块


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>grpc-springboot-cloudartifactId>
        <groupId>com.linshengroupId>
        <version>1.0-SNAPSHOTversion>
    parent>
    <modelVersion>4.0.0modelVersion>

    <artifactId>grpc-springboot-cloud-libartifactId>
    <packaging>jarpackaging>

    <properties>
        <os.plugin.version>1.6.0os.plugin.version>
        <grpc.version>1.15.1grpc.version>
        <protoc.version>3.6.1protoc.version>
        <protobuf.plugin.version>0.6.1protobuf.plugin.version>
    properties>

    <dependencies>
        <dependency>
            <groupId>io.grpcgroupId>
            <artifactId>grpc-allartifactId>
            <version>${grpc.version}version>
        dependency>
    dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.mavengroupId>
                <artifactId>os-maven-pluginartifactId>
                <version>${os.plugin.version}version>
            extension>
        extensions>
        <plugins>
            <plugin>
                <groupId>org.xolstice.maven.pluginsgroupId>
                <artifactId>protobuf-maven-pluginartifactId>
                <version>${protobuf.plugin.version}version>
                <extensions>trueextensions>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}protocArtifact>
                    <pluginId>grpc-javapluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}pluginArtifact>
                    
                    <protoSourceRoot>${project.basedir}/src/main/protoprotoSourceRoot>
                    
                    
                    <outputDirectory>${project.basedir}/src/main/javaoutputDirectory>
                    
                    <clearOutputDirectory>falseclearOutputDirectory>
                    
                configuration>
                <executions>
                    <execution>
                        
                        <phase>compilephase>
                        <goals>
                            
                            <goal>compilegoal>
                            
                            <goal>compile-customgoal>
                        goals>
                    execution>
                executions>
            plugin>
        plugins>
    build>

project>

2 创建grpc-eureka-server模块


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>

    <artifactId>grpc-eureka-serverartifactId>
    <packaging>jarpackaging>

    <name>grpc-eureka-servername>

    <parent>
        <artifactId>grpc-springboot-cloudartifactId>
        <groupId>com.linshengroupId>
        <version>1.0-SNAPSHOTversion>
    parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

3 创建grpc-spring-cloud-provider模块


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>

    <artifactId>grpc-spring-cloud-providerartifactId>
    <packaging>jarpackaging>

    <name>grpc-spring-cloud-providername>

    <parent>
        <artifactId>grpc-springboot-cloudartifactId>
        <groupId>com.linshengroupId>
        <version>1.0-SNAPSHOTversion>
    parent>

    <dependencies>
        <dependency>
            <groupId>com.linshengroupId>
            <artifactId>grpc-springboot-cloud-libartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>net.devhgroupId>
            <artifactId>grpc-server-spring-boot-starterartifactId>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

3 创建grpc-spring-cloud-consumer模块


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>

    <artifactId>grpc-spring-cloud-consumerartifactId>
    <packaging>jarpackaging>

    <name>grpc-spring-cloud-consumername>

    <parent>
        <artifactId>grpc-springboot-cloudartifactId>
        <groupId>com.linshengroupId>
        <version>1.0-SNAPSHOTversion>
    parent>

    <dependencies>
        <dependency>
            <groupId>com.linshengroupId>
            <artifactId>grpc-springboot-cloud-libartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
        <dependency>
            <groupId>net.devhgroupId>
            <artifactId>grpc-client-spring-boot-starterartifactId>
        dependency>
    dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>

project>

三 功能实现

1 在grpc-springboot-cloud-lib模块利用proto文件生成java代码

(1) 编写proto文件

还是在src/main/proto目录编写下一个greeter.proto文件,如下

syntax = "proto3";

option java_package = "com.linshen.grpc.cloud.lib";

service Greeter {
    rpc SayHello ( HelloRequest) returns (  HelloReply) {}

}
message HelloRequest {
    string name = 1;
}
message HelloReply {
    string message = 1;
}

(2) 生成java代码

行protobuf插件的compile和compile-custom功能,生成java代码到src/main/java目录下
Spring Boot 2+gRPC 学习系列2:搭建Spring Cloud +gRPC集群项目_第1张图片

2 编写grpc-eureka-server

这里要实现两个Eureka-Server集群实现服务中心的高可用(两个确实很少,谈不上高可用,这里只是演示一下),需要通过不同的profile来启动不同的Eureka-Server,故需要application.yml、application-slave1.yml和application-slave2.yml三个文件。application.yml定义了公共属性和actuator配置,并且配置了默认active-profile是slave1。其中slave1运行在端口8781上,slave2运行在端口8782上,两者的域名(也可以设置成ip)通过环境变量传入,不传的话默认是在本地(localhost)运行。
最终的工程目录应该如下
Spring Boot 2+gRPC 学习系列2:搭建Spring Cloud +gRPC集群项目_第2张图片

(0)添加@EnableEurekaServer支持

(1)编写application.yml文件

spring:
  application:
    name: grpc-eureka-server
  profiles:
    active: ${grpc-eureka-profile:slave1}
# actuator management
management:
  endpoint:
    health:
      show-details: always
  endpoints:
    web:
      exposure:
        include: '*'

# actuator info
info:
  app:
    encoding:UTF-8
    java.source:1.8
    java.traget:1.8

(2)编写application-slave1.yml文件

server:
  port: 8781
eureka:
  client:
    service-url:
      defaultZone: http://${cloudServerSlave2:localhost}:8782/eureka/
  instance:
    hostname: ${cloudServerSlave1:localhost}
  server:
    enable-self-preservation: false

(2)编写application-slave2.yml文件

server:
  port: 8782
eureka:
  client:
    service-url:
      defaultZone: http://${cloudServerSlave1:localhost}:8781/eureka/
  instance:
    hostname: ${cloudServerSlave2:localhost}
  server:
    enable-self-preservation: false

3 在grpc-spring-cloud-provider模块实现接口

最终的工程目录应该如下
Spring Boot 2+gRPC 学习系列2:搭建Spring Cloud +gRPC集群项目_第3张图片

(0)添加@EnableDiscoveryClient支持

(1) 编写application文件

如下,server.port和grpc.server.port都为0,这样启动的时候服务端口和grpc端口都会随机分配,就不会重复占用了,eureka.instance.instance-id也是通过应用名+uuid的方式来避免重复

server:
  port: 0
grpc:
  server:
    port: 0
spring:
  application:
    name: grpc-spring-cloud-provider
eureka:
  client:
    service-url:
      defaultZone: http://${cloudServerSlave1:localhost}:8781/eureka/,http://${cloudServerSlave2:localhost}:8782/eureka/
  instance:
    instance-id: ${spring.application.name}:${random.uuid}
logging:
  level:
    org.springframework.web.servlet.DispatcherServlet: DEBUG
# actuator management
management:
  endpoint:
    health:
      show-details: always
  endpoints:
    web:
      exposure:
        include: '*'
# actuator info
info:
  app:
    encoding:UTF-8
    java.source:1.8
    java.traget:1.8
    name:grpc-spring-cloud-provider

(2) 编写GreeterService类

package com.linshen.grpcspringcloudprovider;

import com.linshen.grpc.cloud.lib.GreeterGrpc;
import com.linshen.grpc.cloud.lib.GreeterOuterClass;
import io.grpc.stub.StreamObserver;
import lombok.extern.slf4j.Slf4j;
import net.devh.springboot.autoconfigure.grpc.server.GrpcService;

@Slf4j
@GrpcService(GreeterOuterClass.class)
public class GreeterService extends GreeterGrpc.GreeterImplBase {
    @Override
    public void sayHello(GreeterOuterClass.HelloRequest request, StreamObserver<GreeterOuterClass.HelloReply> responseObserver) {
        String message = "Hello " + request.getName();
        final GreeterOuterClass.HelloReply.Builder replyBuilder = GreeterOuterClass.HelloReply.newBuilder().setMessage(message);
        responseObserver.onNext(replyBuilder.build());
        responseObserver.onCompleted();
        log.info("Returning " +message);
    }
}

4 在grpc-spring-cloud-consumer模块调用服务

最终的工程目录应该如下
Spring Boot 2+gRPC 学习系列2:搭建Spring Cloud +gRPC集群项目_第4张图片

(0)添加@EnableDiscoveryClient支持

(1) 编写application文件

不需要配置grpc.server.port,其他的和provider大致相同

(2)编写GrpcClientService服务类

注意这里的@GrpcClient的value是provider的spring.application.name

package com.linshen.grpcspringcloudconsumer;

import com.linshen.grpc.cloud.lib.GreeterGrpc;
import com.linshen.grpc.cloud.lib.GreeterOuterClass;
import io.grpc.Channel;
import net.devh.springboot.autoconfigure.grpc.client.GrpcClient;
import org.springframework.stereotype.Service;

@Service
public class GrpcClientService {

    @GrpcClient("grpc-spring-cloud-provider")
    private Channel serverChannel;

    public String sendMessage(String name) {
        GreeterGrpc.GreeterBlockingStub stub= GreeterGrpc.newBlockingStub(serverChannel);
        GreeterOuterClass.HelloReply response = stub.sayHello(GreeterOuterClass.HelloRequest.newBuilder().setName(name).build());
        return response.getMessage();
    }
}

(3)编写GrpcClientController类

package com.linshen.grpcspringcloudconsumer;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class GrpcClientController {

    @Autowired
    private GrpcClientService grpcClientService;

    @RequestMapping("/")
    public String printMessage(@RequestParam(defaultValue = "LinShen") String name) {
        return grpcClientService.sendMessage(name);
    }
}

四 项目启动

0 install工程

在根工程下运行mvn install -Dmaven.test.skip=true,这一步将项目install到本地仓库,目的是使插件能正常工作,避免出现Failed to collect dependencies问题,如果只是在本地运行的话没遇到依赖问题,这一步也可以省略。

1 多profiles启动grpc-eureka-server

分别指定active-profiles为slave1和slave2启动grpc-eureka-server

2 任意实例数量启动grpc-spring-cloud-provider

重复启动grpc-spring-cloud-provider,这里我启动了两次,获得两个实例

3 启动grpc-spring-cloud-consumer

简单启动一个消费者即可

最终运行情况应该如下
Spring Boot 2+gRPC 学习系列2:搭建Spring Cloud +gRPC集群项目_第5张图片

五 测试

打开eureka-server1,即http://localhost:8781/,如下图,可以看到所有的服务实例到注册到服务中心了
Spring Boot 2+gRPC 学习系列2:搭建Spring Cloud +gRPC集群项目_第6张图片
打开eureka-server2,即http://localhost:8782/,如下图,可以看到服务实例信息还没有eureka-server1完整
Spring Boot 2+gRPC 学习系列2:搭建Spring Cloud +gRPC集群项目_第7张图片
这个时候再打开consumer,即http://localhost:49236/,如下图,运行正常
Spring Boot 2+gRPC 学习系列2:搭建Spring Cloud +gRPC集群项目_第8张图片
把Grpc-Eureka-Server-Slave1杀掉,再查看eureka-server2,可以发现注册信息已经同步到slave2了,这个时候consumer仍可正常使用

六 其他

1 相关文章

gRPC-Java指导:https://grpc.io/docs/tutorials/basic/java.html
gRPC 官方文档中文版1.0:https://doc.oschina.net/grpc
Maven Protocol Buffers Plugin 插件指南:https://www.xolstice.org/protobuf-maven-plugin/index.html

Spring Boot 2+gRPC 学习系列1:搭建Spring Boot 2+gRPC本地项目:https://blog.csdn.net/alinyua/article/details/83030149
Spring Boot 2+gRPC 学习系列2:搭建Spring Cloud +gRPC集群项目:https://blog.csdn.net/alinyua/article/details/83043823

Spring Boot 2+Dubbo 学习系列1:使用Docker部署zookeeper:https://blog.csdn.net/alinyua/article/details/81016734
Spring Boot 2+Dubbo 学习系列2:搭建Spring Boot 2+Dubbo+Zookeeper集群:https://blog.csdn.net/alinyua/article/details/81019925
Spring Boot 2+Dubbo 学习系列3:dubbo-ops 之 Dubbo Admin:https://blog.csdn.net/alinyua/article/details/81034023

2 GitHub项目

linshenkx/grpc-springboot-cloud(Spring Cloud +gRPC集群项目):https://github.com/linshenkx/grpc-springboot-cloud
linshenkx/grpc-springboot-lin(Spring Boot 2+gRPC本地项目):https://github.com/linshenkx/grpc-springboot-lin
linshenkx/spring-cloud-lin(Spring Boot 2整合Spring Cloud):https://github.com/linshenkx/spring-cloud-lin
gRPC-Java的GitHub地址:https://github.com/grpc/grpc-java
yidongnan/grpc-spring-boot-starter的GitHub地址:https://github.com/yidongnan/grpc-spring-boot-starter

你可能感兴趣的:(SpringBoot,Spring,Cloud,RPC)