手写NACOS的服务的注册与发现|心跳机制|轮询调用服务功能

news2024/9/17 7:14:17

背景

手写NACOS的服务的部分核心功能,提高自身的编码能力
本篇文章设计的是单体NACOS后端服务,提供SDK给多个NACOS客户端使用
本文编写了注册与发现|心跳机制|轮询调用服务功能,可当做入门级阅读

nacos-service

项目结构

代码内容

pom配置文件,需要较高版本的maven

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>nacos-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nacos-service</name>
    <description>nacos-service</description>

    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

RestTemplateConfig 类

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder
                .setConnectTimeout(Duration.ofSeconds(1000))
                .setReadTimeout(Duration.ofSeconds(1000))
                .build();
    }
}
ServeController类
@RestController
@RequestMapping("/nacos-service")
public class ServeController {
    /**
     * 注册表
     * 数据示例:order-service -> ["127.0.0.1:8092","127.0.0.1:8093"]
     */
    private final Map<String, List<String>> registerMap = new HashMap<>();
    // 调度线程连接池
    private final ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);
    // 调度任务Map
    private final Map<String, ScheduledFuture> scheduleMap = new HashMap<>();

    @Resource
    RestTemplate restTemplate;

    /**
     * @param registerReq
     * @return
     */
    @PostMapping("/register")
    public String register(@RequestBody Map<String, String> registerReq){
        // 注册
        List<String> serviceValue = registerMap.get(registerReq.get("serviceName"));
        if (!CollectionUtils.isEmpty(serviceValue)){
            serviceValue.add(registerReq.get("serviceValue"));
            registerMap.put(registerReq.get("serviceName"), serviceValue);
        } else {
            List<String> list = new ArrayList<>();
            list.add(registerReq.get("serviceValue"));
            registerMap.put(registerReq.get("serviceName"), list);
        }
        // 通知在线的项目获取配置信息
        List<String> ipAndPortList = new ArrayList<>();
        for (List<String> value : registerMap.values()) {
            ipAndPortList.addAll(value);
        }
        // 异步并发通知
        for (String address : ipAndPortList) {
            CompletableFuture.runAsync(() -> {
                String url = "http://" + address + "/register-table/update";
                restTemplate.getForObject(url, Boolean.class);
            });
        }
        // 心跳
        String ipAndPort = registerReq.get("serviceValue");
        heartBeat(registerReq.get("serviceName"), ipAndPort);
        return "注册成功";
    }

    @GetMapping("/register-service/get")
    public List<String> getRegisterMap(@RequestParam String serviceName){
        return registerMap.get(serviceName);
    }

    @GetMapping("/register-table/all")
    public Map<String, List<String> > allRegisterMap(){
        return registerMap;
    }

    /**
     * 心跳使用调度连接池
     * 多个心跳也可用同一个连接池
     */
    private void heartBeat(String serviceName, String ipAndPort){
        String url = "http://" + ipAndPort + "/heart-beat-check";
        Runnable runnable = () -> {
            try {
                restTemplate.getForObject(url, Boolean.class);
                System.out.println("心跳检测开始-" + ipAndPort);
            } catch (Exception e) {
                System.out.println("心跳检测发生错误: " + ipAndPort + "剔除该心跳定时任务!");
                System.out.println(e);
                // 当请求报错,就断掉心跳,并移除注册表中的地址
                List<String> ipAndPortList = registerMap.get(serviceName);
                ipAndPortList.remove(ipAndPort);
                ScheduledFuture future = scheduleMap.get(ipAndPort);
                future.cancel(true);
            }
        };
        ScheduledFuture<?> scheduledFuture = scheduledThreadPool.scheduleAtFixedRate(runnable, 0, 5, TimeUnit.SECONDS);
        scheduleMap.put(ipAndPort, scheduledFuture);
    }

}

application.properties配置文件

spring.application.name=nacos-service
server.port=8091

nacos-sdk

项目结构

代码内容

pom配置文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>nacos-sdk</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nacos-sdk</name>
    <description>nacos-sdk</description>
    
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

ServeConfig类

@Component
public class ServeConfig implements ApplicationRunner {

