今天心情不錯,公司終于簽下了一個綜合業(yè)務監(jiān)控系統(tǒng)的大單。到底有多大我也不知道,反正連軟件帶硬件不算小。按照銷售的說法,拿下這個項目一個重要的因素就是要提供一個吸引眼球的demo,而我們做的不錯!今天和大家分享一下喜悅和經(jīng)驗!
這個項目是一個省級電信運營商的綜合業(yè)務監(jiān)控系統(tǒng)。公司跟了好長時間了。由于是一個綜合的業(yè)務監(jiān)控系統(tǒng),涉及的管理資源非常多,又要提供大屏幕顯示,所以對系統(tǒng)的呈現(xiàn)效果要求非常高(“政績工程”么)。前前后后提了很多“無禮”要求,陸續(xù)提過的有3D電子地圖、3D機房監(jiān)控、場景仿真、全動畫、Google Earth、全Flash等等….弄的我們暈頭轉(zhuǎn)向、焦頭爛額。其實很多時候,用戶自己也不知道想要什么,反正對廠商的要求就是一個字:“炫”,兩個字“好看”,三個字:“一定好好看!”(不對,好像是四個字哦)。
言歸正傳,項目跟蹤過程中,商務始終告訴我們研發(fā)一定要做好一件事:如何做好呈現(xiàn),尤其是綜合的業(yè)務監(jiān)控和呈現(xiàn),這是獲得項目的關(guān)鍵,一定要“出彩”。這個“綜合呈現(xiàn)”說起來容易,做起來難。作為省級的電信運營商,內(nèi)部的各種軟硬件系統(tǒng)無數(shù),要監(jiān)控從上到下、從軟到硬,真是煩不勝煩:
- 基礎(chǔ)設施層:主要是網(wǎng)絡上的硬件設備,包括交換機、路由器、防火墻、各種主機設備服務器等等;
- 軟件層:這一層主要是主機上面運行的操作系統(tǒng)和各類業(yè)務軟件系統(tǒng),例如操作系統(tǒng)(Windows、AIX/HP-UX/LINUX/SOLARIS)、各種中間件(WebLogic、JBoss、IIS、WebSphere等)、數(shù)據(jù)庫(Oracle、Sybase、MySQL)等;
- 應用層:這一層是指運行在軟件層內(nèi)部的一些細粒度資源,包括一些關(guān)鍵的軟件進程、數(shù)據(jù)庫表空間、業(yè)務連接、消息隊列等等。這一層數(shù)量繁雜、數(shù)量眾多。不過這些資源的狀態(tài)直接會影響其上層支撐的業(yè)務。
- 業(yè)務層:業(yè)務層是最頂層,由以上底層資源所共同支撐起來的面向用戶的各種業(yè)務。對業(yè)務最容易理解的描述就是電信提供給客戶的具體“服務”。例如視頻業(yè)務、短信業(yè)務、WAP業(yè)務、專網(wǎng)業(yè)務等等。這些業(yè)務才是用戶最終關(guān)心的東西,因為這些業(yè)務才是客戶的核心資產(chǎn),是拿來賣錢的最終產(chǎn)品。一旦出問題,將直接影響money!
此外,還有一大堆機房環(huán)境監(jiān)控的要求,什么配電柜供電、開關(guān)狀態(tài)、UPS、蓄電池、空調(diào)、適度溫度漏水消防通風門禁視頻………一大堆。所以,要對業(yè)務進行監(jiān)控,就必須對業(yè)務所支撐的各個底層資源進行監(jiān)控。另外,還要能夠?qū)@些資源的關(guān)系進行連接和定義,一旦發(fā)生故障和問題,能夠從上到下迅速定位故障起源,在最短時間內(nèi)發(fā)現(xiàn)問題、解決問題。如何呈現(xiàn)這些依賴關(guān)系,并對故障和告警進行縱向定位,是軟件呈現(xiàn)的一個核心問題,也是用戶最關(guān)心的一個問題。
用戶要求我們先制作一個demo程序,看我們將如何呈現(xiàn)“綜合監(jiān)控”的效果。在充分了解了用戶需求之后,經(jīng)過討論,我們想做一個完全圖形化的分層、跨層的綜合監(jiān)控界面。界面要美觀,有動畫效果,能夠清晰的顯示資源依賴關(guān)系和告警傳播定位。

