java版gRPC实战之七:基于eureka的注册发现

Posted 程序员欣宸

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java版gRPC实战之七:基于eureka的注册发现相关的知识,希望对你有一定的参考价值。

欢迎访问我的GitHub

名称 链接 备注
项目主页 https://github.com/zq2599/blog_demos 该项目在GitHub上的主页
git仓库地址(https) https://github.com/zq2599/blog_demos.git 该项目源码的仓库地址,https协议
git仓库地址(ssh) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
  • 这个git项目中有多个文件夹,《java版gRPC实战》系列的源码在==grpc-tutorials==文件夹下,如下图红框所示:
  • ==grpc-tutorials==文件夹下有多个目录,本篇文章对应的eureka代码在==cloud-eureka==目录,服务端代码在==cloud-server-side==目录,客户端代码在==cloud-client-side==目录,如下图:

    eureka应用开发

  • 在父工程grpc-turtorials下面新建名为==cloud-eureka==的模块,其build.gradle内容如下:
    
    // 使用springboot插件
    plugins 
    id org.springframework.boot
    

dependencies
implementation org.springframework.boot:spring-boot-starter
// 依赖eureka
implementation org.springframework.cloud:spring-cloud-starter-netflix-eureka-server
// 状态暴露需要的依赖
implementation org.springframework.boot:spring-boot-starter-actuator
// 依赖自动生成源码的工程
implementation project(:grpc-lib)

- 配置文件bootstrap.yml,设置自己的web端口号和应用名,另外eureka.client.serviceUrl.defaultZone的配置请改成自己的IP:
```yml
server:
  port: 8085

spring:
  application:
    name: cloud-eureka

eureka:
  instance:
    hostname: localhost
    prefer-ip-address: true
    status-page-url-path: /actuator/info
    health-check-url-path: /actuator/health
    lease-expiration-duration-in-seconds: 30
    lease-renewal-interval-in-seconds: 30
  client:
    registerWithEureka: false
    fetchRegistry: false
    serviceUrl:
            defaultZone: http://192.168.50.5:8085/eureka/
  server:
    enable-self-preservation: false

endpoints:
 shutdown:
  enabled: true
  • 这个模块只有一个类CloudEurekaApplication.java:
    
    package com.bolingcavalry.grpctutorials;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServerbr/>@SpringBootApplication
public class CloudEurekaApplication

public static void main(String[] args) 
    SpringApplication.run(CloudEurekaApplication.class, args);

- 以上就是一个简单通用的eureka服务了;
### gRPC服务端开发
- 依赖eureka的gRPC服务端,其重点在于:第一,配置使用eureka,第二,不要指定端口;
- 在父工程grpc-turtorials下面新建名为==cloud-server-side==的模块,其build.gradle内容如下,注意要引入gRPC服务端相关的starter:
```groovy
// 使用springboot插件
plugins 
    id org.springframework.boot


dependencies 
    implementation org.projectlombok:lombok
    implementation org.springframework.boot:spring-boot-starter-web
    implementation org.springframework.boot:spring-boot-starter
    // 作为gRPC服务提供方,需要用到此库
    implementation net.devh:grpc-server-spring-boot-starter
    // 作为eureka的client
    implementation org.springframework.cloud:spring-cloud-starter-netflix-eureka-client
    // 状态暴露需要的依赖
    implementation org.springframework.boot:spring-boot-starter-actuator
    // 依赖自动生成源码的工程
    implementation project(:grpc-lib)
    // annotationProcessor不会传递,使用了lombok生成代码的模块,需要自己声明annotationProcessor
    annotationProcessor org.projectlombok:lombok
  • 配置文件application.yml,设置自己的应用名,另外值得注意的是==server.port==和==grpc.server.port==这两个配置的值都是0,这样两个端口就会被自动分配未被占用的值:
    
    spring:
    application:
    name: cloud-server-side

