电信采集项目

# 电信采集子项目个人总结: #

## (1)功能分析: ##

记录使用电信宽带的登录人员信息,获得他们的上线时长,为后面的计费模块做铺垫。

## (2)需求分析: ##

数据采集:将采集到的数据文件通过io流读入到内存,并将数据保存在java对象中;

网络模块:将存储数据的对象集合从客户端发送给服务器,

数据入库:服务器拿到的数据应该保存在数据库中,采用jdbc技术进行java和数据库的交互;

备份模块:是在数据采集中边采集边备份,将异常信息进行备份在一个文件中,并且可以通过唯一标识(登入人员的ip)查询到他们的登录信息;

日志打印:记录一下项目的全过程,采用log4j技术;

配置模块:将配置信息(不要写死在代码中的常量信息)写入xml文件用来做配置文件,采用dom4j技术,进行解析xml文件并将配置信息的值传入每个模块中进行初始化。

## (3)各模块关键性代码分析: ##

# 数据采集: #

## 第一步 ##:有两种读取文件的方式:用字节流或字符流读入

A.采用FileInputStream读入数据文件,最后肯定要有一个read()方法,

使用啥read()方法好呢?   readLine()

将字节流转换成字符流读入文件内容会更加方便,而BufferedReader流就有一个readLine()方法;

`FileInputStream file=new FileInputStream(filepath);`

`BufferedReader bf=new BufferedReader(new InputStreamReader(file));`

B.采用FileReader读入数据文件,代码如下:

`FileReader file=new FileReader(filepath);`

`BufferedReader bf=new BufferedReader(file);`

## 第二步 ##:遍历文件中的内容,根据|进行字段分隔,并将分隔字段保存在split数组中

//#briup1660|037:wKgB1660A|7|1239110900|44.211.221.24

使用while循环进行遍历:

String line=null;

while ((line=bf.readLine())!=null) {

String[] split=line.split("[|]");

1.根据数组下标得到数组中的每一个分隔字段

******substring():截取字符串

String user=split[0].substring(1);//用户名

String NAS_ip=split[1];//NAS_ip

String flag=split[2];//登录标志:7上8下

String time=split[3];//上线时间或下线时间

String login_ip=split[4];//登录ip

2.根据登录标志分情况把每一个分隔字段保存在BIDR这个类的对象中,用bidr的set()方法进行赋值

******flag是String类型,所以用equals()方法进行判断,返回boolean类型

if(flag.equals("7")){

BIDR bidr=new BIDR();

bidr.setAAA_login_name(user);

bidr.setNAS_ip(NAS_ip);

注意:通过读入文件拿到的time是String类型,然而上线时间和下线时间是Timestamp(时间戳)类型,所以需要进行转换

new Timestamp的实例,需要传入一个long类型的值,而string转long用

Long.parseLong(string);

Timestamp time_login=new Timestamp(Long.parseLong(time));

bidr.setLogin_date(time_login);

bidr.setLogin_ip(login_ip);

这样就可以得到多个bidr对象,那么用什么来保存这些对象呢?集合

啥集合好呢?map(k,v)采用键值成对,键是唯一标识的登录ip,值是bidr对象

if(!map.containsKey(login_ip)){

map.put(login_ip, bidr);

}

}

3.现在我们已经将所有的上线数据保存在了map集合中,接下来要将登录者的下线数据与上线数据根据登录ip进行匹配,并将正常数据保存在list集合中;

然后移除map集合中正常数据的对象,使map集合中只保存异常数据(有7没8);

else if(flag.equals("8")){

BIDR bidr = map.get(login_ip);

Timestamp login_date = bidr.getLogin_date();//获取上线时间

Timestamp time_logOut=new Timestamp(Long.parseLong(time));//将下线时间转为时间戳类型为了计算上线时长

int time_deration=(int)(time_logOut.getTime()-login_date.getTime());//获取上线时长=下线时间-上线时间,其中用了getTime()方法进行计算

bidr.setTime_deration(time_deration);//上线时长

将bidr对象用add()方法添加进list集合;

list.add(bidr);

用remove()方法从map集合中移除数据(有7有8)

map.remove(login_ip);

}

温馨提示:在采集的同时,我们也应该将异常数据进行备份!!!

得到备份的实例化,并且调用它的备份方法,传入map集合;

return list;

}

