[教程] 某 php 程序的 bug 定位及修复

前言:

已知程序采用 MVC 架构, 分别为 controllers/views/models 文件夹.

bug 描述及复现:

comment
无论怎么刷新网站的浏览次数在访问后并不进行自增. 数据库中对应字段也没反应. 手动修改字段为 10. 刷新页面
comment
可以看到浏览次数的确改变了. bug 复现完毕.

解决方案分析:

通过复现 bug, 初步认定程序在执行过程中对浏览次数的操作并没有成功执行.

分析:

造成自增操作不成功的因素可能有: 数据库连接失败/SQL语句编写错误/流程控制判定异常/没有编写对应代码.

结论:

首先排除了数据库连接失败, 因为可以从数据库中拿到数据. 那么接下来从源码分析, 判断余下三个可能.

bug 定位及修复:

comment
程序采用 MVC 架构, 分析 URL, 其中 c 指代 controller, a 指代 action. 定位源文件 controllers/contentController.php
comment
为了确定这个 showAction 就是对应的控制器, 添加调试语句打印页面信息.
comment
刷新页面查看
comment
调试语句成功执行, 并打印出了我们需要的信息, 其中 hits 就是浏览次数.
接着分析源码. 一般对浏览次数统计的思路是: 拿到 paper id, 获取 id 对应的 content, 接着对 hits 进行 +1 操作. 所以先从 id 入手.
comment
查找 id 引用, 结合注释及代码分析, 唯一可能会对 hits 进行 +1 操作的可能是 $data = $this->content-find($id); 语句.
按 F12 跟进(此功能为 SBT3 自带). 只要代码风格不是很差劲第一个跳过去的就是目标代码, 如果不是, 多分析几个就是了.
comment
很幸运, 第一此跳过来就找到了. 但是很不幸, 这不是我们要的, 结合注释和源码, 这只是一个 SELECT 操作.
通过 ID 分析这条路不通, 接着走, 分析 $data, 因为除了 id 之外, 唯一会跟 hits 打交道的就是这个 $data 了, 查找引用.
comment
引用很多. 接着分析, 虽然引用很多, 但是可疑的地方只有个别几处, 分别是 87/121/122 这三行.
87 行对 $data 进行了赋值操作, 121 及 122 进行了内容替换操作. 接着 F12 跟进对应的函数. 
comment
comment
并没有我们要的内容. 接着分析, 可能是思路不多, 也可能是跟进的深度不够. 换个思路, 直接搜对 hits 操作的函数. 然后逆推调用的源码.
comment
在网站根目录下执行 cmd 指令, 分析反馈的数据. 排除额外的如 CSS 代码, 注释. 拿到以下内容.
comment
分析得到的数据. APIController.php 下的 hitsAction 嫌疑很大. Comoon.php 和 Image_lib.php 则是来打酱油的.
排除掉之后剩下 ApiController.php / sphinxapi.php / ContentModel.php, 我们知道 sphinxapi 这玩意是个搜索引擎, 所以也可以排除了.
那么就剩下了 ApiController.php 和 ContentModel.php. 而 ContentModel 的语句相似性太高, 而且只有一句. 所以可以排除.
最后剩下了嫌疑最高的 APIController.php, 我们也看到了类似于 update 的语句. 跟进.
comment
看来这就是我们要找的函数了. 接着继续分析, 逆推调用流程. 通过源代码得知.
这个函数是不能通过内部调用进行 update 的, 需要通过 GET 方式传递 id 值. 然后对 hits 进行 update, 测试一下.
comment
返回正常, 接着进数据库查看.
comment
对应条目也变成了 11. 进新闻浏览页刷新.
comment
浏览次数也成功更新, Bug 定位到此完成.

结论:

页面加载时没有调用对应的 hits 更新操作, 所以浏览次数并不会进行自增. 修复也很简单. 使用 AJAX 调用此接口即可.

[ISCC - 2017] 部分 WriteUP

2017年5月26日 排名今天出来了.
看了下, 全国赛排名为 142, 区赛排名 65. 这成绩着实有些丢人啊. orz
这是第二次参加 CTF 比赛, 上一次还是在 2014 年, 四叶草跟吹潮合作的那次.
之前一直瞧不起赛棍, 原因嘛..无非那几点, 这次比赛过后发现, 赛棍还是有赛棍的厉害之处.
不得不承认着实技不如人.
====================== 割以永治 ==============================
以下是战绩:
comment
因为学业的原因后期出的一道 WEB 和 BASIC 没有去打. 比赛只能当放松, 还是要以学业为重.

BASIC 01

加密表:
1:  < ZWAXJGDLUBVIQHKYPNTCRMOSFE <
2:  < KPBELNACZDTRXMJQOYHGVSFUWI <
3:  < BDMAIZVRNSJUWFHTEQGYXPLOCK <
4:  < RPLNDVHGFCUKTEBSXQYIZMJWAO <
5:  < IHFRLABEUOTSGJVDKCPMNZQWXY <
6:  < AMKGHIWPNYCJBFZDRUSLOQXVET <
7:  < GWTHSPYBXIZULVKMRAFDCEONJQ <
8:  < NOZUTWDCVRJLXKISEFAPMYGHBQ <
9:  < XPLTDSRFHENYVUBMCQWAOIKZGJ <
10: < UDNAJFBOWTGVRSCZQKELMXYIHP <
11:    < MNBVCXZQWERTPOIUYALSKDJFHG <
12:    < LVNCMXZPQOWEIURYTASBKJDFHG <
13:    < JZQAWSXCDERFVBGTYHNUMKILOP <

密钥为:2,3,7,5,13,12,9,1,8,10,4,11,6
密文为:NFQKSEVOQOFNP

刚开始看这玩意, 摸不着头脑, 在书安群里一位前辈指点了一下说这是 "杰弗逊轮盘" 密码.
在 WIKI 上看了下, 还行, 根据给出的 密钥 将加密表按密钥的顺序排列. 得到密钥表

['KPBELNACZDTRXMJQOYHGVSFUWI']
['BDMAIZVRNSJUWFHTEQGYXPLOCK']
['GWTHSPYBXIZULVKMRAFDCEONJQ']
['IHFRLABEUOTSGJVDKCPMNZQWXY']
['JZQAWSXCDERFVBGTYHNUMKILOP']
['LVNCMXZPQOWEIURYTASBKJDFHG']
['XPLTDSRFHENYVUBMCQWAOIKZGJ']
['ZWAXJGDLUBVIQHKYPNTCRMOSFE']
['NOZUTWDCVRJLXKISEFAPMYGHBQ']
['UDNAJFBOWTGVRSCZQKELMXYIHP']
['RPLNDVHGFCUKTEBSXQYIZMJWAO']
['MNBVCXZQWERTPOIUYALSKDJFHG']
['AMKGHIWPNYCJBFZDRUSLOQXVET']

接着使用 PY 的字典特性, 将密钥表每行的列进行互换, 对比给出的密文得到明文表, FLAG 我已经标出来了.

