参加工作的第一个月,我开始写下第一篇博客

我参加工作了

关于从学生到职场的转变与心态起伏,已然有许多文笔好的朋友感叹过,我想自己作为一个平凡的人,相较他人也不会有更为特别的感受,自然也就不值当多说。只简单聊聊自身的情况,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的包地址,但并不起作用(格式没问题),所以请读到此文的各位博友不吝赐教。

目前水平较低,难免对各种各样的问题理解不够深彻,还是希望各位博友多多留言交流。

猜你喜欢

转载自www.cnblogs.com/LeeMouRen/p/8992509.html