java 版 gRPC 实战之七:基于 eureka 的注册发现
欢迎访问我的 GitHub
这里分类和汇总了欣宸的全部原创(含配套源码): http://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 调用,分为以下章节:
-
eureka 应用开发
-
gRPC 服务端开发
-
gRPC 客户端开发
-
验证
-
一点疑惑
源码下载
-
本篇实战中的完整源码可在 GitHub 下载到,地址和链接信息如下表所示(http://github.com/zq2599/blog_demos):
-
这个 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.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:
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 服务注册发现的开发和验证就完成了,希望本文可以给您带来一些参考,让您的服务在注册中心的加持下更加灵活和可靠;
欢迎关注 InfoQ:程序员欣宸
- MySQL 存储引擎
- 为什么说 DevOps 治理是实现快速开发的关键
- Observability 之深度采样 Sampling 场景和落地案例(下)
- IIR 数字滤波器设计(数字信号处理)
- SAP ABAP 里存在 Java List 这种集合工具类么?CL_OBJECT_COLLECTION 了解一下
- 用 manim 写一个排序算法动画
- 【精通内核】计算机程序的本质、内存组成与 ELF 格式
- Kubernetes 之父:他们现在在哪里?
- 将 50 万个文件放在一个 Git 存储库中,我们做了这些
- 数据治理(四):数据仓库数据质量管理
- 直接上瘾!百万人都在学的 Docker
- 开源一夏 | 使用 JavaScript 和 CSS 做一个图片转 PDF 的转换器
- 梦回战国,领略两千多年前公孙龙如何将面向对象运用得炉火纯青
- 对话钉钉 CTO:从低代码到酷应用,To B、To C 技术大融合的必然趋势
- DatenLord 开源 Xline:实现跨数据中心数据一致性管理
- 数据治理(三):数据质量管理
- SpringBoot 实战:国际化组件 MessageSource 的执行逻辑与源码
- 深入了解 Spring 篇之 BeanDefinition 结构
- 5 亿用户如何高效沟通?钉钉首次对外揭秘即时消息服务 DTIM
- 代码臃肿已成常态:99% 计算机资源都被浪费掉了