    @Value("${service.name}")
    private String serveName;
    @Value("${remote.nacos.service.ip}")
    private String remoteIp;
    @Value("${remote.nacos.service.port}")
    private Integer remotePort;
    @Value("${server.port}")
    private Integer port;
    @Resource
    private RestTemplate restTemplate;
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        String hostAddress = InetAddress.getLocalHost().getHostAddress();
        String url = "http://" + remoteIp + ":" + remotePort + "/nacos-service/register";
        Map<String, Object> registerReq = new HashMap<>();
        registerReq.put("serviceName", serveName);
        String ipToPort = hostAddress + ":" + port;
        registerReq.put("serviceValue", ipToPort);
        String s = restTemplate.postForObject(url, registerReq, String.class);
        System.out.println("已经注册到nacos中心" + s);

    }
}
TemplateConfig类
@Configuration
public class TemplateConfig {

    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder) {
        return builder
                .setConnectTimeout(Duration.ofSeconds(1000))
                .setReadTimeout(Duration.ofSeconds(1000))
                .build();
    }
}
ConfigController类
@RestController
public class ConfigController {

    // 缓存一份注册信息保存在本地
    private final static Map<String, List<String> > registerMap = new HashMap<>();
    // 服务轮训控制器
    private final static Map<String, Integer> servicePolling = new HashMap<>();

    @Value("${remote.nacos.service.ip}")
    private String remoteIp;
    @Value("${remote.nacos.service.port}")
    private Integer remotePort;
    @Resource
    private RestTemplate restTemplate;

    @GetMapping("/heart-beat-check")
    public boolean heartBeatAccess(){
        return true;
    }

    // 接收服务端的通知
    @GetMapping("/register-table/update")
    public void updateRegisterTable(){
        String url = "http://" + remoteIp + ":" + remotePort + "/nacos-service/register-table/all";
        Map result = restTemplate.getForObject(url, Map.class);
        registerMap.putAll(result);
    }

    /**
     * 轮训调用服务
     * 根据服务名获取具体的ip地址
     */
    public String getAddressByServiceName(String serviceName){
        List<String> services = registerMap.get(serviceName);
        if (services.size() == 1){
            return services.get(0);
        } else {
            Integer index = servicePolling.get(serviceName);
            if (Objects.isNull(index)){
                index = 0;
            } else {
                index = index + 1;
                if (index > services.size() - 1) {
                    index = 0;
                }
            }
            servicePolling.put(serviceName, index);
            return services.get(index);
        }
    }
}

serviceA

项目结构

代码内容

pom配置文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>serviceA</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>serviceA</name>
    <description>serviceA</description>

    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.demo</groupId>
            <artifactId>nacos-sdk</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
HelloController类
@RestController
public class HelloController {

    @Resource
    ConfigController controller;
    @Resource
    RestTemplate restTemplate;

    /**
     * 调用B服务获取资源
     */
    @GetMapping("/test")
    public String test(){
        String address = controller.getAddressByServiceName("order-service");
        String url = "http://" + address + "/hello";
        String template = restTemplate.getForObject(url, String.class);
        System.out.println("我从B服务:" + address + "获取的数据:" + template);
        return "我从B服务获取的数据" + template;

    }
}
ServiceAApplication类
@SpringBootApplication
@ComponentScan(value = {"com.demo.nacossdk","com.demo.servicea"})
public class ServiceAApplication {

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

}

application.properties配置类

spring.application.name=serviceA
server.port=8093
service.name=game-service
# nacos服务器IP地址
remote.nacos.service.ip=192.xxx.xxx.xxx
remote.nacos.service.port=8091

serviceB

项目结构

代码内容

pom.xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.3.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>serviceB</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>serviceB</name>
    <description>serviceB</description>

    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.demo</groupId>
            <artifactId>nacos-sdk</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>
HelloController类
@RestController
public class HelloController {

    /**
     * 调用B服务获取资源
     */
    @GetMapping("/hello")
    public String test(){
        return "黑神话-悟空";
    }

}
ServiceBApplication类
@SpringBootApplication
@ComponentScan(value = {"com.demo.nacossdk","com.demo.serviceb"})
public class ServiceBApplication {

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

}

application.properties配置类

spring.application.name=serviceB
server.port=8095
service.name=order-service
remote.nacos.service.ip=192.xxx.xxx.xxx
remote.nacos.service.port=8091

运行结果展示

起nacos-service服务,serviceA服务,两个serviceB服务

心跳结果

三个服务的心跳

