反问面试官:如何实现集群内选主

news2024/11/16 15:37:15

面试官经常喜欢问什么zookeeper选主原理、什么CAP理论、什么数据一致性。经常都被问烦了,我就想问问面试官,你自己还会实现一个简单的集群内选主呢?估计大部分面试官自己也写不出来。

本篇使用 Java 和 Netty 实现简单的集群选主过程的示例。

这个示例展示了多个节点通过投票选举一个新的主节点的过程。Netty 用于节点间的通信,而每个节点则负责发起和响应选举消息。

集群选主流程

选主流程

咱们且不说zookeeper如何选主,单说人类选主,也是采用少数服从多数的原则。人类选主时,中间会经历如下过程:

(1)如果我没有熟悉的或者没找到能力比我强的,首先投给自己一票。

(2)随着时间推移,可能后面的人介绍了各自的特点和实力,那我可能会改投给别人。

(3)所有人将投票信息放入到统计箱中。

(4)最终票数最多的人是领导者。

同样的,zookeeper在选主时,也是这样的流程。假设有5个服务器

  1. 服务器1先给自身投票
  2. 后续起来的服务器2也会投自身一票,然后服务器1观察到服务器2的id比较大,则会改投服务器2
  3. 后续起来的服务器3也会投自身一票,然后服务1和服务器2发现服务器3的id比较大,则都会改投服务器3。服务器3被确定为领导者。
  4. 服务器4起来后也会投自身一票,然后发现服务器3已经有3票了,立马改投服务器3。
  5. 服务器5与服务器4的操作一样。

选主协议

在选主过程中采用的是超过半数的协议。在选主过程中,会需要如下几类消息:

  • 投票请求:节点发出自己的投票请求。
  • 接受投票:其余节点作出判断,如果觉得id较大,则接受投票。
  • 选举胜出:当选主节点后,广播胜出消息。

代码实现

下面模拟3个节点的选主过程,核心步骤如下:

1、定义消息类型、消息对象、节点信息

public enum MessageType {
        VOTE_REQUEST, // 投票请求
        VOTE,         // 投票
        ELECTED       // 选举完成后的胜出消息
}
    
public class ElectionMessage implements Serializable {
    private MessageType type;
    private int nodeId;   // 节点ID
    private long zxId;    // ZXID:类似于ZooKeeper中的逻辑时钟,用于比较
    private int voteFor;  // 投票给的节点ID
}

public class ElectionNode {
    private int nodeId; // 当前节点ID
    private long zxId;  // 当前节点的ZXID
    private volatile int leaderId; // 当前选举的Leader ID
    private String host;
    private int port;
    private ConcurrentHashMap<Integer, Integer> voteMap = new ConcurrentHashMap<>(); // 此节点对每个节点的投票情况
    private int totalNodes; // 集群总节点数
}

2、每个节点利用Netty启动Server

public void start() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ch.pipeline().addLast(
                                    new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
                                    new ObjectEncoder(),
                                    new ElectionHandler(ElectionNode.this));
                        }
                    });

            ChannelFuture future = serverBootstrap.bind(port).sync();
            System.out.println("Node " + nodeId + " started on port " + port);

            // 启动后开始选举过程
            startElection();
//            future.channel().closeFuture().sync();


        } catch (Exception e) {

        } finally {
//            bossGroup.shutdownGracefully();
//            workerGroup.shutdownGracefully();
        }
    }

3、启动后利用Netty发送投票请求

public void sendVoteRequest(String targetHost, int targetPort) {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ch.pipeline().addLast(
                                    new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
                                    new ObjectEncoder(),
                                    new ElectionHandler(ElectionNode.this));
                        }
                    });

            ChannelFuture future = bootstrap.connect(targetHost, targetPort).sync();
            ElectionMessage voteRequest = new ElectionMessage(ElectionMessage.MessageType.VOTE_REQUEST, nodeId, zxId, nodeId);
            future.channel().writeAndFlush(voteRequest);
//            future.channel().closeFuture().sync();
        } catch (Exception e) {

        } finally {
//            group.shutdownGracefully();
        }
    }

