程序整體框架概述
所接觸的是數(shù)據(jù)采集方面的任務(wù)况鸣,整個數(shù)據(jù)采集模塊主框架被封裝在一個a.jar包之中搔驼,程序入口common通過啟動命令指定,使用nohuo與&指令將服務(wù)運(yùn)行在后臺:
nohup java -cp 'pwd'/a.jar main.common [args]>>nohup.out &
進(jìn)入common繼續(xù)往下看:
首先關(guān)注啟動命令中所指定的args參數(shù)签赃,此參數(shù)指定配置文件路徑痪欲,(程序啟動后咬腋,a.jar文件所在目錄即為根目錄绢馍,指定路徑以此為基礎(chǔ))向瓷。通過jar包sysconfig類中定義的靜態(tài)方法,讀取cfg配置文件內(nèi)容舰涌,依次賦值給sysconfig類中的靜態(tài)變量猖任,作后續(xù)使用。
之后利用jar包Server類中的ServerRun靜態(tài)方法瓷耙,通過讀取上述sysconfig類中的port靜態(tài)變量朱躺,使用mina框架提供的socket通信方法,監(jiān)聽本機(jī)端口搁痛。
public static void ServerRun(int PORT) throws Exception {
SocketAcceptor acceptor = new NioSocketAcceptor();
acceptor.setReuseAddress(true);
DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
chain.addLast("threadPool", new ExecutorFilter(Executors.newFixedThreadPool(sysconfig.socket_poolsize)));
acceptor.setHandler(new Server());
acceptor.getSessionConfig().setUseReadOperation(true);
acceptor.getSessionConfig().setReadBufferSize(30000);
acceptor.bind(new InetSocketAddress(PORT));
System.out.println("Listening on port " + PORT);
for (;;) {
Thread.sleep(1000);
}
}
如此一來长搀,通過telnet連接至指定端口,就可以做到與程序進(jìn)行交互鸡典。Server類繼承mina框架的IoHandAdapter類源请,便于管理連接,[mian Iohander事件觸發(fā)機(jī)制可以參考該博文]
(https://blog.csdn.net/boonya/article/details/51583823)
當(dāng)消息被接收到時彻况,messageReceived事件被觸發(fā)谁尸,這是應(yīng)用程序需要處理輸入信息的地方。這里將首次與外部js文件產(chǎn)生調(diào)用纽甘,(關(guān)于java與js的相互調(diào)用良蛮,大家可自行查閱相關(guān)資料)而后續(xù)大量的操作均需要通過外部js代碼進(jìn)行完成,如此重要的操作自然需要一個專門的類進(jìn)行封裝悍赢。js類概要功能如下:
import javax.script.*;
public class js {
public ScriptEngine engine = null;
public static ScriptEngineManager manager = new ScriptEngineManager();
public js(String args) throws Exception {
m_args = args;
engine = manager.getEngineByName("JavaScript");
engine.put("engine", engine);
engine.put("core", this);
engine.put("args", args);
...
m_files.add(sysconfig.js_run_path + sysconfig.entry_js);
}
public Object runfile(String filename) throws Exception {
java.io.FileReader reader = null;
try {
reader = new java.io.FileReader(filename);
return engine.eval(reader);
} finally {
try{ reader.close(); } catch(Exception e){}
}
}
public Object include(String filename) throws Exception{
return this.runfile(sysconfig.js_run_path + filename);
}
}
簡單地說决瞳,就是通過getEngineByName初始化一個ScriptEngine,然后通過put()對變量賦值左权,通過eval執(zhí)行js程序皮胡。
了解了js類的主要方法,回過頭繼續(xù)看messageReceived方法中所作的處理涮总。
public class Server extends IoHandlerAdapter {
@Override
public void messageReceived(IoSession session, Object message) throws UnsupportedEncodingException {
String buf = new String(((IoBuffer) message).array(), sysconfig.socket_recv_encode);
buf = buf.trim();
if (buf.length() > 0) {
try {
js jengine = new js(buf);
jengine.engine.put("client", new Requ(session));
jengine.include(sysconfig.entry_js);
}
jengine.engine.put("input_0_0_0", buf);
jengine.engine.eval("main(input_0_0_0)");
} catch (Exception ex) {
ex.printStackTrace();
Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
session.closeOnFlush();
}
}
}
}
這里首先生成一個js對象,之后通過put方法將一個Requ對象與client字符串綁定祷舀,Requ對象即為負(fù)責(zé)與telnet進(jìn)行通信的實(shí)現(xiàn)對象瀑梗,包括send和recv方法烹笔。(在后續(xù)程序中通過client.send(“msg”)即可往控制臺輸出內(nèi)容今魔,方便了解程序交互運(yùn)行的過程)榔幸。
緊接著調(diào)用include方法調(diào)用執(zhí)行一個外部js文件(sysconfig.entry_js在配置文件中的值為main.js夭坪,通過include方法拼湊出文件路徑拱层,進(jìn)一步調(diào)用runfile方法執(zhí)行main.js)钠署。而事實(shí)上核蘸,mian.js里面僅僅定義了一個main(args)函數(shù)鳍怨,因此在jengine.engine.put("input_0", buf);jengine.engine.eval("main(input_0)");
這兩句代碼的作用僅僅是將接收到的buf數(shù)據(jù)傳輸給main(args)執(zhí)行选酗,其中由telnet傳輸?shù)腶rgs[0]此處一般為js文件名蒿柳,args[1]為后續(xù)參數(shù)饶套。通過main(args)函數(shù)繼續(xù)調(diào)用args[0]執(zhí)行各類操作。(這么做顯得main.js多此一舉垒探,完全可以在Server類中做同樣處理妓蛮,但我想既然存在,自然是有一定的道理圾叼,畢竟架構(gòu)師的經(jīng)驗(yàn)要豐富的多)蛤克。
到此處,基本的架構(gòu)邏輯捋順夷蚊,接下來就是在此基礎(chǔ)上各種具體應(yīng)用构挤,如果有人愿意看,下一篇就選取一個常用的數(shù)據(jù)庫指標(biāo)采集進(jìn)行詳細(xì)的陳述惕鼓。
所思所得
java與js文件的互相調(diào)用配合筋现,架構(gòu)師的作用
在java程序中通過js類封裝了ScriptEngine,并將core與當(dāng)前js對象綁定呜笑。之后在執(zhí)行的各js文件中夫否,通過core.include("*.js"),就可以繼續(xù)調(diào)用執(zhí)行其他js程序叫胁。js類中還有一個重要的方法newobject(String classname)
凰慈,這個方法實(shí)現(xiàn)在js腳本程序中初始化jar包中封裝的類。
public Object newobject(String classname) throws Exception {
return Class.forName(classname).newInstance();
}
這樣在js腳本程序中通過Object_classname= core.newobject(classname);
就可以得到classname的實(shí)例對象驼鹅。
如此一來微谓,將主要功能模塊封裝在jar包中,具體使用時則通過js程序來調(diào)用输钩,這種設(shè)計(jì)使得程序的靈活性大大提高豺型,程序后期的擴(kuò)展性也得到了保障。(ps:在學(xué)校也曾讀過一兩本關(guān)于設(shè)計(jì)模式的書买乃,淺嘗輒止姻氨,只能在以后的工作中多花功夫了)
java與telnet之間的命令傳輸
在java程序中通過Server類啟動并監(jiān)聽PORT端口,當(dāng)有一個遠(yuǎn)程端口接入剪验,就生成一個Requ對象負(fù)責(zé)通信肴焊,而根據(jù)約定前联,遠(yuǎn)程端口發(fā)送的命令格式為js文件名+" "+參數(shù)列表
,解析命令字符串娶眷,通過core.include與runfile方法就可以實(shí)現(xiàn)執(zhí)行js程序并傳參的功能預(yù)期似嗤。
js奇妙體驗(yàn)
以前對js沒怎么研究過,只是初步知道是解釋型語言届宠,原生支持json烁落,語法簡單了解過。在這一個月的"使用"過程中豌注,深深體會到它的便捷與易用性伤塌,最簡單的如編寫一個js文件,代碼僅僅是一個方法幌羞,甚至只是幾個變量的初始化寸谜,而當(dāng)被eval()執(zhí)行過后,該方法就被加載到了內(nèi)存中属桦,之后就可以在其它地方直接調(diào)用執(zhí)行熊痴,產(chǎn)生一種C#擴(kuò)展方法的錯覺。
所遇到的一個問題
問題描述
在添加對postgresql數(shù)據(jù)庫指標(biāo)的采集功能時聂宾,發(fā)現(xiàn)無法采集數(shù)據(jù)果善,經(jīng)排查發(fā)現(xiàn)原因在于無法連接至postgresql數(shù)據(jù)庫。
解決問題過程中的所見所得
為什么無法連接postgresql數(shù)據(jù)庫系谐?明明代碼什么的都沒問題巾陕,自己編寫的數(shù)據(jù)庫連接測試的小程序也可以成功連接,不得不請教技術(shù)專家來解決纪他。問題描述清楚后鄙煤,老師鍵指如飛,我一個初出茅廬的小兵是眼花繚亂茶袒,再也插不上一句話梯刚。幾分鐘后老師排查出問題所在,原因很簡單薪寓,jar包沒有自動引用postgresql數(shù)據(jù)庫驅(qū)動包亡资。事后通過查看歷史命令,將大致排查過程簡單還原如下:
starce 命令跟蹤---查到數(shù)據(jù)庫連接不通
ps -ef | grep *.jar---得到程序pid
cd /proc/pid/fd ---進(jìn)入程序文件描述符文件夾
ll -h | grep *.jar---查看引用的所有jar包---發(fā)現(xiàn)沒有引用postgresql驅(qū)動jar包
解決方案
為了進(jìn)一步確認(rèn)驅(qū)動包是否可用向叉,在測試環(huán)境下進(jìn)行了一次破壞性試驗(yàn)锥腻。
cd /'home'/lib/
for name in `ls | grep *.jar$`;do `jar -xvf $name`;done
rm -rf *.jar
jar -cvf ojdbc14.jar *
由于ojdbc14.jar是一定會被引用,所以將所有的jar包解包后全部壓入ojdbc14.jar中母谎。重新運(yùn)行程序瘦黑,發(fā)現(xiàn)已經(jīng)可以正常連接至postgresql數(shù)據(jù)庫。技術(shù)與經(jīng)驗(yàn)的差距,僅僅不到10分鐘幸斥,不僅排查了問題存崖,還給出一個臨時的試驗(yàn)解決方案,當(dāng)然最終的解決是重新編譯打包程序睡毒。
事后嘗試直接改寫jar包中的META-INF/MANIFEST.MF文件,在其Class-Path后加上postgresql的包地址冗栗,但并不起作用(格式?jīng)]問題)演顾,所以請讀到此文的各位博友不吝賜教。
目前水平較低隅居,難免對各種各樣的問題理解不夠深徹钠至,還是希望各位博友多多留言交流。