server:
port: 0
grpc:
server:
port: 0
eureka:
instance:
prefer-ip-address: true
instanceId: $spring.application.name:$vcap.application.instance_id:$spring.application.instance_id:$random.value
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone:
http://192.168.50.5:8085/eureka/

- 启动类CloudServerSideApplication.java:
```java
package com.bolingcavalry.grpctutorials;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableEurekaClient
@EnableDiscoveryClient
@SpringBootApplication
public class CloudServerSideApplication 

    public static void main(String[] args) 
        SpringApplication.run(CloudServerSideApplication.class, args);
    
  • 提供gRPC服务的类GrpcServerService,和local-server模块中的一样:
    
    package com.bolingcavalry.grpctutorials;

import com.bolingcavalry.grpctutorials.lib.HelloReply;
import com.bolingcavalry.grpctutorials.lib.SimpleGrpc;
import net.devh.boot.grpc.server.service.GrpcService;
import java.util.Date;

@GrpcService
public class GrpcServerService extends SimpleGrpc.SimpleImplBase

@Override
public void sayHello(com.bolingcavalry.grpctutorials.lib.HelloRequest request,
                     io.grpc.stub.StreamObserver<HelloReply> responseObserver) 
    HelloReply reply = HelloReply.newBuilder().setMessage("1. Hello " + request.getName() + ", " + new Date()).build();
    responseObserver.onNext(reply);
    responseObserver.onCompleted();

- 以上就是服务端代码了,可见除了将gRPC端口设置为0,以及常规使用eureka的配置,其他部分和local-server模块是一样的;

### gRPC客户端开发
- 依赖eureka的gRPC客户端,其重点在于:第一,配置使用eureka,第二,配置中的gRPC配置项的名字要等于gRPC服务端在eureka注册的名字,如下图红框所示:
![在这里插入图片描述](https://s4.51cto.com/images/blog/202201/09232358_61dafe0e86ce566639.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
- 在父工程grpc-turtorials下面新建名为==cloud-client-side==的模块,其build.gradle内容如下,注意要引入gRPC客户端相关的starter:
```groovy
// 使用springboot插件
plugins 
    id org.springframework.boot


dependencies 
    implementation org.projectlombok:lombok
    implementation org.springframework.boot:spring-boot-starter-web
    implementation org.springframework.boot:spring-boot-starter
    // 作为gRPC服务使用方,需要用到此库
    implementation net.devh:grpc-client-spring-boot-starter
    // 作为eureka的client
    implementation org.springframework.cloud:spring-cloud-starter-netflix-eureka-client
    // 状态暴露需要的依赖
    implementation org.springframework.boot:spring-boot-starter-actuator
    // 依赖自动生成源码的工程
    implementation project(:grpc-lib)
    // annotationProcessor不会传递,使用了lombok生成代码的模块,需要自己声明annotationProcessor
    annotationProcessor org.projectlombok:lombok
  • 配置文件application.yml,设置自己的web端口号,另外值得注意的是gRPC配置项==cloud-server-side==的名字要等于gRPC服务端在eureka注册的名字,并且==不需要address配置项==:
    server:
    port: 8086
    spring:
    application:
    name: cloud-client-side
    eureka:
    instance:
    prefer-ip-address: true
    status-page-url-path: /actuator/info
    health-check-url-path: /actuator/health
    instanceId: $spring.application.name:$vcap.application.instance_id:$spring.application.instance_id:$random.value
    client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://192.168.50.5:8085/eureka/
    grpc:
    client:
    # gRPC配置的名字,GrpcClient注解会用到
    cloud-server-side:
      enableKeepAlive: true
      keepAliveWithoutCalls: true
      negotiationType: plaintext
  • 启动类CloudClientSideApplication.java,使用了eureka相关的注解:
    
    package com.bolingcavalry.grpctutorials;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@EnableEurekaClientbr/>@EnableDiscoveryClient
@SpringBootApplication
public class CloudClientSideApplication

public static void main(String[] args) 
    SpringApplication.run(CloudClientSideApplication.class, args);

