整套大数据学习资料(视频+笔记)百度网盘无门槛下载:http://www.edu360.cn/news/content?id=3377

14.4.2 可复原的ZooKeeper应用

hadoop 花牛 12℃ 0评论

 

关于分布式计算的第一个误区是“网络是可靠的”。按照他们的观点,程序总是有一个可靠的网络,因此当程序运行在真正的网络中时,往往会出现各种各样的故障。让我们看看各种可能的故障模式,以及能够解决故障的措施,使我们的程序在面对故障时能够及时复原。

在Java API中的每一个ZooKeeper操作都在其throws子句中声明了两种类 型的异常,分别是 InterruptedException 和 KeeperException。

1. InterruptedException 异常

如果操作被中断,则会有一个InterruptedException异常被抛出。在 Java语言中有一个取消阻塞方法的标准机制,即针对存在阻塞方法的线程调用interrupts()。一个成功的取消操作将产生一个InterruptedException 异常。ZooKeeper也遵循这一机制,因此你可以使用这种方法来取消一个 ZooKeeper操作。使用了ZooKeeper的类或库通常会传播 InterruptedException异常,使客户端能够取消它们的操作。

InterruptedException异常并不意味着有故障,而是表明相应的操作已经被取消,所以在配置服务的示例中,可以通过传播异常来中止应用程序的运行。

2. KeeperException 异常

如果ZooKeeper服务器发出一个错误信号或与服务器存在通信问题,抛出 的则是KeeperException异常。针对不同的错误情况,KeeperException 异常存在不同的子类。例如/KeeperException.NoNodeException是 KeeperException的一个子类,如果你试图针对一个不存在的znode执行操作,抛出的则是该异常。

每一个KeeperException异常的子类都对应一个关于错误类型信息的代 码。例如,KeeperException‘NoNoc/eException 异常的代码是 KeeperException.

有两种方法被用来处理KeerException异常:一种是捕捉KeeperException异常并且通过检测它的代码来决定采取何种补救措施;另一种是捕捉等价KeeperException子类并且在每段捕捉代码中执行相应的操作。

KeeperException异常分为三大类。

•状态异常当一个操作因不能被应用于znode树而导致失败时,就 会出现状态异常。状态异常产生的原因通常是在同-时间有另外一 个进程正在修改znode。例如,如果一个znode先被另外一个进程 更新了,根据版本号执行setData操作的进程就会失败,并收到一个KeeperException.BadVersionException异常,这是因为版本号不匹配。程序员通常都知道这种冲突总是存在的,也都会写代码来进行处理。

               —些状态异常会指出程序中的错误,例如KeeperException. NoChildrenForEphemeralsException 异常,                 试图在短暂znode下创建子节点时就会抛出该异常。

  

.可恢复的异常可恢复的异常是指那些应用程序能够在同一个 ZooKeeper会话中恢复的异常。一个可恢复的异常是通过KeeperException.ConnectionLossException 来表示的,它意 味着已经丢失了与ZooKeeper的连接。ZooKeeper会尝试重新连 接,并且在大多数情况下重新连接会成功,并确保会话是完整的。

但是 ZooKeeper 不能判断与 KeeperException.Connection LossExction异常相关的操作是否成功执行。这种情况就是部分 失败的一个例子(在本章开始时提到的)。这时程序员有责任来解决 这种不确定性,并且根据应用的情况来采取适当的操作。

在这一点上,就需要对“幂等”idempotent)操作和“非幂等” (Nonidempotent)操作进行区分。幂等操作是指那些一次或多次执行都会产生相同结果的操作,例如读请求或无条件执行的setData 作。对于幂等操作,只需要简单地进行重试即可。

对于非幂等操作,就不能盲目地进行重试,因为它们多次执行 的结果与一次执行是完全不同的。程序可以通过在znode的路径和 它的数据中编码信息来检测是否非幂等操作的更新已经完成。在 14.4.3节对可恢复的异常的讨论中,我们将通过实现一个锁服务来讨论如何处理失畋的非幂等操作。