4、节点接受到投票请求后,做相关处理

节点在收到消息后,做相关逻辑处理:处理投票请求、处理确认投票、处理选主结果

**处理投票请求:**判断是否是否接受投票信息。只有在主节点没确定并且zxId较大时,才发送投票消息。如果接受了投票请求的话,则更新本地的投票逻辑,然后给投票节点发送接受投票的消息

处理确认投票:如果投票消息被接受了,则更新本地的投票逻辑。

处理选主结果:如果收到了选主结果的消息,则更新本地的主节点。

public class ElectionHandler extends ChannelInboundHandlerAdapter {
    private final ElectionNode node;

    public ElectionHandler(ElectionNode node) {
        this.node = node;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ElectionMessage electionMessage = (ElectionMessage) msg;
        System.out.println("Node " + node.getNodeId() + " received: " + electionMessage);

        if (electionMessage.getType() == ElectionMessage.MessageType.VOTE_REQUEST) {
            // 判断是否是否接受投票信息。只有在主节点没确定并且zxId较大时,才发送投票消息
            // 如果接受了投票请求的话,则更新本地的投票逻辑,然后给投票节点发送接受投票的消息
            if (electionMessage.getZxId() >= node.getZxId() && node.getLeaderId() == 0) {
                node.receiveVote(electionMessage.getNodeId());
                ElectionMessage voteMessage = new ElectionMessage(ElectionMessage.MessageType.VOTE, electionMessage.getNodeId(), electionMessage.getZxId(), electionMessage.getNodeId());
                ctx.writeAndFlush(voteMessage);
            } else {
                // 如果已经确定主节点了,直接发送ELECTED消息
                sendLeaderInfo(ctx);
            }
        } else if (electionMessage.getType() == ElectionMessage.MessageType.VOTE) {
            // 如果投票消息被接受了,则更新本地的投票逻辑。
            if (electionMessage.getZxId() >= node.getZxId() && node.getLeaderId() == 0) {
                node.receiveVote(electionMessage.getNodeId());
            } else {
                // 如果已经确定主节点了,直接发送ELECTED消息
                sendLeaderInfo(ctx);
            }
        } else if (electionMessage.getType() == ElectionMessage.MessageType.ELECTED) {
            if (node.getLeaderId() == 0) {
                node.setLeaderId(electionMessage.getVoteFor());
            }
        }
    }

5、接受别的节点的投票

这里是比较关键的一步,当确定接受某个节点时,则更新本地的投票数,然后判断投票数是否超过半数,超过半数则确定主节点。同时,再将主节点广播出去。

此时,其余节点接收到选主确认的消息后,都会更新自己的本地的主节点信息。

public void receiveVote(int nodeId) {
    voteMap.merge(nodeId, 1, Integer::sum);
    // 比较出votes里值,取出最大的那个对应的key
    int currentVotes = voteMap.values().stream().max(Integer::compareTo).get();

    if (currentVotes > totalNodes / 2 && leaderId == 0) {
        setLeaderId(nodeId);
        broadcastElected();
    }
}

6、广播选主结果

/**
 * 广播选举结果
 */
private void broadcastElected() {
    for (int i = 1; i <= totalNodes; i++) {
        if (i != nodeId) {
            sendElectedMessage(host, 9000 + i);
        }
    }
}

/**
 * 发送选举结果
 *
 * @param targetHost
 * @param targetPort
 */
public void sendElectedMessage(String targetHost, int targetPort) {
    EventLoopGroup group = new NioEventLoopGroup();
    try {
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        ch.pipeline().addLast(
                                new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
                                new ObjectEncoder(),
                                new ElectionHandler(ElectionNode.this));
                    }
                });

        ChannelFuture future = bootstrap.connect(targetHost, targetPort).sync();
        ElectionMessage electedMessage = new ElectionMessage(ElectionMessage.MessageType.ELECTED, leaderId, zxId, leaderId);
        future.channel().writeAndFlush(electedMessage);
//            future.channel().closeFuture().sync();
    } catch (Exception e) {

    } finally {
//            group.shutdownGracefully();
    }
}

7、完整代码