- 封装gRPC调用的服务类GrpcServerService,和local-server模块中的一样,GrpcClient注解对应配置中的gRPC配置项:
```java
package com.bolingcavalry.grpctutorials;

import com.bolingcavalry.grpctutorials.lib.HelloReply;
import com.bolingcavalry.grpctutorials.lib.HelloRequest;
import com.bolingcavalry.grpctutorials.lib.SimpleGrpc;
import io.grpc.StatusRuntimeException;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Service;

@Service
public class GrpcClientService 

    @GrpcClient("cloud-server-side")
    private SimpleGrpc.SimpleBlockingStub simpleStub;

    public String sendMessage(final String name) 
        try 
            final HelloReply response = this.simpleStub.sayHello(HelloRequest.newBuilder().setName(name).build());
            return response.getMessage();
         catch (final StatusRuntimeException e) 
            return "FAILED with " + e.getStatus().getCode().name();
        
    
  • 再做一个web接口类,这样我们就能通过web调用验证gRPC服务了:
    
    package com.bolingcavalry.grpctutorials;

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 = "will") String name) 
    return grpcClientService.sendMessage(name);


- 客户端开发完毕,接下来可以验证了;
### 验证
- 启动cloud-eureka:
![在这里插入图片描述](https://s4.51cto.com/images/blog/202201/09232358_61dafe0ea74f765779.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
- 启动cloud-server-side,可见gRPC服务端口自动分配了65141,不过我们无需关心这个值,因为客户端可以从eureka获取到:
![在这里插入图片描述](https://s4.51cto.com/images/blog/202201/09232358_61dafe0ebcd0456378.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
- 接下来启动cloud-client-side,启动成功后eureka上可见两个服务的注册信息:
![在这里插入图片描述](https://s4.51cto.com/images/blog/202201/09232358_61dafe0ec9c3c93251.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
- 浏览器访问cloud-client-side提供的web接口,响应如下,可见cloud-client-side成功调用了cloud-server-side的gRPC服务:
![在这里插入图片描述](https://s4.51cto.com/images/blog/202201/09232358_61dafe0ecaba477106.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
### 一点疑惑
- 如果您对eureka有所了解,可能会产生一点疑惑:cloud-client-side从eureka取得的cloud-server-side信息,应该是http服务的地址和端口,不应该有gRPC的端口号,因为eureka的注册发现服务并不包含gRPC有关的!
- 篇幅所限,这里不适合将上述问题展开分析,咱们来关注最核心的地方,相信聪明的您看上一眼就会豁然开朗;
- DiscoveryClientNameResolver来自grpc-client-spring-boot-autoconfigure.jar,用来保存从eureka取得的服务端信息,该类的注释已经说得很清楚了,从metadata的gRPC.port配置项中取得gRPC端口号:
![在这里插入图片描述](https://s4.51cto.com/images/blog/202201/09232358_61dafe0ec3a7988518.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
- 在DiscoveryClientNameResolver的代码中打上断点,查看成员变量instanceList,可见metadata中确实有gRPC端口的信息:
![在这里插入图片描述](https://s4.51cto.com/images/blog/202201/09232359_61dafe0f03bbb33626.png?x-oss-process=image/watermark,size_14,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_20,type_ZmFuZ3poZW5naGVpdGk=)
- 至此,基于eureka的gRPC服务注册发现的开发和验证就完成了,希望本文可以给您带来一些参考,让您的服务在注册中心的加持下更加灵活和可靠;

### 欢迎关注公众号:程序员欣宸
> 微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界...

以上是关于java版gRPC实战之七:基于eureka的注册发现的主要内容,如果未能解决你的问题,请参考以下文章

java版gRPC实战之五:双向流

java版gRPC实战之五:双向流

java版gRPC实战之六:客户端动态获取服务端地址

springboot grpc eureka demo实战项目

基于gRPC的注册发现与负载均衡的原理和实战

基于 gRPC 的服务注册与发现和负载均衡的原理与实战