2020-02-08 gRPC从入门到破产(2)SpringCloud与gRPC整合

本文已经与(1)整合后发在了我的个人网站,欢迎访问:http://www.wendev.site/article/25

写在前面

前一段时间学习SpringCloud时尝试了SpringCloud整合Dubbo,感觉非常棒。那比Dubbo更快、还有着跨语言特性的gRPC与SpringCloud又会碰撞出什么样的火花呢?今天就来试一试。

当然,一开始写的也并不复杂,还是从最简单的一个服务提供者,一个服务消费者,发送一条HelloWorld并显示端口号开始。

服务注册与发现中心选择了Consul,本来用的是Nacos,结果出了莫名其妙的Bug(这个在下面会写),换Consul之后一下子就好了。。。

版本

  • Java:11
  • Consul:1.6.3
  • Spring Cloud:Hoxton.RELEASE
  • Spring Boot:2.2.4.RELEASE
  • gRPC:1.25.0
  • grpc-spring-boot-strater:2.6.2.RELEASE GitHub地址

使用Docker启动Consul

以前一直是使用直接运行可执行文件启动Consul,而使用docker比直接运行jar包更方便管理,所以这次就使用Docker来运行。

这里顺便记录一下Nacos的Docker启动方式:

Nacos

docker pull nacos/nacos-server
docker run --env MODE=standalone --name nacos -d -p 8848:8848 nacos/nacos-server

Consul

只启动一个实例的话可以这样启动:

docker pull consul
docker run --name consul -p 8500:8500 -d consul

运行完毕就可以通过localhost:8500访问了。

开始!

整个项目的目录结构:

2020-02-08 gRPC从入门到破产(2)SpringCloud与gRPC整合_第1张图片

父级依赖管理工程



    4.0.0

    site.wendev.grpc
    grpc-spring-cloud-final
    pom
    1.0-SNAPSHOT

    
        org.springframework.boot
        spring-boot-starter-parent
        2.2.4.RELEASE
    

    
        wendev-api
        wendev-provider
        wendev-consumer
    

    
        UTF-8
        UTF-8
        11
        Hoxton.RELEASE
        0.9.0.RELEASE
        1.18.10
        1.3.2
        1.25.0
        2.6.2.RELEASE
    

    
        
            
            
                site.wendev.grpc
                wendev-api
                ${project.version}
            

            
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring.cloud.version}
                pom
                import
            

            
            
                org.springframework.cloud
                spring-cloud-starter-alibaba-nacos-config
                ${nacos.version}
            
            
                org.springframework.cloud
                spring-cloud-starter-alibaba-nacos-discovery
                ${nacos.version}
                
                    
                        org.springframework.cloud
                        spring-cloud-starter-netflix-ribbon
                    
                
            

            
            
                io.grpc
                grpc-all
                ${grpc.version}
            
            
                io.grpc
                grpc-netty-shaded
                ${grpc.version}
            
            
                io.grpc
                grpc-protobuf
                ${grpc.version}
            
            
                io.grpc
                grpc-stub
                ${grpc.version}
            
            
                javax.annotation
                javax.annotation-api
                ${java.annotation.api.version}
                provided
            

            
            
                net.devh
                grpc-server-spring-boot-starter
                ${grpc.spring.boot.starter.verison}
            
            
                net.devh
                grpc-client-spring-boot-starter
                ${grpc.spring.boot.starter.verison}
            
        
    

这个grpc-spring-boot-starter貌似是国人写的,赞!

公共API模块

2020-02-08 gRPC从入门到破产(2)SpringCloud与gRPC整合_第2张图片