# 网络模块 #:将存放数据的集合从客户端发送给服务器

Socket:网络套接字,包含IP、端口号,能够向网络发送请求,能够对其它主机的请求进行响应

基于TCP协议网络客户端编程步骤:

1)创建Socket,指定服务器地址、端口

Socket s = new Socket(ip,port);

2)获取服务器端的输入、输出流

s.getInputStream();

s.getOutputStream();

3)封装输入、输出流

将输入输出的字节流根据需求封装成文件、对象等输入、输出流

4)进行读、写操作

read(),writer()

5)释放资源

close()

代码如下:

Socket socket=new Socket(ip, port);

OutputStream out=socket.getOutputStream();

ObjectOutputStream ob=new ObjectOutputStream(out);

ob.writeObject(arg0);

if(ob!=null){

ob.flush();

ob.close();

}

if(socket!=null){

socket.close();

}

基于TCP协议网络服务器端编程步骤:

1)创建服务器端Socket,并绑定在某一端口上

ServerSocket ss = new ServerSocket(port);

2)接收客户请求,获取客户端Socket

Socket s = ss.accept();

3)通过客户端Socket,获取客户端的输入、输出流

s.getInputStream();

s.getOutputStream();

4)封装输入、输出流

将输入输出的字节流根据需求封装成文件、对象等输入、输出流

5)进行读、写操作

read(),writer()

6)释放资源

close()

代码如下:

ServerSocket server=new ServerSocket(port);

Socket socket = server.accept();

InputStream in=socket.getInputStream();

ObjectInputStream ob=new ObjectInputStream(in);

Collection<BIDR> bidr = (Collection<BIDR>) ob.readObject();

return bidr;

# 数据库入库模块 #:采用jdbc技术,将服务器接收到的数据集合保存在数据库中,进行java和数据库的交互

jdbc编程步骤:

1)准备四大参数

private String driver;

private String url;

private String username;

private String password;

2)注册/加载驱动

Class.forName(driver);

3)获取连接:DriverManager.getConnection()方法

Connection connection = DriverManager.getConnection(url,username, password);

4)创建statement或者preparedstatement对象

一般使用preparedstatement,因为它可以通过?进行动态传值,为了防止sql攻击。

给?号传值使用的是方法是setString(),setInt()等根据类型判断用setXXX()方法

pstmt.setType(index,value);

index从1开始:表示第几个?

5)执行sql语句:三种方法

execute():返回boolean类型的值,代表是否有结果集返回

executeUpdate():返回int类型的值,代表操作执行完成后受影响的数据库的行数(针对于insert,update,delete)

executeQuery:返回的是ResultSet结果集,专门针对于select语句

6)处理结果:有结果集,处理结果集

遍历结果集的方法:next()和getXXX()方法

while(rs.next()){

rs.getType(index/columnName);注意:如果传的是index,那么索引是从1开始的。

7)关闭资源:遵循后开的先关原则

statement

connection

具体代码如下:

//准备四大参数:

private static String driver;

private static String url;

private static String username;

private static String password;

//注册驱动

Class.forName(driver);

//获取连接

Connection conn=DriverManager.getConnection(url,username,password);

//设置手动提交

conn.setAutoCommit(false);

//将服务器端接收的集合强转为list集合

List<BIDR> list=(List<BIDR>) arg0;

//遍历该集合,得到每一个bidr对象;

//创建preparedstatement对象,传入sql语句:插入语句,将每一个bidr对象插入进数据库