停掉其中一个B服务

轮询调用结果

A服务调用B服务

总结

1、开发nacos-servic、nacos-sdk,打包nacos-sdk

2、serviceA、serviceB 引入 nacos-sdk依赖

3、serviceA通过注册中心调用serviceB获取内容

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2116254.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Detect It Easy

Detect It Easy&#xff08;简称 DIE&#xff09;项目的网址为 https://github.com/horsicq/Detect-It-Easy 下载完安装包后&#xff0c;直接双击die.exe即可进入到操作界面 工具介绍&#xff1a; 它可以用来检测程序架构和文件类型。如图所示。其中&#xff0c;「模式」说明程…

UE5 贝塞尔曲线导弹

首先创建导弹Actor蓝图 代码逻辑&#xff0c;这其中创建的所有变量都不用添加值&#xff0c;这些逻辑要画图来解释&#xff0c;比较麻烦&#xff0c;大家自行理解一下 接下来进入人物蓝图编写代码逻辑&#xff0c;我这里是在两个不同的位置发射两枚导弹 宏中的代码&#xff0c;…

时序预测|基于粒子群优化支持向量机的时间序列预测Matlab程序PSO-SVM 单变量和多变量 含基础模型

时序预测|基于粒子群优化支持向量机的时间序列预测Matlab程序PSO-SVM 单变量和多变量 含基础模型 文章目录 一、基本原理1. 问题定义2. 数据准备3. SVM 模型构建4. 粒子群优化&#xff08;PSO&#xff09;5. 优化与模型训练6. 模型评估与预测7. 流程总结8. MATLAB 实现概述 二、…

Python QT实现A-star寻路算法

目录 1、界面使用方法 2、注意事项 3、补充说明 用Qt5搭建一个图形化测试寻路算法的测试环境。 1、界面使用方法 设定起点&#xff1a; 鼠标左键双击&#xff0c;设定红色的起点。左键双击设定起点&#xff0c;用红色标记。 设定终点&#xff1a; 鼠标右键双击&#xf…

轻松上手,高效产出:音频剪辑工具年度精选

不知道你有没有拍vlog记录生活的习惯&#xff0c;有时候视频里穿插进自己的声音能让视频更加丰富贴上自己的标签。这次我们一起探讨当下有哪些好用的在线音频剪辑工具。 1.FOXIT音频剪辑 链接直达>>https://www.foxitsoftware.cn/audio-clip/ 这个工具是一款专业的音…

GNU的伪操作 (25)

这里主要是 对 GNU的 各个伪操作进行 详细的解释。 先来看着几个 伪操作。 .byte, .short, .long, .quad , .float , 这个是关于 字节的。 .string .ascii 是关于字符串的。 这个字符串编译器是可以自动在末尾补0 的。 举例&#xff1a; val: .word 0x11223344 mov r…

计算机组成原理(SRAM电路图示)

1.该电路由6个MOS管&#xff08;T1-T6&#xff09;组成 2.T1-T4是一个由MOS管组成的触发器基本电路&#xff1b; T5&#xff0c;T6像开关&#xff0c;受行地址选择信号控制&#xff1b; T7,T8受列地址选择控制&#xff0c;分别与位线A&#xff0c;和相连 3.假设触发器…

FinOps原则:云计算成本管理的关键

导语&#xff1a; FinOps 原则为我们提供了北极星&#xff08;North Star&#xff09;&#xff0c;在我们实践云财务管理时指导我们的活动。这些原则由 FinOps 基金会成员制定&#xff0c;并通过经验磨练出来。 北极星&#xff08;North Star&#xff09;的含义&#xff1a; …

不用管理员权限直接修改windows中hosts值的方法

本文只适用于少数经常修改hosts文件的程序员帅哥和美女们。 背景&#xff1a;直接修改hosts文件的不足 修改C:\Windows\System32\drivers\etc\hosts时&#xff0c;必须要管理员权限。 经常修改&#xff0c;会觉得有一丝丝麻烦。 方法1 &#xff08;安全性低&#xff0c;不…

ThinkPHP5 5-rce远程代码执行漏洞复现

启动容器 docker-compose up -d 查看端口 docker ps 端口为:8080,访问网站&#xff0c;搭建成功 漏洞复现 &#xff08;1&#xff09;输出关于 PHP 配置的信息 &#xff08;2&#xff09;将php代码写入文件 接着访问shell.php 由于存在过滤&#xff0c;需要用到base64加密来使…

