Zookeeper——分布式ID和负载均衡原理

article/2025/10/10 5:47:00

摘要

本文主要是介绍zookeeper的除了大部分人都知道的特性意外的一些其他的特性,对于整体的了解一个分布式注册中心的实现具有完整的了解,同时利用zookeeper的其他的特性在工作中,有利于的更好的解决工作的问题。zookeeper相关的特性或许在解决某一些问题上能够取得意想不到的结果。

一、分布式ID生成原理

无论是单机环境还是分布式环境,都有使用唯一标识符标记某一资源的使用场景。比如在淘宝、京东等购物网站下单时,系统会自动生成订单编号,这个订单编号就是一个分布式 ID 的使用。

分布式 ID 生成器就是通过分布式的方式,实现自动生成分配 ID 编码的程序或服务。在日常开发中,Java 语言中的 UUID 就是生成一个 32 位的 ID 编码生成器。根据日常使用场景,我们生成的 ID 编码一般具有唯一性、递增性、安全性、扩展性这几个特性

唯一性:ID 编码作为标记分布式系统重要资源的标识符,在整个分布式系统环境下,生成的 ID 编码应该具有全局唯一的特性。如果产生两个重复的 ID 编码,就无法通过 ID 编码准确找到对应的资源,这也是一个 ID 编码最基本的要求。

递增性:递增性也可以说是 ID 编码的有序特性,它指一般的 ID 编码具有一定的顺序规则。比如 MySQL 数据表主键 ID,一般是一个递增的整数数字,按逐条加一的方式顺序增大。我们现在学习的 ZooKeeper 系统的 zxID 也具有递增的特性,这样在投票阶段就可以根据 zxID 的有序特性,对投票信息进行比对。

安全性:有的业务场景对 ID 的安全性有很高的要求,但这里说的安全性是指,如果按照递增的方式生成 ID 编码,那么这种规律很容易被发现。比如淘宝的订单编码,如果被恶意的生成或使用,会严重影响系统的安全性,所以 ID 编码必须保证其安全性。

扩展性:该特性是指 ID 编码规则要有一定的扩展性,按照规则生成的编码资源应该满足业务的要求。还是拿淘宝订单编码为例,假设淘宝订单的 ID 生成规则是:随机产生 4 位有效的整数组成编码,那么最多可以生成 6561 个订单编码,这显然是无法满足淘宝系统需求的。所以在设计 ID 编码的时候,要充分考虑扩展的需要,比如编码规则能够生成足够多的 ID,从而满足业务的要求,或者能够通过不同的前缀区分不同的产品或业务线 。

1.1 常见的分布ID生成方案

高并发项目设计——分布式ID生成设计方案_庄小焱-CSDN博客

1.2 zookeeper的分布式ID生成原理

上面介绍的几种策略,有的和底层编码耦合比较大,有的又局限在某一具体的使用场景下,并不满足作为分布式环境下一个公共 ID 生成器的要求。接下来我们就利用目前学到的 ZooKeeper 知识,动手实现一个真正的分布式 ID 生成器。

首先,我们通过 ZooKeeper 自身的客户端和服务器运行模式,来实现一个分布式网络环境下的 ID 请求和分发过程。每个需要 ID 编码的业务服务器可以看作是 ZooKeeper 的客户端。ID 编码生成器可以作为 ZooKeeper 的服务端。客户端通过发送请求到 ZooKeeper 服务器,来获取编码信息,服务端接收到请求后,发送 ID 编码给客户端。

在代码层面的实现中,如上图所示。我们可以利用 ZooKeeper 数据模型中的顺序节点作为 ID 编码。客户端通过调用 create 函数创建顺序节点。服务器成功创建节点后,会响应客户端请求,把创建好的节点信息发送给客户端。客户端用数据节点名称作为 ID 编码,进行之后的本地业务操作。 

