简单的数据挖掘的实现
客户端的实现:
该客户端运行在电信运行的unix云服务器上,用于周期性的读取系统日志文件wtmpx搜集在该服务器上使用的用户信息,将每个用户的登入登出日志进行匹配,然后发送给计费服务器保存。
将wtmpx文件中需求的部分读取出来,其中,wtmpx文件中的数据都是以二进制的形式存在。每条日志的记录为372个字节。但是372个字节中我们只需要取出需求的五个部分(user,pid,time,type,host)。
在客户端写构造函数初始化客户端相关数据的时候,一般先写一个配置文件(config.xml),将所有的属性写成一个树形结构,客户端相关的属性的初始值作为xml文件中元素的属性值。再通过loadConfig()方法读取项目根目录下的配置文件config.xml,遍历每一个配置项,将xml文件中的标签名作为key,标签中间的文本作为value存入一个Map集合中返回。在构造方法中调用该方法loadConfig(),获得一个Map集合,然后输出该集合检查配置文件的读取是否正确( 即打桩)。读取正确后下一步写一个方法init(Map<String,String> config)根据配置文件中的配置项初始化相关属性,再在构造方法中调用该方法即可完成初始化客户端的相关属性,这样做的好处是便于后期软件的维护以及修改等等操作,只需要修改下配置文件即可,而不要修改源代码。需要注意的是:配置文件xml中元素的大小写非常敏感。至此构造方法对客户端属性的初始化完成。
对于系日志文件中的每一条日志,可以定义一个类LogData来进行描述,在该类中定义所有需要读取的属性,以及属性在日志文件中的起始位置以及长度等等。
搜集分为三个步骤:
一:批量读取wtmpx文件中的多条日志并进行解析,将解析后的日志写入另一个文件保存。
第一步需要用到的方法是:读取unix系统日志文件中的batch条日志文件,将每一条日志解析为一条可读日志,最终将这些日志写入到textLogFile表示的文件中。实现的具体步骤为:
1.必要的判断工作
1.1:unix系统日志文件必须存在!即属性logFile表示的文件(wtmpx)
1.2:根据书签信息,判断unix系统日志文件中是否还有日志可以析,
若有日志可以解析,将上次读取的位置返回即可,若返回-1表示没有日志可以解析了(此处写一个方法来实现判断,判断的依据是:系统日志文件的总长度与书签文件中记录的上次解析后的位置之间的差异是否还够一条日志文件的长度,若有日志文件可以解析,则返回每次读取之后指针的位置,否则返回-1),根据返回值来判断是否有日志可以分析,返回值为-1,则提示用户“没有日志可以读取”,否则返回一个指针当前的位置。
1.3:暂时保留
2.创建RandomAccessFile来读取unix系统日志文件.(logFile)
3.先将RandomAccessFile的指针seek到上一次最后读取的位置,准备开始解析
4.创建一个List集合,用来保存每一条解析出来的日志。
5.循环batch次,来进行解析日志,并将每一条日志都存入List集合中将每一条日志转换为一个LogData实例。
6.将集合中的每一条日志(LogData实例)toString返回的字符串按行写入到textLoginFile表示的文件中。
7.将RandomAccessFile当前指针位置写入到lastPositionFile文件中
(注:在进行读写操作时,可以先定义一个工具类,负责客户DMSClient所有的读写操作,可以达到代码重用。在需要读写操作时,直接调用工具类的方法即可。)
二:读取保存解析后的日志文件,将日志文件进行登入登出匹配,并将配对的日志存入另一个文件logRecFile中等待发送,没有配对成功的日志写入一个文件loginLogFile中再配对,定义两个文件的属性之后,在init()方法中初始化两个属性,依旧是把属性的初始化值写在配置文件config.xml中,在init()方法中解析xml中需要的值.例如:logRecFile =new File(config.get("logrecfile");
然后用方法matcheLogs()进行日志的配对,进行日志的配对时,
主要有以下几个步骤:
1.必要的判断
1.1:判断第一步生成的用于保存解析后的日志的文件textLogFile是否存在
1.2:判断保存配对日志文件logRecFile是否存在,若存在无需重复配对,若不存在则再开始新的配对工作。
2.将textLogFile中等待配对的日志都读取出来并入一个List集合中等待配对。(此处需要对textLogFile中的内容进行读取,所以在工具类IOUtil中添加一个方法readLogs(File file)来从给定的文件中读取每一行字符串,并将其转换为一个LogData实例,存在一个list集合中返回)
3.查看上次第二步执行完毕后生成的用于保存所有没配对成功的日志文件loginLogFile是否存在,若存在则将上次没有配对成功的日志也读取出来放入List集合等待一起配对(此处同上一步,调用工具类IOUtil中的方法readLogs(File file))
4.归类日志:4.1创建一个Map用于保存所有登入日志,Key为日志的"user pid,host”,遍历第二步的list集合,获取user,pid,host作为Map集合的key
4.2创建一个Map用于保存所有登出日志,key的格式与上面相同。
4.3创建一个List,用于保存所有的配对日志
4.4遍历保存所有待配对的集合,将每一条日志按照登入和登出分别存入两个Map中
5.根据登出找登入,进行配对。遍历保存所有登入日志的Map,取出每条登出日志,并使用其对应的key去登入Map中提取登入日志,然后将这两条日志转换为一个LogRec实例保存并存入用于保存配对日志的集合中,然后将该登入日志从登入的Map中删除。这样一来,保存配对日志的集合中就有这次所有配对成功的日志了,并且登入Map中也仅仅剩下所有没配对成功的登入日志了
6.保存:将所有配对成功的日志写入logRecFile文件,将保存登入Map中剩下的所有登入日志写入到loginLogFile中
7.配对成功后将第一步生成的textLogFile文件删除
三:读取保存配对日志的文件,将每一个配对日志发送给服务器。
步骤有:
1.必要的判断 1.1:保存配对日志的文件要存在!
2.将所有配对日志读取回来并存入一个List集合中,等待发送(在工具类中写方法readLogRec(File file)来读取日志,返回一个List集合)
3.连接服务端,
4.将所有配对日志发送至服务端,用输出流来实现
5.所有日志发送后再发送一个over字符串表示所有日志发送完毕。注意此处发送后需要调用flush()方法
6.读取服务端发送回来的响应内容,若是“ok”,表示服务端成功保存.那么我们将第二步生成的这个配对文件logRecFile删除 ,此处需要用到输入流.
服务端的实现:
服务端的功能是:接收所有客户端发送过来的配对日志并存入到本地文件中保存
由功能可知其所需要的属性,首先是服务端的ServerSocket,然后是线程池threadPool,用来管理与客户端交互的线程;用来保存所有客户端发送过来的配对日志的文件serverLogFile,以及一个消息队列BlockingQueue<String> messageQueue,该消息队列的作用是:用于临时存放所有客户端发送过来的配对日志,方便写入文件的线程获取日志后写出,所有与客户端交互的线程在获取了日志后会将日志存入该队列。依旧是把属性的初始值写在配置文件server_config.xml里面,在构造方法中加载配置文件,然后通过初始化方法init()来解析配置文件,对属性进行初始化.创建start()方法来启动服务端,先是启动用于保存客户端发送的日志的线程,然后循环监听服务端口,等待客户端的连接.通过自定义一个线程来实现与客户端之间进行交互'
1.通过Socket获取输入值,并包装为缓冲字符输入流,准备读取客户端发送过来的配对日志
2.循环读取每一条日志,直到读取到“OVER”为止,并将每一条日志写入文件保存
3.若全部接收成功并存入文件后,回应客户端“ok”,若其中出现异常,则回应客户端“ERROR”;
通过线程该线程SaveLogHandler来负责循环从消息队列中取出每一条日志并写入到serverLogFile文件中,最后在Main()方法中实例化DMSServer方法,并调用start()方法
至此大致实现DMS的基本功能