參加工作 關於從學生到職場的轉變與心態起伏,已然有許多文筆好的朋友感嘆過,我想自己作為一個平凡的人,相較他人也不會有更為特別的感受,自然也就不值當多說。只簡單聊聊自身的情況,1月份畢業於非電腦專業,3月底檔案上的職業欄從 變成了 手動滑稽,編程功底相比電腦專業的各位前輩有較大差距,但這就是我職業 ...
參加工作
關於從學生到職場的轉變與心態起伏,已然有許多文筆好的朋友感嘆過,我想自己作為一個平凡的人,相較他人也不會有更為特別的感受,自然也就不值當多說。只簡單聊聊自身的情況,1月份畢業於非電腦專業,3月底檔案上的職業欄從學生
變成了軟體開發高級工程師
手動滑稽,編程功底相比電腦專業的各位前輩有較大差距,但這就是我職業生涯的起點。
進入部門一個月,接觸的東西大多都是在學校聽過但沒有用過的,考慮到腦容量的問題,防止以後持續的東忘西,終於決定將一部分通用的技術或理念或心得記錄下來,就從現在開始。
程式整體框架概述
我所接觸的是數據採集方面的任務,整個數據採集模塊主框架被封裝在一個a.jar包之中,程式入口common通過啟動命令指定,使用nohuo與&指令將服務運行在後臺:
nohup java -cp 'pwd'/a.jar main.common [args]>>nohup.out &
進入common繼續往下看:
首先關註啟動命令中所指定的args參數,此參數指定配置文件路徑,(程式啟動後,a.jar文件所在目錄即為根目錄,指定路徑以此為基礎)。通過jar包sysconfig類中定義的靜態方法,讀取cfg配置文件內容,依次賦值給sysconfig類中的靜態變數,作後續使用。
之後利用jar包Server類中的ServerRun靜態方法,通過讀取上述sysconfig類中的port靜態變數,使用mina框架提供的socket通信方法,監聽本機埠。
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連接至指定埠,就可以做到與程式進行交互。Server類繼承mina框架的IoHandAdapter類,便於管理連接,[mian Iohander事件觸發機制可以參考該博文]
(https://blog.csdn.net/boonya/article/details/51583823)
當消息被接收到時,messageReceived事件被觸發,這是應用程式需要處理輸入信息的地方。這裡將首次與外部js文件產生調用,(關於java與js的相互調用,大家可自行查閱相關資料)而後續大量的操作均需要通過外部js代碼進行完成,如此重要的操作自然需要一個專門的類進行封裝。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執行js程式。
瞭解了js類的主要方法,回過頭繼續看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對象即為負責與telnet進行通信的實現對象,包括send和recv方法。(在後續程式中通過client.send(“msg”)即可往控制台輸出內容,方便瞭解程式交互運行的過程)。
緊接著調用include方法調用執行一個外部js文件(sysconfig.entry_js在配置文件中的值為main.js,通過include方法拼湊出文件路徑,進一步調用runfile方法執行main.js)。而事實上,mian.js裡面僅僅定義了一個main(args)函數,因此在jengine.engine.put("input_0", buf);jengine.engine.eval("main(input_0)");
這兩句代碼的作用僅僅是將接收到的buf數據傳輸給main(args)執行,其中由telnet傳輸的args[0]此處一般為js文件名,args[1]為後續參數。通過main(args)函數繼續調用args[0]執行各類操作。(這麼做顯得main.js多此一舉,完全可以在Server類中做同樣處理,但我想既然存在,自然是有一定的道理,畢竟架構師的經驗要豐富的多)。
到此處,基本的架構邏輯捋順,接下來就是在此基礎上各種具體應用,如果有人願意看,下一篇就選取一個常用的資料庫指標採集進行詳細的陳述。
所思所得
java與js文件的互相調用配合,架構師的作用
在java程式中通過js類封裝了ScriptEngine,並將core與當前js對象綁定。之後在執行的各js文件中,通過core.include("*.js"),就可以繼續調用執行其他js程式。js類中還有一個重要的方法newobject(String classname)
,這個方法實現在js腳本程式中初始化jar包中封裝的類。
public Object newobject(String classname) throws Exception {
return Class.forName(classname).newInstance();
}
這樣在js腳本程式中通過Object_classname= core.newobject(classname);
就可以得到classname的實例對象。
如此一來,將主要功能模塊封裝在jar包中,具體使用時則通過js程式來調用,這種設計使得程式的靈活性大大提高,程式後期的擴展性也得到了保障。(ps:在學校也曾讀過一兩本關於設計模式的書,淺嘗輒止,只能在以後的工作中多花功夫了)
java與telnet之間的命令傳輸
在java程式中通過Server類啟動並監聽PORT埠,當有一個遠程埠接入,就生成一個Requ對象負責通信,而根據約定,遠程埠發送的命令格式為js文件名+" "+參數列表
,解析命令字元串,通過core.include與runfile方法就可以實現執行js程式並傳參的功能預期。
js奇妙體驗
以前對js沒怎麼研究過,只是初步知道是解釋型語言,原生支持json,語法簡單瞭解過。在這一個月的"使用"過程中,深深體會到它的便捷與易用性,最簡單的如編寫一個js文件,代碼僅僅是一個方法,甚至只是幾個變數的初始化,而當被eval()執行過後,該方法就被載入到了記憶體中,之後就可以在其它地方直接調用執行,產生一種C#擴展方法的錯覺。
所遇到的一個問題
問題描述
在添加對postgresql資料庫指標的採集功能時,發現無法採集數據,經排查發現原因在於無法連接至postgresql資料庫。
解決問題過程中的所見所得
為什麼無法連接postgresql資料庫?明明代碼什麼的都沒問題,自己編寫的資料庫連接測試的小程式也可以成功連接,不得不請教技術專家來解決。問題描述清楚後,老師鍵指如飛,我一個初出茅廬的小兵是眼花繚亂,再也插不上一句話。幾分鐘後老師排查出問題所在,原因很簡單,jar包沒有自動引用postgresql資料庫驅動包。事後通過查看歷史命令,將大致排查過程簡單還原如下:
starce 命令跟蹤---查到資料庫連接不通
ps -ef | grep *.jar---得到程式pid
cd /proc/pid/fd ---進入程式文件描述符文件夾
ll -h | grep *.jar---查看引用的所有jar包---發現沒有引用postgresql驅動jar包
解決方案
為了進一步確認驅動包是否可用,在測試環境下進行了一次破壞性試驗。
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中。重新運行程式,發現已經可以正常連接至postgresql資料庫。技術與經驗的差距,僅僅不到10分鐘,不僅排查了問題,還給出一個臨時的試驗解決方案,當然最終的解決是重新編譯打包程式。
事後嘗試直接改寫jar包中的META-INF/MANIFEST.MF文件,在其Class-Path後加上postgresql的包地址,但並不起作用(格式沒問題),所以請讀到此文的各位博友不吝賜教。
目前水平較低,難免對各種各樣的問題理解不夠深徹,還是希望各位博友多多留言交流。