for (int i = 0; i < list.size(); i++) {

BIDR bidr=list.get(i);

//获取日期是当前月份的某一天:才知道插入哪一张表,跟sql语句中的day相对应

int day=bidr.getLogin_date().getDate();

String sql="insert into t_detail_"+day+" values(?,?,?,?,?,?)";//注意:sql语句需采用字符串进行拼接

*********用log4j日志记录sql语句:

*********//new LoggerImpl().debug(sql);

//采用动态传值的方法加载参数

PreparedStatement pst=conn.prepareStatement(sql);

pst.setString(1,bidr.getAAA_login_name());

pst.setString(2, bidr.getLogin_ip());

pst.setTimestamp(3, bidr.getLogin_date());

pst.setTimestamp(4, bidr.getLogout_date());

pst.setString(5, bidr.getNAS_ip());

pst.setInt(6, bidr.getTime_deration()/1000/60);

//执行sql语句

pst.execute();

//提交数据

conn.commit();

//关闭资源

pst.close();

}

# 备份模块:边采集边备份,所以要在采集模块中将异常数据传给备份模块,而备份的异常数据需要保存在文件中,并且我们可以在该文件中根据登录ip查询到该登录者的相关信息#

用io流将采集模块的异常数据写入备份文件中(异常数据保存在map集合中),也就是说采集模块传给备份模块一个map集合,那么应该用ObjectOutputStream流进行接收map集合,并写入到备份文件中(用writeObject()方法);

ObjectOutputStream oos  =new ObjectOutputStream (new FileOutputStream(filepath));

oos.writeObject(map);

oos.flush();//刷新

oos.close();//关流

同样用io流读取备份文件,根据登录ip查询异常数据
        代码如下:
             ObjectInputStream ois=new ObjectInputStream(new FileInputStream(filepath));
             Map<String, BIDR> map=(Map<String, BIDR>) ois.readObject();
             for(String key:map.keySet()){
                 //根据参数中的IP值,获取对应的对象
                 if (key.equals(arg0)) {
                    return map.get(key);
                }
             }
             ois.close();//关流
        
    # 日志模块:用日志记录项目的流程,该模块我们需要掌握的是采用log4j技术写一个自定义日志输出器,也就是写一个自定义的log4j的配置文件 #    
        log4j配置文件编程步骤:
        1)配置日志记录器 Logger:分为五个级别:DEBUG、INFO、WARN、ERROR和FATAL。
            Log4j有一个规则:只输出级别不低于设定级别的日志信息,假设Loggers级别设定为INFO,则INFO、WARN、ERROR和FATAL级别的日志信息都会输出,而级别比INFO低的DEBUG则不会输出。
            配置语法:log4j.rootLogger = 日志级别,appenderName,appenderName (配置根记录器) 
                     log4j.logger.自定义记录器名= 日志级别,appenderName ,appenderName (配置自定义记录器)

2)配置日志信息输出的地方 Appender:允许把日志输出到不同的地方,如控制台(Console)、文件(Files),可以根据天数或者文件大小产生新的文件,可以以流的形式发送到其它地方等等。
            配置语法:log4j.appender.appenderName = className
            className=org.apache.log4j.ConsoleAppender(控制台)
            className=org.apache.log4j.FileAppender(文件)
            className=org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件)
            className=org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件)
            className=org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)

若是FileAppender选项有四个参数需要注意:
            Threshold=日志级别:指定日志信息的最低输出级别,默认为DEBUG。
            ImmediateFlush=true:表示所有消息都会被立即输出,设为false则不输出,默认值是true。
            Append=false:true表示消息增加到指定文件中,false则将消息覆盖指定的文件内容,默认值是true。
            File=文件路径:指定消息输出到某个文件中。
            
            log4j.appender.file1 = org.apache.log4j.FileAppender
            log4j.appender.file1.File = 文件路径
            log4j.appender.file1.Append = true

