⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7385905203646742566 「青石路」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

需求描述

一天,领导找到我,巴拉巴拉一顿沟通,需求很清晰,我就不细说了(因为跟本文关系不大),总结下就是

有两个服务:任务分发中心、任务执行器

分发中心负责任务拆分,然后将拆分后的任务下发给执行器,执行器执行任务

任务分发中心和任务执行器都要支持水平扩节点

我说:技术实现没什么难点,引入注册中心就行

但领导紧接着就说:考虑到客户硬件资源的局限性以及部署的复杂性,不能单独部署注册中心

当时我就懵了,心里想的是

既要马儿跑,又要马儿不吃草,咋不让我上天?

工作要紧

我接着说道:任务分发中心兼职注册中心,任务执行器能够自动注册到分发中心?

领导:对,你总结的言简意赅,就是要实现这样的效果

领导又补充道:你可以简单点实现,不用像 Nacos 那样复杂,服务支持水平扩展,能够监控任务执行器状态就行

相信大家都明白需求了,总结成一句话

拿着卖白菜的钱,要干卖白粉的事!

需求实现

主流的注册中心,像Zookeeper、Nacos、etcd、、Consul 以及 Eureka,都需要单独部署;如果能单独部署,那实现就简单了,可现在要求是不能单独部署,但又要有注册中心的基本功能

宝宝心里苦

说白了,就是要自实现一个简易版的注册中心,虽说是简易版,但基本功能还是要有吧

  1. 服务发现
    注册中心存储和管理所有可用服务的地址信息,其他服务可以通过查询注册中心发现所需的服务
  2. 服务注册
    服务提供者可以将自己的地址信息注册到注册中心,以便其他服务发现和调用
  3. 健康检查
    注册中心定期对注册的服务进行健康检查,以确保服务的可用性
    某个服务出现故障或不可用,注册中心能够将其从可用服务列表中移除,防止其他服务调用失败
  4. 支持水平拓展
    注册中心肯定要支持水平拓展来保障其高可用
    注册中心节点之间要能正常通信,节点之间的可用服务列表能够及时同步,并且要保证一致

这些基本功能实现起来简不简单?也许你们觉的简单,可我觉的并不简单
但事已至此,已无退路,只能硬着头皮上了

分发中心集成 Eureka Server

几个主流的注册中心,我对 Eureka 比较熟悉,感觉其实现还算比较简单,参考它来实现注册中心应该可行。一头扎进去看源码,一天下来收效甚微,发现并非想象中的那么简单,正当我准备放弃时,脑中闪过一个念头

任务分发中心可不可以集成 Eureka Server?

Eureka Server 是注册中心,这个我们都知道,但我们忽略了一个点,它还是一个普通 Web 服务,只是我们平时不用它来处理业务请求,仅仅是把它当做一个注册中心。从定位来讲,Eureka Server 确实只应该做注册中心该做的事,不应该关联到业务,尤其是服务多时,Eureka Server 维护压力大,更不应该关联业务
但有时候也需要特殊情况特殊处理,比如我目前的需求,注册中心维护的服务就一个 任务执行器,任务执行器的节点也不会很多,顶天 10 个,也就是 Eureka Servr 维护服务的压力会很小,同时业务处理也很简单:任务拆分,然后分发,这么分析下来,任务分发中心集成 Eureka Server 是可行的,说干就干!

干就完了

Eureka server 搭建不要太简单,你们肯定都会

gitee.com/youzhibing/…[1]

搭建过程就不演示了,偷懒的小伙伴可以直接用我提供的代码

此时的代码结构如下(未包含业务代码)

代码结构

搭建完成后启动服务,Eureka 监控如下

eureka搭建完成

至此,Eureka 集成完成

你们发现没,前面的 Eureka 搭建跟我们平时的 Eureka 搭建是不是没区别?

肯定没区别呀,我就是按平时的搭建流程来的,具体区别还要往下看

分发中心集成 Eureka Client

为了更好的体现 任务分发中心 集成 Eureka Client,我们简化业务实现

任务执行器 就提供一个接口给 任务分发中心 调用

任务执行器接口_exec

任务分发中心 也提供一个接口给其他服务(例如 调度中心)调用

分发中心接口

重点来了,任务分发中心如何将拆分后的任务分发到任务执行器?

细化来讲,分 3 点
1.任务分发中心能不能拿到任务执行器服务列表
2.能拿到的话,如何拿
3.拿到之后如何负载均衡的下发任务

第 1 点毋庸置疑,肯定能拿到,任务分发中心作为 Eureka Server,存储了任务执行器的服务实例列表

第 2 、3点,我们可以将 任务分发中心 又当做 Eureka Client 试试,试试有不要钱

1.引入依赖

Eureka Server` 包含了`Eureka Client` ,不需要额外引入`Eureka Client

但需要引入 open-feign

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

2.开启 Eureka Client 和 open-feign

