不单独部署注册中心,又要具备注册中心的功能,咋不让我上天?

· 浏览次数 : 0

小编点评

文章标题:从零开始,轻松实现一个简易版注册中心 作者:qsl 在项目中,我们需要在单个应用中实现服务发现和负载均衡功能。由于业务需求,我们需要一个简易版的注册中心。本文将介绍如何在不单独部署的情况下,利用现有的服务实现注册中心的基本功能。 一、需求背景 项目需要实现服务发现和负载均衡功能,而主流的注册中心如Zookeeper、Nacos、etcd和Consul等都需要单独部署。为了解决这个问题,我们考虑使用Eureka Server作为注册中心,并将其与任务分发中心集成。 二、技术实现 1. 任务分发中心集成Eureka Server 我们将任务分发中心(Eureka Server)和任务执行器整合在一起,使其具备注册中心的功能。由于任务执行器数量有限,因此Eureka Server的压力相对较小。 2. 任务分发中心集成Eureka Client 为了让任务分发中心能够发现和调用任务执行器,我们需要将任务分发中心(Eureka Server)作为Eureka Client。为此,我们引入了open-feign依赖并开启了Eureka Client功能。 3. 服务发现与负载均衡 我们通过Eureka Server获取任务执行器的服务实例列表,并根据负载情况将任务分发到合适的任务执行器。这样,任务执行器可以自动注册到Eureka Server,并被其他服务发现和调用。 三、实践与挑战 在实际开发过程中,我们遇到了以下挑战: 1. 任务分发中心与任务执行器的通信问题 为了解决这个问题,我们引入了Eureka Server作为注册中心,并将任务分发中心(Eureka Server)作为Eureka Client。这样,任务分发中心可以直接从Eureka Server获取任务执行器的服务实例列表,并进行负载均衡。 2. 注册中心的高可用性 为了确保注册中心的高可用性,我们采用了水平拓展的方式。当某个节点出现故障时,其他节点可以自动接管,保证服务的正常运行。 四、总结 本文介绍了如何在单个应用中实现服务发现和负载均衡功能,通过集成Eureka Server和任务分发中心,我们成功地解决了需求背景中的问题。虽然过程中遇到了一些挑战,但通过不断尝试和学习,我们最终实现了简易版注册中心的功能。希望这篇文章能为有类似需求的开发者提供一些帮助。

正文

开心一刻

暗恋公司的一个女同事,聊了快一年了,一直没勇气表白

上个月突然找我借 5000 块钱,我直接转给她了

我:这钱干嘛用的?

她:给男朋友买个手机

我强颜欢笑说:你真贴心

几天后我收到一个快递,打开一看是部手机!!!

我压抑着内心的激动,放下手头的工作,立马微信上问她怎么回事

她说:手机她男朋友不喜欢,商家也不给退,就当还我钱了……

杀人诛心

需求描述

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

有两个服务:任务分发中心、任务执行器
分发中心负责任务拆分,然后将拆分后的任务下发给执行器,执行器执行任务
任务分发中心和任务执行器都要支持水平扩节点

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

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

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

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

工作要紧

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

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

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

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

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

需求实现

主流的注册中心,像ZookeeperNacosetcd、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 搭建不要太简单,你们肯定都会

https://gitee.com/youzhibing/qsl-project/tree/master/integrate-eureka

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

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

代码结构

搭建完成后启动服务,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 Clientopen-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-dispatchertask-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-dispatchertask-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. 要敢于尝试,不试怎么知道不行?

与不单独部署注册中心,又要具备注册中心的功能,咋不让我上天?相似的内容:

不单独部署注册中心,又要具备注册中心的功能,咋不让我上天?

开心一刻 暗恋公司的一个女同事,聊了快一年了,一直没勇气表白 上个月突然找我借 5000 块钱,我直接转给她了 我:这钱干嘛用的? 她:给男朋友买个手机 我强颜欢笑说:你真贴心 几天后我收到一个快递,打开一看是部手机!!! 我压抑着内心的激动,放下手头的工作,立马微信上问她怎么回事 她说:手机她男朋

《系列二》-- 5、单例bean缓存的获取

[TOC] > 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。 写在开始前的话: 阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了

《系列二》-- 8、单例bean的创建

[TOC] > 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。 写在开始前的话: 阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了

Spring源码阅读系列--全局目录

> 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。 写在开始前的话: 阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了: - be

《系列二》-- 1、BeanFactory.getBean 总览

[TOC] > 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。 写在开始前的话: 阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了

《系列二》-- 3、FactoryBean 的使用

[TOC] > 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。 写在开始前的话: 阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了

《系列二》-- 2、bean 的作用域: Scope 有哪些

[TOC] > 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。 写在开始前的话: 阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了

《系列二》-- 4、循环依赖及其处理方式

[TOC] > 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。 写在开始前的话: 阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了

《系列二》-- 6、从零开始的 bean 创建

[TOC] > 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。 写在开始前的话: 阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了

《系列二》-- 7、后置处理器-PostProcessor

[TOC] > 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主要内容需要看官自己去源码中验证。全系列文章基于 spring 源码 5.x 版本。 写在开始前的话: 阅读spring 源码实在是一件庞大的工作,不说全部内容,单就最基本核心部分包含的东西就需要很长时间去消化了