3)配置日志信息的布局 Layout:提供四种日志输出样式,如根据HTML样式、自由指定样式、包含日志级别与信息的样式和包含日志时间、线程、类别等信息的样式。
            log4j.appender.appenderName.layout =className
            className=org.apache.log4j.HTMLLayout(以HTML表格形式布局)
            className=org.apache.log4j.PatternLayout(可以灵活地指定布局模式)
            className=org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串)
            className=org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等信息)
            若是自由布局模式,应添加一个参数:写入具体的自定义的布局样式
            log4j.appender.appender2.layout.ConversionPattern=利用%x的特定含义进行自定义。

-: 信息输出时左对齐;
            %p: 输出日志信息优先级,即DEBUG,INFO,WARN,ERROR,FATAL,
            %d: 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
            %r: 输出自应用启动到输出该log信息耗费的毫秒数
            %c: 输出日志信息所属的类目,通常就是所在类的全名
            %t: 输出产生该日志事件的线程名
            %l: 输出日志事件的发生位置,相当于%C.%M(%F:%L)的组合,包括类目名、发生的线程,以及在代码中的行数。
            %x: 输出和当前线程相关联的NDC(嵌套诊断环境),尤其用到像java servlets这样的多客户多线程的应用中。
            %%: 输出一个"%"字符
            %F: 输出日志消息产生时所在的文件名称
            %L: 输出代码中的行号
            %m: 输出代码中指定的消息,产生的日志具体信息
            %n: 输出一个回车换行符,Windows平台为"\r\n",Unix平台为"\n"输出日志信息换行
            
            ## 配置根记录器代码 ##
            log4j.rootLogger = debug,console1,file1
            log4j.appender.console1 = org.apache.log4j.ConsoleAppender
            log4j.appender.console1.layout = org.apache.log4j.SimpleLayout
            log4j.appender.file1 = org.apache.log4j.FileAppender
            log4j.appender.file1.File = log.txt
            log4j.appender.file1.Append = true
            log4j.appender.file1.layout = org.apache.log4j.PatternLayout
            log4j.appender.file1.layout.ConversionPattern=[woss_gather] -%d %p -------%m%n
        
            ## 配置自定义记录器代码 ##
            log4j.logger.mylogger= debug,console,file
            log4j.appender.console = org.apache.log4j.ConsoleAppender
            log4j.appender.console.layout = org.apache.log4j.SimpleLayout
            log4j.appender.appender2 = org.apache.log4j.FileAppender
            log4j.appender.file.File = mylogger.txt
            log4j.appender.file1.Append = true
            log4j.appender.file.layout = org.apache.log4j.PatternLayout
            log4j.appender.file.layout.ConversionPattern=[woss_gather]-%d %-5p -[%m]%n

## log4j日志的使用##
            注册log4j: PropertyConfigurator.configure("log4j配置文件路径");
            配置不同的级别的日志:
            Logger.getLogger("记录器名").日志级别(String string);

# 配置模块:将配置信息(不要写死在代码中的常量信息)写入xml文件用来做配置文件,采用dom4j技术,进行解析xml文件并将配置信息的值传入每个模块中进行初始化 #
            采用dom4j的解析步骤:
            1)导入dom4j的jar包
            2)创建解析器
                SAXReader reader=new SAXReader();
            3)获取document对象:使用read()方法读入解析文件
                Document doc=reader.read("解析文件的路径");
            4)获取根元素:使用getRootElement()方法
                Element root = doc.getRootElement();
            5)获取所有一级子元素:使用elements()方法,返回元素集合对象
                 List<Element> element1 = root.elements();//记得加泛型哦
            6)遍历一级子元素的集合:使用增强for循环
                 for (Element e1 : element1) {
                       String name=e1.getName();//得到一级子元素的标签名

7)获取所有一级子元素的属性:使用attributes()方法,返回属性集合对象
                       List<Attribute> attribute = e1.attributes();//记得加泛型哦
                    //遍历所有一级子元素的属性的集合:使用增强for循环
                       for (Attribute attribute : attribute) {
                          String attName=attribute.getName();//得到属性名
                          String attValue=attribute.getValue();//得到属性值
                     }