完整代码:javacore/src/main/java/com/ycl/election/ElectionHandler.java · yclxiao/specialty - Gitee.com

总结

本文主要演示了一个简易的多Server的选主过程,以上代码是一个简单的基于Netty实现的集群选举过程的示例。在实际场景中,选举逻辑远比这个复杂,需要处理更多的网络异常、重复消息、并发问题等。

希望对你有帮助,如遇问题可加V交流。

本篇完结!欢迎 关注、加V交流、全网可搜(程序员半支烟)

原文链接:反问面试官:如何实现集群内选主

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

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

相关文章

JS---获取浏览器可视窗口的尺寸

innerHeight 和 innerWidth 这两个方法分别是用来获取浏览器窗口的宽度和高度&#xff08;包含滚动条的&#xff09; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible&q…

Linux驱动开发(速记版)--高级字符设备进阶

第二十四章 IO 模型引入 24.1 IO 的概念 IO是计算机系统内外数据交换过程&#xff0c;冯诺依曼架构下各部件协同工作。用户输入&#xff0c;CPU处理&#xff0c;结果输出。磁盘IO是内存与磁盘数据交换的核心&#xff0c;对信息交互至关重要。 24.2 IO 执行过程 Linux操作系统…

C++11中智能指针以及标准模板库 My_string My_stack

My_string.h #ifndef MY_STRING_H #define MY_STRING_H#include <iostream> #include <cstring> #include <stdexcept>using namespace std;template<typename T> class My_string { private:T *ptr; // 指向字符数组的指针int size; /…

你的下一台手机会是眼镜吗?RTE 大会与你一同寻找下一代计算平台丨「空间计算和新硬件」论坛报名

周四 Meta 刚公布新一代 AR 眼镜 Orion 后&#xff0c;Perplexity 的 CEO 发了一条状态&#xff1a;「如果你还在做软件&#xff0c;请转型硬件。」 一家估值 30 亿美元的 AI 软件公司 CEO 说出这样的言论&#xff0c;既有有见到「最强 AR 眼镜」Orion 后的激动情绪&#xff0c…

【Redis】持久化机制--RDB和AOF

目录 1. RDB持久化 1.1 触发机制 1.2 流程说明 1.3 RDB文件的处理 1.4 RDB机制演示 1.5 RDB的优缺点 2. AOF持久化 2.1 使用AOF与基本演示 2.2 AOF的工作流程 2.3 文件同步&#xff08;缓冲区刷新策略&#xff09; 2.4 重写机制 2.5 AOF重写流程 2.6 启动时数据恢复 …

基于Diffusion的图像修复方法

基于Diffusion的图像修复方法 本文介绍基于 Diffusion 的几个图像修复的工作。图像修复任务有两种应用的场景&#xff0c;一是图片的某部分真的缺失了&#xff0c;需要修复处这部分内容&#xff1b;二是想要修改图片中的某个部分&#xff0c;更换/新增/删除物体&#xff0c;这…

godot4.2入门项目 dodge_the_creep学习记录

前言 在学习博客Godot4 你的第一个2d游戏中的项目时&#xff0c;遇到了点小问题&#xff0c;记录一下。 官方项目 传送门 问题 怪兽直接从屏幕中间部分冒出来&#xff0c;以及角色出现时位于屏幕外角色被设置的背景图遮挡 解决方法 1.节点的位置没有对齐&#xff0c;正确示例…

李宏毅机器学习2022-HW8-Anomaly Detection

文章目录 TaskBaselineReportQuestion2 Task 异常检测Anomaly Detection 将data经过Encoder&#xff0c;在经过Decoder&#xff0c;根据输入和输出的差距来判断异常图像。training data是100000张人脸照片&#xff0c;testing data有大约10000张跟training data相同分布的人脸…

9.27每日作业

将之前实现的顺序表、栈、队列都更改成模板类 顺序表&#xff1a; list.hpp #ifndef LIST_HPP #define LIST_HPP#include <iostream> #include<memory.h> #include<stdlib.h> #include<string.h> using namespace std;//typedef int T; //类…

nginx常用的性能优化

