博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
NJ4X源码阅读分析笔记系列(二)—— nj4x-ts初步分析
阅读量:7125 次
发布时间:2019-06-28

本文共 20632 字,大约阅读时间需要 68 分钟。

nj4x-ts简介

nj4x-ts是NJ4X项目中的终端服务器,参考我的上篇博客中的图:

方框中就是nj4x-ts的角色。

nj4x-ts起着承上启下的作用,nj4x-ts提供了面向中断的网络通讯接口,操作底层的C++函数操作mt4的程序。然后让整个系统跑起来。

包结构

包结构如图:

作用如下:

  • gui 界面
  • io 引用dll库,文件操作
  • jmx JMX服务器
  • net 网络通讯相关
  • xml xml操作类

编译

编译的时候,会出现问题,大致就是找不到dll,到时候要注意调整dll的路径,编译会直接生成课执行jar包,exe可执行程序程序,如下图:

其中antrun文件夹下是ant的编译脚本。

这样就编译完了整个项目。

启动过程

nj4x-ts的启动过程非常值得一提。

入口函数

入口函数在com.jfx.ts.net.TerminalServer类中。

首先开始读取main函数的参数args,我们一般不给这个写参数。

if (args.length % 2 == 0) {                for (int i = 0; i < args.length; i++) {                    String pn = args[i++];                    String pv = args[i];                    System.setProperty(pn, pv);   //指定系统的全局属性                }            }

然后加载dll库:

LibrariesUtil.initEmbeddedLibraries();

initEmbeddedLibraries()加载dll之前,LibrariesUtil有一个静态块,静态块在构造函数之前运行,用以判断操作系统的位数,以及初始化程序的相关路径:

static {        String osName = System.getProperty("os.name");        isWindows = osName.toLowerCase().contains("windows");        String arch = System.getProperty("os.arch");        isX64 = arch.contains("amd64") || arch.contains("x64");        LIBS_DIR = System.getProperty("program_data_dir", "C:\\ProgramData\\nj4x") + "\\bin";//        LIBS_DIR = System.getProperty("user.dir");    }

然后再调用initEmbeddedLibraries()函数,根据位数选择相应的dll。奇怪的是,作者并没有直接复制dll,而是直接二进制读取原路径的dll,然后在目标路径新建一个dll,把二进制写入新的dll文件,然后再System.load(),很奇怪,也许作者这样做是想确保每次的dll都是最新的。代码如下:

final File libFile = new File(libFileName);   //     C:\ProgramData\nj4x\bin\PSUtils_x64.dll//final InputStream in = nativeLibraryUrl.openStream();final OutputStream out = new BufferedOutputStream(new FileOutputStream(libFile));//int len;byte[] buffer = new byte[160000];while ((len = in.read(buffer)) > -1)    out.write(buffer, 0, len);  //写入dll文件out.close();in.close();//System.load(libFile.getAbsolutePath()); //加载PSUtil_X64.dll这个dll

至此,dll加载完毕。

接下来就判断程序是不是以管理员身份登录的:

asAdministrator = PSUtils.asAdministrator();

具体方法在com.jfx.ts.io.PSUtils中:

public static boolean asAdministrator() {    if (!isAdministrator()) {        String compat_layer = System.getenv().get("__COMPAT_LAYER");        if (compat_layer == null || !compat_layer.contains("RunAsAdmin")) {            return false;        }    }    return true;}

isAdministrator()是一个native静态方法:

public static native boolean isAdministrator();

这个方法是使用的PSUtils_x64.dll的native方法。因为com.jfx.ts.io.PSUtils中有一个静态块:

// Load the dll that exports functions callable from javastatic {    if (!LibrariesUtil.IS_OK) {        String dllFileName = null;        try {            if (LibrariesUtil.isX64) {                dllFileName = "PSUtils_x64.dll";            } else {                dllFileName = "PSUtils.dll";            }            String libFileName = LibrariesUtil.LIBS_DIR + File.separator +             System.load(libFileName);        } catch (Throwable t) {            t.printStackTrace();            try {                System.load(dllFileName);            } catch (Exception e) {                System.exit(2);            }        }    }}

没错,这就是战斗民族的代码。

接下来就是确定最大线程数,不知道战斗民族为什么把代码写的如此复杂:

//最大线程数,不知道为什么要搞得这么复杂            MAX_TERMINAL_STARTUP_THREADS = Integer.parseInt(System.getProperty("max_terminal_connection_threads", "" +                    (AVAILABLE_PROCESSORS >= 24 ? AVAILABLE_PROCESSORS / 2                            : (AVAILABLE_PROCESSORS >= 12 ? AVAILABLE_PROCESSORS / 3                            : (AVAILABLE_PROCESSORS > 3 ? AVAILABLE_PROCESSORS - 2                            : AVAILABLE_PROCESSORS)))));

然后判断,要不要启动专家系统,默认是不启动的。

IS_DEPLOY_EA_WS = System.getProperty("deploy_EA_WS", "false").equals("true");

作者在代码中大量的使用System.getProperty()方法操作参数,思路不错。

真正的部署程序在这个方面里面

deploy(port);

deploy(port)方法首先打印出所有的系统环境变量和程序设置的参数:

Logger logger = TS.LOGGER;//logger.info("--");logger.info("--");logger.info("--");logger.info("-- TS " + TS.NJ4X + " STARTUP --");logger.info("-- " + TS.NJ4X_UUID + " --");logger.info("--");logger.info("--");logger.info("--");logger.info("-- System properties --");for (Map.Entry e : new TreeMap<>(System.getProperties()).entrySet()) {    logger.info("" + e.getKey() + "=" + e.getValue());}logger.info("-- Environment --");for (Map.Entry e : new TreeMap<>(System.getenv()).entrySet()) {    logger.info("" + e.getKey() + "=" + e.getValue());}logger.info("-- Deployment --");

然后实例化TS类,TS作用下一节再分析。

接下来就部署WebService服务,具体由TsWS类实现。

关于nj4x-ts的网络通信部分,会在下一篇博客中做介绍,在此略过。

但是作者在最后又默认启动专家系统的webserive,不知道为什么,现在没有计划研究专家系统,以后有可能。

//部署专家系统webservicedeployEaWs(true);

总之,启动后的样子:

这个界面就不介绍了,懂的人自然懂。

TS类的分析

TS类要单独拿出来分析,因为,以上的启动过程都是表面上的,程序启动真正干活的是TS类。

TS类中的各个静态变量,可以自己看。

首先,TS类有三个静态块:

第一个我不知道是干嘛用的,目的不明确:

static {    try {        //不知道为什么要外部程序,不知道是干什么的,跑起来之后看看hostname是个什么鬼        //但是这个是简化外部当前JVM进程的执行。        ExternalProcess p = new ExternalProcess("hostname");        p.run();        TS.hostname = p.getOut().trim();    } catch (Exception e) {        TS.LOGGER.error("hostname cmd error", e);        TS.hostname = System.getenv("COMPUTERNAME");    }}

第二个初始化JFX的HOME路径:

static {    //貌似是初始化JFX的HOME路径    JFX_HOME = System.getProperty("home", System.getProperty("user.hom    System.setProperty("home", JFX_HOME);    String tmout = System.getenv("JFX_TERM_IDLE_TMOUT_SECONDS");    if (tmout == null) {        JFX_TERM_IDLE_TMOUT_SECONDS = 3600 * 6;    } else {        JFX_TERM_IDLE_TMOUT_SECONDS = Long.parseLong(tmout);    }}

第三个非常重要,其作用是初始化程序运行的各种路径文件:

static {    //mkdir函数,如果存在就返回false    new File(getTargetTermDir()).mkdirs();    //    new File(JFX_HOME_CONFIG).mkdirs();    new File(JFX_HOME_SRV_DIR).mkdirs();    new File(JFX_HOME_EA_DIR).mkdirs();    new File(JFX_HOME_EXPERTS_DIR).mkdirs();    new File(JFX_HOME_INDICATORS_DIR).mkdirs();    new File(JFX_HOME_CHR_DIR).mkdirs();    //    TerminalClient terminalClient = null;    try {        terminalClient = new TerminalClient("127.0.0.1", Integer.parseInt(System.getProperty("port", "7788")));        TS.P_GUI_ONLY = true;        if (!TS.P_USE_MSTSC) {            String mode = terminalClient.ask(ClientWorkerThread.GETMODE).toLowerCase();            if (mode.contains("mstsc=true")) {                TS.P_USE_MSTSC = true;            }        }    } catch (IOException e) {        //ignore, seems this is 1st TS instance or port is used by another app    } finally {        if (terminalClient != null) {            try {                terminalClient.close();            } catch (IOException ignore) {            }        }    }    //    LOGGING_CONFIG_XML = JFX_HOME_CONFIG + File.separatorChar + (P_GUI_ONLY ? "gui_" : "") + "logging.xml";    if (!new File(LOGGING_CONFIG_XML).exists()) {        try {            String loggingXml = ResourceReader.getClassResourceReader(TerminalServer.class, true).getProperty("logging.xml");            if (P_GUI_ONLY) {                loggingXml = loggingXml.replace("jfx_term.log", "gui_jfx_term.log");            }            writeFile(LOGGING_CONFIG_XML, loggingXml.replace("./jfx_term/", JFX_HOME.replace('\\', '/') + "/").getBytes());        } catch (IOException e) {            e.printStackTrace();        }    }    //    if (!new File(JMX_CONFIG_XML).exists()) {        try {            writeFile(JMX_CONFIG_XML, ResourceReader.getClassResourceReader(TerminalServer.class).getProperty("mbean_config.xml").getBytes());        } catch (IOException e) {            e.printStackTrace();        }    }    //    DOMConfigurator.configureAndWatch(LOGGING_CONFIG_XML);    LOGGER = Logger.getLogger(TS.class);    //    String termDirLn = System.getenv("SystemDrive") + "\\." + System.getProperty("port", "7788");    File tmpDirLnk = new File(termDirLn);    if (tmpDirLnk.exists() && !P_GUI_ONLY) {        String linkDir = getLinkDir(tmpDirLnk);        if (linkDir == null || !new File(getTargetTermDir()).equals(new File(linkDir))) {            tmpDirLnk.delete();        }    }    if (!tmpDirLnk.exists()) {        tmpDirLnk.delete();        ExternalProcess mklink = new ExternalProcess("cmd", "/C", "mklink", "/J", termDirLn, getTargetTermDir());        try {            mklink.run();            TERM_DIR = tmpDirLnk.exists() ? termDirLn : null;        } catch (Exception e) {            e.printStackTrace();        }    } else {        TERM_DIR = termDirLn;    }    //    tsConfig = new TSConfig();}

要注意几个路径:

C:\ProgramData\nj4x\bin目前看来只包含PSUtils_x64.dll文件。

C:\Users\Micheal\jfx_term文件夹比较复杂,总之就是包含了jfx所有的配置文件,如图:

chr文件夹是空的,不知道干嘛的。

config文件夹中,是一些配置文件,logging.xmllog4j的配置文件。配置方式都不一样,战斗民族;mbean-config.xml文件是网络配置文件,下次博客再讲;ts_config.xml文件里面就有一句话,不知道什么意思:

Properties removed at Thu Nov 17 16:28:14 CST 2016

log文件夹中是日志。

srv文件夹中是各个交易商的配置文件,这个和交易商有关:

zero_term文件夹中存放着一份mt4的程序。没有提到的文件夹就是空的。

C:\Users\Micheal\.jfx_terminals这个文件夹中存放的是mt4的程序,每一个账号,每一个交易商的不同服务器都会创建一份新的程序,应该是配置文件不同。

TS.scheduledExecutorService分析。

作者在Ts类中定义了一个scheduledExecutorService

/**     * The constant scheduledExecutorService.     */    public static ScheduledExecutorService scheduledExecutorService            = java.util.concurrent.Executors.newScheduledThreadPool(64);

据我统计,有10个地方用到了这个scheduledExecutorService,也就是至少有10个定时任务。

1.MT4程序连接监控,监控有没有网络连接到MT4程序。这个里面有两个,不知道为什么里面还有一个,这两个任务是一样的:

if (mt4Module.isCheckRequired && mt4Module.checkFuture == null) {    mt4Module.checkFuture = scheduledExecutorService.schedule(new Runnable() {        @Override        public void run() {            Mt4Module mt4Module = incomingConnectionModule.get(tp.strategy);            if (mt4Module.isCheckRequired) {                try {                    String status = tp.checkTerminal(clientWorker);                    if (status.startsWith("OK")) {                        mt4Module.checkFuture = scheduledExecutorService.schedule(this, 15, TimeUnit                    } else {                        mt4Module.checkFuture = null;                        incomingConnectionError.put(tp.strategy, status);                    }                } catch (NoSrvConnection e) {                    String m = "No connection to server: " + e;                    TS.LOGGER.error(m, e);                    incomingConnectionError.put(tp.strategy, m);                } catch (SrvFileNotFound e) {                    String m = "SRV file not found: " + e;                    TS.LOGGER.error(m, e);                    incomingConnectionError.put(tp.strategy, m);                } catch (MaxNumberOfTerminalsReached e) {                    String m = "Reached max number of terminals: " + e;                    TS.LOGGER.error(m, e);                    incomingConnectionError.put(tp.strategy, m);                } catch (InvalidUserNamePassword e) {                    String m = "Invalid user name or password: " + e;                    TS.LOGGER.error(m, e);                    incomingConnectionError.put(tp.strategy, m);                } catch (TerminalInstallationIsRequired e) {                    String m = e.getMessage();                    TS.LOGGER.error(m, e);                    incomingConnectionError.put(tp.strategy, m);                } catch (Throwable e) {                    e.printStackTrace();                    String m = "Unexpected error: " + e;                    TS.LOGGER.error(m, e);                    incomingConnectionError.put(tp.strategy, m);                }            }        }    }, 15, TimeUnit.SECONDS);

2.磁盘监控,从这个就能看出对磁盘的操作

//这个磁盘监控每60s进行一次6spaceMonitoringJob =    scheduledExecutorService.scheduleAtFixedRate(new Runnable() {    @Override    public void run() {}},5, 60, TimeUnit.SECONDS);

3.监控新的session

TS.scheduledExecutorService.submit(newSessionCreator);

4.监控终端变化

TS.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {    public void run() {        if (Log4JUtil.isConfigured() && TS.LOGGER.isTraceEnabled()) {            TS.LOGGER.trace("Timer: update session's load level");        }        if (TS.P_USE_MSTSC) {            try {                loadExistingSessions();            } catch (Throwable e) {                TS.LOGGER.error("Error loading current sessions", e);            }        }        updateTerminals();    }}, 30, 60, TimeUnit.SECONDS);

4.监控google网盘设置变化

TS.scheduledExecutorService.schedule(new Runnable() {    @Override    public void run() {        int nextSyncInSeconds = nextSyncInSeconds();        if (nextSyncInSeconds > 0) {            TS.scheduledExecutorService.schedule(this, 1/*Math.min(nextSyncInSeconds, 5)*/, TimeUnit.SECONDS);            return;        }        try {            checking = true;            boolean somethingDone = false;            for (DownloadSetup ds : downloads.values()) {                somethingDone |= ds.run();            }            if (somethingDone) {                TS.LOGGER.info("Google Drive: All Downloads Complete!");            }            checking = false;        } finally {            lastSyncTime = System.currentTimeMillis();            TS.scheduledExecutorService.schedule(this, 1/*Math.max(nextSyncInSeconds(), 5)*/, TimeUnit.SECONDS);        }    }

5.日志压缩任务

6.断线终端的关闭任务

ScheduledFuture schedule = TS.scheduledExecutorService.schedule(new Runnable() {    public void run() {        if (Log4JUtil.isConfigured() && TS.LOGGER.isInfoEnabled()) {            TS.LOGGER.info("Timer: Stop terminal " + processName);        }        ts.killProcess(processName, true);    }}, TS.JFX_TERM_IDLE_TMOUT_SECONDS, TimeUnit.SECONDS);TS.terminations.put(processName, schedule);

7.更新终端状态任务

TS.scheduledExecutorService.schedule(new Runnable() {    public void run() {        ts.updateTerminals();    }}, 2, TimeUnit.SECONDS);

8.监控GUI中的网盘设置项变化

TS.scheduledExecutorService.schedule(new Runnable() {    @Override    public void run() {        SwingUtilities.invokeLater(new Runnable() {            @Override            public void run() {                try {                    if (!text.equals(updateInterval.getText())) {                        return;                    }                    if (text.replace("0", "").length() > 0) {                        try {                            if (Integer.parseInt(text) <= 0) {                                JOptionPane.showMessageDialog(null,                                        "Error: Please enter number bigger than 0", "Error Message",                                        JOptionPane.ERROR_MESSAGE);                            } else {                                TS.setConfigurationValue("cloud_update_interval", text);                                TS.LOGGER.info("Google Drive update interval set to " + text + " sec");                            }                        } catch (NumberFormatException e) {                            JOptionPane.showMessageDialog(null,                                    "Error: Please enter valid number >0", "Error Message",                                    JOptionPane.ERROR_MESSAGE);                        }                    }                } catch (HeadlessException e) {                    TS.LOGGER.error("At GUI", e);                }            }        });    }}, KEY_PRESSED_ACTION_DELAY, TimeUnit.MILLISECONDS);

9.貌似是界面的刷新

TS.scheduledExecutorService.schedule(new Runnable() {    @Override    public void run() {        SwingUtilities.invokeLater(x);    }}, 1, TimeUnit.SECONDS);

10.监控正在进行连接操作的终端

TS.scheduledExecutorService.schedule(new Runnable() {    public void run() {          将正在连接的终端列表下这个map        final HashMap
tp = new HashMap<>(); synchronized (connectionsInProgress) { for (TerminalParams p : connectionsInProgress) { tp.put(p.getTerminalDirPathName(), p); } } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { synchronized (connectionsInProgressTable) { DefaultTableModel model = (DefaultTableModel) connectionsInProgressTable.getModel(); Vector dataVector = model.getDataVector(); int sz = dataVector.size(); boolean allInPlace = sz > 0; for (int row = 0; row < sz; row++) { String path = (String) model.getValueAt(row, model.findColumn(PATH_COLUMN)); TerminalParams terminalParams = tp.get(path); if (terminalParams == null) { allInPlace = false; break; } else { model.setValueAt( // "
" + ((System.curre 设置连接的时间 new Integer((int) ((System.currentTimeMillis() - terminalParams.start.getT row, model.findColumn(DURATION_COLUMN) ); } } 设置好时间之后开始显示 if (!allInPlace) { dataVector.clear(); // new Object[]{"Broker", "Account", "Path", "Start Time", "Duration (s)"} for (Map.Entry
p : tp.entrySet()) { Vector row = new Vector(); TerminalParams terminalParams = p.getValue(); row.add(terminalParams.getSrv()); row.add(terminalParams.getUser()); row.add(new SimpleDateFormat("MMM d, HH:mm:ss").format(terminalParams.start)); row.add( //"
" + ((System.currentTimeMillis() - terminalParams.start.getTim new Integer((int) ((System.currentTimeMillis() - terminalParams.start.getT ); row.add(terminalParams.getTerminalDirPathName()); // dataVector.add(row); } model.fireTableDataChanged(); } //这个才是真真的显示 SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { TitledBorder border = (TitledBorder) connectionsInProgressPanel.getBorder(); border.setTitle(tp.size() > 0 ? (tp.size() == 1 ? "1 Connection In Progress" : "" + tp.size() + " Co : "Connections In Progress" ); connectionsInProgressPanel.repaint(); } catch (Exception e) { TS.LOGGER.error("At GUI", e); } } }); } } catch (Exception e) { TS.LOGGER.error("At GUI", e); } } }); //不知道这句话是干什么用的,为什么要this,this指的是这个new runable类,不知道加不加有什么区别 TS.scheduledExecutorService.schedule(this, 1, TimeUnit.SECONDS); }}, 1, TimeUnit.SECONDS);

11.监控日志显示列表的变化

//监听日志限制输入框的输入变化TS.scheduledExecutorService.schedule(new Runnable() {    @Override    public void run() {        SwingUtilities.invokeLater(new Runnable() {            @Override            public void run() {                try {                    if (!text.equals(limitRowsField.getText())) {                        return;                    }                    if (text.replace("0", "").length() > 0) {                        try {                            if (Integer.parseInt(text) <= 0) {                                JOptionPane.showMessageDialog(null,                                        "Error: Please enter number bigger than 0", "Error Message",                                        JOptionPane.ERROR_MESSAGE);                            } else {                                applyNewRowsLimit();                            }                        } catch (NumberFormatException e) {                            JOptionPane.showMessageDialog(null,                                    "Error: Please enter valid number bigger than 0", "Error Message",                                    JOptionPane.ERROR_MESSAGE);                        }                    }                } catch (HeadlessException e) {                    TS.LOGGER.error("At GUI", e);                }            }        });    }}, KEY_PRESSED_ACTION_DELAY, TimeUnit.MILLISECONDS);

此外,还有其他的任务,后面会提到

转载地址:http://epeel.baihongyu.com/

你可能感兴趣的文章
Jni Error(app bug): accessed stale local reference 的另类出现方式
查看>>
myeclipse 解决乱码问题
查看>>
iOS Https 配置 及AFN 相关配置
查看>>
Table-Valued Parameters in SQL Server 2008 (ADO.NET)
查看>>
推荐系统常用的算法参考
查看>>
[Todo] Java并发编程学习
查看>>
Redis cluster学习 & Redis常识 & sort操作
查看>>
mysql 中实现多条数据同时更新
查看>>
2011 ACM/ICPC 成都赛区(为2013/10/20成都现场赛Fighting)
查看>>
Linux技术进阶示意图
查看>>
php设计模式课程---6、策略模式如何使用
查看>>
html5--6-8 CSS选择器5
查看>>
20145328 《信息安全系统设计基础》第6周学习总结
查看>>
TFS命令行操作
查看>>
分披萨
查看>>
激活IDEA 2019.1
查看>>
简谈如何提高APP的积分墙收入!
查看>>
Metasploit
查看>>
被忽略却很有用的html标签
查看>>
Python学习之==>文件操作
查看>>