一、如何实现可用性战术
恢复和修复是可用性的重要方面,为了阻止错误发展成故障,至少能够把错误限制在一定的范围内,从而使修复成为可能。维持可用性的所有方法包括某种类型的冗余,用来检测故障的某种类型的健康监视,以及当检测到故障时某种类型的恢复。有些情况下,监视或恢复是自动进行的,有时需要手动。
我们事项考虑错误检测,然后分析错误恢复,最后讨论错误预防。
1> 错误检测
用于识别错误的3个战术是命令/响应、心跳和异常
⑴命令/响应。一个组件发出一个命令,并希望在预定义的时间内收到一个来自审查组件的响应。可以把该战术用在共同负责某项任务的一组组件内。客户机也可以使用这种战术,以确保服务器对象和到服务器的通信路径在期望的性能边界内操作。可以用一种层级形式组织“命令/响应”错误探测器,其中最底层的探测器对与其共享一个处理器的软件进程发出命令,较高层的错误探测器对较低层的探测器发出命令。与所有进程发出命令的远程错误探测器相比,这种战术所使用的通信带宽更少。
⑵心跳。一个组件定期发出一个心跳消息,另一个组件接收听该信息。如果心跳失败,则假定最初的组件失败,并通知错误纠正组件。心跳还可以传递数据。例如,自动柜员机定期向服务器发送一次交易日志。该消息不仅起到心跳的作用,而且传送了要处理的数据。
⑶异常。识别错误的一个方法就是遇到了异常。
命令/响应和心跳战术在不同的进程中操作,异常战术在一个进程中操作。异常处理程序通常将错误在语义上转换为可以被处理的形式。
2> 错误恢复
错误恢复由准备恢复和修复系统两部分组成。
⑴表决。运行在冗余处理器上的每个进程都具有相同的输入,它们计算发送给表决者的一个简单的输出值。如果表决者检测到单处理器的异常行为,那么就中止这一行为。表决算法可以是“多数规则”或“首选组件“或其他算法。该方法用于纠正算法的错误操作或者处理器的故障,通常用在控制系统。每个冗余组件的软件可以由不同的小组开发,并且在不同平台上执行。稍微好一点情况是在不同平台上开发一个软件组件,但是这样的开发和维护费用非常昂贵。
⑵主动冗余(热重启)。所有的冗余组件都以并行的方式对事件做出响应。因此他们都处在相同的状态。仅使用一个组件的响应,丢弃其他组件的响应。错误发生时,使用该战术的系统停机时间通常是几毫秒,因为备份是最新的,所以恢复所需要的时间就是切换时间。
⑶被动冗余(暖重启/双冗余/三冗余)
一个组件(主要的)对事件做出响应,并通知其他组件(备用的)必须进行状态更新。当错误发生时,在继续提供服务前,系统必须首先确保备用状态是最新的。该方法也用在控制系统中,通常情况是在输入信息通过通信通道或传感器到来时,如果出现故障必须从主组件切换到备用组件时使用。
⑷备件
备用件是计算平台配置用于更换各种不同的故障组件。出现故障时,必须将其重新启动为适当的软件配置,并对其状态进行初始化。定期设置持久设备的系统状态的检查点,并记录持久设备的所有状态变化能够使备件设置为适当的状态。这通常用作备用客户机工作站,出现故障时,用户可以离开。该战术的停机时间通常是几分钟。
⑸Shadow操作。以前出现故障的组件可以在短时间内以“shadow模式”运行,以确保在恢复该组件前,模仿工作组件行为。
⑹状态再同步。主动和被动冗余战术要求恢复的组件在重新提供服务前更新其状态。更新的方法取决于可以承受的停机时间、更新的规模以及更新所要求的消息的数量。
⑺检查点/回滚。检查点就是记录所创建的一致状态,或者是定期进行,或者是对具体事件做出响应。有时系统会以一种不同寻常的方式出现故障,可检测到其状态不一致。在这种情况下,应该使用上一个一致状态检查点和拍了快照后所发生的事务日志来恢复系统。
3> 错误预防
⑴从服务中删除。该战术从操作中删除了系统的一个组件,以执行某些活动来防止预期发生的故障。一个示例就是重新启动组件,以防止内存泄露导致故障的发生。如果从服务中删除是自动的,则可以设计架构策略来支持它。如果是人工进行的,则必须对系统进行设计以对其提供支持。
⑵事务。事务就是绑定几个有序的步骤,以能够立刻撤销整个绑定。如果进程中的一个步骤失败的话,可以使用事务来防止任何数据受到影响,还可以使用事务来防止访问相同数据的几个同时线程之间发生冲突。
⑶进程监视器。一旦检测到进程中存在着错误,监视进程就可以删除非执行进行,并为该进程创建一个新的实例,就像在备件战术中一样,初始化为某个适当的状态。
可用性指标是网站架构设计的重要指标,通常是用多少个9来衡量网站的可用性
网站不可用时间(故障时间)=故障修复时间点-故障发现(报告)时间点
网站年度可用性指标=(1-网站不可用时间/年度总时间)*100%
造成系统不可用的因素有很多,可以是服务器宕机,磁盘损坏等,也可以是代码层出现问题,当服务器宕机时,会通过心跳检测机制发现该服务器失去响应,就会从服务器列表中删除,而将请求发送到其他的服务器上
二、如何设计编码实现可用性战术
在代码层面上:
1.经典型的分层模型MVC,即应用层、服务层、数据层。应用层主要负责具体业务逻辑处理;服务层负责提供可复用的服务;数据层负责数据的存储与访问。在代码中使用不同的类完成不同的功能,避免由于某一功能出现故障导致无法正常运行,当发生错误时也可以快速锁定并修正错误
2.可以进行数据备份和失效转移机制,定期的将数据复制到某种存储介质上并物理存档保管;当一个数据副本不能访问时,就可以快速切换到其他的数据副本
java将所有的非正常情况分为两种:Error(错误)和Exception(异常),错误是无法恢复或不可能捕获的,而我们能处理的是Exception类下的错误。Exception则分为两大类,RuntimeException(运行时异常)和其他异常(Checked异常),其他异常(Checked异常)是各种形式的编译错误,是我们必须显示处理才可以通过变异的;而运行时错误顾名思义就是程序已经通过了编译,在运行时出现的错误,若是对这些异常置之不理会导致程序停止运行、占用资源无法释放甚至导致系统崩溃。
3.java异常处理机制及实现方法
(1)主要依赖于try、catch、finally、throw和throws这五个关键字。(throw和throws本篇不涉及)
(2)try…catch…finally处理机制:try关键字后跟一个花括号栝起的代码块(即使该代码块只有一行也不能省略花括号),简称try块。catch对应异常类型和代码块,用于表明更改catch块用于处理该种类型的异常。一个try块后可以跟多个catch块。在catch块后还可以跟一个finally块,finally块用于回收在try块里打开的资源。
public class DivTest { public static void main(String[] args) { try { int a = Integer.parseInt(args[0]); int b = Integer.parseInt(args[1]); int c = a/b; System.out.println("您输入的两个数相除的结果是:" + c); } catch(IndexOutOfBoundsException ie) { System.out.println("数组越界"); } catch(NumberFormatException ne) { System.out.println("数字格式异常"); } catch(ArithmeticException ae) { System.out.println("算术异常"); } catch(Exception e) { System.out.println("未知异常"); } } }
在使用try…catch语句块时需要知道或注意以下几点:
1) 处理过程:代码在执行的时候,进入try块,若是在try块中出现了异常,系统会自动生成一个一场对象,该对象被提交给java运行时环境,这就是异常的抛出;在java运行时环境收到异常对象时则把该对象交给catch块处理,这个过程叫做异常的捕获;若找到相应的catch块就执行catch块中的代码,若没有找到,则运行时环境终止,程序也退出。
2) 执行一次try块只执行一个catch块
3) 有多个catch块并有继承关系的情况下必须先写子类后写父类(即先捕获小异常再捕获大异常),若写反在编译时就会报错
4) Java7提供的多异常捕获:在Java7之前,每一个catch块只能捕获一种异常,但从java7开始,一个catch块可以捕获多种类型的异常。在使用多异常捕获应注意两点:
(1) 多种异常之间用竖线( | )隔开
(2) 多种异常对象被final隐式修饰,因此程序不能对其重新赋
(3)使用finally回收资源:有些时候我们在try块中打开了一些物理资源(例如数据库链接、网络连接和磁盘文件等),这些资源都应进行显示回收。有人说java不是有垃圾回收机制吗?java的垃圾回收机制是自动回收堆内存中对象所占用的内存,而物理资源是不会自动回收的。
1) 执行过程以及引入finally的原因:finally最后执行并且最后执行,物理资源回收放在finally块中的原因就是finally块一定会被执行。相反,若是放在try块中,在执行之前就出现异常则跳转至catch块中,则回收资源的代码不会被执行;同样的,若是放在catch块中,若不发生异常,那么catch块就不会被执行
2) 若是在catch快中有return语句,则先执行完finally中的程序后再回到catch块中并执行return语句
3) 若是在finally中有return语句,那么try块和catch块中的return语句都会失效,不会被执行
4) 若在catch块中强制退出虚拟机,如使用System.exit(1)语句,则会直接退出程序,finally也不会得到执行
public
class
DivTest {
public
static
void
main(String[] args) {
try
{
int
a = Integer.parseInt(args[
0
]);
int
b = Integer.parseInt(args[
1
]);
int
c = a/b;
System.out.println(
"您输入的两个数相除的结果是:"
+ c);
}
catch
(IndexOutOfBoundsException ie) {
System.out.println(
"数组越界"
);
}
catch
(NumberFormatException ne) {
System.out.println(
"数字格式异常"
);
}
catch
(ArithmeticException ae) {
System.out.println(
"算术异常"
);
}
catch
(Exception e) {
System.out.println(
"未知异常"
);
}
}
}
原文:https://www.cnblogs.com/zhang12345/p/12396116.html