需要監(jiān)控和管理的資源
接下來要寫代碼了。肯定先用我熟悉的TWaver試試。研究了一下,TWaver中有一個平行四邊形的Group對象,適合做上圖中的“層”的概念。先如下封裝并設置屬性:
1
package demo;
2
3
import java.awt.Color;
4
import twaver.Group;
5
import twaver.TWaverConst;
6
7
public class LayerGroup extends Group
{
8
9
public LayerGroup()
{
10
init();
11
}
12
13
public LayerGroup(Object id)
{
14
super(id);
15
init();
16
}
17
18
private void init()
{
19
this.setGroupType(TWaverConst.GROUP_TYPE_PARALLELOGRAM);
20
this.putGroupAngle(45);
21
22
this.putGroup3D(true);
23
this.putGroupDeep(10);
24
this.putGroupOutline(false);
25
this.putGroupFillColor(Color.green.darker());
26
this.putGroupGradient(true);
27
this.putGroupGradientFactory(TWaverConst.GRADIENT_LINE_E);
28
this.putGroupHandlerVisible(false);
29
this.putGroupDoubleClickEnabled(false);
30
this.putBorderColor(Color.white);
31
this.putBorderInsets(3);
32
this.putBorderAntialias(true);
33
this.putBorderStroke(TWaverConst.STROKE_SOLID_4);
34
this.putBorderVisible(false);
35
this.putLabelHighlightable(false);
36
37
this.setEnableAlarmPropagationFromChildren(false);
38
}
39
}
40
通過這個簡單的封裝,再往Group里頭放幾個節(jié)點和連線,顯示效果如下:

用Group制作的“層”效果
怎么樣,有點意思吧?開頭不錯,繼續(xù)改進!再依次排列4個Group,用不同顏色,試試效果:
1
createLayer(Color.orange, 50, 0, 10, "7.png", "<html><center>軟件<br>業(yè)務層</center></html>");
2
createLayer(Color.green.darker(),180, 200, 15, "8.png", "<html><center>技術(shù)<br>應用層</center></html>");
3
createLayer(Color.magenta.darker(),280, 350, 5, "5.png", "<html><center>技術(shù)<br>軟件層</center></html>");
4
createLayer(Color.cyan.darker(),400, 570, 7, "1.png", "<html><center>基礎(chǔ)<br>設施層</center></html>");
5
以上代碼封裝了創(chuàng)建一個層的函數(shù),給定顏色、坐標位置、內(nèi)部節(jié)點數(shù)量、圖標、文字等等。上面代碼中的HTML風格字符串是為了在TWaver中(好像Swing中也是這樣的)顯示換行的標簽。每一個層作為容器包含了很多不同類型的資源。顯示效果如下圖:

四層拓撲圖顯示效果
注意其中的連線有下垂的彎曲效果。這是我以前在做項目封裝過的一個TWaver技巧:通過重寫twaver的Link的UI類,重新指定path走向?qū)崿F(xiàn)的。其實也很簡單,首先獲得link的from點和to點,取值中間點,再把y縱向增加20,把這個點作為quadTo的控制點畫曲線即可。對TWaver熟悉的朋友可以看一下這段代碼(其實這個效果也是從TWaver Java的demo源代碼中學習到的):
1
package demo;
2
3
import java.awt.Point;
4
import java.awt.geom.GeneralPath;
5
import twaver.network.TNetwork;
6
import twaver.network.ui.LinkUI;
7
8
public class InnerLinkUI extends LinkUI
{
9
10
public InnerLinkUI(TNetwork network, InnerLink link)
{
11
super(network, link);
12
}
13
14
@Override
15
public GeneralPath getPath()
{
16
GeneralPath customPath = new GeneralPath();
17
Point p1 = this.getFromPoint();
18
Point p2 = this.getToPoint();
19
customPath.moveTo(p1.x, p1.y);
20
int offset = 20;
21
customPath.quadTo((p1.x + p2.x) / 2, (p1.y + p2.y) / 2 + offset, p2.x, p2.y);
22
return customPath;
23
}
24
}
25
用這種link做出的拓撲圖比較生動美觀。多加幾個節(jié)點連線就能看出來了:

四層復雜拓撲圖顯示效果
不過發(fā)現(xiàn)平行四邊形Group一個問題:當兩個Layer疊加后,下面的節(jié)點會被完全覆蓋,看不見了。用戶說:能不能也能看見?(暈,蓋住了也要看見。誰讓人家是甲方呢?)于是詢問TWaver的人,一個哥們說Group有透明屬性。于是試了一下,效果不還錯:
1
this.putGroupOpaque(false);

層的透明與覆蓋
下一步,關(guān)鍵了:要增加層與層之間資源的“依賴關(guān)系”。例如一個Oracle跑在一臺主機上,而Oracle中的一個關(guān)鍵表空間需要重點監(jiān)控,它決定了上層一個視頻點播業(yè)務是否能夠正常。為了體現(xiàn)這個依賴關(guān)系,在跨層的節(jié)點中間建立link。這個link和層內(nèi)部link顯示上應當有所區(qū)別:
1
package demo;
2
3
import java.awt.Color;
4
import twaver.Link;
5
import twaver.Node;
6
import twaver.TWaverConst;
7
import twaver.base.OrthogonalLinkDirectionType;
8
9
public class LayerLink extends Link
{
10
public LayerLink(Node from, Node to)
{
11
super(from, to);
12
init();
13
}
14
15
public LayerLink(Object id, Node from, Node to)
{
16
super(id, from, to);
17
init();
18
}
19
20
private void init()
{
21
this.putLink3D(true);
22
this.putLinkWidth(4);
23
this.putLinkOutlineWidth(0);
24
this.putLinkColor(Color.lightGray);
25
this.putLinkAntialias(false);
26
this.setLinkType(TWaverConst.LINK_TYPE_ORTHOGONAL);
27
}
28
}
29
顯示出來后,效果并不理想,有點亂。主要是沒有“跨層”的立體感。

跨層連線效果
圖中跨層的link沒有呈現(xiàn)出“穿透層”的感覺,多了以后反而破壞了整個拓撲圖的立體感和生動感,需要再改進。最好能夠顯示“穿層而過”的效果。需求變態(tài)么?不弄點猛藥還想拿單子么,程序員就是要與各種“不可能”說“不”嘛!經(jīng)過反復研究和實驗,終于做出了一個更好的效果,如下圖:

連線的跨層穿透效果
注意觀察其中穿層效果,不知大家是否喜歡?

連線的透明穿透
怎么做到的呢?其實也簡單,一點就破,我就不點破了吧,賣個關(guān)子先。大家可以先猜猜看,源代碼里頭也能看到答案。接下來,可以增加一些跨層連線了!看看下圖效果:

跨層連線的綜合效果圖
效果還不錯吧?銷售看過后非常滿意,連說有新意。不過還有最后一個很頭大的問題:需要顯示告警及其傳播路線,也就是告警發(fā)生后,要從底層一直沿著依賴關(guān)系傳播到上層。于是開始研究TWaver的AlarmPropagator告警傳播器。通過研究發(fā)現(xiàn),其實告警傳播也不復雜,主要原理是當告警發(fā)生后,它會根據(jù)AlarmPropagator的“指示”和定義的規(guī)則,沿著一個特定的“路徑”進行告警傳播。被傳播過的地方,會顯示一個有告警顏色的外框,標志其告警狀態(tài)。
但是問題是,TWaver的告警傳播器是按照“父子關(guān)系”進行傳播的。也就是默認情況下,告警總是從孩子傳給父親,一直到?jīng)]有parent為止。按照這個規(guī)則,這個demo中一個節(jié)點發(fā)生告警后,會傳播給平行四邊形這個層對象,這顯然是沒有意義的,不符合我的要求。我們需要告警沿著層的“依賴關(guān)系”進行跨層傳播。于是重寫AlarmPropagator!也不難,調(diào)試了幾個小時,用一個遞歸法總算搞定了。代碼如下:
1
package demo;
2
3
import java.util.Collection;
4
import java.util.Iterator;
5
import twaver.AlarmSeverity;
6
import twaver.Element;
7
import twaver.Node;
8
9
public class DemoPropagator
{
10
11
public void propagate(Element element)
{
12
AlarmSeverity severity = element.getAlarmState().getHighestNativeAlarmSeverity();
13
if (element instanceof Node)
{
14
Node node = (Node) element;
15
16
Collection links = node.getAllLinks();
17
if (links != null && !links.isEmpty())
{
18
Iterator it = links.iterator();
19
while (it.hasNext())
{
20
Object o = it.next();
21
if (o instanceof LayerLink)
{
22
LayerLink link = (LayerLink) o;
23
if (link.getAlarmState().isEmpty())
{
24
link.getAlarmState().addAcknowledgedAlarm(severity);
25
26
Node anotherNode = link.getFrom();
27
28
if (anotherNode.getAlarmState().isEmpty())
{
29
anotherNode.getAlarmState().addAcknowledgedAlarm(severity);
30
if (anotherNode != node)
{
31
propagate(anotherNode);//這里遞歸!
32
}
33
}
34
}
35
}
36
}
37
}
38
}
39
}
40
}
41
這里代碼的邏輯主要是判斷是不是跨層link,如果是就沿著它進行傳播。噢吼!上面代碼好像泄露了上面“穿透Layer”的秘密了,呵呵。最后,再來一個“告警模擬器”來模擬隨機、隨時發(fā)生告警,也就是用一個單獨的線程在里面sleep然后生成Alarm并發(fā)送到拓撲圖的節(jié)點上。直接上代碼:
1
package demo;
2
3
import java.util.Iterator;
4
import javax.swing.SwingUtilities;
5
import twaver.AlarmSeverity;
6
import twaver.Element;
7
import twaver.TDataBox;
8
import twaver.TWaverUtil;
9
10
public class AlarmMocker extends Thread
{
11
12
private TDataBox box = null;
13
private DemoPropagator propagator = new DemoPropagator();
14
15
public AlarmMocker(TDataBox box)
{
16
this.box = box;
17
}
18
19
@Override
20
public void run()
{
21
while (true)
{
22
try
{
23
Thread.sleep(1 * 1000);
24
} catch (InterruptedException ex)
{
25
ex.printStackTrace();
26
}
27
28
SwingUtilities.invokeLater(new Runnable()
{
29
30
public void run()
{
31
32
if (TWaverUtil.getRandomInt(5) == 1)
{
33
//clear all alarm and propagation.
34
Iterator it = box.iterator();
35
while (it.hasNext())
{
36
Element e = (Element) it.next();
37
e.getAlarmState().clear();
38
}
39
}
40
41
Element element = box.getElementByID("4." + TWaverUtil.getRandomInt(10));
42
if (element != null)
{
43
element.getAlarmState().addNewAlarm(AlarmSeverity.getRandomSeverity());
44
propagator.propagate(element);
45
}
46
}
47
});
48
}
49
}
50
}
告警模擬器把最底層的里面的節(jié)點隨機產(chǎn)生告警,再隨機的清除,模擬現(xiàn)實網(wǎng)絡的監(jiān)控狀態(tài)。然后運行demo,觀察其告警傳播的路線是否符合預期,也就是沿著層進行傳播。
注意一個細節(jié):由于上面告警模擬器在一個單獨的Thread線程中運行,在產(chǎn)生告警并更改界面時候,需要用SwingUtilities.invokeLater進行代碼封裝調(diào)用,保證它在Swing線程中執(zhí)行,避免屏幕和界面“花”或不可預知的顯示結(jié)果。唉,誰讓Swing是單線程而且是線程不安全呢?這是個古老話題,就不羅嗦了。
廢話不多說,直接上最終效果圖:

demo最終運行界面

