详解租约机制以及在hbase中的应用

发布时间 - 2026-01-10 23:09:39    点击率:

详解租约机制以及在hbase中的应用

为什么需要Lease

分布式系统中为什么需要租约机制,这是因为在分布式系统,为了保证服务的高可用,需要在服务发生故障的时候及时启动另外一个服务实例以替换故障服务。这样就需要在服务端和客户端或者服务端和控制中心维持一个心跳信息,用于服务进程向控制中心汇报当前自己的健康情况,如果控制中心在一段时间收不到服务进程上报的心跳,则会启动新的进程继续对外提供服务。

但是,由于实际网络情况的复杂性,控制中心无法收到心跳时不能准确地判断究竟是服务故障了还是服务进程和控制中心之间的网络发生了故障。这种情况下控制中心冒然地启用新进程有可能会造成“双主”这种情况出现。

为避免上述情况的发生引入了租约机制,此时服务节点持续向控制中心申请短时间租约,控制中心在已派发的租约过期之前,不会启用新服务节点,而服务节点租约过期时若还无法从控制中心申请到新租约,自己中断客户链接。

此外,租约机制还可用于客户端和服务端之间的解藕,避免客户端进程失去响应时,其占用的服务端资源长期得不到释放进而影响到服务端的稳定。

Lease的实现

在实际系统中,如果依赖一个中心结点向外发布lease存在很大的风险,那就是如果该中心结点发生宕机或者网络故障,那么服务节点由于接收不到新的租约那么会导致整个服务集群进入不可用状态。因此,在实际使用中,对外提供lease服务的往往是由多个进程实例组成的另外一套集群,该集群具有高可用性,可以对外提供lease服务,比如zookeeper集群。

HRegionServer的租约Lease管理

租约线程的初始化

在HRegionServer的run主循环里会调用preRegistrationInitialization预先初始化一些线程,包括初始化集群连接信息setupClusterConnection()、healthCheckChore、pauseMonitor、initializeZookeeper以及initializeThreads()。

其中在initializeThreads()中会初始化各类线程,这些线程包括了这台regionServer的lease线程:

this.compactionChecker = new CompactionChecker(this, this.threadWakeFrequency, this); //检查合并请求 
this.periodicFlusher = new PeriodicMemstoreFlusher(this.threadWakeFrequency, this);  //周期性地检查memstore的flush请求 
this.leases = new Leases(this.threadWakeFrequency); 

 Leases类的定义如下,它继承了HasThread这个抽象类,并定义了如下几个主要的成员变量:

public static final int MIN_WAIT_TIME = 100; 
private final Map<String, Lease> leases = new ConcurrentHashMap<String, Lease>(); 
 
protected final int leaseCheckFrequency; 
protected volatile boolean stopRequested = false; 

 其中Map型成员变量leases负责管理该regionserver进程中的lease实例,我们看看lease类都定义了哪些变量:

private final String leaseName; 
private final LeaseListener listener; 
private int leaseTimeoutPeriod; 
private long expirationTime; 

 leaseTimeoutPeriod是租约时间,expirationTime会在lease被创建时被置位为系统时间与leaseTimeoutPeriod之和,用于周期性地计算该租约已经被使用多长时间,如果租约已经超过了leaseTimeoutPeriod定义的到期时间,则会触发一个expired事件,LeaseListener会监听该事件并调用leaseExpired方法,不同类型的lease都会继承LeaseListener接口并实现自己的leaseExpired方法,如下所示是scan lease对该方法的实现:

@Override 
public void leaseExpired() {    //处理租约过期 
  RegionScannerHolder rsh = scanners.remove(this.scannerName); 
  if (rsh != null) { 
   RegionScanner s = rsh.s; 
   LOG.info("Scanner " + this.scannerName + " lease expired on region " 
     + s.getRegionInfo().getRegionNameAsString()); 
   try { 
    Region region = regionServer.getRegion(s.getRegionInfo().getRegionName()); 
    if (region != null && region.getCoprocessorHost() != null) { 
     region.getCoprocessorHost().preScannerClose(s); 
    } 
 
    s.close(); 
    if (region != null && region.getCoprocessorHost() != null) { 
     region.getCoprocessorHost().postScannerClose(s); 
    } 
   } catch (IOException e) { 
    LOG.error("Closing scanner for " 
     + s.getRegionInfo().getRegionNameAsString(), e); 
   } 
  } else { 
   LOG.warn("Scanner " + this.scannerName + " lease expired, but no related" + 
    " scanner found, hence no chance to close that related scanner!"); 
  } 
 } 

 客户端的scan请求是分解成多次RPC请求发到服务端的,分解的次数是scan的总数据量与客户端setCache两者的比值。每个scan请求发到服务端后会租用一个scanner,用于当前的scan结束后,后续的scan可以直接复用已有的资源,但是为防止scanner长期占用服务端资源,通过租约管理,关闭不再使用的scanner。

OK,回到前面的Leases类,看看它是如何管理regionServer进程中的各个lease的,这部分逻辑在它覆写的run方法中:

