某小公司自動化智慧監控平臺的實踐

邵磊發表於2018-01-11

前言

首先介紹下背景:我們公司的產品,會直接部署在甲方,因為甲方比較多,且他們包含個性化需求較多,所以,每個甲方可以理解為我們產品的一條git分支。 由於甲方的機器環境、網路環境各不相同,時常出現一些執行時的問題,於是,我設計了這套簡易的智慧監控系統,用來實時監測各個甲方介面情況。

適用範圍

該套方案衍生的適用範圍如下:

  1. docker下多容器執行專案,且暫不具備介面健康檢測,該套方案可實施檢測多個執行點狀態情況。
  2. 公司同一套程式碼有多個執行環境,需要監控各個環境狀態。
  3. 專案分支化後,部署在各個甲方(含個性化需求)------我們是這個。

效果圖

某小公司自動化智慧監控平臺的實踐
滑鼠移到某個介面,可看某節點產生的時間和當時介面的出參。[這裡只顯示變動節點]

某小公司自動化智慧監控平臺的實踐
其中200為出參json的一個狀態碼,我們專案普遍採用。如出參無法解析出狀態碼(比如伺服器500錯誤),則不顯示。

單擊節點可以再次模擬該次節點,在瀏覽器中顯出出參。

雙擊某個節點可以複製該節點出參。

主體分析

因為我們產品已經完成了前後端分離(前幾篇文章有介紹),所以,我們重點監控介面。

我的方案是:

  1. 每隔一定時間,比如3分鐘,主動get(或者post)一下http介面(含使用者資訊),獲得介面出參。
  2. 比對此次出參和前一次該介面出參是否相同,如果不通則標記下。
  3. 前端展示,每個介面對應每個專案所有變動的節點,並排列一張節點差異圖。
  4. 這裡,我們監控並不關心介面出參的內容,不對節點出參進行校驗是否合法,因為我們面向所有介面。

對某個介面分析

這裡因為每個公司的各個介面,入參、出參加密方式不通,而且身份認證也是不相同,我們會在獨立寫一個方法來實現加解密,所以,我只介紹未加密、已解密的引數;關於出參下文以{"code":"200","data":null,"message"::"success"}為例,身份認證我以簡單token進行講解本文。

某個獲取新聞列表介面:

http://*.com/v1/news?token=2c789e34dc81d79feba6a005ad63902b
複製程式碼

解密後的出參:

{"code":"200","data":[{"id":"1","title":"這是一條假新聞","url":"http://"},{"id":"2","title":"這是一條假新聞","url":"http://"}],"message"::"success"}
複製程式碼

由介面可知

  1. GET 方法
  2. 入參token=2c789e34dc81d79feba6a005ad63902b
  3. 出參為{"code":"200","data":[],"message"::"success"}
  4. 其中*.com對於各個環境可能各不相同,比如容器下為10.0.0.1、10.0.0.2多個地址
  5. 其中token在各個甲方不相同。

某小公司自動化智慧監控平臺的實踐

新增新聞獲取介面時:

相對url:

v1/news
複製程式碼

請求型別:

get
複製程式碼

body主體:

token={token}
複製程式碼

corn表示式:

0 0/3 * * * ?(每隔3分鐘執行一次)
複製程式碼

新增A環境時:

host(必填)

http://a.com
複製程式碼

引數(list)

token 2c789e34dc81d79feba6a005ad63902b
複製程式碼

新增B環境時:

host(必填)

http://b.com
複製程式碼

引數(list)

token 4297f44b13955235245b2497399d7a93
複製程式碼

繫結介面和環境

  1. 一個介面可繫結多個環境
  2. 一個環境可繫結多個介面
  3. 資料庫中只記錄單向繫結,邏輯上是雙向的。

比如在新聞獲取介面繫結A、B環境,則每隔3分鐘請求一輪介面。

A:

A環境host+相對url=

http://a.com/v1/news
複製程式碼

請求型別:

get
複製程式碼

body主體:

token=2c789e34dc81d79feba6a005ad63902b
複製程式碼

B:

B環境host+相對url=

http://b.com/v1/news
複製程式碼

請求型別:

get
複製程式碼

body主體:

token=4297f44b13955235245b2497399d7a93
複製程式碼

我們框架本身並不會關心有幾個引數,只會遍歷介面中帶佔位符的,把佔位符中欄位替換為環境裡的引數list中變數。 比如一個body為token=2c789e34dc81d79feba6a005ad63902b&type=1&mobile=2

則新增介面時,body填入:

token={token}&type={type}&mobile={mobile} 其中佔位符可以隨便起名字
複製程式碼

配置環境時引數(list)如下:

k v
token 2c789e34dc81d79feba6a005ad63902b
type 1
mobile 2

引數和佔位符裡的對應,和url其他變數不相關。

前端展示

某小公司自動化智慧監控平臺的實踐
橫軸代表時間,每個變動的節點都是一次出參變更,節點包含,當時的入參,出參,單擊左鍵可以再次模擬請求得出解密後的出參;雙擊可以複製出參。

對於不同的狀態碼,我們有不同的顏色,對應自己產品中錯誤碼選擇合適顏色即可,錯誤等級越高,顏色越顯眼。

某小公司自動化智慧監控平臺的實踐
某個節點右鍵可以檢視歷史解決方案,以及新增新的解決方案,比如解決了這個錯誤程式碼為999,是重啟服務解決的,則,輸入重啟服務,再點新增。 下次其他人就可以看到這個問題怎麼解決的,優先參照做。

不同的展現

包含兩種不同的緯度,可以直接點選進入,檢視A環境所有介面的監控情況,或者檢視介面1所有環境的監控情況。