SPIRNGBOOT+VUE实现浏览器播放音频流并合成音频

一、语音合成支持流式返回&#xff0c;通过WS可以实时拿到音频流&#xff0c;那么我们如何在VUE项目中实现合成功能呢。语音合成应用非常广泛&#xff0c;如商家广告合成、驾校声音合成、新闻播报、在线听书等等场景都会用到语音合成。 二、VUE下实现合成并使用浏览器播放代码…

学习记录:js算法(二十八):删除排序链表中的重复元素、删除排序链表中的重复元素II

文章目录 删除排序链表中的重复元素我的思路解法一&#xff1a;循环解法二&#xff1a;递归 网上思路 删除排序链表中的重复元素 II我的思路网上思路 总结 删除排序链表中的重复元素 给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出…

软件质量保障:故障演练介绍

目录 背景&#xff1a;架构变化带来的问题 什么是故障演练 为什么需要故障演练 故障演练场景有哪些 不同演练类型和目标 如何对工具进行评估 功能评测项 告警评测项 观测指标评测项 总结 背景&#xff1a;架构变化带来的问题 随着架构越来越复杂、应用越来越多样&…

外卖霸王餐对接接口为用户提供了哪些好处?

外卖霸王餐对接接口为用户提供了多种好处&#xff0c;以下是一些主要优势&#xff1a; 免费或低成本的美食体验&#xff1a;用户可以通过霸王餐活动免费或以非常低的价格尝试不同的餐厅和菜品。发现新餐厅和菜品&#xff1a;霸王餐活动可以帮助用户发现新的餐厅和他们可能感兴趣…

【C/C++IO流汇总】C/C++IO流以及系统调用open/read/write等详解

IO流学习分享 1、C中文件IO使用1.1、文件函数1.2、文件的使用方式1.3、文件的读写函数1.4、示例1.4.1、fgetc()函数1.4.2、getc()函数1.4.3、fputc()函数1.4.4、putc()函数1.4.5、fgets()函数1.4.6、fputs()函数1.4.7、fscanf()函数1.4.8、fprintf()函数 1.5、fread()函数1.5.1…

stm32之SPI通信协议

文章目录 前言一、SPI通信协议1.1 SPI简介1.2 SPI通信特点1.3 SPI与I2C对比 二、SPI硬件电路三、SPI通信原理四、SPI时序单元4.1 起始和终止条件4.2 交换一个字节(模式1)4.3 交换一个字节(模式0)4.4 交换一个字节(模式2和3) 五、SPI时序5.1 发送指令5.2 指定地址写5.3 指定地址…

软件部署-Docker容器化技术

开始前的环境说明 VMware 17 Pro Centos release 7.9.2009(防火墙已关闭) Docker 26.1.4 Docker镜像加速器配置:"https://do.nark.eu.org", "https://dc.j8.work", "https://docker.m.daocloud.io", "https://dockerproxy.com", &…

PDF 软件如何帮助您编辑、转换和保护文件。

如何找到最好的 PDF 编辑器。 无论您是在为您的企业寻找更高效的 PDF 解决方案&#xff0c;还是尝试组织和编辑主文档&#xff0c;PDF 编辑器都可以在一个地方提供您需要的所有工具。市面上有很多 PDF 编辑器 — 在决定哪个最适合您时&#xff0c;请考虑这些因素。 1. 确定您的…

十一、MySQL高级—工具和技巧拾遗~视图 VIEW(4)

&#x1f33b;&#x1f33b; 目录 一、是什么二、作用三、适用场景四、语法五、注意事项(适用5.5) 文章大纲 &#x1f447;&#x1f447; 一、是什么 将一段查询sql封装为一个虚拟的表。 这个虚拟表只保存了sql逻辑&#xff0c;不会保存任何查询结果。 二、作用 1、封装复杂sql…

开放式系统互连(OSI)模型的实际意义

0 前言 开放式系统互连&#xff08;OSI&#xff0c;Open Systems Interconnection&#xff09;模型&#xff0c;由国际标准化组织&#xff08;ISO&#xff09;在1984年提出&#xff0c;目的是为了促进不同厂商生产的网络设备之间的互操作性。 定义了一种在层之间进行协议实现…