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

详解租约机制以及在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时间,用于决定前面的睡眠时间。

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

(0)

相关推荐

  • JavaScript实现的鼠标响应颜色渐变效果完整实例

    本文实例讲述了JavaScript实现的鼠标响应颜色渐变效果.分享给大家供大家参考,具体如下: 运行效果图如下: 完整代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/199

  • java中@ModelAttribute注解的作用

    问题描述:当我们使用表单传值得时候,如果定义了,name,address,age.等属性,那么通过表单传过去的值就只有这3个属性,但是我们需要的name,pass,address,age等四个属性.那么在提交表单并赋值的时候就会出现,未得到的值为null的情况.如下图: 获取的pass为null 那么我们需要用@ModelAttribute注解来解决他: 实现代码如下: @ModelAttribute public void getUsers(@RequestParam(value="name&

  • Java 图片与byte数组互相转换实例

    实例如下: //图片到byte数组 public byte[] image2byte(String path){ byte[] data = null; FileImageInputStream input = null; try { input = new FileImageInputStream(new File(path)); ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buf = new byte[

  • 【Java】BigDecimal实现加减乘除运算代码

    java.math.BigDecimal BigDecimal一共有4个够造方法,让我先来看看其中的两种用法: 第一种:BigDecimal(double val) Translates a double into a BigDecimal. 第二种:BigDecimal(String val) Translates the String repre sentation of a BigDecimal into a BigDecimal. 使用BigDecimal要用String来够造,要做一个

  • Java使用SFTP上传文件到服务器的简单使用

    最近用到SFTP上传文件查找了一些资料后自己做了一点总结,方便以后的查询.具体代码如下所示: /** * 将文件上传到服务器 * * @param filePath * 文件路径 * @param channelSftp * channelSftp对象 * @return */ public static boolean uploadFile(String filePath, ChannelSftp channelSftp) { OutputStream outstream = null; In

  • JavaScript中无法通过div.style.left获取值的解决方法

    一.问题总结: 样式必须直接写在元素内部才能通过div.style.left直接获取属性值(也就是必须是内联样式才行),定义在css中的样式不能通过这种方式获取. 让元素移动到200停止 setTimeout ( function () { var div = document.getElementById("div4"); //var left = parseInt(div.style.left) + 5; var left = div.offsetLeft + 5; div.sty

  • java 线程同步详细介绍及实例代码

    java 线程同步 概要: 为了加快代码的运行速度,我们采用了多线程的方法.并行的执行确实让代码变得更加高效,但随之而来的问题是,有很多个线程在程序中同时运行,如果它们同时的去修改一个对象,很可能会造成讹误的情况,这个时候我们需要用一种同步的机制来管理这些线程. (一)竞争条件 记得操作系统中,让我印象很深的有一张图.上面画的是一块块进程,在这些进程里面分了几个线程,所有这些线程齐刷刷统一的指向进程的资源.Java中也是如此,资源会在线程间共享而不是每个线程都有一份独立的资源.在这种共享的情况下

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

    详解租约机制以及在hbase中的应用 为什么需要Lease 分布式系统中为什么需要租约机制,这是因为在分布式系统,为了保证服务的高可用,需要在服务发生故障的时候及时启动另外一个服务实例以替换故障服务.这样就需要在服务端和客户端或者服务端和控制中心维持一个心跳信息,用于服务进程向控制中心汇报当前自己的健康情况,如果控制中心在一段时间收不到服务进程上报的心跳,则会启动新的进程继续对外提供服务. 但是,由于实际网络情况的复杂性,控制中心无法收到心跳时不能准确地判断究竟是服务故障了还是服务进程和控制中心

  • 详解如何在C#/.NET Core中使用责任链模式

    最近我有一个朋友在研究经典的"Gang Of Four"设计模式.他经常来询问我在实际业务应用中使用了哪些设计模式.单例模式.工厂模式.中介者模式 - 都是我之前使用过,甚至写过相关文章的模式.但是有一种模式是我还没有写过文章,即责任链模式. 什么是责任链?# 责任链模式(之前我经常称之为命令链模式)是一种允许以使用分层方式"处理"对象的模式.在维基百科中的经典定义是 在面向对象设计中,责任链模式是一种由命令对象源及其一系列处理对象组成的设计模式.每个处理对象包含了

  • 详解如何在ASP.Net Core中实现健康检查

    健康检查 常用于判断一个应用程序能否对 request 请求进行响应,ASP.Net Core 2.2 中引入了 健康检查 中间件用于报告应用程序的健康状态. ASP.Net Core 中的 健康检查 落地做法是暴露一个可配置的 Http 端口,你可以使用 健康检查 去做一个最简单的活性检测,比如说:检查网络和系统的资源可用性,数据库资源是否可用,应用程序依赖的消息中间件或者 Azure cloud service 的可用性 等等,这篇文章我们就来讨论如何使用这个 健康检查中间件. 注册健康检查

  • 详解MySQL数据类型DECIMAL(N,M)中N和M分别表示的含义

    同事问MySQL数据类型DECIMAL(N,M)中N和M分别表示什么含义,M不用说,显然是小数点后的小数位数,但这个N究竟是小数点之前的最大位数,还是加上小数部分后的最大位数?这个还真记不清了.于是乎,创建测试表验证了一番,结果如下: 测试表,seller_cost字段定义为decimal(14,2) CREATE TABLE `test_decimal` ( `id` int(11) NOT NULL, `seller_cost` decimal(14,2) DEFAULT NULL ) EN

  • 详解C#编程获取资源文件中图片的方法

    详解C#编程获取资源文件中图片的方法 本文主要介绍C#编程获取资源文件中图片的方法,涉及C#针对项目中资源文件操作的相关技巧,以供借鉴参考.具体内容如下: 例子: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Drawing; namespace CL { public class RES { /

  • 详解三种方式在React中解决绑定this的作用域问题并传参

    在React中时常会遇到this指向的作用域问题 从而导致undefined报错 先来个Demo: 功能很简单 点击按钮改变文字 import React from 'react'; export default class BindWithThis extends React.Component { constructor(props) { super(props); this.state = { msg:"BindWithThis" } } render() { return &l

  • 详解Java8合并两个Map中元素的正确姿势

     1. 介绍 本入门教程将介绍Java8中如何合并两个map. 更具体说来,我们将研究不同的合并方案,包括Map含有重复元素的情况. 2. 初始化 我们定义两个map实例 private static Map<String, Employee> map1 = new HashMap<>(); private static Map<String, Employee> map2 = new HashMap<>(); Employee类 public class

  • 详解如何在VS2019和VScode中配置C++调用python接口

    why 看到这个标题有些人说了,为什么好好的C++你非要调用python?人家明明是两种语言呀! 但是在实际应用中,有时候会用到C/C++调用python来更简单地去完成一些功能,不然人家python为什么有一个文件夹叫include,里边全是.h文件呢? VScode中配置环境 首先是在VScode中为C++调用python接口配置环境,这里假设你已经配置好了c++编程环境! 1.配置step1 用快捷键Ctrl+Shift+X打开Extensions 商店,输入python,install:

  • 详解template标签用法(含vue中的用法总结)

    一.html5中的template标签 html中的template标签中的内容在页面中不会显示.但是在后台查看页面DOM结构存在template标签.这是因为template标签天生不可见,它设置了display:none;属性. <!--当前页面只显示"我是自定义表现abc"这个内容,不显示"我是template",这是因为template标签天生不可见--> <template><div>我是template</div

  • 详解如何在ASP.NET Core中编写高效的控制器

    通过遵循最佳实践,可以编写更好的控制器.所谓的"瘦"控制器(指代码更少.职责更少的控制器)更容易阅读和维护.而且,一旦你的控制器很瘦,可能就不需要对它们进行太多测试了.相反,你可以专注于测试业务逻辑和数据访问代码.瘦控制器的另一个优点是,它更容易维护控制器的多个版本. 这篇文章讨论了使控制器变胖的坏习惯,然后探索了使控制器变瘦和易于管理的方法.我列出编写控制器的最佳实践可能并不全面,但我已经讨论了最重要的一些,并在适当的情况下提供了相关的源代码.在接下来的几节中,我们将研究什么是胖控制

随机推荐