NACZDTRXMJQOYHGVS F UWIKPBEL
FHTEQGYXPLOCKBDMA I ZVRNSJUW
QGWTHSPYBXIZULVKM R AFDCEONJ
KCPMNZQWXYIHFRLAB E UOTSGJVD
SXCDERFVBGTYHNUMK I LOPJZQAW
EIURYTASBKJDFHGLV N CMXZPQOW
VUBMCQWAOIKZGJXPL T DSRFHENY
OSFEZWAXJGDLUBVIQ H KYPNTCRM
QNOZUTWDCVRJLXKIS E FAPMYGHB
OWTGVRSCZQKELMXYI H PUDNAJFB
FCUKTEBSXQYIZMJWA O RPLNDVHG
NBVCXZQWERTPOIUYA L SKDJFHGM
PNYCJBFZDRUSLOQXV E TAMKGHIW

FLAG -> FIREINTHEHOLE

BASE 02

# IDAT = "636A56355279427363446C4A49454A7154534230526D6843";
# IDAT = "56445A31614342354E326C4B4946467A5769426961453067";
IDAT = "636A56355279427363446C4A49454A7154534230526D6
  84356445A31614342354E326C4B4946467A5769426961453067"
open("./test.txt","wb").write(IDAT.decode("hex"));

题目给出的是两串 HEX 编码, 写到文件里打开, 得到两串 BASE64 过的密文

cjV5RyBscDlJIEJqTSB0RmhC
VDZ1aCB5N2lKIFFzWiBiaE0g

base64 decode 之后得到两串键盘密码

# s1 = "r5yG lp9I BGjM tFhB";
# s2 = "T6uh y7iJ Q1sZ bhM";

讲道理我起初以为栅栏, 瞎鸡儿去算, 卡了几天, 有个大佬说看看键盘.
以为是和上次的 Oops 的一样是字符, 在那里瞎几把画, 没看懂. 最后是一位朋友提点才看出来是圈, 不是画.
r5yG 圈出了 T 字符. 按理得到 TONGYUAN 字符.
FLAG -> TONGYUAN

BASE 03

comment
没什么好说的, 隐写术. binwalk 拿到第二个数据段, 提取保存为另外一张图

root@abs:/home/abs/root$ binwalk base.png

DECIMAL     HEX         DESCRIPTION
-----------------------------------------------------------------------------
0           0x0         PNG image, 438 x 435, 8-bit/color RGB, non-interlaced
181626      0x2C57A     PNG image, 860 x 189, 8-bit colormap, non-interlaced

comment
起初看不懂, 以为是啥, 各种看通道各种翻, 最后在贴吧翻到一个 CTF 各种加密姿势, 才发现这玩意是猪圈密码.
其实题目已经给出了提示, 共济会. 只是没注意.
FLAG -> goodluck

BASE 04

没什么好说的, 不是伪加密, 跑密码爆破.

#!/usr/bin/env python
import zipfile
import threading
def zipbp(zfile, pwd):
    try:
        zfile.extractall(pwd=pwd)
        print 'password found : %s' % pwd
        exit(0);
    except:
        return
def main():
    zfile = zipfile.ZipFile('test.zip')
    # pwdall = open('aa.txt')
    # for pwda in pwdall.readlines():
    for pwd in xrange(0,99999999):
        print "[+] => "+str(pwd);
        # pwd = pwd.strip('\n')
        pwd = str(pwd);
        try:
            zfile.extractall(pwd=pwd)
            print "[!!!!!!!!!!] Password Is Found: "+pwd;
            break;
        except:
            continue;
        # t = threading.Thread(target=zipbp, args=(zfile, pwd))
        # t.start()
        # t.join()
    print "[-] check Done!;";
if __name__ == '__main__':
    main()

密码 123456, 解压得到 flag.txt, 打开得到 flag
FLAG ->daczcasdqwdcsdzasd

BASE 06

这题的最后一步其实我没做. 有个之前交流过思路的基佬直接发给我了...
这题主要考对 RSA 算法以及签名的理解, 以下是我的解题思路.
这题网上有原题. 地址 -> https://github.com/RandomsCTF/write-ups/tree/master/Hack.lu%20CTF%202015/Creative%20Cheating%20%5Bcrypto%5D%20(150)