通过上面的介绍,我们发现,使用 ZooKeeper 实现一个分布式环境下的公用 ID 编码生成器很容易。利用 ZooKeeper 中的顺序节点特性,很容易使我们创建的 ID 编码具有有序的特性。并且我们也可以通过客户端传递节点的名称,根据不同的业务编码区分不同的业务系统,从而使编码的扩展能力更强。

虽然使用 ZooKeeper 的实现方式有这么多优点,但也会有一些潜在的问题。其中最主要的是,在定义编码的规则上还是强烈依赖于程序员自身的能力和对业务的深入理解。很容易出现因为考虑不周,造成设置的规则在运行一段时间后,无法满足业务要求或者安全性不够等问题。

更多的有关于分布式的ID生成的设计方案请详细的参考:

高并发项目设计——分布式ID生成设计方案_庄小焱-CSDN博客

二、负载均衡的实现原理

负载均衡可以理解为运行在网络中的服务器或软件,其主要作用是扩展网络服务器的带宽、提高服务器处理数据的吞吐量,提高网络的可用性。比如我们经常用到的网络服务器、邮件服务器以及很多商业系统的服务器,都采用负载均衡的方式来协调工作。

这些系统一般会采用集群的方式进行部署,由于这些服务器彼此所处的网络环境各不相同,在某一段时间内所接收并处理的数据有多有少,如果整个集群没有一个专门进行管理和协调的角色,随着网络请求越来越多,就会出现某一台服务器比较忙,而网络中其他服务器没有什么任务要处理的情况。

负载均衡通过监控网络中各个服务器的运行情况,对整个集群的计算资源进行合理地分配和调整,避免由于请求处理的无序性导致的短板,从而限制整个集群性能。

2.1 负载均衡算法原理

2.1.1 轮询法

轮询法是最为简单的负载均衡算法,当接收到来自网络中的客户端请求后,负载均衡服务器会按顺序逐个分配给后端服务。比如集群中有 3 台服务器,分别是 server1、server2、server3,轮询法会按照 sever1、server2、server3 这个顺序依次分发会话请求给每个服务器。当第一次轮询结束后,会重新开始下一轮的循环。

2.1.2 随机法

随机算法是指负载均衡服务器在接收到来自客户端的请求后,会根据一定的随机算法选中后台集群中的一台服务器来处理这次会话请求。不过,当集群中备选机器变的越来越多时,通过统计学我们可以知道每台机器被抽中的概率基本相等,因此随机算法的实际效果越来越趋近轮询算法。

2.1.3 原地址哈希法

原地址哈希算法的核心思想是根据客户端的 IP 地址进行哈希计算,用计算结果进行取模后,根据最终结果选择服务器地址列表中的一台机器,处理该条会话请求。采用这种算法后,当同一 IP 的客户端再次访问服务端后,负载均衡服务器最终选举的还是上次处理该台机器会话请求的服务器,也就是每次都会分配同一台服务器给客户端

2.1.4 加权轮询法

在实际的生成环境中,一个分布式或集群系统中的机器可能部署在不同的网络环境中,每台机器的配置性能也有优劣之分。因此,它们处理和响应客户端请求的能力也各不相同。采用上面几种负载均衡算法,都不太合适,这会造成能力强的服务器在处理完业务后过早进入限制状态,而性能差或网络环境不好的服务器,一直忙于处理请求,造成任务积压。

为了解决这个问题,我们可以采用加权轮询法,加权轮询的方式与轮询算法的方式很相似,唯一的不同在于选择机器的时候,不只是单纯按照顺序的方式选择,还根据机器的配置和性能高低有所侧重,配置性能好的机器往往首先分配。

2.1.5 加权随机法

加权随机法和我们上面提到的随机算法一样,在采用随机算法选举服务器的时候,会考虑系统性能作为权值条件。

2.1.6 最小连接数法

最小连接数算法是指,根据后台处理客户端的连接会话条数,计算应该把新会话分配给哪一台服务器。一般认为,连接数越少的机器,在网络带宽和计算性能上都有很大优势,会作为最优先分配的对象。

2.2、ZooKeeper实现负载均衡原理

