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

简介: 之前的服务端信息都是在配置文件中写入的,是时候用上注册中心了

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码): https://github.com/zq2599/blog_demos

关于eureka

前面咱们在开发客户端应用时,所需的服务端地址都是按如下步骤设置的:

  • 在application.yml中配置,如下图:

在这里插入图片描述

  • 在用到gRPC的bean中,使用注解GrpcClient即可将Stub类注入到成员变量中:

在这里插入图片描述

  • 上述操作方式的优点是简单易用好配置,缺点也很明显:服务端的IP地址或者端口一旦有变化,就必须修改application.yml并重启客户端应用;
  • 聪明的您一定想到了应对之道:注册中心!没错,有了注册中心,咱们的客户端只要能从注册中心取得最新的服务端地址,就不再需要手动配置了,以下是常规的eureka作用说明:

在这里插入图片描述

本篇概览

  • 如果您有Spring Cloud的开发经验,对resttemplate和feign等应该很熟悉,但是Spring Cloud环境下的gRPC调用却没有那么常用,本篇的目标是通过实战与大家一起掌握Spring Cloud环境下的gRPC调用,分为以下章节:
  1. eureka应用开发
  2. gRPC服务端开发
  3. gRPC客户端开发
  4. 验证
  5. 一点疑惑

源码下载

名称 链接 备注
项目主页 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:
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;

@EnableEurekaServer
@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:
// 使用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.portgrpc.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:
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注册的名字,如下图红框所示:

在这里插入图片描述

  • 在父工程grpc-turtorials下面新建名为cloud-client-side的模块,其build.gradle内容如下,注意要引入gRPC客户端相关的starter:
// 使用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;

@EnableEurekaClient
@EnableDiscoveryClient
@SpringBootApplication
public class CloudClientSideApplication {

    public static void main(String[] args) {
        SpringApplication.run(CloudClientSideApplication.class, args);
    }
}
  • 封装gRPC调用的服务类GrpcServerService,和local-server模块中的一样,GrpcClient注解对应配置中的gRPC配置项:
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:

在这里插入图片描述

  • 启动cloud-server-side,可见gRPC服务端口自动分配了65141,不过我们无需关心这个值,因为客户端可以从eureka获取到:

在这里插入图片描述

  • 接下来启动cloud-client-side,启动成功后eureka上可见两个服务的注册信息:

在这里插入图片描述

  • 浏览器访问cloud-client-side提供的web接口,响应如下,可见cloud-client-side成功调用了cloud-server-side的gRPC服务:

在这里插入图片描述

一点疑惑

  • 如果您对eureka有所了解,可能会产生一点疑惑:cloud-client-side从eureka取得的cloud-server-side信息,应该是http服务的地址和端口,不应该有gRPC的端口号,因为eureka的注册发现服务并不包含gRPC有关的!
  • 篇幅所限,这里不适合将上述问题展开分析,咱们来关注最核心的地方,相信聪明的您看上一眼就会豁然开朗;
  • DiscoveryClientNameResolver来自grpc-client-spring-boot-autoconfigure.jar,用来保存从eureka取得的服务端信息,该类的注释已经说得很清楚了,从metadata的gRPC.port配置项中取得gRPC端口号:

在这里插入图片描述

  • 在DiscoveryClientNameResolver的代码中打上断点,查看成员变量instanceList,可见metadata中确实有gRPC端口的信息:

在这里插入图片描述

  • 至于cloud-server-side如何将端口号提交到eureka,以及cloud-client-side为何会使用DiscoveryClientNameResolver来处理eureka的服务列表信息,就不在本文中讨论了,您要是有兴趣深入研究eureka,可以参考《程序员欣宸文章汇总(Spring篇)》中的Eureka源码分析专题,如下图:

在这里插入图片描述

  • 至此,基于eureka的gRPC服务注册发现的开发和验证就完成了,希望本文可以给您带来一些参考,让您的服务在注册中心的加持下更加灵活和可靠;

欢迎关注阿里云开发者社区博客:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...
相关文章
|
15天前
|
存储 SQL 安全
Java 安全性编程:基本概念与实战指南
【4月更文挑战第27天】在当今的软件开发领域,安全性编程是一个至关重要的方面。Java,作为广泛使用的编程语言之一,提供了多种机制来保护应用免受常见的安全威胁。本博客将探讨 Java 安全性编程的基本概念,并通过实际示例来展示如何实现这些安全措施。
23 3
|
5天前
|
存储 监控 安全
JVM工作原理与实战(十六):运行时数据区-Java虚拟机栈
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了运行时数据区、Java虚拟机栈等内容。
11 0
|
6天前
|
Java
Java中的多线程编程:基础知识与实战技巧
【5月更文挑战第6天】多线程编程是Java中的一个重要特性,它允许我们在一个程序中同时执行多个任务。本文将介绍Java多线程的基础知识,包括线程的创建、启动、同步和通信,以及如何在Java中实现多线程编程。通过实例代码和解析,帮助读者深入理解Java多线程编程的概念和应用。
|
10天前
|
存储 Java 数据格式
Java实战:轻松掌握文件重命名与路径提取技巧
Java实战:轻松掌握文件重命名与路径提取技巧
18 0
|
12天前
|
设计模式 算法 安全
Java多线程编程实战:从入门到精通
【4月更文挑战第30天】本文介绍了Java多线程编程的基础,包括线程概念、创建线程(继承`Thread`或实现`Runnable`)、线程生命周期。还讨论了线程同步与锁(同步代码块、`ReentrantLock`)、线程间通信(等待/通知、并发集合)以及实战技巧,如使用线程池、线程安全设计模式和避免死锁。性能优化方面,建议减少锁粒度和使用非阻塞算法。理解这些概念和技术对于编写高效、可靠的多线程程序至关重要。
|
13天前
|
XML Java 测试技术
Java异常处理神器:Guava Throwables类概念与实战
【4月更文挑战第29天】在Java开发中,异常处理是保证程序稳定性和可靠性的关键。Google的Guava库提供了一个强大的工具类Throwables,用于简化和增强异常处理。本篇博客将探讨Throwables类的核心功能及其在实战中的应用。
27 2
|
13天前
|
安全 Java 测试技术
利用Java反射机制提高Spring Boot的代码质量:概念与实战
【4月更文挑战第29天】Java反射机制提供了一种强大的方法来在运行时检查或修改类和对象的行为。在Spring Boot应用中,合理利用反射可以提高代码的灵活性和可维护性。本篇博客将探讨Java反射的核心概念,并展示如何通过反射提高Spring Boot项目的代码质量。
29 0
|
13天前
|
存储 Java 大数据
JAVA:编程的艺术与实战解析
JAVA:编程的艺术与实战解析
20 2
|
15天前
|
监控 Java API
Java 模块化设计:概念与实战应用
【4月更文挑战第27天】模块化设计是现代软件开发的关键,它帮助开发者构建可管理、可维护的大型系统。Java 平台的模块化支持始于 Java 9,引入了一种全新的模块系统。
22 3
|
15天前
|
设计模式 算法 Java
Java 设计模式:探索策略模式的概念和实战应用
【4月更文挑战第27天】策略模式是一种行为设计模式,它允许在运行时选择算法的行为。在 Java 中,策略模式通过定义一系列的算法,并将每一个算法封装起来,并使它们可以互换,这样算法的变化不会影响到使用算法的客户。
22 1