不可恢复的异常在某些情况下,ZooKeeper会话会失效——也许因为超时或因为会话被关闭(两种情况下都会收到KeeperException.SessionExpiredException 异常),或因为身 份验证失败(KeeperException.AuthFailedException 异常)。无论上述哪种情况,所有与会话相关联的短暂znode都将丢失,因此应用程序需要在重新连接到ZooKeeper之前重建它的状态。


3. 可靠的配置服务

让我们回到ActiveKeyValueStorewrite()方法,它由一个exists操作紧跟着一个create操作或setData操作组成:

public void write(String path, String value) throws InterruptedException, 
KeeperException {
    Stat stat = zk.existsCpath, false); 
    if (stat == null) {
        zk.create(path, value.getBytes(CHARSET), Ids.OPEN_ACL_UNSAFEj CreateMode.PERSISTENT);
        }else {
        zk.setData(path, value.getBytes(CHARSET), -1);
        }
}

作为一个整体,write()方法是一个幂等操作,所以我们可以对它进行无条件重试。这里有一个write()方法修改后的版本,能够循环执行重试。

其中设置了重试的最大次数MAX_RETRIES和两次重试之间的时间间隔 RETRY_PERIOD_SECONDS

public void write(String path, String value) throws InterruptedException, 
KeeperException { 
int retries = 0; 
while (true) { 
    try {
        Stat stat = zk.exists(path, false); 
            if (stat == null) {
            zk.create(path, value.getBytes(CHARSET), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            } else {
            zk.setData(path, value.getBytes(CHARSET), stat.getVersion()); 
            }
            return;
        } catch (KeeperException.SessionExpiredException e) { 
        throw e;
        } catch (KeeperException e) { 
        if (retries++ == MAX_RETRIES) {
        throw  e;
        }
        // sleep then retry
        TimeUnit .SECONDS. sleep(RETRY__PERIOD_SECONDS);
        }
    }
}

这段代码没有在KeeperException.SessionExpiredException异常处进行重试,因为当一个会话过期时,ZooKeeper对象会进入CLOSED状态,此状态下它不能再进行重新连接(参见图M-3)。我们只是简单地将这个异常 重新抛出并且让调用者创建一个新的ZooKeeper实例,以重试整个write()方法。一个简单的创建新实例如方法是创建一个新的于恢复过期会话:

public static void main(String[] args) throws Exception { 
  while (true) { 
    try {
      ResilientConfigUpdater configUpdater = new ResilientConfigUpdater(args[0]); 
      configUpdater.run();
      } catch (KeeperException.SessionExpiredException e) { 
            // start a new session 
        } catch (KeeperException e) {
            // already retried, so exit 
            e.printStackTrace(); 
            break;
        }
    }
}

处理会话过期的另一种方式是在观察中(在这个例子中应该是 ConnectionWatcher)监测类型为 Expired 的 KeeperState,然后在监测的时候创建一个新的连接。即使我们收到KeeperExceptionSessionExpiredException异常,由于连接最终是能够重新建立的,我们 就可以使用这种方式在write()方法内不断进行重试。不管我们采用何种 机制从过期会话中恢复,重要的是需要对这种不同干连接丢失的故障类型 进行不同的处理。

实际上,这里忽略了另一种故障模式。当ZooKeeper对象被创建时,它会尝试连接一个ZooKeeper服务器。如果连接失败或超时, 那么它会尝试连接集合体中的另一台服务器。如果在尝试集合体中所有服务器之后仍然无法建立连接,它会抛出一个IOException异 常。由于所有ZooKeeper服务器都不可用的可能性很小,所以一些应用程序选择循环重试操作,直到ZooKeeper服务可用为止。

这仅仅是一种重试处理策略,还有许多其他策略,例如使用“指数退回” (exponential backoff),每次将重试的间隔乘以一个常数。Hadoop内核中的 org.apache.hadoop.io.retry包是一组工具,能够以可重用的方式将重试逻辑加入用户代码,因此对于构建ZooKeeper应用是非常有用的。

转载请注明:全栈大数据 » 14.4.2 可复原的ZooKeeper应用

喜欢 (0)or分享 (0)
发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址