Maven依赖,主要是gRPC的依赖:



    4.0.0

    
        grpc-spring-cloud-final
        site.wendev.grpc
        1.0-SNAPSHOT
    

    wendev-api
    wendev-api
    jar

    
        1.6.2
        3.10.0
        0.6.1
    

    
        
        
            io.grpc
            grpc-all
        

        
        
            javax.annotation
            javax.annotation-api
        
        
            org.springframework.boot
            spring-boot-autoconfigure
        
    

    
        
            
            
                kr.motd.maven
                os-maven-plugin
                ${os.plugin.version}
            
        
        
            
            
                org.xolstice.maven.plugins
                protobuf-maven-plugin
                ${protobuf.plugin.version}
                
                    com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}
                    
                    grpc-java
                    io.grpc:protoc-gen-grpc-java:1.25.0:exe:${os.detected.classifier}
                    /Users/jiangwen/tools/protoc-3.10.0/bin/protoc
                    false
                
                
                    
                        compile
                        
                            compile
                            compile-custom
                        
                    
                
            
        
    

首在main文件夹下建立一个proto文件夹,写个proto文件:

hello_world.proto

syntax = "proto3";

option java_package = "site.wendev.grpc.api";
option java_multiple_files = true;
option java_outer_classname = "HelloProto";

service HelloWorld {
    rpc Hello (HelloRequest) returns (HelloResponse) {
    }
}

message HelloRequest {
    string message = 1;
}

message HelloResponse {
    string response = 1;
}

然后像上一篇讲的那样把代码生成出来,放到相应目录下,再执行mvn clean install把这个模块安装在本地,供其他服务调用。

服务提供者

2020-02-08 gRPC从入门到破产(2)SpringCloud与gRPC整合_第3张图片

Maven依赖:



    4.0.0
    
        grpc-spring-cloud-final
        site.wendev.grpc
        1.0-SNAPSHOT
    

    wendev-provider
    ${project.version}
    wendev-provider

    
        11
    

    
        
        
            site.wendev.grpc
            wendev-api
        

        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-actuator
        

        
        
            org.springframework.cloud
            spring-cloud-starter-consul-discovery
        

        
        
            net.devh
            grpc-server-spring-boot-starter
        

        
            org.projectlombok
            lombok
            true
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    



配置:

bootstrap.yml

server:
  port: 8763
grpc:
  server:
    port: 0
spring:
  application:
    name: wendev-provider
  cloud:
    consul:
      host: 127.0.0.1
      port: 8500
      discovery:
        hostname: 127.0.0.1

gRPC的端口是0,这样写是“端口号随机”的意思,可以保证每次启动端口都不同,就不用手动修改了。server.port写死了是因为需要把这个值注入,返回给服务消费者,如果不需要返回端口号也可以写0。

然后编写服务提供者:

/**
 * 服务提供者:返回服务消费者发送的信息和端口号
 * 使用@GrpcService注解声明这个服务提供者
 *
 * @author 江文
 * @date 2020/2/7 5:12 上午
 */
@GrpcService
public class HelloWorldService extends HelloWorldGrpc.HelloWorldImplBase {
    @Value("${server.port}")
    private String port;