public void run() { 
  long toWait = leaseCheckFrequency; 
  Lease nextLease = null; 
  long nextLeaseDelay = Long.MAX_VALUE; 
 
  while (!stopRequested || (stopRequested && !leases.isEmpty()) ) { 
 
   //睡眠一段时间 
 
   nextLease = null; 
   nextLeaseDelay = Long.MAX_VALUE; 
   for (Iterator<Map.Entry<String, Lease>> it = leases.entrySet().iterator(); it.hasNext();) { 
    Map.Entry<String, Lease> entry = it.next(); 
    Lease lease = entry.getValue(); 
    long thisLeaseDelay = lease.getDelay(TimeUnit.MILLISECONDS); 
    if ( thisLeaseDelay > 0) { 
     if (nextLease == null || thisLeaseDelay < nextLeaseDelay) { 
      nextLease = lease; 
      nextLeaseDelay = thisLeaseDelay; 
     } 
    } else { 
     // A lease expired. Run the expired code before removing from map 
     // since its presence in map is used to see if lease exists still. 
     if (lease.getListener() == null) { 
      LOG.error("lease listener is null for lease " + lease.getLeaseName()); 
     } else { 
      lease.getListener().leaseExpired(); 
     } 
     it.remove(); 
    } 
   } 
  } 
  close(); 
 } 

 我们省略掉一些异常处理,在while的循环周期中会逐一便利map中管理的lease,计算每个lease的thisLeaseDelay以检查改lease是否已经过期。判断lease是否过期的方法很简单,就是取出当前时间与lease中定义的expirationTime做差,如果差值小于0,则说明该租约已经到期,则调用lease中定义的leaseExpired方法,这与上面我们讲过的关联上了。其中thisLeaseDelay决定了下一次的lease检查在多久之后发生,thisLeaseDelay的计算依据是选择选取所有未过期lease中leaseDelay最短的,通过thisLeaseDelay计算toWait时间,用于决定前面的睡眠时间。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!


# 详解租约机制以及在hbase中的应用  # 租约机制hbase中的应用  # java 线程同步详细介绍及实例代码  # JavaScript中无法通过div.style.left获取值的解决方法  # java中@ModelAttribute注解的作用  # 【Java】BigDecimal实现加减乘除运算代码  # JavaScript实现的鼠标响应颜色渐变效果完整实例  # Java 图片与byte数组互相转换实例  # Java使用SFTP上传文件到服务器的简单使用  # 控制中心  # 服务端  # 客户端  # 自己的  # 则会  # 中会  # 到新  # 几个  # 多个  # 上了  # 是由  # 在实际  # 会在  # 它是  # 可用性  # 这部  # 希望能  # 很简单  # 可以直接  # 这种情况 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: EditPlus中的正则表达式实战(5)  活动邀请函制作网站有哪些,活动邀请函文案?  网站设计制作书签怎么做,怎样将网页添加到书签/主页书签/桌面?  Laravel怎么导出Excel文件_Laravel Excel插件使用教程  Mybatis 中的insertOrUpdate操作  详解Android——蓝牙技术 带你实现终端间数据传输  如何批量查询域名的建站时间记录?  如何用好域名打造高点击率的自主建站?  php嵌入式断网后怎么恢复_php检测网络重连并恢复硬件控制【操作】  如何用wdcp快速搭建高效网站?  合肥制作网站的公司有哪些,合肥聚美网络科技有限公司介绍?  如何正确下载安装西数主机建站助手?  Laravel如何使用Laravel Vite编译前端_Laravel10以上版本前端静态资源管理【教程】  Laravel怎么生成URL_Laravel路由命名与URL生成函数详解  如何用狗爹虚拟主机快速搭建网站?  微博html5版本怎么弄发超话_超话进入入口及发帖格式要求【教程】  如何获取上海专业网站定制建站电话?  猪八戒网站制作视频,开发一个猪八戒网站,大约需要多少?或者自己请程序员,需要什么程序员,多少程序员能完成?  如何在阿里云ECS服务器部署织梦CMS网站?  Win11怎么修改DNS服务器 Win11设置DNS加速网络【指南】  Laravel如何实现全文搜索_Laravel Scout集成Algolia或Meilisearch教程  详解Huffman编码算法之Java实现  如何在Windows环境下新建FTP站点并设置权限?  如何快速配置高效服务器建站软件?  🚀拖拽式CMS建站能否实现高效与个性化并存?  南京网站制作费用,南京远驱官方网站?  VIVO手机上del键无效OnKeyListener不响应的原因及解决方法  Python数据仓库与ETL构建实战_Airflow调度流程详解  如何用免费手机建站系统零基础打造专业网站?  Win11任务栏卡死怎么办 Windows11任务栏无反应解决方法【教程】  node.js报错:Cannot find module &#39;ejs&#39;的解决办法  Laravel数据库迁移怎么用_Laravel Migration管理数据库结构的正确姿势  轻松掌握MySQL函数中的last_insert_id()  如何在腾讯云服务器快速搭建个人网站?  音响网站制作视频教程,隆霸音响官方网站?  独立制作一个网站多少钱,建立网站需要花多少钱?  C#如何调用原生C++ COM对象详解  如何在云主机上快速搭建多站点网站?  Laravel如何使用Telescope进行调试?(安装和使用教程)  黑客如何利用漏洞与弱口令入侵网站服务器?  如何在不使用负向后查找的情况下匹配特定条件前的换行符  ChatGPT怎么生成Excel公式_ChatGPT公式生成方法【指南】  javascript日期怎么处理_如何格式化输出  Linux虚拟化技术教程_KVMQEMU虚拟机安装与调优  Laravel怎么在Controller之外的地方验证数据  Laravel如何处理文件上传_Laravel Storage门面实现文件存储与管理  Laravel怎么连接多个数据库_Laravel多数据库连接配置  奇安信“盘古石”团队突破 iOS 26.1 提权  Laravel如何使用Gate和Policy进行授权?(权限控制)  nodejs redis 发布订阅机制封装实现方法及实例代码