从上面介绍的几种负载均衡算法中不难看出。一个负载均衡服务器的底层实现,关键在于找到网络集群中最适合处理该条会话请求的机器,并将该条会话请求分配给该台机器。因此探测和发现后台服务器的运行状态变得最为关键。

2.2.1 节点状态收集

首先我们来实现网络中服务器运行状态的收集功能,利用 ZooKeeper 中的临时节点作为标记网络中服务器的状态点位。在网络中服务器上线运行的时候,通过在 ZooKeeper 服务器中创建临时节点,向 ZooKeeper 的服务列表进行注册,表示本台服务器已经上线可以正常工作。通过删除临时节点或者在与 ZooKeeper 服务器断开连接后,删除该临时节点。

最后,通过统计临时节点的数量,来了解网络中服务器的运行情况。如下图所示,建立的 ZooKeeper 数据模型中 Severs 节点可以作为存储服务器列表的父节点。用于之后通过负载均衡算法在该列表中选择服务器。在它下面创建 servers_host1、servers_host2、servers_host3等临时节点来存储集群中的服务器运行状态信息。

在代码层面的实现中,我们首先定义一个 BlanceSever 接口类。该类规定在 ZooKeeper 服务器启动后,向服务器地址列表中,注册或注销信息以及根据接收到的会话请求,动态更新负载均衡情况等功能。如下面的代码所示:

