在體驗完這個簡單的插件之後,接下來要登場的就是難度比較高一點的插件了。插件可以被任何語言所寫成,只要它是能被執行,並且反回特定格式的訊息,它的內容是c也好,java也好,perl也好,都是被允許的。用什麼語言來實作並不是一件重要的事,只要能夠達成我們想要的目的,就算及格,所以對java不熟的讀者,看到這一章也不用灰心,因為只要你懂了觀念,你可以用你所會的語言,來將這一個插件,進行改寫。
●需求定義與設計
所有的插件的產生,都是來自於需求。
假設,有一天,在上班的時後,專案經理對小明提出了一個要求,希望小明能夠利用nagios完美的性能,開發出一個定期監視台積電的股價的插件,並且在股價發生變動時,發一封信通知專案經理進場護盤。
接到這一個要求的小明馬上就列出了下面這一個規格書,並且請經理作確認:
輸入:股票代碼目標價 停損價
輸出:當股票現值超過目標價,則輸出警告,低於停損價,則發出危急
在這個簡單的介面定義書裡,我們也提供了高值與低值的設定選項,因為我們不曉得專案經理對於股價的企圖心在那裡,所以我們把這個設定值空出來讓經裡自行設定。
與專案經理進行確認之後,就開始要準備進行開發的工作。
●開發
小明評估了需求,以及時間,決定爭取時效,使用有很多方便的程式庫的java語言,來作為開發的工具。
第一步:取得即時的股價:
為了要知道股票目前最近的股價,唯一的來源就是一些入口網站提供的服務了。所以小明決定到yahoo財經,來取得即時的股價。比方說,台積電的股價,只要訪問下列這一個網站,就可以得到所需的資料了。
http://tw.stock.yahoo.com/q/q?s=2330
為了要能夠及時的去把這一個網頁的資料抓回來,小明決定使用由JakartaCommons所提供的HttpClient程式庫。這一個專案的網頁位在
http://jakarta.apache.org/commons/httpclient/
在下載回來之後,小明使用了這個程式庫,寫了一段抓取網頁的程式碼:
程式碼:
InputStream responseBody=method.getResponseBodyAsStream(); //使用中文的編碼,準備將網頁讀進來 BufferedReader br=newBufferedReader(newInputStreamReader(responseBody,"Big5") );
網頁讀取進來之後,一大段的文字並沒有什麼實用的價值,小明必須要想辦法在這一大段的文字裡,找到一個專案經裡所關心的資訊,也就是當時的股價。此時考慮到時效性,小明決定不採用由另外一個專案所提供的html解析套件,改採暴力破解法。
小明使用了文字編輯器,將取得的html原始碼一行一行的往下數下來時,發現當前的股價所記載的位置,正好是在第164行的位置。於是,小明寫了一個回圈,來將取得的大段文章裡,找到164行的內容。正可謂是,股海淘淘,只取一飄呀!
在以上的方法裡我們作了二件事情,一件事是價格比較,另一件事將結果以nagios所規定的格式與以反回,並且報知目前的價格,以便讓使用者可以在cgi的畫面上得到更多的資訊。
?
三、選項的讀取
在前面的二個步驟之後,接下來所剩下的,就是讀取外部的選項了。在之前的介面定義書裡,我們知道這個程式需要接受三個選項,而且我們也決定要遵守開發的原則,在使用者輸入了錯誤的選項之時,能夠回送出適當的提示訊息,所以我們決定,使用由由JakartaCommons所提供的CLI:Command LineInterface程式庫。這一個專案的網頁位在
http://jakarta.apache.org/commons/cli/
在參照了文件的說明之後,小明依照需求,寫出了如下的程式碼:
上面我們預先設定了code,top price,lowprice的三個選項,當使用者有輸入三個值的時後,才讓程式運行,沒有輸入或是輸入不正確,少了那些項目之時,則列印出錯誤的訊息,來提醒使用者。這一個程式庫有一個好處是,我們定義好選項之後,其他的事情我們都不用多寫程式碼,比方說我們要印出輔助訊息的method,程式碼只有下面這幾行:
是不是相當的簡單呢?
接下來,我們要把在使用者所輸入的這三個值,一一的傳給之前所開發的程式,傳遞的方法,分別是:
將code 傳給 網頁取得method
將tp,lp傳給價格比較method
程式碼如下所示:
在這個式裡我們將比較的結果利用system.out來作輸出,這結果會直接被傳送到stdout上,然後由nagios所接受。
到此,小明已經完成了核心程式的工作,在此我們先將完整的程式碼給列出來:
接下來我們要進行編譯的動作,由於這個程式我們用到了
1 httpclient
2 cli
這兩個程式庫,所以我們在編譯時,必須要將這兩個含式庫加到路徑裡面:
完成之後,我們試著執行所產生的class檔,注意,因為我們程式碼裡有引用這兩個程式庫,所以執行之時,也一樣要在路徑裡作宣告。
此時,因為我們什麼都沒有輸入,所以依照所排演的,應該要吐出一些訊息:
還有也作出了下列的元件部屬圖
我們將可以進行設定的部份以java語言來實作,將會是如以下的部份:
因為需要接受語多的命令,所以採用了common之下的getopt這一個方便精巧的功具:
這個工具可以幫助我們產生一些命令行,提示使用者該如何使用命令,以及讀取使用者的命令,非常的方便。
還有,我們也知道股價的變動是一天不會超過或低於百分之七的,所以如果經裡的設定值,與現在的股價比起來,如果低於這個範圍,那我們就可以使得這一個插件休兵一天,不用一直去查詢網站。而相反的,如果說這一天的股價,很有可能到達這一個設定值,則我們就要希望能夠增加我們訪問這個股價的數量。
將這一個邏輯以java語言來作表示的話,將會是如下的範例:
接下來是這個程式核心的邏輯部份,也就是我們要從網站上得到股價:
這個時候我們使用的是commons程式庫的httpclient
我們始我下面這一段程式碼來存取網站:
程式碼將如下所示:
這個程式碼送出一個get要求,從yahoo網站上得到了台積電網頁。
這一個網頁,裡的第一行是股價,所以我們試著取得網頁上的第一行。
然後這一個股價的位置位於這一行的第三個TDTAG之間,所以我們利用一個簡單的regexp,來取得這一個股價的位置。
到目前為止,我們已經完成了這一個插件的大半部份,接下來就是將它們組合起來,成為一個完整的程式:
將這個程式放到nagios的server上,接著,進行編譯及測式:
javacxxxx
執行這個程式:
java–cpxxxxxxxxx
結果我們發現這一個結果:
依照這一個命令所是示的,我們假設我們的股價是100元,作出以下的設定:
結果nagios反回了這一個結果:
因為現在台積電的股價還低於設定值,所以ok。
接著我們假設我們的目標股價是50元
出現了下列這一個訊息:
critical
果然台積電的股價以經有點危險了。
看來沒什麼問題了。接下來我們將這個plugin安裝到nagios上。
然後,如果這個股價超過了這一個臨界值之後,通當會超過之後就一直超過,或是超過之後又低回來,當股價超過之後,如果一直不停的發送信息也不是辦法。
所以我們要對這個細節,來對nagios作出設定:
設定檔如下所示:
一切就緒之後,接下來我們打開畫面,出現了以下的場景:
接下來的問題,如果要將這個plug擴充成不只讓台責電能夠使用,該怎麼作好?
我想很多程式高手們早就發覺該怎麼做了,不過我還是雞婆一下把這個程式碼給出來,看了這個程式完成之後,我們就可以將我們所想要注意的股票一隻一隻的登入上去:
畫面如下所示:
不過,這一個程式還有一個問題,那就是程式股價如果到了設定值了,就會一直呈現緊急的狀態,當然,此時我們可以從新的設定我們的目標,或者是經由cgi來關閉股價的通知。
或者,你也可以提供一個到達目標之後,自動再設定目標的超強大機能,不過這一個內容,就交由讀者來自行發揮了。
packagecom.kbmj.someproject;importjava.io.BufferedReader;importjava.io.IOException;importjava.io.InputStream;importjava.io.InputStreamReader;importorg.apache.commons.cli.BasicParser;importorg.apache.commons.cli.CommandLine;importorg.apache.commons.cli.CommandLineParser;importorg.apache.commons.cli.HelpFormatter;importorg.apache.commons.cli.Option;importorg.apache.commons.cli.Options;importorg.apache.commons.cli.ParseException;importorg.apache.commons.httpclient.HttpClient;importorg.apache.commons.httpclient.HttpException;importorg.apache.commons.httpclient.HttpStatus;importorg.apache.commons.httpclient.methods.GetMethod;public classApp { publicstaticvoidmain(String args[]){ floattarget_price =0; floatlow_price =0; intcode=2330; Options options =newOptions(); //option 1 Option target_price_opt =newOption("tp", true, "TargetPrice,forexample -tp=58.5 "); target_price_opt.setRequired(true); // option2 options.addOption(target_price_opt); Option low_price_opt =newOption("lp", true, "LowPrice,forexample -lp=58.5 "); low_price_opt.setRequired(true); // option3 options.addOption(low_price_opt); Option code_opt =newOption("c",true,"code,forexample-c=2330"); code_opt.setRequired(true); options.addOption(code_opt); //3.然後parseinput CommandLineParser parser =newBasicParser(); CommandLine cmd; try{ cmd = parser.parse(options, args); } catch(ParseException pe) { //4.發生錯誤就用usage() method給使用這提示 usage(options); return; } // if(cmd.hasOption("tp")){ target_price=Float.parseFloat(cmd.getOptionValue("tp")); } if(cmd.hasOption("lp")){ low_price=Float.parseFloat(cmd.getOptionValue("lp")); } if(cmd.hasOption("c")){ code=Integer.parseInt(cmd.getOptionValue("c")); } //System.out.println("code = "+code); try{ floatcurrent= getCurrentValue(code); System.out.println(compareValues(code,current,target_price,low_price)); }catch(Exception e) { System.out.println("Critical;"+e.getMessage() ); } } privatestaticString compareValues(intcode ,floatcurrent,floattarget_price ,floatlow_price ) { intstatus =-1; String result="UNKNOWN"; String message="["+code +"]current:"+current+",targetis"+target_price+",lowis"+low_price; if(current > target_price ) status =0; elseif(current > low_price ) status =1; elseif(current < low_price ) status =2; switch(status) { case0: result="WARNING;"; break; case1: result="OK;"; break; case2: result="CRITICAL;"; break; case-1: result="UNKNOWN;"; break; } returnresult+message; } privatestaticvoidusage(Optionsoptions) { HelpFormatter formatter =newHelpFormatter(); formatter.printHelp("CommandLineUI", options); } privatestaticfloatgetCurrentValue(intcode)throwsHttpException, IOException{ floatp=-1; String url="http://tw.stock.yahoo.com/q/q?s="+code; //HttpClientの生成 HttpClient client=newHttpClient(); //methodインスタンスの生成 GetMethod method =newGetMethod(url); intstatusCode = client.executeMethod(method); if(statusCode != HttpStatus.SC_OK) { //notok; returnp; //System.out.println("失敗:"+method.getStatusLine()); }// HTMLソースを取得 InputStream responseBody=method.getResponseBodyAsStream(); //getdatainnoXXX BufferedReader br=newBufferedReader(newInputStreamReader(responseBody,"Big5") ); String line=null; intline_no=0; while((line = br.readLine())!=null){ line_no++; if(line_no ==164){ } returnp; }}
このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー と 利用規約 が適用されます。
1文字以上入力してください
本文は少なくとも1文字以上必要です。
1文字以上入力してください。
下から選んでください: