参加工作的第一个月,我开始写下第一篇博客
参加工作
关于从学生到职场的转变与心态起伏,已然有许多文笔好的朋友感叹过,我想自己作为一个平凡的人,相较他人也不会有更为特别的感受,自然也就不值当多说。只简单聊聊自身的情况,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的包地址,但并不起作用(格式没问题),所以请读到此文的各位博友不吝赐教。
目前水平较低,难免对各种各样的问题理解不够深彻,还是希望各位博友多多留言交流。