X老师怀疑一些调皮的学生在一次自动化计算机测试中作弊,他使用抓包工具捕获到了Alice和Bob的通信流量。
狡猾的Alice和Bob同学好像使用某些加密方式隐藏通信内容,使得X老师无法破解它,也许你有办法帮助X老师。
X老师知道Alice的RSA密钥为 (n, e) = (0x53a121a11e36d7a84dde3f5d73cf, 0x10001) (192.168.0.13)?,
Bob的RSA密钥为 (n, e) =(0x99122e61dc7bede74711185598c7, 0x10001) (192.168.0.37)
已知 N => 求 N 的模值 (http://factordb.com/);
Alice :
    0x53a121a11e36d7a84dde3f5d73cf  =>
      1696206139052948924304948333474767  =>
        38456719616722997 * 44106885765559411
Bob :
    0x99122e61dc7bede74711185598c7  =>
      3104649130901425335933838103517383  =>
        49662237675630289 * 62515288803124247
Alice
    bd = 1696206139052948841741342951192360
已知 p q e 求 d
    Alice :
      d => 37191940763524230367308693117833;
    Bob :
      d => 1427000713644866747260499795119265
Alice:
    n => 1696206139052948924304948333474767
    e => 65537
    d => 37191940763524230367308693117833
Bob:
    n = > 3104649130901425335933838103517383
    e => 65537
    d => 1427000713644866747260499795119265

以下是某基佬的解题代码

class RSAPerson(object):
    def __init__(self, e, p, q):
        self.n = p * q
        self.e = e
        self.p = p
        self.q = q
        self.d = long(gmpy2.invert(e, (p-1)*(q-1)))
        self.key = RSA.construct((long(self.n), long(self.e), self.d))
    def sign(self, message):
        return self.key.sign(message, '')
    def verify(self, message, signature):
        return self.key.publickey().verify(message, [signature])
    def encrypt(self, message):
        return self.key.publickey().encrypt(message)
    def decrypt(self, message):
        return self.key.decrypt(message)
alice = RSAPerson(
    0x10001,
    38456719616722997,
    44106885765559411
)
bob = RSAPerson(
    0x10001,
    49662237675630289,
    62515288803124247
)
packets = []
with open('test.txt') as lines:
    for line in lines:
        decoded = base64.b64decode(line)
        match = regex.match(decoded).groups()
        seq = int(match[0])
        signature = int(match[2], 16)
        data = int(match[1], 16)
        data = bob.decrypt(data)
        if alice.verify(data, signature):
            data = chr(data)
            packets.append((
                seq,
                data,
                signature
            ))
print ''.join([packet[1] for packet in sorted(packets)])

因为我本地装的是 py 2.7 不是 3 所以代码就不运行演示了.
以下为部分信号量数据, 从嗅探包中提取出来的.

U0VRID0gMTM7IERBVEEgPSAweDNiMDRiMjZhMGFkYWRhMmY2NzMyNmJiMGM1ZDZMOyBTSUcgPSAweDJlNWFiMjRmOWRjMjFkZjQwNmE4N2RlMGIzYjRMOw==
U0VRID0gMDsgREFUQSA9IDB4NzQ5MmY0ZWM5MDAxMjAyZGNiNTY5ZGY0NjhiNEw7IFNJRyA9IDB4YzkxMDc2NjZiMWNjMDQwYTRmYzJlODllM2U3TDs=

FLAG -> flag{n0th1ng_t0_533_h3r3_m0v3_0n}

BASE 07

comment
一张二维码, 扫了扫, 拿到提示: the password of the route is our flag
老规矩, binwalk 走一遭.

DECIMAL      HEX         DESCRIPTION
-------------------------------------------------------------------------------------------------------
0           0x0         PNG image, 370 x 370, 1-bit grayscale, non-interlaced
694         0x2B6       Zip archive data, at least v2.0 to extract, compressed size: 54990, 
                         uncompressed size: 292875, name: "C8-E7-D8-E8-E5-88_handshake.cap"
56152       0xDB58      End of Zip archive

提取 zip 爆破后解压得到C8-E7-D8-E8-E5-88_handshake.cap 和一个 "破解记录.txt".
提示为: "前四位是ISCC 后四位由大写字母和数字构成"
看了下嗅探包. 很明显是无线协议的包, 考虑到之前无线破解比较火, 根据提示生成字典, 开aircrack-ng 干它.
# aircrack-ng -w pass.txt -b c8:e7:d8:e8:e5:88 C8-E7-D8-E8-E5-88_handshake.cap
得到密码也就是 FLAG
FLAG ->ISCC16BA

BASE 08

这题也是在某基佬的提示下做出来的.
题目给了两个文件.
comment
根据第一个文件的格式, WINDoWsSeviCEss 一共十五位.
按照五位一组的培根加密方式得到密码. BIT
解压后拿到 BASE64 过的FLAG
FLAG ->Flag:{Ly319.i5d1f*iCult!}

BASE 09

给出了一个 encrypt 函数和一串密文

<?php
function encrypt($data,$key)
{
    $key = md5('ISCC');
    $x = 0;
    $len = strlen($data);
    $klen = strlen($key);
    for ($i=0; $i < $len; $i++) { 
        if ($x == $klen)
        {
            $x = 0;
        }
        $char .= $key[$x];
        $x+=1;
    }
    for ($i=0; $i < $len; $i++) {
        $str .= chr((ord($data[$i]) + ord($char[$i])) % 128);
    }
    return base64_encode($str);
} ?>
output: fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=

根据 encrypt 函数写对应的 decrypt

<?php
function decrypt($str) {
    $mkey = "729623334f0aa2784a1599fd374c120d";
    $klen = strlen($mkey);
    $tmp = $str;
    $tmp = base64_decode($tmp);  // 对 base64 后的字符串 decode
    $md_len = strlen($tmp); //获取字符串长度
    for ($i=0; $i < $md_len; $i++) {  //  取二次加密用 key;
        if ($x == $klen)  // 数据长度是否超过 key 长度检测
            $x = 0;
        $char .= $mkey[$x];  // 从 key 中取二次加密用 key
        $x+=1;
    } 
    $md_data = array();
    for($i=0;$i<$md_len;$i++) { // 取偏移后密文数据
        array_push($md_data, ord($tmp[$i]));
    }
    $md_data_source = array();
    $data1 = "";
    $data2 = "";
    foreach ($md_data as $key => $value) { // 对偏移后的密文数据进行还原
        $i = $key;
        if($i >= strlen($mkey)) {$i = $i - strlen($mkey);}
        $dd = $value;
        $od = ord($mkey[$i]);
        array_push($md_data_source,$dd);
        $data1 .= chr(($dd+128)-$od);  // 第一种可能, 余数+128-key 为回归数
        $data2 .= chr($dd-$od);  // 第二种可能, 余数直接-key 为回归数
    }
    print "data1 => ".$data1."<br>\n";
    print "data2 => ".$data2."<br>\n";
}
$str = "fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=";
decrypt($str);
?>

FLAG ->Flag:{asdqwdfasfdawfefqwdqwdadwqadawd}

MISC 02

眼见非实, 一个 DOCX , 用解压软件解压, 找到 word/document.xml 拿到 FLAG
FLAG ->flag{F1@g}

MISC 03

给了一个嗅探包, 用 wireshark 的 follow 功能把 RSA 私钥公钥和 key.txt 拿出来,然后用 openssl 解密
openssl rsautl -decrypt -in key.txt -inkey test.key -out key.desc
cat key.desc 拿到 FLAG
FLAG ->haPPy_Use_0penSsI

MISC 04

用 Audacity 看到了文件头的那一部分诡异的高低电平, 但是无从入手. 没做

MISC 05

把每一张图片有数字的格子涂黑, 可以看到是一个分尸了的二维码. 还原后拿到 FLAG
FLAG ->flag{y0ud1any1s1}

MISC 06

再见李华... 一时之间没有脑洞, 基佬说注意读题. 这题密码要爆破, 一共九位数, 前四位是随机数, 后五位为 LiHua
直接跑就行了. 解压拿到 FLAG
FLAG ->Stay hungry, Stay foolish.

WEB 01

签到题, 据基佬指出,读题发现 FLAG 字样为 f1ag, 根据提示一步步构造最后在返回头拿到 FLAG
flag=f1ag&hiddenflag=f1ag&f1ag=f1ag 一共三个 f1ag
FLAG ->f1ag: {N0w_go1Odo!otherw3b}

WEB 02

Welcome to Mysql
通过分析发现不能直接上传 .php 文件, 最后扔了一个 .php5 上去, 找到 数据库连接方式, 拿到 FLAG
FLAG -> Flag:{Iscc_1s_Fun_4nd_php_iS_Easy}

WEB 03

i have a jpg, i upload txt
这题卡了蛮久, 主要是没注意看给出的代码, 注意力全在 upload 那一块的. 根据大佬指出目标转移到 rename 那里.

                    $rename='txt'; 
                    $rand=mt_rand(); 
                    $fp=fopen('./uploads/'.$rand.'.txt','w'); 
                    foreach($re2 as $key=>$value) 
                    { 
                        if($key==0) 
                        { 
                            $rename=$value; 
                        } 
                        else 
                        { 
                            if(file_exists('./uploads/'.$value.'.txt')&&is_numeric($value)) 
                            { 
                                $file=file_get_contents('./uploads/'.$value.'.txt'); 
                                fwrite($fp,$file); 
                            } 
                        } 
                    } 
                    fclose($fp); 
                    waf($rand,$rename); 
                    rename('./uploads/'.$rand.'.txt','./uploads/'.$rand.'.'.$rename); 
                    echo "you success rename!./uploads/$rand.$rename";

这才是核心代码.可以看到 fclose 是在 fwrite 事务完成后才执行的. 那么只要 key 不等于 0, 我们就能直接绕过 upload 的限制.
通过自带的 fwrite 拼接两个文件, 构造成一个完整的可执行 php.
rename 之后访问构造的 php, 随着 302 跳转会返回一个 flag. 至于怎么绕过 KaIsA, 直接写了个脚本跑了下 0x00 到 0xff, 拿到了解密表.
FLAG ->flag{54a5bd4fe6193580020487b56acff6c5}

WEB 04

一起来日站. 最轻松的一题.
robots.txt -> 御剑扫后台 -> 登录框注入.
构造 username=admin'&password=a' and select 1-- 拿到 FLAG
FLAG ->Flag:{ar32wefafafqw325t4rqfcafas}

WEB 05

打破常规

<?php 
$v1=0;$v2=0;$v3=0;
$a=(array)json_decode(@$_GET['iscc']); 
if(is_array($a)){
    is_numeric(@$a["bar1"])?die("nope"):NULL;
    if(@$a["bar1"]){
        ($a["bar1"]>2016)?$v1=1:NULL;
    }
    if(is_array(@$a["bar2"])){
        if(count($a["bar2"])!==5 OR !is_array($a["bar2"][0])) die("nope");
        $pos = array_search("nudt", $a["bar2"]);
        $pos===false?die("nope"):NULL;
        foreach($a["bar2"] as $key=>$val){
            $val==="nudt"?die("nope"):NULL;
        }
        $v2=1;
    }   
}
$c=@$_GET['cat'];
$d=@$_GET['dog'];
if(@$c[1]){
    if(!strcmp($c[1],$d) && $c[1]!==$d){
        echo $c[0].$d;
        eregi("3|1|c",$d.$c[0])?die("nope4"):NULL;
        strpos(($c[0].$d), "isccctf2017")?$v3=1:NULL;
    }
}
echo $v1,$v2,$v3;
if($v1 && $v2 && $v3){ 
   echo "success";
}
?>

分析代码构造对应的 URL 即可. 这里感谢雨牛给出的 %00 截断提示.
?iscc={"bar1":"83333ff","bar2":[[true],"1","1","1",true]}&cat[]=%00isccctf2017&cat[]=aaa&dog[]=false
FLAG ->flag{sfklljljdstuaft}

WEB 08

where is your flag
卡了好一会,摸不着头脑, 不知道什么玩意, 基佬提示 宽字节注入和 ID 关键字. (妹的, 以后看见个玩意我一定要随手打个 id 试试)
懒得琢磨直接丢 sqlmap. 拿到 flag
flag:{441b7fa1617307be9632263a4497871e}

[5.12]蠕虫事件应急响应

2017 年 5 月 12 日晚. 项目群有人发了一张图, 某大学被勒索病毒攻击.
十分钟后截了一张公告, 病毒使用 "MS17-010" 进行传播, 看到这马上想起 4 月底 FB 上的一篇帖子.
NSA 漏洞库, MS17-010, 通杀全版本. 马上安排相关人员对涉外及与教育网内部连接的服务器进行加固.
于第二日 5 月 13 日, 九点, 上课途中, 领导连来五个电话安排对机房的相关服务器进行排查.
同时在机房内发现机子出现被攻击成功, 即刻联系组员进中心机房, 对相关机房进行断网处理.
临时对涉毒大楼进行物理阻隔(拔网线). 同时对服务器数据进行备份.
因为实验大楼使用的是云端教学, 被攻击的客户端为云端镜像, 不算什么大事. 对客户端进行升级即可.
于 十一点左右 联系相关负责人, 通过核心交换, 配置 ACL, 阻隔相应端口, 封闭对应 Vlan / IP 段. 阻止进一步传播.
同时对网段内电脑进行排查. 发现实验大楼内有另一教室也出现勒索病毒. 教师端也不能幸免.
考虑云端的特殊性. 便把教室与云端服务器进行物理断网. 关闭所有在用客户端. 从源下手. 对镜像进行升级加固.
于下午五点半, 升级完毕. 晚八点恢复网络进行机房测试, 至晚 十点半 无发现病毒复现状况. 自此事件响应完毕.

总结:

主要是漏洞威力过于巨大, 只要对 SMB 服务进行阻隔即可.

[运维]基于域名系统的外网WEB端口共享实现

基于域名反向代理WEB端口共享

Named + Nginx + Apache

环境搭建:

Centos 7 * 3;
Server 1 :
  HostName: DNS
  Software: named
  Ip Address: 192.168.4.8
Server 2:
  HostName: Nginx
  Software: Nginx
  Ip Address: 192.168.113.20
Server 3:
  HostName: Apache
  Software: Apache
  Ip Address: 192.168.113.18

相关配置:

Dns → Named: 使用 named 程序为Nginx 提供Dns 查询服务. 参考网址: http://www.linuxde.net/2011/11/2000.html
yum install bind cache-nameserver –y 安装完毕后对相关域名进行设置 配置文件地址: /etc/named.conf
初始化域名空间, 文件地址: /etc/named.zones
对域名空间初始化后在 named 资源文件夹建立域名解析配置文件编辑域名解析文件, 编辑完毕后可以使用named-chkzone zone filename 指令对zone 域名解析配置文件进行格式校验, 如果格式校验不通过, 在重启named 服务时将会报错. 报错的内容可以通过systemctl status named.service 指令查看相关的错误日志.
重启服务检查配置是否正确解析

Nginx -> nginx: 安装完毕后编辑配置文件 /etc/nginx/nginx.conf 通过添加 proxy_xx 等控制语句设置相关的代理属性. 上图为 nginx 的全局配置, 关于具体站点的相关私有代理属性以及域名的监听等设置在~/conf.d/ 目录下, 以.conf 后缀结尾, 可以通过上图的倒数第二行看出. 此配置文件包含站点的监听端口,代理目的服务器等, 编辑完毕后通过nginx –t 指令校验配置文件是否有错误以下为默认站点配置, 可以通过proxy_pass 语句看出, 代理目的地可以不只是域名, 也可以是相关的ip 甚至是不同的端口.
以下为非默认站点配置举例, proxy_pass 指向为一个域名地址, 此域名通过设置的dns 服务器, 也就是resolve 指定的192.168.4.53 解析为对应的IP 地址后转发至相关的Ip 端口, 实现代理转发的目的. Proxy_pass通过与域名服务器的结合, 相互搭配达到外网共享80 端口的规划. 在实际配置中, 如果主机默认的DNS 主机为Dns SVR 可不设置resolve. 或者在HOSTS 文件中指定了静态解析也不需要配置resolve 语句.

Apache → apache 安装完毕后, 根据安全手册配置好相关的容器安全配置, 然后添加监听对应域名的虚拟主机即可, 不需要做额外的配置去适应Nginx 的反向代理.
以下给出 Apache 虚拟主机配置参考模板:

[NodeJs][运维] 运维手册 - 第一章: 服务保障

<h2 style="margin-left:0cm;text-indent:0cm;"></h2><h2 style="margin-left:0cm;text-indent:0cm;"></h2><h2 style="margin-left:0cm;text-indent:0cm;"> <span style="font-family:宋体;">架构设计</span><span></span></h2><h3 style="margin-left:0cm;text-indent:0cm;"> <span style="font-family:&quot;font-weight:normal;">  </span><span style="font-family:&quot;font-weight:normal;">NodeJs </span><span style="font-family:宋体;font-weight:normal;">的优势</span><span style="font-family:&quot;"></span></h3><p class="MsoNormal" align="left"> <span style="font-size:12.0pt;">         </span><span style="font-family:宋体;">事件型非阻塞异步高速处理架构引擎</span><span>. </span><span style="font-family:宋体;">具备单线程多进程处理能力</span><span>, </span><span style="font-family:宋体;">对于高并发的</span><span> URI </span><span style="font-family:宋体;">请求有良好的处理能力</span><span>. </span><span style="font-family:宋体;">常用于前端代理</span><span>, </span><span style="font-family:宋体;">前端解析</span><span>, </span><span style="font-family:宋体;">后台代理</span><span>, WEB </span><span style="font-family:宋体;">请求代理等数据处理并发量较大领域</span><span>. </span><span style="font-family:宋体;">同时因为较好的事件处理能力应用于包括但不限于</span><span>WEB/</span><span style="font-family:宋体;">大数据</span><span>/</span><span style="font-family:宋体;">嵌入式</span> <span style="font-family:宋体;">等方面</span><span>.</span></p><h3 style="margin-left:0cm;text-indent:0cm;"> <span style="font-family:&quot;font-weight:normal;">  </span><span style="font-family:&quot;font-weight:normal;">NodeJs </span><span style="font-family:宋体;font-weight:normal;">代理结构简述</span></h3><p class="MsoNormal" align="left"> <span style="font-size:12.0pt;">         </span><span style="font-family:宋体;">相对于旧有的负载均衡体系</span><span>, </span><span style="font-family:宋体;">如</span><span> Nginx+Apache </span><span style="font-family:宋体;">反向代理架构</span><span>, NodeJs </span><span style="font-family:宋体;">具备针对性强</span><span>, </span><span style="font-family:宋体;">并发处理</span><span>, </span><span style="font-family:宋体;">异常反馈等优于</span><span> Nginx </span><span style="font-family:宋体;">等轻量化容器的处理能力</span><span>, </span><span style="font-family:宋体;">并且具备</span><span>Nginx </span><span style="font-family:宋体;">反向代理架构不具备的简易部署</span><span>,</span><span style="font-family:宋体;">方便架设</span><span>/</span><span style="font-family:宋体;">错误定位</span><span>/</span><span style="font-family:宋体;">二次开发等优点</span><span>. </span><span style="font-size:12.0pt;"></span></p><h3 style="margin-left:0cm;text-indent:0cm;"> <span style="font-family:&quot;font-weight:normal;">  </span><span style="font-family:&quot;font-weight:normal;">NodeJs </span><span style="font-family:宋体;font-weight:normal;">代理架构设计</span><span style="font-family:&quot;"></span></h3><p> <span style="font-family:宋体;font-weight:normal;">
</span>
</p><p class="MsoNormal" align="left"> <span style="font-size:12.0pt;"></span></p><p class="MsoNormal" align="left"> <span style="font-size:12.0pt;">         </span><span style="font-family:宋体;">旧有的</span><span> MVC </span><span style="font-family:宋体;">等</span><span> WEB </span><span style="font-family:宋体;">架构基于一台</span><span> SVR, </span><span style="font-family:宋体;">数据服务以及功能应用在同一台</span><span> SVR </span><span style="font-family:宋体;">上</span><span>, </span><span style="font-family:宋体;">因为资源有限</span><span>, </span><span style="font-family:宋体;">在代码以及架构的优化上偏向于保守以及轻量化</span><span>, </span><span style="font-family:宋体;">此架构适用于中小型非门户论坛级别的公司或者个人博客页面</span><span>, </span><span style="font-family:宋体;">一旦当用户访问量增大</span><span>, </span><span style="font-family:宋体;">数据量达到百万级</span><span>, </span><span style="font-family:宋体;">并发达到</span><span> 5k+ </span><span style="font-family:宋体;">时</span><span>, </span><span style="font-family:宋体;">此架构会带来隐患</span><span>, </span><span style="font-family:宋体;">传统的</span><span> J2EE/ASP.NET/PHP </span><span style="font-family:宋体;">等架构服务器因为资源不足</span><span>, </span><span style="font-family:宋体;">服务器长期处于高负载状态</span><span>, </span><span style="font-family:宋体;">应用访问错误</span><span>, </span><span style="font-family:宋体;">资源响应缓慢等都可能导致客户流失</span><span>.  </span><span style="font-family:宋体;">在此基础上</span><span>, </span><span style="font-family:宋体;">虽然有</span><span>: </span><span style="font-family:宋体;">服务器集群</span><span>, </span><span style="font-family:宋体;">负载均衡</span><span>, </span><span style="font-family:宋体;">前后端分离等技术</span><span>, </span><span style="font-family:宋体;">但是也存在如</span><span>: </span><span style="font-family:宋体;">技术能力要求偏高</span><span>, </span><span style="font-family:宋体;">后期维护工作量巨大</span><span>, </span><span style="font-family:宋体;">故障定位复杂等问题</span><span>.</span></p><p class="MsoNormal" align="left"> <span>        NodeJs </span><span style="font-family:宋体;">的出现让基于</span> <span style="font-family:宋体;">前后端分离</span> <span style="font-family:宋体;">技术的</span><span> WEB </span><span style="font-family:宋体;">应用能够在高并发以及应用响应间取得较为可观的缓冲</span><span>, </span><span style="font-family:宋体;">并进一步降低了技术门槛以及部署和运维成本</span><span>. </span></p><p class="MsoNormal" align="left" style="text-indent:28.9pt;"> <span style="font-family:宋体;">在</span> <span>2014 </span><span style="font-family:宋体;">年</span><span> 4 </span><span style="font-family:宋体;">月</span><span>, </span><span style="font-family:宋体;">淘宝的前端团队提出的</span> <span>“</span><span style="font-family:宋体;">中途岛</span><span>” </span><span style="font-family:宋体;">架构</span><span>, </span><span style="font-family:宋体;">便是基于</span><span> NodeJs </span><span style="font-family:宋体;">代理的一种前端高并发前后端分离的服务架构</span><span>. </span><span style="font-family:宋体;">在此架构中</span><span>, </span><span style="font-family:宋体;">用户</span><span>(Client) </span><span style="font-family:宋体;">访问网站提供的基于</span><span> JS</span><span style="font-family:宋体;">架构纯前端</span><span> View</span><span style="font-family:宋体;">层</span><span>(UI Layer), </span><span style="font-family:宋体;">通过</span><span> Ajax</span><span style="font-family:宋体;">等交互控制与代理端</span><span>(NodeServer)</span><span style="font-family:宋体;">进行交互</span><span>, </span><span style="font-family:宋体;">而后代理端</span><span>(NS)</span><span style="font-family:宋体;">再与纯后端的服务端</span><span>(JavaServer)</span><span style="font-family:宋体;">进行数据交互后对数据进行呈现然后返回给</span><span>View</span><span style="font-family:宋体;">层</span><span>. </span><span style="font-family:宋体;">这种架构可以针对服务器资源进行高效的分配</span><span>, </span><span style="font-family:宋体;">并能够快速定位故障发生的位置层</span><span>. </span><span style="font-family:宋体;">方便运维及开发人员及时修复</span><span>.</span></p><p class="MsoNormal" align="left"> <span style="font-size:12.0pt;"></span><span style="font-size:12.0pt;"></span></p><h2 style="margin-left:0cm;text-indent:0cm;"> <span style="font-family:宋体;">高服务性</span><span></span></h2><p class="MsoNormal" style="margin-left:21.0pt;"> <span>  </span><span style="font-family:宋体;">应用需要可持续的提供有效服务</span><span>, </span><span style="font-family:宋体;">并尽量避免服务中断</span><span>. </span><span style="font-family:宋体;">当</span> <span>NodeJs </span><span style="font-family:宋体;">代码出现异常错误的时候程序往往会中断在异常发生的代码处</span><span>, </span><span style="font-family:宋体;">并杀死整个进程</span><span>, </span><span style="font-family:宋体;">这样将会导致服务不可用</span><span>. </span><span style="font-family:宋体;">所以需要尽量保障服务的正常运行</span><span>, </span><span style="font-family:宋体;">哪怕是代码运行出现异常</span><span>(</span><span style="font-family:宋体;">我们应该尽量确保自己的代码有足够的容错而不会导致程序中断</span><span>).</span></p><h3 style="margin-left:0cm;text-indent:0cm;"> <span>  </span><span style="font-family:宋体;">进程守护</span><span></span></h3><p class="MsoNormal" style="margin-left:21.0pt;"> <span>  </span><span style="font-family:宋体;">当我们使用一个</span> <span>Session </span><span style="font-family:宋体;">开启一个</span> <span>node </span><span style="font-family:宋体;">的进程</span><span>, </span><span style="font-family:宋体;">这个进程即会挂在到我们当前的</span> <span>Session </span><span style="font-family:宋体;">下</span><span>, </span><span style="font-family:宋体;">当</span> <span>Session </span><span style="font-family:宋体;">结束时</span><span>, </span><span style="font-family:宋体;">因为资源回收制度</span><span>, </span><span style="font-family:宋体;">挂在我们这个</span> <span>Session </span><span style="font-family:宋体;">下的所有进程都会被中断或者杀死</span><span>. </span><span style="font-family:宋体;">所以需要一个进程守护机制来保护我们的进程</span><span>, </span><span style="font-family:宋体;">当</span><span> Session </span><span style="font-family:宋体;">结束时不会被杀死</span><span>. </span><span style="font-family:宋体;">依旧能够持续提供服务</span><span>.</span></p><h4 style="margin-left:0cm;text-indent:0cm;"> <span style="font-size:12.0pt;line-height:156%;font-weight:normal;">            Nohup</span></h4><p class="MsoNormal" style="margin-left:21.0pt;"> <span>         </span><span>Nohup </span><span style="font-family:宋体;">是</span> <span>Linux </span><span style="font-family:宋体;">下的一个命令</span><span>, </span><span style="font-family:宋体;">可将应用进程运行于后台而不会中断</span><span>.</span><span></span></p><h4 style="margin-left:0cm;text-indent:0cm;"> <span style="font-size:12.0pt;line-height:156%;font-family:&quot;font-weight:normal;">    Screen</span></h4><p class="MsoNormal" style="margin-left:21.0pt;"> <span style="font-size:12.0pt;font-family:&quot;">   </span><span>Screen </span><span style="font-family:宋体;">是</span> <span>Linux </span><span style="font-family:宋体;">常用的一个进程守护程序</span><span>, </span><span style="font-family:宋体;">可以达到当用户</span> <span>Session </span><span style="font-family:宋体;">结束时保持进程活性的功能</span><span>, </span><span style="font-family:宋体;">从附图可以看到</span><span>, </span><span style="font-family:宋体;">甚至有些进程是四个月前开启的</span><span>.(</span><span style="font-family:宋体;">个人有写脚本判断服务是否死掉</span><span>, </span><span style="font-family:宋体;">死掉后会重启服务</span><span>)</span><span style="font-family:&quot;font-weight:normal;"></span></p><p class="MsoNormal" style="margin-left:21.0pt;"> <span style="font-size:12.0pt;"></span><span style="font-size:12.0pt;font-family:&quot;"></span></p><h4 style="margin-left:0cm;text-indent:0cm;"> <span style="font-size:12.0pt;line-height:156%;font-family:&quot;font-weight:normal;">    Forever</span></h4><p class="MsoNormal" style="margin-left:21.0pt;"> <span style="font-size:12.0pt;font-family:&quot;">   </span><span>Forever </span><span style="font-family:宋体;">可以做到当进程对应的文件修改时自动重启的功能</span><span>. </span><span style="font-family:宋体;">同时也能确保进程不会被</span> <span>Session </span><span style="font-family:宋体;">异常事件给中断</span><span>. </span><span style="font-family:宋体;">但同时也有配置困难等问题</span><span>.</span><span style="font-family:宋体;"></span></p><p class="MsoNormal" style="margin-left:21.0pt;"> <span style="font-size:12.0pt;"></span><span style="font-size:12.0pt;font-family:&quot;"></span></p><h4 style="margin-left:0cm;text-indent:0cm;"> <span style="font-size:12.0pt;line-height:156%;font-family:&quot;font-weight:normal;">    Pm2</span></h4><p class="MsoNormal" style="margin-left:21.0pt;"> <span style="font-family:宋体;">     经过大量工程师的努力现在已经不输于 <span>Forever </span>了<span>, </span>在集群以及大规模部署下比 <span>Forever </span>要方便不少<span>.</span></span><span style="font-family:宋体;font-weight:normal;"></span></p><p class="MsoNormal" align="left" style="margin-left:21pt;"> <span style="font-size:12.0pt;"></span><span style="font-size:12.0pt;font-family:&quot;"></span></p><h3 style="margin-left:0cm;text-indent:0cm;"> <span>  </span><span style="font-family:宋体;">多进程代理</span><span></span></h3><p class="MsoNormal"> <span></span><span></span></p><p class="MsoNormal" style="margin-left:21.0pt;"> <span style="font-family:宋体;">    当一个进程因为异常事件不得不关闭的时候</span><span>, </span><span style="font-family:宋体;">服务将会处于不可用状态</span><span>, </span><span style="font-family:宋体;">如何确保当进程因为异常退出时能够自动恢复</span><span>, </span><span style="font-family:宋体;">是首要</span><span>. </span><span style="font-family:宋体;">并能够对资源的利用达到最大化</span><span>, </span><span style="font-family:宋体;">改善</span> <span>Node </span><span style="font-family:宋体;">本身的缺点提高性能也是重点</span><span>.<span style="font-family:&quot;font-weight:normal;"></span></span></p><h4 style="margin-left:0cm;text-indent:0cm;"> <span style="font-size:12.0pt;line-height:156%;font-family:&quot;font-weight:normal;">    Cluster</span></h4><p class="MsoNormal" style="margin-left:21.0pt;"> <span style="font-size:12.0pt;font-family:&quot;">   </span><span style="font-family:宋体;">通过 <span>cluster </span>能够建立多个基于同一<span> Socket </span>的进程来对数据进行处理<span>, </span>善用多核机制<span>.</span></span><span style="font-family:宋体;font-weight:normal;"></span></p><p class="MsoNormal" style="margin-left:21.0pt;"> <span style="font-family:宋体;"></span><span style="font-family:宋体;font-weight:normal;"></span></p><p class="MsoNormal" style="margin-left:21.0pt;text-indent:21.0pt;"> <span style="font-family:宋体;">Cluster </span><span style="font-family:宋体;">能够将请求根据系统负载分配到空闲的进程上对请求进行处理<span>, </span>但是有个不足的情况是<span>, </span>在不涉及 <span>IO </span>操作下<span>,</span>当连接为<span> “</span>短连接<span>”</span>时将会导致 <span>QPS </span>以及可用率的下降<span>, </span>但是也能保持<span> QPS </span>在<span> 3K+, </span>利用率在<span> 99%/10W </span>中<span>. </span>所以尽量在建立连接时使用 <span>kepp-alive </span>方式<span>.</span></span><span style="font-family:宋体;font-weight:normal;"></span></p><h4 style="margin-left:0cm;text-indent:0cm;"> <span style="font-family:&quot;font-weight:normal;">    </span><span style="font-family:宋体;font-weight:normal;">子进程代理</span><span style="font-family:&quot;font-weight:normal;"></span></h4><p class="MsoNormal" style="margin-left:21.0pt;"> <span style="font-size:12.0pt;font-family:&quot;">  </span><span style="font-family:宋体;">模拟</span><span style="font-family:&quot;"> <span>Cluster </span></span><span style="font-family:宋体;">建立子进程</span><span style="font-family:&quot;">, </span><span style="font-family:宋体;">主要业务使用放置于子进程中</span><span style="font-family:&quot;">, </span><span style="font-family:宋体;">使用父进程对子进程进行监控</span><span style="font-family:&quot;">, </span><span style="font-family:宋体;">当子进程因为业务异常中断时自动重启子进程</span><span style="font-family:&quot;">. </span><span style="font-family:宋体;">确保业务的可持续利用</span><span style="font-family:&quot;">.</span><span style="font-family:&quot;font-weight:normal;"></span></p><p class="MsoNormal" style="margin-left:21.0pt;"> <span></span><span></span></p><h3 style="margin-left:0cm;text-indent:0cm;"> <span>  </span><span style="font-family:宋体;">异常触发</span><span></span></h3><h4 style="margin-left:0cm;text-indent:0cm;"> <span style="font-family:&quot;font-weight:normal;">    Try..catch..</span><span style="font-family:宋体;font-weight:normal;">语句</span><span style="font-family:&quot;font-weight:normal;"></span></h4><p class="MsoNormal"> <span style="font-family:&quot;">      </span><span style="font-family:宋体;">在开发中善于使用</span><span style="font-family:&quot;"> <span>try catch </span></span><span style="font-family:宋体;">语句能够将大部分的异常事件处理掉</span><span style="font-family:&quot;">, </span><span style="font-family:宋体;">但是</span><span style="font-family:&quot;"> try </span><span style="font-family:宋体;">语句有个问题</span><span style="font-family:&quot;">, </span><span style="font-family:宋体;">那就是当异常问题处于</span><span style="font-family:&quot;"> <span>callback </span></span><span style="font-family:宋体;">函数中</span><span style="font-family:&quot;">, catch </span><span style="font-family:宋体;">语句并不能够捕获到异常信息</span><span style="font-family:&quot;">. </span><span style="font-family:宋体;">将会导致程序中断</span><span style="font-family:&quot;">.</span><span style="font-family:&quot;font-weight:normal;"></span></p><p class="MsoNormal"> <span style="font-family:&quot;">         </span><span></span><span style="font-family:&quot;font-weight:normal;"></span></p><h4 style="margin-left:0cm;text-indent:0cm;"> <span style="font-family:&quot;font-weight:normal;">    </span><span style="font-family:宋体;font-weight:normal;">使用</span><span style="font-family:&quot;font-weight:normal;"> <span>Domain </span></span><span style="font-family:宋体;font-weight:normal;">捕获</span><span style="font-family:&quot;font-weight:normal;">Callback </span><span style="font-family:宋体;font-weight:normal;">类型异常</span><span style="font-family:&quot;font-weight:normal;"></span></h4><p class="MsoNormal"> <span style="font-family:&quot;">      </span><span style="font-family:宋体;">进程抛出了异常</span><span>, </span><span style="font-family:宋体;">没有被任何的</span><span style="font-size:12pt;font-family:Consolas;background:#FCFAFA;">try catch</span><span style="font-family:宋体;">捕获到</span><span>, </span><span style="font-family:宋体;">这时候将会触发整个</span><span>process</span><span style="font-family:宋体;">的</span><span style="font-size:12pt;font-family:Consolas;background:#FCFAFA;">processFatal</span><span>, </span><span style="font-family:宋体;">此时如果在</span><span>domain</span><span style="font-family:宋体;">包裹之中</span><span>, </span><span style="font-family:宋体;">将会在</span><span>domain</span><span style="font-family:宋体;">上触发</span><span style="font-size:12pt;font-family:Consolas;background:#FCFAFA;">error</span><span style="font-family:宋体;">事件</span><span>,</span><span style="font-family:宋体;">反之将会在</span><span>process</span><span style="font-family:宋体;">上触发</span><span style="font-size:12pt;font-family:Consolas;background:#FCFAFA;">uncaughtException</span><span style="font-family:宋体;">事件</span><span>.</span></p><p class="MsoNormal"> <span></span><span style="font-family:Calibri, sans-serif;font-weight:normal;"></span></p><h3 style="margin-left:0cm;text-indent:0cm;"> <span>  </span><span style="font-family:宋体;">心跳检查</span><span></span></h3><p class="MsoNormal" style="margin-left:21.0pt;text-indent:10.5pt;"> <span style="font-family:宋体;">服务上线期间</span><span>, </span><span style="font-family:宋体;">服务是否可用不能依靠用户反馈</span><span>, </span><span style="font-family:宋体;">当用户察觉服务异常时</span><span>, </span><span style="font-family:宋体;">服务可能已经处于崩溃多时的状态</span><span>. </span><span style="font-family:宋体;">所以对服务是否可用的状态监测是非常重要的</span><span>. </span><span style="font-family:宋体;">可以使用脚本定期访问判断的形式来检查服务是否在线</span><span>.</span></p><h2 style="margin-left:0cm;text-indent:0cm;"> <span style="font-family:宋体;">异常处理</span><span></span></h2><p class="MsoNormal" align="left"> <span style="font-family:宋体;">    一旦遇到异常事件<span>, </span>首先需要记录异常日志<span>, </span>对异常的触发条件<span>, </span>处理过程<span>, </span>异常模块<span>, </span>输出内容进行捕获<span>,</span>交付日志处理系统对异常进行下一步处理<span>.</span></span></p><h3> <span style="font-family:&quot;font-weight:normal;">  </span><span style="font-family:宋体;font-weight:normal;">异常捕获</span><span style="font-family:&quot;"></span></h3><p class="MsoNormal" style="text-indent:21.0pt;"> <span style="font-family:宋体;">通常在程序执行过程中<span>, </span>一旦遇到异常事件可能导致程序中断或者异常退出<span>,</span>直接返回异常数据<span>, </span>在<span> WEB </span>事件中<span>, </span>当返回一个异常<span>, </span>可能导致前端处理错误<span>,</span>用户交互出错等不友好行为<span>. </span>通过在<span> Node </span>的入口文件添加异常处理模块<span>, </span>将异常事件捕获<span>, </span>并通过日志的形式保存异常事件详细信息<span>, </span>为开发人员提供修复参考</span><span style="font-size:12.0pt;font-family:宋体;">.</span><span></span></p><p class="MsoNormal" align="center" style="text-align:center;"> <span style="font-size:12.0pt;font-family:宋体;"></span><span style="font-size:12.0pt;font-family:宋体;"></span></p><h4 style="margin-left:0cm;text-indent:0cm;"> <span style="font-family:&quot;font-weight:normal;">    </span><span style="font-family:宋体;font-weight:normal;">异常信息的判断</span><span style="font-family:&quot;font-weight:normal;"></span></h4><p class="MsoNormal" style="margin-left:21.0pt;"> <span>         </span><span style="font-family:宋体;">一般来说</span><span>, </span><span style="font-family:宋体;">异常指的是程序运行出错</span><span>, </span><span style="font-family:宋体;">如常见的</span> <span>Error,</span><span style="font-family:宋体;">溢出</span> <span style="font-family:宋体;">等将会导致程序崩溃的</span><span> BUG/</span><span style="font-family:宋体;">错误</span> <span style="font-family:宋体;">诸如此类的警告信息</span><span>. </span><span style="font-family:宋体;">但是在正常的应用服务过程中</span><span>, </span><span style="font-family:宋体;">我们要关心的不单单只是这些错误信息</span><span>, </span><span style="font-family:宋体;">这些会造成服务中断的</span><span> WRONG </span><span style="font-family:宋体;">等级是非常之高的</span><span>. </span><span style="font-family:宋体;">我们在开发以及运维过程中应当尽量避免这些错误的出现</span><span>, </span><span style="font-family:宋体;">将他们的出现的概率降低到最小</span><span>. </span><span style="font-family:宋体;">同时我们也要收集一些看起来不太重要的信息</span><span>, </span><span style="font-family:宋体;">用来统计</span><span>, </span><span style="font-family:宋体;">分析</span><span>, </span><span style="font-family:宋体;">将错误尽量的解决在发生之前</span><span>.</span></p><p class="MsoNormal" style="margin-left:21.0pt;"> <span>          </span><span style="font-family:宋体;">首先是对异常信息的分级制度</span><span>, </span><span style="font-family:宋体;">但是找了几个搜索引擎</span><span>, </span><span style="font-family:宋体;">看到的都是软件测试中所定义的错误分级</span><span>. </span><span style="font-family:宋体;">对于程序内部产生的日志信息分级</span><span>, </span><span style="font-family:宋体;">则是没见到有公开的标准</span><span>. </span><span style="font-family:宋体;">虽然像</span><span> Apache, Nginx </span><span style="font-family:宋体;">等具备如</span> <span>Debug, Info, Warn, Error, Fatal </span><span style="font-family:宋体;">此类的日志等级</span><span>, </span><span style="font-family:宋体;">但是在我们的程序运行过程中</span><span>, </span><span style="font-family:宋体;">是没有的</span><span>, </span><span style="font-family:宋体;">需要我们自己来对输出的日志进行定义</span><span>, </span><span style="font-family:宋体;">并将其输出</span><span>.</span></p><p class="MsoNormal" style="margin-left:21.0pt;"> <span>         </span><span style="font-family:宋体;">如</span><span>: Java </span><span style="font-family:宋体;">的异常参考</span><span></span></p><p class="MsoNormal" style="margin-left:21.0pt;"> <span></span><span></span></p><h4 style="margin-left:0cm;text-indent:0cm;"> <span style="font-family:&quot;font-weight:normal;">    </span><span style="font-family:宋体;font-weight:normal;">异常信息的存储</span><span style="font-weight:normal;"></span></h4><p class="MsoNormal" align="left" style="margin-left:21pt;"> <span style="font-size:12.0pt;font-family:宋体;">   </span><span style="font-family:宋体;">异常信息产生之后</span><span>, </span><span style="font-family:宋体;">我们需要将其输出</span><span>, </span><span style="font-family:宋体;">并妥善的保存起来</span><span>, </span><span style="font-family:宋体;">为开发人员后续的修复工作提供可供分析的有效参考</span><span>. </span><span style="font-family:宋体;">所以异常信息的存储</span><span>, </span><span style="font-family:宋体;">是很有必要的</span><span>.</span></p><p class="MsoNormal" align="left" style="margin-left:21pt;"> <span>          </span><span style="font-family:宋体;">通常来说异常信息都是直接输出到控制台中</span><span>, </span><span style="font-family:宋体;">也有写入文件的</span><span>, </span><span style="font-family:宋体;">也有同时输出在控制台和写入文件的</span><span>. </span><span style="font-family:宋体;">或者是在线存储</span><span>, </span><span style="font-family:宋体;">通过反馈机制将异常信息通过</span> <span>Ajax </span><span style="font-family:宋体;">等操作将异常信息回传给日志服务器统一存储</span><span>.</span><span style="font-size:12.0pt;font-family:宋体;"></span></p><p class="MsoNormal" align="left"> <span style="font-size:12.0pt;font-family:宋体;">    </span><span style="font-family:宋体;">这将在第二章中详细说明</span><span></span></p>