某小公司自動化智慧監控平臺的實踐
或者:
某小公司自動化智慧監控平臺的實踐

環境編輯介面:

某小公司自動化智慧監控平臺的實踐

任務編輯介面:

某小公司自動化智慧監控平臺的實踐

核心技術

既然是週期性監控,自然少不了使用cron表示式,我們又是java專案,所以採用了quartz框架。

quartz核心程式碼

public class QuartzSchedule {

    private static SchedulerFactory sf = new StdSchedulerFactory();
    private static Scheduler sched;
    final static String groupName = "task";


    public static void init() throws IOException, SchedulerException {
        //查詢所有需要執行的任務和專案列表
        // 使用類載入器,載入mybatis的配置檔案
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        // 構造sqlSession工廠
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        MprojectTaskDao mprojectTaskDao = sqlSession.getMapper(MprojectTaskDao.class);
        sched = sf.getScheduler();
        //只查詢所有在m_project_include表裡面的任務
        List<MprojectTask> mprojectTaskList = mprojectTaskDao.findList();
        for (MprojectTask mprojectTask : mprojectTaskList) {
            startJob(mprojectTask);
        }
    }

    public static void stopTask(MprojectTask mprojectTask) {
        TriggerKey triggerKey = TriggerKey.triggerKey(mprojectTask.getId(), groupName);
        try {
            sched.pauseTrigger(triggerKey);// 停止觸發器
            sched.unscheduleJob(triggerKey);// 移除觸發器
            sched.deleteJob(JobKey.jobKey(mprojectTask.getId(), groupName));// 刪除任務
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /*
     停止和暫停均可使用
     */
    public static void startTask(MprojectTask mprojectTask) {
        //無論是否關閉,先關閉任務再開啟。
        stopTask(mprojectTask);
        startJob(mprojectTask);
    }


    private static void startJob(MprojectTask mprojectTask) {
        try {
            JobDetail jobDetail = JobBuilder.newJob(TaskQuzrtzJob.class).withIdentity(mprojectTask.getId(), groupName).build();
            // 觸發器
            TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
            // 觸發器名,觸發器組
            triggerBuilder.withIdentity(mprojectTask.getId(), groupName);
            triggerBuilder.startNow();
            // 觸發器時間設定
            triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(mprojectTask.getCron()));
            // 建立Trigger物件
            CronTrigger trigger = (CronTrigger) triggerBuilder.build();
            // 排程容器設定JobDetail和Trigger
            sched.scheduleJob(jobDetail, trigger);
            // 啟動
            if (!sched.isShutdown()) {
                sched.start();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
複製程式碼

job核心程式碼

public class TaskQuzrtzJob implements Job {

    public TaskQuzrtzJob() throws IOException {
    }

    //JobExecutionContext context傳遞引數值
    public void execute(JobExecutionContext context) {
        //通過context得到該任務的所有環境
        //遍歷所有環境
            //拼湊出請求地址
            //使用封裝的框架加密傳送請求
            //得出出參解密後的結果
            //與上次進行比對,不相同則標記,入節點表
            //入記錄表
    }
}
複製程式碼

資料量

因為我們這個監控每個介面(任務)有多個環境,假設100個介面100個環境,每3分鐘監控一次,則每天記錄量為

100 * 100 * 20 * 24=4800 000條記錄,這個記錄比較驚人,所以我們分了兩張表,一張表只記錄記錄,另外一張表記錄變更,展示節點的時候只查詢變更表,可解決效能上的問題。

前端

前端採用css來畫圓和顏色

round {
    border-radius: 50%;
    text-align: center;
    width: 25px;
    height: 25px;
    line-height: 25px;
}

.on {
    border: 1px solid #7CBA23;
}
複製程式碼

線條使用line畫線

line {
    border-bottom: 1px solid gainsboro;
    height: 2px;
    width: 20px;
}
複製程式碼

整體採用angular js渲染列表

資料庫

我們使用mysql資料庫,mybatis框架連線。

異常提醒

我們會對每個介面和狀態碼進行關聯,異常時,傳送郵件,測試人員核實問題後反饋給開發,開發解決後,錄入解決方案,以便下次查詢。

總結

這個平臺已經試行1個多月了,總體滿足我們的需求,以往,我們跑測試指令碼,往往不能實時監測伺服器健康狀態,現在,任何一個環境有問題,我們都實時,並且可以統計出某個環境從什麼時間到什麼時間介面處於異常。

這套平臺,功能不多,但極大的簡化了我們的對環境穩定性的評估,也收集了大量的資料資訊便於後期統計分析。 其次,該方案稍作改造,可運用於多種監測場景中。

展望

未來,我會融入一些智慧化的分析,比如下文是某次郵件提醒的內容:

(智慧監控) 發現《A客戶私有環境》中 【獲取新聞列表】 介面異常,異常程式碼為102; 出參為{"data":null,"message":"成功","state":102}

歷史解決方案有:

  1. 到管理端重啟容器。
  2. xx表中有異常髒資料。 根據歷史統計,方案1可能性為80%,方案2為10%。

該環境介面在5分鐘前是正常的,出參為{"data":null,"message":"暫無資料","state":200}

發現有另外2個其它環境異常,分別為XXX、YYY環境。但異常狀態和該校不相似,排除介面war包bug的原因。

由於《XXX環境》其他介面在5分鐘內某次監控均為異常,所以(智慧監控) 已自主呼叫容器自愈模組,目前已恢復服務,本次收集資料耗時5分鐘,分析問題耗時100毫秒,自愈耗時60秒,該環境中斷服務時間為6分鐘,本月中斷服務總時長為15分鐘,高可用性為98.33%。

over

相關文章