@SpringBootApplication
@EnableEurekaServer
@EnableEurekaClient
@EnableFeignClients
public class TaskDispatcherApplication {

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

3.实现 TaskExecutorService,完成对 任务执行器 负载均衡调用

@FeignClient("TASK-EXECUTOR")
public interface TaskExecutorService {

@GetMapping("task/exec")
String exec(@RequestParam("taskId") Long taskId);
}
@GetMapping("dispatch")
public String dispatch(Long jobId) {
LOGGER.info("收到作业[jobId={}]执行请求", jobId);
LOGGER.info("作业[jobId={}]拆分任务中...", jobId);
List<Long> taskIds = Arrays.asList(123L, 666L, 888L, 999L);
LOGGER.info("作业[jobId={}]拆分完成,得到作业列表[{}]", jobId, taskIds);
for (Long taskId : taskIds) {
String execResult = taskExecutorService.exec(taskId);
LOGGER.info("任务[{}]执行结果:{}", taskId, execResult);
}
return "success";
}

启动 task-dispatcher、task-executor ,然后调用接口

http://192.168.2.10:8080/dispatcher/job/dispatch?jobId=689

你会发现 500 了

500

看下日志,关键信息如下

Load balancer does not contain an instance for the service TASK-EXECUTOR

负载均衡器不包含 TASK-EXECUTOR 服务实例,这是因为我们有个配置关闭了

fetch-registry-false

导致 Eureka Client 未拉取其他服务,那么 Load balancer 自然就获取不到 TASK-EXECUTOR 实例了
我们将其开启(设置成 true),然后启动task-dispatcher、task-executor,然后调用接口

http://192.168.2.10:8080/dispatcher/job/dispatch?jobId=689

结果 success,日志输出如下

1. task-dispatcher

2024-06-30 10:51:51.653|INFO|http-nio-8080-exec-3|25|com.qsl.task.web.JobController          :收到作业[jobId=689]执行请求
2024-06-30 10:51:51.653|INFO|http-nio-8080-exec-3|26|com.qsl.task.web.JobController :作业[jobId=689]拆分任务中...
2024-06-30 10:51:51.653|INFO|http-nio-8080-exec-3|28|com.qsl.task.web.JobController :作业[jobId=689]拆分完成,得到作业列表[[123, 666, 888, 999]]
2024-06-30 10:51:51.657|INFO|http-nio-8080-exec-3|31|com.qsl.task.web.JobController :任务[123]执行结果:success
2024-06-30 10:51:51.660|INFO|http-nio-8080-exec-3|31|com.qsl.task.web.JobController :任务[666]执行结果:success
2024-06-30 10:51:51.663|INFO|http-nio-8080-exec-3|31|com.qsl.task.web.JobController :任务[888]执行结果:success
2024-06-30 10:51:51.665|INFO|http-nio-8080-exec-3|31|com.qsl.task.web.JobController :任务[999]执行结果:success

2. task-executor

2024-06-30 10:51:51.656|INFO|http-nio-8081-exec-6|17|com.qsl.task.web.TaskController         :收到任务[taskId=123]执行请求
2024-06-30 10:51:51.656|INFO|http-nio-8081-exec-6|18|com.qsl.task.web.TaskController :任务[taskId=123]执行中...
2024-06-30 10:51:51.656|INFO|http-nio-8081-exec-6|19|com.qsl.task.web.TaskController :任务[taskId=123]执行完成
2024-06-30 10:51:51.659|INFO|http-nio-8081-exec-9|17|com.qsl.task.web.TaskController :收到任务[taskId=666]执行请求
2024-06-30 10:51:51.659|INFO|http-nio-8081-exec-9|18|com.qsl.task.web.TaskController :任务[taskId=666]执行中...
2024-06-30 10:51:51.659|INFO|http-nio-8081-exec-9|19|com.qsl.task.web.TaskController :任务[taskId=666]执行完成
2024-06-30 10:51:51.662|INFO|http-nio-8081-exec-8|17|com.qsl.task.web.TaskController :收到任务[taskId=888]执行请求
2024-06-30 10:51:51.662|INFO|http-nio-8081-exec-8|18|com.qsl.task.web.TaskController :任务[taskId=888]执行中...
2024-06-30 10:51:51.662|INFO|http-nio-8081-exec-8|19|com.qsl.task.web.TaskController :任务[taskId=888]执行完成
2024-06-30 10:51:51.664|INFO|http-nio-8081-exec-7|17|com.qsl.task.web.TaskController :收到任务[taskId=999]执行请求
2024-06-30 10:51:51.664|INFO|http-nio-8081-exec-7|18|com.qsl.task.web.TaskController :任务[taskId=999]执行中...
2024-06-30 10:51:51.664|INFO|http-nio-8081-exec-7|19|com.qsl.task.web.TaskController :任务[taskId=999]执行完成

目前 task-executor 是单实例,我们再启动一个实例,来看看负载均衡效果

负载均衡

至此,需求是不是实现了

总结

  1. 注册中心推荐单独部署,不要掺杂业务代码
  2. Eureka Server 也可以做 Eureka Client,虽说不推荐,但有时候能止渴
  3. 要敢于尝试,不试怎么知道不行?

Reference

[1]gitee.com/youzhibing/…: https://gitee.com/youzhibing/qsl-project/tree/master/integrate-eureka

文章目录
  1. 1. 需求描述
  2. 2. 需求实现
    1. 2.1. 分发中心集成 Eureka Server
    2. 2.2. 分发中心集成 Eureka Client
      1. 2.2.0.1. 1.引入依赖
      2. 2.2.0.2. 2.开启 Eureka Client 和 open-feign
      3. 2.2.0.3. 3.实现 TaskExecutorService,完成对 任务执行器 负载均衡调用
      4. 2.2.0.4. 1. task-dispatcher
      5. 2.2.0.5. 2. task-executor
  • 3. 总结