第一步调整工作进程数&#xff1a; 设置成auto&#xff0c;会自动按照CPU核心数来启动工作进程数&#xff0c;如果设置具体数字&#xff0c;则只会使用指定数量的CPU核心&#xff0c;无法将CPU同一时间都能用得到&#xff0c;所以就不能发挥服务器的最大的性能。 第二步增加进程…

章管家 listUploadIntelligent.htm SQL注入漏洞

漏洞描述&#xff1a; 章管家 listUploadIntelligent.htm 接口处存在SQL注入漏洞&#xff0c;未经身份验证的远程攻击者除了可以利用 SQL 注入漏洞获取数据库中的信息&#xff08;例如&#xff0c;管理员后台密码、站点的用户个人信息&#xff09;之外&#xff0c;甚至在高权限…

基于大数据的高血压人群数据分析及可视化系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

MMD模型及动作一键完美导入UE5-衣服布料模拟(四)

1、给角色刷布料 1、打开角色,通过Window->Clothing打开模型布料窗口 2、选中裙子右键,创建布料数据 3、选择裙子,右键->应用布料数据 4、激活布料画笔,就可以开始绘制布料了 5、调整画笔大小和布料值进行绘制,布料值为0表示刚体

网络安全:保护数字时代的堡垒

网络安全&#xff1a;保护数字时代的堡垒 引言&#xff1a; 在数字化时代&#xff0c;网络安全的重要性日益凸显。它不仅关系到个人隐私保护&#xff0c;还涉及国家安全和经济发展。随着技术的发展&#xff0c;网络安全的威胁也在不断进化&#xff0c;从个人设备到企业网络&am…

2024图纸加密软件集锦|10款好用不踩雷的图纸加密软件推荐!

小李&#xff1a;“老张&#xff0c;最近咱们公司的设计图纸泄密事件频发&#xff0c;真是让人头疼啊&#xff01;你有没有什么好的图纸加密软件推荐&#xff0c;能帮我们加强设计文件的安全性&#xff1f;” 老张&#xff1a;“小李啊&#xff0c;你算是问对人了。随着数字化…

《pyqt+open3d》open3d可视化界面集成到qt中

《pyqtopen3d》open3d可视化界面集成到qt中 一、效果显示二、代码三、资源下载 一、效果显示 二、代码 参考链接 main.py import sys import open3d as o3d from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget from PyQt5.QtGui import QWindow from PyQt5.Qt…

高通Android 12 push framework.jar和service.jar

1、Android framework.jar和service.jar替换注意事项 2、单编 adb push service.jar脚本 如下 adb root adb disable-verity adb remountadb push services.jar system/framework adb push services.jar.prof system/framework adb push oat/arm64/services.art /system/fram…

重磅首发!大语言模型LLM学习路线图来了!

ChatGPT的出现在全球掀起了AI大模型的浪潮&#xff0c;2023年可以被称为AI元年&#xff0c;AI大模型以一种野蛮的方式&#xff0c;闯入你我的生活之中。 从问答对话到辅助编程&#xff0c;从图画解析到自主创作&#xff0c;AI所展现出来的能力&#xff0c;超出了多数人的预料&…

酒店智能门锁SDK接口pro[V10] 对接酒店收银-模块封装C#-SAAS本地化-未来之窗行业应用跨平台架构

一、代码 public class CyberWin_hoteldoor_prousbv10_2024{[DllImport("024.dll", CharSet CharSet.Ansi, CallingConvention CallingConvention.StdCall, EntryPoint "GetDLLVersion")]public static extern int GetDLLVersion(StringBuilder sDllVer…

新手答疑 | 零基础该怎么学习嵌入式?嵌入式Linux学习路线是什么?嵌入式开发板推荐?

很多初学者想要涉足嵌入式Linux开发领域&#xff0c;但往往在刚入门阶段&#xff0c;会因为初次接触到大量复杂的概念术语和深奥的技术文档感到压力重重&#xff0c;面对这些内容不知从何下手&#xff0c;感到十分迷茫&#xff0c;网上的内容也纷繁复杂&#xff0c;没有清晰的学…