public class BlanceSever{public void register()// 注册节点public void unregister()// 删除节点public void addBlanceCount()// 添加临时节点public void takeBlanceCount()// 删除临时节点}

之后我们创建 BlanceSever 接口的实现类 BlanceSeverImpl,在 BlanceSeverImpl 类中首先定义服务器运行的 Session 超时时间、会话连接超时时间、ZooKeeper 客户端地址、服务器地址列表节点 ‘/Severs’ 等基本参数。并通过构造函数,在类被引用时进行初始化 ZooKeeper 客户端对象实例。

public class BlanceSeverImpl implements BlanceSever{private static final Integer SESSION_TIME_OUTprivate static final Integer CONNECTION_TIME_OUTprivate final ZkClient zkclientprivate static final SERVER_PATH="/Severs"public BlanceSeverImpl(){init...}}

接下来,在定义当服务器启动时,向服务器地址列表注册信息的 register 函数。在函数的内部,通过在 SERVER_PATH 路径下创建临时子节点的方式来注册服务器信息。如下面的代码所示,首先获取服务器的 ip 地址,利用 ip 地址作为临时节点的 path 来创建临时节点。

public register() throws Exception{InetAddress address = InetAddress.getLocalHost();String serverIp=address.getHostAddress()zkclient.createEphemeral(SERVER_PATH+serverIp)}

register 函数在服务器启动并注册服务器信息后,我们再来定义 unregister 方法,该方法是当服务器关机或由于其他原因不再对外提供服务时,通过调用 unregister 方法,注销该台服务器在服务器列表中的信息。

注销后的机器不会被负载均衡服务器分发处理会话。如下面的代码所示,在 unregister 函数的内部,我们主要通过删除 SERVER_PATH 路径下临时节点的方式注销服务器。

public unregister() throws Exception{zkclient.delete(SERVER_PATH+serverIp)}

2.2.2 节点选择

实现服务器列表后,接下来我们就进入负载均衡最核心的内容:如何选择服务器。这里我们通过采用“最小连接数”算法,来确定究竟如何均衡地分配网络会话请求给后台客户端。

整个实现的过程如下图所示。首先,在接收到客户端的请求后,通过 getData 方法获取服务端 Severs 节点下的服务器列表,其中每个节点信息都存储有当前服务器的连接数。通过判断选择最少的连接数作为当前会话的处理服务器,并通过 setData 方法将该节点连接数加 1。最后,当客户端执行完毕,再调用 setData 方法将该节点信息减 1。

首先,我们定义当服务器接收到会话请求后。在 ZooKeeper 服务端增加连接数的 addBlance 方法。如下面的代码所示,首先我们通过 readData 方法获取服务器最新的连接数,之后将该连接数加 1,再通过 writeData 方法将新的连接数信息写入到服务端对应节点信息中。

public void addBlance() throws Exception{InetAddress address = InetAddress.getLocalHost();String serverIp=address.getHostAddress()Integer con_count=zkClient.readData(SERVER_PATH+serverIp)++con_countzkClient.writeData(SERVER_PATH+serverIp,con_count)}

博文参考


http://chatgpt.dhexx.cn/article/GvxuMswD.shtml

相关文章

OpenFeign 整合 Nacos负载均衡原理

一.OpenFeign介绍 OpenFeign是实现微服务间调用的工具,功能包括编解码、构造http请求等。同时OpenFeign又集成了ribbon功能实现客户端负载均衡能力,Bibbon默认的客户端负载均衡能力是与Eurake集成,Nacos通过重写ribbon的ServerList功能实现ri…

负载均衡技术原理

参看文章: 快速理解高性能HTTP服务端的负载均衡技术原理 简介几种负载均衡原理 浅谈几种常用负载均衡架构 一篇读懂分布式架构下的负载均衡技术:分类、原理、算法、常见方案等 一、 引言 负载均衡(Load Balance)是指将负载(工作任…

Docker Swarm 内部服务发现和负载均衡原理

1. 集群环境准备 搭建三台服务器,并安装docker环境,并保证能正常连接互联网,后面会使用其他镜像做负载均衡测试。 192.168.104.79192.168.104.80192.168.104.81 首先修改hostname,便于后面区分当前操作所在机器:使用…

Spring Cloud - Ribbon 负载均衡原理、负载策略、懒加载

目录 ​编辑 一、Ribbon 负载均衡原理 1.1、前言 1.2、负载均衡的工作流程 二、负载均衡策略 2.1、策略原理 2.2、负载均衡自定义方式 三、Ribbon 加载方式 一、Ribbon 负载均衡原理 1.1、前言 ps:案例是上一章所讲的 “根据订单id查询订单的同时&#xff0…

Ribbon-负载均衡原理

负载均衡原理 SpringCloud底层其实是利用了一个名为Ribbon的组件,来实现负载均衡功能的。 那么我们发出的请求明明是http://userservice/user/1,怎么变成了http://localhost:8081的呢? 源码跟踪 为什么我们只输入了service名称就可以访问了…

Nginx负载均衡原理与实战

Nginx 负载均衡原理与实践 本篇摘自《亿级流量网站架构核心技术》第二章 Nginx 负载均衡与反向代理 部分内容。 当我们的应用单实例不能支撑用户请求时,此时就需要扩容,从一台服务器扩容到两台、几十台、几百台。然而,用户访问时是通过如的…

Nginx负载均衡原理

Nginx简介 Nginx是通过反向代理实现的负载均衡。 什么是正向代理与反向代理? 正向代理就是,客户端通过一台代理服务器访问服务端。 反向代理就是,服务端通过代理服务器为客户端提供服务。 看起来似乎没有什么区别,举个例子。 …

负载均衡原理及算法

目录 背景概述原理分类按照软硬件分类硬件负载均衡软件负载均衡 按照地理结构分类本地负载均衡全局负载均衡 按照实现技术DNS负载均衡IP负载均衡链路层负载均衡混合型负载均衡 按照OSI层次二层负载均衡(数据链路层)三层负载均衡(网络层&#…

Ribbon负载均衡原理

Ribbon restTemplate相结合实现负载均衡,具体原理图详见以下截图: LoadBalancerClient 类执行具体的负载均衡,其继承于 LoadBalancerBase。LoadBalancerInterceptor 中注入了 LoadBalancerClient 对象,LoadBalancerClient执行具…

什么是负载均衡,负载均衡的原理解析是怎么样的

负载均衡对于很多大型企业来说,不管网游,商城,金融等业务,他的重要性无需多说,今天带来负载均衡的原理解析。 开头先理解一下所谓的“均衡”。 不能狭义地理解为分配给所有实际服务器一样多的工作量,因为…

负载均衡工作原理详解

负载均衡是由多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,都可以单独对外提供服务而无须其他服务器的辅助。通过某种负载分担技术,将外部发送来的请求均匀分配到对称结构中的某一台服务器上,而接收到请求的服务器独立地回应客户的请求。 均衡负…

负载均衡原理及实现

什么是负载均衡? 负载均衡( LoadBalance ),顾名思义就是把任务压力进行平衡的分摊到集群中各个操作单元(或机器)上,使得避免集群中部分机器压力过大而部分机器过于空闲。经过负载均衡,使得每个机器获取适合自己的处理能力负载。 …

Android 获取手机已安装的应用列表(适配)

权限 <uses-permissionandroid:name"android.permission.QUERY_ALL_PACKAGES"tools:ignore"QueryAllPackagesPermission" /> 加上权限才能获取到所有的应用 获取代码 public static boolean hasApplication(Context context, String packageName) …

如何在没有微软商店的情况下在Windows 10上安装应用程序

如何在没有微软商店的情况下在Windows 10上安装应用程序 通过微软商店&#xff0c;你可以轻松地在Windows 10设备上安装应用程序&#xff0c;就像使用Google Play或AppleStore一样。IT部门经常从设备上删除微软商店&#xff0c;或设置组策略禁用Windows更新&#xff0c;以防止…

“打开文件所在位置“失败,提示“该文件没有与之关联的应用来执行操作。请安装应用,若已经安装应用...““

问题点:装了某个软件,卸载后出现"打开文件所在位置"失败,出现以下提示: 该文件没有与之关联的应用来执行操作。请安装应用&#xff0c;若已经安装应用&#xff0c;请在“默认应用设置"页面中创建关联 解决办法: 打开注册表,找到HKEY_CLASSES_ROOT\Folder\shell,…

Win 11 打开未知文件/打开方式 该文件没有与之关联的应用来执行该操作。请安装应用,若已经安装应用,请在“默认应用设置”页面中创建关联。

问题 鼠标右键选中文件 - 打开方式&#xff0c;或者选择其他应用 弹窗提示&#xff1a; 该文件没有与之关联的应用来执行该操作。请安装应用&#xff0c;若已经安装应用&#xff0c;请在“默认应用设置”页面中创建关联。 打开注册表&#xff0c;查看是否存在以下路径 HKEY_C…

该文件没有与之关联的应用来执行该操作。请安装应用,若已经安装应用,请在“默认应用设置“页面中创建关联

在任意文件上鼠标右键&#xff0c;选择打开方式或者选择其他应用时报错“该文件没有与之关联的应用来执行该操作。请安装应用&#xff0c;若已经安装应用&#xff0c;请在"默认应用设置"页面中创建关联”&#xff0c;错误现象如下&#xff1a; 打开注册表&#xf…

安装软件程序

包管理基础 在深入了解Linux软件包管理之前&#xff0c;本章将先介绍一些基础知识。各种主流Linux发行版都采用了某种形式的包管理系统来控制软件和库的安装。 PMS利用一个数据库来记录各种相关内容&#xff1a;  Linux系统上已安装了什么软件包&#xff1b;  每个包安装了…

android 获取已安装应用(App)列表

效果图 获取手机上已安装应用列表&#xff0c;将获取到的信息用集合返回&#xff0c;可以自己定义是否过滤系统应用&#xff0c;Appinfo是一个实体类&#xff0c;包含应用的名称 包名 图标icon等等 /*** 获取手机已安装应用列表* param ctx* param isFilterSystem 是否过滤系统…

应用程序安装流程

https://maoao530.github.io/2017/01/18/package-install/ 本文介绍APK的安装流程。 一、安装流程图 APK安装流程&#xff0c;总体可以下图流程&#xff0c;用ProcessOn画的&#xff0c;凑合看&#xff1a; 从上图我们可以看到apk安装到最后都会调用到这个flow&#xff1a; PM…