雙擊層動畫旋轉(zhuǎn)并放大
看到告警跨層傳播的效果了嗎?最后,根據(jù)客戶的要求,又增加了一些動畫效果:雙擊平行四邊形后,平行四邊形會動畫變成矩形、動畫飛到屏幕中間,然后動畫放大內(nèi)部拓撲圖,供用戶查看細節(jié);再次雙擊,平行四邊形快速旋轉(zhuǎn)縮小并回到原來位置。demo程序交付并演示后,獲得客戶高度評價。用我們商務人員的話來說就是:“我們的demo最出彩!”作為程序員,自己做的東西能為公司創(chuàng)造價值和利潤就是最大的肯定和成就感!
由于demo摻雜了不少公司的代碼,我花了一點時間整理一下,弄出了一個干凈的demo代碼,請見附件,請感興趣的朋友請直接下載,歡迎留言討論。 解壓demo.zip后,中有兩個jar包和run.bat,雙擊run.bat就可以運行demo。我是用JDK6編譯的,請各位確保首先安裝了JAVA 6環(huán)境。如有任何運行問題請留言。
demo所限內(nèi)容僅供技術(shù)交流和參考,請慎作商業(yè)用途。
補充:
看到留言中很多朋友都說“第一次看到用程序做demo”,也把我弄“驚詫”了。因為自己從業(yè)這些年基本上都是用程序做demo,已經(jīng)習以為常,甚至成為理所當然了。看到很多朋友說“用PPT和美工圖片”做DEMO,確實感覺自己有點out了。在電信、電力這些行業(yè)里面,項目都比較大、復雜,沒有一定的商務和技術(shù)實力以及經(jīng)驗是很難獲得項目機會的。PPT作為產(chǎn)品介紹和各種交流肯定是沒問題的;但如果說要做大項目的“系統(tǒng)演示”那可是夠“空對空”的。可以想象一下:如果我們是局方、甲方或客戶,要花幾百萬做一個項目,給各個廠商一個月的時間來準備一個技術(shù)交流和產(chǎn)品演示,我們是愿意把項目交給用精美PPT演示的廠商,還是愿意給效果同樣漂亮但用實實在的程序或DEMO來演示的廠商呢?誰家更有實力?誰家更重視項目?誰家更有技術(shù)和人才?誰家更有想法?誰家更有經(jīng)驗?這個問題幾乎不用回答。
一個有實力的軟件公司,打大單不拿點真家伙,光靠美工弄幾個花里胡哨的圖片或者PPT就能拿下項目,那絕對是太不可思議了。我接觸的這些公司哪個不是靠多年的行業(yè)積累和系統(tǒng)經(jīng)驗,哪次面對這樣的大戰(zhàn)役不是要抽出一兩個技術(shù)好手來加班加點的做些真家伙(半真也行,老系統(tǒng)扣出一部分模塊和代碼,就算在此基礎(chǔ)上修修改改去運行也可)去演示?甚至有的時候去演示的幾乎就是可以上線的系統(tǒng),或在另外一個老的類似項目緊急改進出來的半成品系統(tǒng)。如果沒有這些項目和技術(shù)經(jīng)驗做沉淀和積累,一旦中標,很多時候系統(tǒng)上線只給3個月的時間,完全從頭來個瀑布式的從需求分析、從頭開發(fā)?做夢吧。現(xiàn)在行業(yè)競爭越來越慘烈,客戶要求越來越高越復雜,技術(shù)變化快,你不能做到又快又好的提供系統(tǒng)和解決方案,只能靠邊站了。
這是一個大的監(jiān)控系統(tǒng),涉及很多子系統(tǒng)和其他業(yè)務系統(tǒng)。這里僅僅是一個簡潔的高層次全網(wǎng)監(jiān)控界面,是很小一部分,是一個小UI而已。但是這個UI會被投射到一個2*4的大型液晶屏幕墻上,讓全屋子的人抬頭就能看到全網(wǎng)的鏈接情況、告警情況;讓公司領(lǐng)導經(jīng)常過來看到;讓兄弟省市領(lǐng)導經(jīng)常參觀到。所以,它的作用不可小視(提高形象的作用不也是作用么)。這也是為什么客戶這么重視的原因。另外麻雀雖小,五臟俱全;以管窺豹也可以時見一斑。我們搞技術(shù)的就是應當多思考多創(chuàng)新多學習多交流、踏踏實實在細節(jié)上下功夫,也許才能更好的體現(xiàn)我們的價值。
如果demo所體現(xiàn)出來的UI呈現(xiàn)思路和設計想法或者代碼中的技巧能夠給大家?guī)硪稽c點啟發(fā),本人就感到非常非常滿足了!
謝謝各位!

附:帶窗口的Demo運行圖