在體驗完這個簡單的插件之後,接下來要登場的就是難度比較高一點的插件了。插件可以被任何語言所寫成,只要它是能被執行,並且反回特定格式的訊息,它的內容是c也好,java也好,perl也好,都是被允許的。用什麼語言來實作並不是一件重要的事,只要能夠達成我們想要的目的,就算及格,所以對java不熟的讀者,看到這一章也不用灰心,因為只要你懂了觀念,你可以用你所會的語言,來將這一個插件,進行改寫。
●需求定義與設計
所有的插件的產生,都是來自於需求。
假設,有一天,在上班的時後,專案經理對小明提出了一個要求,希望小明能夠利用nagios完美的性能,開發出一個定期監視台積電的股價的插件,並且在股價發生變動時,發一封信通知專案經理進場護盤。
接到這一個要求的小明馬上就列出了下面這一個規格書,並且請經理作確認:
輸入:股票代碼目標價 停損價 輸出:當股票現值超過目標價,則輸出警告,低於停損價,則發出危急 |
在這個簡單的介面定義書裡,我們也提供了高值與低值的設定選項,因為我們不曉得專案經理對於股價的企圖心在那裡,所以我們把這個設定值空出來讓經裡自行設定。
與專案經理進行確認之後,就開始要準備進行開發的工作。
●開發
小明評估了需求,以及時間,決定爭取時效,使用有很多方便的程式庫的java語言,來作為開發的工具。
第一步:取得即時的股價:
為了要知道股票目前最近的股價,唯一的來源就是一些入口網站提供的服務了。所以小明決定到yahoo財經,來取得即時的股價。比方說,台積電的股價,只要訪問下列這一個網站,就可以得到所需的資料了。
http://tw.stock.yahoo.com/q/q?s=2330
為了要能夠及時的去把這一個網頁的資料抓回來,小明決定使用由JakartaCommons所提供的HttpClient程式庫。這一個專案的網頁位在
http://jakarta.apache.org/commons/httpclient/
在下載回來之後,小明使用了這個程式庫,寫了一段抓取網頁的程式碼:
程式碼: String url="http://tw.stock.yahoo.com/q/q?s=2330";//生成一個httpclient的物件 HttpClient client=new HttpClient(); //生成一個get方法的物件 GetMethod method = new GetMethod(url); intstatusCode = client.executeMethod(method); if(statusCode != HttpStatus.SC_OK) { //如果訪問失敗時的處理; System.out.println("失敗:"+method.getStatusLine()); } //網頁連線成功,開始取得網頁的資料 InputStream responseBody=method.getResponseBodyAsStream(); |
網頁讀取進來之後,一大段的文字並沒有什麼實用的價值,小明必須要想辦法在這一大段的文字裡,找到一個專案經裡所關心的資訊,也就是當時的股價。此時考慮到時效性,小明決定不採用由另外一個專案所提供的html解析套件,改採暴力破解法。
小明使用了文字編輯器,將取得的html原始碼一行一行的往下數下來時,發現當前的股價所記載的位置,正好是在第164行的位置。於是,小明寫了一個回圈,來將取得的大段文章裡,找到164行的內容。正可謂是,股海淘淘,只取一飄呀!
程式碼: BufferedReaderbr=newBufferedReader(newInputStreamReader(responseBody, "Big5") );String line=null; intline_no=0; while((line = br.readLine())!= null ){ line_no++; if(line_no ==164){ |
<tdalign="center"bgcolor="#FFFfff"nowrap><b>54.00</b></td> |
程式碼 3 |
---|
# |
程式碼 4:用來取得即時股價的方法 |
---|
# |
程式碼:價何比較的方法 |
privatestatic String compareValues(intcode , floatcurrent, floattarget_price , float low_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; } |
在以上的方法裡我們作了二件事情,一件事是價格比較,另一件事將結果以nagios所規定的格式與以反回,並且報知目前的價格,以便讓使用者可以在cgi的畫面上得到更多的資訊。
?
?
三、選項的讀取
在前面的二個步驟之後,接下來所剩下的,就是讀取外部的選項了。在之前的介面定義書裡,我們知道這個程式需要接受三個選項,而且我們也決定要遵守開發的原則,在使用者輸入了錯誤的選項之時,能夠回送出適當的提示訊息,所以我們決定,使用由由JakartaCommons所提供的CLI:Command LineInterface程式庫。這一個專案的網頁位在
http://jakarta.apache.org/commons/cli/
在參照了文件的說明之後,小明依照需求,寫出了如下的程式碼:
?
?
程式碼: |
---|
Options options
=new Options(); //option 1 Option target_price_opt = new Option("tp", true, "TargetPrice,forexample -tp=58.5 "); target_price_opt.setRequired(true ); // option2 options.addOption(target_price_opt); Option low_price_opt = new Option("lp", true, "LowPrice,forexample -lp=58.5 "); low_price_opt.setRequired(true ); // option3 options.addOption(low_price_opt); Option code_opt = new Option("c",true,"code,forexample-c=2330"); code_opt.setRequired(true ); options.addOption(code_opt); //3.然後parseinput CommandLineParser parser = new BasicParser(); CommandLine cmd; try{ cmd = parser.parse(options, args); } catch(ParseException pe) { //4.發生錯誤就用usage() method給使用這提示 usage(options); |
上面我們預先設定了code,top price,lowprice的三個選項,當使用者有輸入三個值的時後,才讓程式運行,沒有輸入或是輸入不正確,少了那些項目之時,則列印出錯誤的訊息,來提醒使用者。這一個程式庫有一個好處是,我們定義好選項之後,其他的事情我們都不用多寫程式碼,比方說我們要印出輔助訊息的method,程式碼只有下面這幾行:
程式碼: |
---|
private static voidusage(Optionsoptions){ HelpFormatter formatter = new HelpFormatter(); formatter.printHelp("CommandLineUI", options); } |
是不是相當的簡單呢?
接下來,我們要把在使用者所輸入的這三個值,一一的傳給之前所開發的程式,傳遞的方法,分別是:
將code 傳給 網頁取得method
將tp,lp傳給價格比較method
程式碼如下所示:
程式碼: |
---|
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() ); } |
在這個式裡我們將比較的結果利用system.out來作輸出,這結果會直接被傳送到stdout上,然後由nagios所接受。
到此,小明已經完成了核心程式的工作,在此我們先將完整的程式碼給列出來:
股價監視程式完整程式列表: |
# |
接下來我們要進行編譯的動作,由於這個程式我們用到了
1 httpclient
2 cli
這兩個程式庫,所以我們在編譯時,必須要將這兩個含式庫加到路徑裡面:
javac -cp XXXX.jar:XXXXX.jar :./ App.java |
完成之後,我們試著執行所產生的class檔,注意,因為我們程式碼裡有引用這兩個程式庫,所以執行之時,也一樣要在路徑裡作宣告。
java -cp XXXX.jar:XXXXX.jar :./ App |
此時,因為我們什麼都沒有輸入,所以依照所排演的,應該要吐出一些訊息:
還有也作出了下列的元件部屬圖
#元件部屬圖 |
---|
# | 元件部屬圖 | # |
---|---|---|
# | # | # |
# | 由以上的圖可以看出,在黑盒子的部份是主要開發的原件,這個原件,而由於在server上要運行java的程式,需要設定一些路徑,所以為了簡單起見,小明寫了一個shell語言,來將執行時的複雜部份包裝起來。 | # |
我們將可以進行設定的部份以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; |