8)获取所有二级子元素:使用elements()方法,返回元素集合对象
                    List<Element> element2 = element.elements();
                     for (Element e2 : element2) {
                        String name2=e2.getName();
                        String value2 = e2.getText();
                    }
                 }
            
            ## 配置模块编程步骤: ##
            第一步:解析xml文件:采用dom4j技术

第二步:将解析好的配置信息以键值对的方式保存在properties对象中;
                   private static Properties properties=new Properties();//存放的是一级子元素的标签名和属性值,为了通过反射拿到该类的实例
                   properties.setProperty(name, attValue);
                   private static Properties properties2=new Properties();//存放的是二级子元素的标签名和文本内容,为了对每个实例需要的常量信息传值(即初始化)
                   properties2.setProperty(name2, value2);
                   private static Properties properties3=new Properties();//存放的是一级子元素的标签名和二级子元素的所有内容
                注意:properties3起的是唯一标识作用(通过一级子元素的名字找到它相应的二级子元素的值),但是假设properties2这个存放二级子元素的集合中出现了相同的子元素,值却不相同,这时后一个值会覆盖掉前一个值的内容,这也是一个需要改进的地方哦。    
            
            第三步:通过反射得到每个模块的实例,并且调用它们的init()方法进行动态传值。
                以备份模块为例:
                public BackUP getBackup() throws Exception {
                String classname=properties.getProperty("backup");//得到全限定名:包名+类名
                BackUP backup=(BackUP) Class.forName(classname).newInstance();//通过反射拿到备份模块的实例
                backup.init((Properties) properties3.get("backup"));//调用备份模块的init()方法进行传值
                return backup;
                }
                这时backup的init()方法应该拿到配置信息,使用getProperty()方法,并且传入二级子元素的标签名
                public void init(Properties arg0) {
                    backfile=arg0.getProperty("back-temp");
                }
                其他模块代码同理可得。

注意:配置模块已完成,那么进行测试是不能在new一个类的实例,而是首先得到配置模块的实例,调用它相对应的方法进行对其他类的实例化。
                new ConfigurationImp1().getBackup();//得到了备份模块的一个实例
            
                ## 优化代码: 既然每个模块都需要通过反射获取实例并且调用init()方法进行初始化,那么我们可以将这几行代码进行封装##
                private WossModule getModule(String name) throws Exception{
                    String className=properties1.getProperty(name);
                    WossModule wm=(WossModule) Class.forName(className).newInstance();
                    wm.init((Properties)properties2.get(name));
                    return wm;
                }
                WossModule是所有类的父类;
                以备份模块举例进行调用该方法;
                public BackUP getBackup() throws Exception {
                        return (BackUP)getModule("backup");
                }
                其他模块调用该方法同理可得。

时间: 2024-10-07 00:17:50

电信采集项目的相关文章

电信采集子项目2(具体实现)

1.项目所用到jar包: woss-interface.jar:规范项目的各个模块 各个接口的功能在我的上一篇文章中有介绍,感兴趣的去找一下. demo4j-1.6.1.jar:解析xml文件 log4j-1.2.15:提供日志记录 ojdbc14.jar:jdbc的jar包 3.流程图 画的不好别见怪,嘿嘿. 2.项目的各个模块的具体实现 2.1客户端采集模块 假如我们需要采集的数据的结构如下: #briup1660|037:wKgB1660A|7|1239110900|44.211.221.

电信采集之数据处理