    @Override
    public void hello(HelloRequest request, StreamObserver responseObserver) {
        String message = String.format("Welcome to WenDev, your message is %s, from port %s."
                + "From: Spring Cloud + gRPC.",
                request.getMessage(), port);
        HelloResponse response = HelloResponse.newBuilder().setResponse(message).build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

题外话:大家可以从注释中看到创建时间是昨天早上,所以我踩坑踩了整整一天。。。写此文的目的除了总结,就是希望大家不要继续踩我踩过的坑。

逻辑很简单,主要就是生成一条响应信息,然后返回响应。代码并不比使用Dubbo复杂多少。

这里遇到个大坑,差点没把我坑死。。。

最新的gRPC版本是1.27.0,然而grpc-spring-boot-starter只支持到1.25,还不兼容1.27。。。晕死,为了换个版本还得重新下一个3.10版本的protoc(1.27是用的3.11.3版本的),然后重新生成代码。。。说不定我应该去grpc-spring-boot-starter的仓库里提个issue?

服务消费者

2020-02-08 gRPC从入门到破产(2)SpringCloud与gRPC整合_第4张图片

首先是依赖,与服务提供者非常相似,就是grpc-spring-boot-starter换成了服务消费者的:



    4.0.0
    
        grpc-spring-cloud-final
        site.wendev.grpc
        1.0-SNAPSHOT
    

    wendev-consumer
    ${project.version}
    wendev-consumer

    
        11
    

    
        
        
            site.wendev.grpc
            wendev-api
        

        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-actuator
        

        
        
            org.springframework.cloud
            spring-cloud-starter-consul-discovery
        

        
        
            net.devh
            grpc-client-spring-boot-starter
        

        
            org.projectlombok
            lombok
            true
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    



配置,也没什么区别。因为没有CA证书,用OpenSSL生成又一直报各种奇奇怪怪的错误,就设定为不加密的plaintext了:

bootstrap.yml

server:
  port: 8720
spring:
  application:
    name: wendev-consumer
  cloud:
    consul:
      host: 127.0.0.1
      port: 8500
      discovery:
        hostname: 127.0.0.1
grpc:
  client:
    GLOBAL:
      security:
        enable-keep-alive: true
      keep-alive-without-calls: true
      negotiation-type: plaintext

业务逻辑代码,首先是Service:

/**
 * 服务消费者:远程调用服务提供者,并且接收消息返回给Controller。
 *
 * @author 江文
 * @date 2020/2/7 5:33 上午
 */
@Service
public class HelloWorldService {
    @GrpcClient("wendev-provider")
    private HelloWorldGrpc.HelloWorldBlockingStub stub;

    public String rpc(String message) {
        HelloRequest request = HelloRequest.newBuilder().setMessage(message).build();
        return stub.hello(request).getResponse();
    }
}

这个@GrpcClient加在stub上,或者如同网上绝大多数资料那样加在channel上都可以,不过官方推荐加在stub上,就按照推荐的来。

代码同样也不复杂。

然后是Controller层,调用Service里的rpc方法,返回调用结果:

/**
 * Controller层,只有一个方法。
 *
 * @author 江文
 * @date 2020/2/7 5:36 上午
 */
@RestController
public class HelloWorldController {
    final HelloWorldService service;

    @GetMapping("/{message}")
    public String helloWorld(@PathVariable String message) {
        return service.rpc(message);
    }

    HelloWorldController(HelloWorldService service) {
        this.service = service;
    }
}

运行

先说一下,这里又一个巨坑!比上一个要大得多!排查这个花了我整整一天,最后发现居然不是我的错(虽说选了不合适的注册中心也算是我的错吧)。。。

本来是用的Nacos做的服务注册与发现中心,结果请求总是出错,报network closed for unknown reason的异常。后来换了Consul,就改了依赖和加上了Consul服务注册与发现的配置代码,业务代码改都没改一下子就好了。。。

看来这是Nacos的bug,具体原因不明。不过用Consul不会出问题,似乎用Eureka也不会。

先启动服务提供者,修改server.port多启动几份,然后启动服务消费者。在Consul里可以看到,启动成功了:

不过这个健康检查失败也不知道是哪里出了问题,但是服务间调用正常,可能因为我没在yml文件里配置?

可以看到Tags里出现了gRPC的端口号,gRPC客户端(服务消费者)就是通过这个Tag找到gRPC服务端(服务提供者)的端口的。

请求127.0.0.1:8720/HelloWorld可以发现消息成功返回了:

2020-02-08 gRPC从入门到破产(2)SpringCloud与gRPC整合_第5张图片

多刷新几次,可以看到Consul提供的负载均衡效果:

通过这个例子,我们发现gRPC和Spring Cloud在grpc-spring-boot-strater的帮助下可以配合得非常好。虽然坑有些多,但毕竟gRPC不是Dubbo那种无缝集成的,出现一些坑也是可以理解的。

你可能感兴趣的:(2020-02-08 gRPC从入门到破产(2)SpringCloud与gRPC整合)