三大点:
一、错误检测
(1)命令/响应
个人理解响应就像是一个暗号,我发过去上半部分,然后你能返回对应的下半部分,那么这个过程就没毛病
例1:
1 import requests 2 r=requests.get("http://www.baidu.com") 3 r.status_code
其中 r.status_code 就是获取向网页请求返回的结果
如果结果是200则成功,如果是其他的则失败,这一例是对 是否成功获取网页的一个响应
例2:
1 #include<stdio.h> 2 int main() 3 { 4 char ch; 5 int i = system("ping 192.168.1.1"); 6 printf("%d ",i); 7 system("pause"); 8 return 0; 9 }
这里的 system("ping ")即为测试ping是否通,
若返回的 i=1,则表示“未ping通”;若 i=0,则表示 “ping通”
(2)心跳(dead man 计时器)
1 Page({ 2 /** 3 * 页面的初始数据 4 */ 5 data: { 6 timer: '',//定时器名字 7 countDownNum: '60'//倒计时初始值 8 }, 9 10 onShow: function(){ 11 //什么时候触发倒计时,就在什么地方调用这个函数 12 this.countDown(); 13 }, 14 15 countDown: function () { 16 let that = this; 17 let countDownNum = that.data.countDownNum;//获取倒计时初始值 18 //如果将定时器设置在外面,那么用户就看不到countDownNum的数值动态变化,所以要把定时器存进data里面 19 that.setData({ 20 timer: setInterval(function () {//这里把setInterval赋值给变量名为timer的变量 21 //每隔一秒countDownNum就减一,实现同步 22 countDownNum--; 23 //然后把countDownNum存进data,好让用户知道时间在倒计着 24 that.setData({ 25 countDownNum: countDownNum 26 }) 27 //在倒计时还未到0时,这中间可以做其他的事情,按项目需求来 28 if (countDownNum == 0) { 29 //这里特别要注意,计时器是始终一直在走的,如果你的时间为0,那么就要关掉定时器!不然相当耗性能 30 //因为timer是存在data里面的,所以在关掉时,也要在data里取出后再关闭 31 clearInterval(that.data.timer); 32 //关闭定时器之后,可作其他处理codes go here 33 } 34 }, 1000) 35 }) 36 } 37 })
1 <view class='countDown'>倒计时:<text style='color:red'>{{countDownNum}}</text>s</view>
这个是微信小程序上实现的一个简单计时器的代码,目的就是计时用,而将其用到实例中可以控制页面的跳转、提示信息等
如下,这是一个答题的js,里面的内容是300s内答完页面的题目,如果未在300内答完则后退到上一页,若答完,则评判分数,答题入库。
1 // pages/exam/exam.js 2 import '../../utils/util.js'; //导入日期格式化模块 3 //获取应用实例 4 const app = getApp() 5 6 Page({ 7 8 /** 9 * 页面的初始数据 10 */ 11 data: { 12 questions: [{}], 13 presentQ: 0, 14 uAnswer: [], 15 content: "", 16 tag: false, 17 color: "#ff6f10", 18 time: 300, 19 time_control: 1, 20 }, 21 22 /** 23 * 生命周期函数--监听页面加载 24 */ 25 onLoad: function (options) { 26 27 let that = this 28 let chapter = options.chapter 29 let course = options.course 30 // let chapter = '第一章' 31 // let course = 'C语言' 32 wx.request({ 33 url: app.globalData.host + '/exam/wx_question.php?chapter=' + chapter + '&course=' + course, 34 dataType: 'json', 35 success(res) { 36 console.log(res.data) 37 that.setData({ questions: res.data }) //加载下一题 38 } 39 }) 40 }, 41 42 /** 43 * 生命周期函数--监听页面初次渲染完成 44 */ 45 onReady: function () { 46 47 }, 48 49 /** 50 * 生命周期函数--监听页面显示 51 */ 52 onShow: function () { 53 54 }, 55 56 /** 57 * 生命周期函数--监听页面隐藏 58 */ 59 onHide: function () { 60 61 }, 62 63 /** 64 * 生命周期函数--监听页面卸载 65 */ 66 onUnload: function () { 67 let that = this; 68 that.setData({ 69 time: that.data.time = 1, 70 time_control: that.data.time_control = 0 71 }) 72 }, 73 74 /** 75 * 页面相关事件处理函数--监听用户下拉动作 76 */ 77 onPullDownRefresh: function () { 78 79 }, 80 81 /** 82 * 页面上拉触底事件的处理函数 83 */ 84 onReachBottom: function () { 85 86 }, 87 88 /** 89 * 用户点击右上角分享 90 */ 91 onShareAppMessage: function () { 92 93 }, 94 selectAnswer: function (e) { 95 //将uAnswer(用户答案)添加进question 96 var p = 'questions[' + this.data.presentQ + '].uAnswer' 97 this.setData({ [p]: e.detail.value }) 98 }, 99 //填空 100 fillblank: function (res) { 101 var p = 'questions[' + this.data.presentQ + '].uAnswer' 102 this.setData({ [p]: res.detail.value }) 103 }, 104 105 106 //下一题 107 next: function () { 108 //清空输入框 109 this.setData({ content: "" }) 110 let that = this 111 //下一题 112 113 var p = this.data.presentQ 114 if (this.data.questions[p].uAnswer) { 115 this.setData({ presentQ: p + 1 }) //presentQ++ 116 this.setData({ tag: false }) 117 that.setData({}) 118 } 119 console.log(this.data.presentQ) 120 121 //如果是最后一题 122 123 if (this.data.presentQ >= this.data.questions.length) { 124 //显示答题结果 125 var score = 0 126 for (var i = 0; i < this.data.questions.length; i++) { 127 if (this.data.questions[i].ANSWER == this.data.questions[i].uAnswer) score += parseInt(this.data.questions[i].SCORE) 128 129 } 130 this.setData({ score: score }) 131 } 132 133 134 }, 135 136 137 138 setTime() { 139 let that = this 140 let myTime = setInterval(function () { 141 that.setData({ 142 time: that.data.time - 1 143 }) 144 console.log(that.data.time) 145 if (that.data.time == 0 && that.data.time_control != 0) { 146 clearInterval(myTime) 147 wx.navigateBack({}) 148 } 149 else if (that.data.time_control == 0) { 150 clearInterval(myTime) 151 } 152 }, 1000) 153 }, 154 155 156 insertScore: function (question) { 157 let correct = 0 158 if (question.ANSWER == question.uAnswer) { 159 correct = 1 160 } else { 161 correct = 0 162 } 163 wx.request({ 164 url: app.globalData.host + '/exam/wx_insertScore.php?sid=' + app.globalData.userInfo.ID + '&UID=' + question.UID + '&Chapter=' + question.CHAPTER + '&Course=' + question.COURSE + '&Correct=' + correct, 165 success(res) { 166 }, 167 168 }) 169 }, 170 171 redo: function () { 172 //this.setData({presentQ:0}) 173 wx.navigateBack({}) 174 } 175 })
(3)异常
我所理解的异常就是 检测程序运行中各种的异常,例如数据异常,返回异常等。
下面是一个简单的方法体用“throw”抛出异常
1 public void dev(){ 2 int a = 5 ; 3 int b = 1 ; 4 if(b==0){ 5 throw new ArithmeticException() ; 6 }else{ 7 System.out.println(a/b); 8 } 9 }
这里可以很简单的看出若b=0则抛出异常,反之输出a/b,即为5
二、错误恢复
(1)表决
个人理解是:程序运行有很多算法,里面有一类称作表决算法,实行的是裁决功能,他可以监视其他的算法/处理器是否运行错误,若错,则纠正
虽然我没用过这么高级的东西,但是我通过百度查到了一个叫“Simplex”单纯形法算法,它是一个表决算法,解决的是线性规划问题
简介:一般线性规划问题中当线性方程组的变量数大于方程个数,这时会有不定数量的解,而单纯形法是求解线性规划问题的通用方法。具体步骤是,从线性方程组找出一个个的单纯形,每一个单纯形可以求得一组解,然后再判断该解使目标函数值是增大还是变小了,决定下一步选择的单纯形。通过优化迭代,直到目标函数实现最大或最小值
而至于具体怎么用,移步
https://baike.baidu.com/item/%E5%8D%95%E7%BA%AF%E5%BD%A2%E6%B3%95/8580570?fr=aladdin 百度百科
这是我看到的一个关于代码层次的Simplex算法分析很好的博客,也对我学习Simplex算法代码实现有很大帮助
https://www.cnblogs.com/Kenneth-Wong/p/8451343.html Kenneth-Wong 的博客园
(2)主动冗余(热重启)
冗余的含义:提前对关键的地方做备份,做应急处理,例如网络冗余、服务器冗余、磁盘冗余、数据冗余等
主动和被动的区别就是:
主动:所有的冗余组件(无论主,还是备份)同时在运行,同步更新,一旦主发生故障,备份立即顶替
被动:一个组件(主)在运行,备份进行定时的更新,一旦主发生故障,备份顶替,但不是故障前主最新的状态
主动的例子:
地铁控制系统中,两个与硬件直接通讯的主备RTU(REMOTE TERMINAL UNIT)之间就采用自控方式。当两个RTU之间不能通讯时,或者当前系统中仅有一个RTU时,它们会将自己设定为主节点,提供所有服务。
(3)被动冗余(暖重启/双冗余/三冗余)
检查点/回滚:
这个就像电脑系统的备份精灵,在某一个节点备份当前的状态,若主发生错误,则回到之前备份节点的状态
一种是IDEA的回滚例如:来源:https://www.cnblogs.com/smile-fanyin/p/11007696.html
在 IDEA 编辑器里面,右键操作代码所在文件夹,选择 git ,点击 show history 。如下图:
历史记录里面,根据自己提交的时间,和右侧显示的 commit 的文件,判断要回滚到哪个版本。
第二种代码回滚的话,百度了下,有:git代码回滚
其一是本地文件回滚,其二是远程文件回滚
本地很简单,三步骤(PS:素材来自https://blog.csdn.net/leo_csdn_/article/details/84838514):
1、查看log
输入git log 查看commit记录
[xxxxxxx]$ git log
2、寻找到想要回滚的commit
3、确定需要回滚到的commitId,输入 git reset --hard{commitId},实现本地文件回滚
[xxxxxxx]$ git push -f
远程回滚,抱歉我有点看不懂,但是不妨碍看是不:
请移步 https://www.cnblogs.com/lwh-note/p/9639835.html
这个写的不错
三、错误预防
(1)从服务中删除
我的理解就是在使用完某个连接后及时的关闭连接(也可理解为删除这个连接),不耽误下一次的使用
目前咱应用很多的就是DBUtil,mysql数据库的连接
1 package com.util; 2 3 import java.sql.*; 4 5 public class DBUtil { 6 private static String driver; 7 private static String url; 8 private static String username; 9 private static String password; 10 11 static { 12 driver = "com.mysql.jdbc.Driver"; 13 url = "jdbc:mysql://127.0.0.1:3306/wang?useSSL=false&characterEncoding=utf8"; 14 username = "root"; 15 password = "123456"; 16 try { 17 Class.forName(driver); 18 } catch (ClassNotFoundException e) { 19 e.printStackTrace(); 20 } 21 } 22 23 public static Connection getConn() throws SQLException { 24 return DriverManager.getConnection(url, username, password); 25 } 26 27 public static void close(Connection conn, Statement st, ResultSet rs) { 28 try { 29 if (conn != null) { 30 conn.close(); 31 } 32 if (st != null) { 33 st.close(); 34 } 35 if (rs != null) { 36 rs.close(); 37 } 38 } catch (SQLException e) { 39 e.printStackTrace(); 40 } 41 } 42 }
这个里面有正常运行的开启连接,也有close关闭连接的方法,
在其他的java文件中,每一次开启mysql的连接,在使用完之后都会有个close的使用,目的就是错误预防
(2)事务
这个我的理解是对 “并发进程” 排队,按照规则有序得进行
想到的例子就是数据库课程里讲的事务锁(X锁,S锁,IS锁,IX锁等)
共享锁(S锁、多锁):事务获得元组的共享锁后,其它事务也只能获得该元组的共享锁,而不能获得排它锁;获得共享锁的事务可以对元组进行读操作。并发性:良好
排它锁(X锁、写锁):事务获得元组的排它锁后,其它事务既不能获得该元组的共享锁,也不能获得排它锁;获得排它锁的事务可以对元组进行写操作。并发性:差
意向共享锁(IS锁):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
意向排它锁(IX锁):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁。
S、X、IS、IX锁的兼容性如下图(PS:源自https://www.cnblogs.com/maying3010/p/8804941.html):
何为死锁?
死锁是指两个或两个以上的事务在执行过程中,因争夺锁资源而造成的一种互相等待.
等待图(wait-for-graph)可以用来检测死锁,通过深度优先算法实现,只要图中存在循环的回路.那么存在死锁.
解决死锁时会回滚当中undo日志最小的一个事务,回滚又出现了,没错,这里当然要回滚了,因为错误了
(3)进程监视器
一旦检测到进程中存在错误,监视进程就可以删除非执行进程,并为该进程创建一个新的实例,就像在备件战术中一样,初始化为某个适当的状态。
这个感觉和被动冗余,回滚一样,回到之前的某个状态