该项目的实质就是就是将3A服务器记录下来的日志封装成对象保存在数据库中 一.需求分析 1.1需求概述 所谓电信采集指的是3A服务器通过中心处理系统对一个记录了用户上线.下线等用户信息的文件的一个采集,将获取到的信息传送到数据库中进行持久化保存,并将为传输到数据库中的信息进行备份. 1.2需求分析 将一段时间内采集到的数据记录为数据清单,记作t_detail_x,通过中心处理系统对数据清单中的数据进行分析,根据七上八下的原则将数据分为完全信息和不完全信息(只包含7的即只有上线记录的为不完全信息,包

STM32F103频率和AD采集项目总结

刚刚做了我的第一个嵌入式项目----基于STM32F103的频率和AD采集系统. 是一个大系统的一个部分,没用操作系统,使用库函数编写,虽然东西很简单,但是对于我这个还没入门的菜鸟来说还是有点难度的.前后断断续续已经两周多了,到目前为止系统已经完成了接近90%,在这个过程中犯了很多错误,也走了很多的弯路,在这里记录下来,让自己以后不要再犯同样的错误,也给那些和我一样刚入门的朋友一点提示. 1.USART初始化问题: STM32F103启动文件默认有两个入口,一个是systemInit(),另一个

如何在织梦的采集项目加限制条件之水印去除方法

我说的其实就是如上图所示在右侧加一个限制条件 首先要找到这个文件/dede/templets/co_add_step2.htm 找到如下图所示的位置 加一句 <input name="iswater_<?php echo $tname; ?>" type="checkbox" id="iswater_<?php echo $tname; ?>" value="1" class="np&q

日志采集项目框架

1,项目图如下: 2, 实现过程 启动zookeeper(三台): bin/zkServer.sh start 启动kafka(三台): [email protected]1:/usr/local/kafka# bin/kafka-server-start.sh config/server.properties >logs/kafka3.log 2>&1 在131中创建access的topic: [email protected]1:/usr/local/kafka# bin/kafk

电信采集子项目1(大体架构)

大体分为两个方面: 1.客户端  负责采集各个区域产生的数据信息分为5个模块 1.1 配置模块 1.2 日志模块 1.3 采集模块 1.4 备份模块 1.5 网络模块 2.服务器 接收数据 1.1 配置模块 1.2 日志模块 1.3 入库模块 1.4 备份模块 1.5 网络模块 BIDR类 用来分装采集好的数据 接口 Gather接口 客户端采集模块 负责采集指定文件中的数据(AAA服务器产生的文件) 需要实现的方法: Collection<BIDR> gather(); 采集文件样例: #b

20.采集项目流程篇之清洗数据绑定到hive表中

先启动hive 在mydb2这个数据库中创建表: create external table mydb2.access(ip string,day string,url string,upflow string) row format delimited fields terminated by ','; 把清洗后的数据导入到刚刚创建的hive表中 load data inpath '/uvout/hive/' into table mydb2.access; 原文地址:https://www.

采集项目之使用资料

1 如何用easyui动态加载表格标题  https://my.oschina.net/missGu/blog/393069?fromerr=ltaovswk 2 easyui 官方文档 http://www.jeasyui.net/ 原文地址:https://www.cnblogs.com/Samuel-Leung/p/12522507.html

采集系统优化:大家接手过的最烂的项目,最坑爹的项目是哪个?

接手一个采集项目,性能极其低下,一跑公司数据库直接瘫痪. 采集数据访问密集,但对数据及时性无大要求. 下面是我接触过最痛苦的一个项目,也应该是此生见过最烂的项目. 代码层面 1:一次采集10W条,直接进内存 优化:分割,以两千条分步处理. 2:数据进内存时,逐条插入,我不想多说了,(插入前还要验是否有重复数据,走全表扫描,无索引,此条如果规划合适,完全可以避免) 优化:1):结合业务和数据,比如,此是采集当天,完全可以把当天的需验证的字段全进内存,在内存维护一张小表(或缓存服务器),进行重复验证