3.1 数据生产模块

此情此景,对于该模块的业务,即数据生产过程,一般并不会让你来进行操作,数据生产是一套完整且严密的体系,这样可以保证数据的安全性。

但是如果涉及到项目的一体化方案的设计(数据的产生、存储、分析、展示),则必须清楚每一个环节是如何处理的,包括其中每个环境可能隐藏的问题, 数据结构,数据内容可能出现的问题。


3.1.1 数据结构

想象一次完整通话一般会产生哪些数据:

  • 主要信息:主叫手机号, 被叫手机号, 开始建立通话的时间(通话时间戳), 通话时长

  • 其他一些信息: 主叫位置(主叫所在基站), 被叫位置(被叫所在基站) ...

这些数据都应该是用户在打电话的时候生成的.

对于本项目我们使用 Java 代码来生成上述的"主要信息", 关于"其他一些信息"本次不涉及.


3.1.2 编写代码

  1. 创建 Java 集合类存放模拟的电话号码和联系人;

  2. 随机选取两个手机号码当作“主叫”与“被叫”(注意判断两个手机号不能重复),产出call1call2字段数据;

  3. 创建随机生成通话建立时间的方法,可指定随机范围,最后生成通话建立时间,产出date_time字段数据

  4. 随机生成一个通话时长,单位:秒,产出 duration字段数据

  5. 将产出的一条数据拼接封装到一个字符串

  6. 使用 IO 操作将产出的一条通话数据写入到本地文件中

创建 module: data-producer

类: com.atguigu.telecom.dataproducer.Data

这个类存储我们的电话号码和姓名

package com.atguigu.telecom.dataproducer;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Data {
    /**
     * 电话号码列表
     */
    public final static List<String> phones;
    /**
     * 存储联系人: 电话号码->姓名
     */
    public final static Map<String, String> contacts;

    /**
     * 初始化一些我们需要的联系人信息
     */
    static {
        phones = new ArrayList<>();
        contacts = new HashMap<>();

        phones.add("15369468720");
        phones.add("19920860202");
        phones.add("18411925860");
        phones.add("14473548449");
        phones.add("18749966182");
        phones.add("19379884788");
        phones.add("19335715448");
        phones.add("18503558939");
        phones.add("13407209608");
        phones.add("15596505995");
        phones.add("17519874292");
        phones.add("15178485516");
        phones.add("19877232369");
        phones.add("18706287692");
        phones.add("18944239644");
        phones.add("17325302007");
        phones.add("18839074540");
        phones.add("19879419704");
        phones.add("16480981069");
        phones.add("18674257265");
        phones.add("18302820904");
        phones.add("15133295266");
        phones.add("17868457605");
        phones.add("15490732767");
        phones.add("15064972307");

        contacts.put("15369468720", "李雁");
        contacts.put("19920860202", "卫艺");
        contacts.put("18411925860", "仰莉");
        contacts.put("14473548449", "陶欣悦");
        contacts.put("18749966182", "施梅梅");
        contacts.put("19379884788", "金虹霖");
        contacts.put("19335715448", "魏明艳");
        contacts.put("18503558939", "华贞");
        contacts.put("13407209608", "华啟倩");
        contacts.put("15596505995", "仲采绿");
        contacts.put("17519874292", "卫丹");
        contacts.put("15178485516", "戚丽红");
        contacts.put("19877232369", "何翠柔");
        contacts.put("18706287692", "钱溶艳");
        contacts.put("18944239644", "钱琳");
        contacts.put("17325302007", "缪静欣");
        contacts.put("18839074540", "焦秋菊");
        contacts.put("19879419704", "吕访琴");
        contacts.put("16480981069", "沈丹");
        contacts.put("18674257265", "褚美丽");
        contacts.put("18302820904", "孙怡");
        contacts.put("15133295266", "许婵");
        contacts.put("17868457605", "曹红恋");
        contacts.put("15490732767", "吕柔");
        contacts.put("15064972307", "冯怜云");
    }
}

类: com.atguigu.telecom.dataproducer.bean.CallRecord

JavaBean类, 表示生产的每条记录对象.

并且在构造函数内计算出来了需要的数据.

package com.atguigu.telecom.dataproducer.bean;

import com.atguigu.telcom.util.Util;
import com.atguigu.telecom.dataproducer.Data;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * 表示一条通话记录
 */
public class CallRecord {
    /**
     * 电话号码1
     */
    private String call1;

    /**
     * 电话号码2
     */
    private String call2;

    /**
     * 通话建立建立时间
     */
    private long startTime;

    /**
     * 通话时长
     */
    private String duration;

    /**
     * 通话类型:
     * 站在电话号码1的角度是主叫还是被叫
     * <p>
     * 0 : 表示主叫
     * 1: 表示被叫
     */
    private int flag = 0;

    /**
     * 时间格式化对象
     */
    private SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

    /**
     * 随机生成:call1, call2, 通话建立时间, 通话时长, flag = 0 默认call1是主叫
     */
    public CallRecord() {
        //1. 生成call1和call2. 还要保证call1和call2不相等
        String[] calls = produceCalls();
        setCall1(calls[0]);
        setCall2(calls[1]);
        //2. 随机生成通话家建立时间(时间戳)
        setStartTime(produceRandomTime());
        //3. 随机生成通话时长(至少5s)
        setDuration(produceRandomDuration());
        //4. flag 设置为0
        setFlag(0);
    }

    /**
     * 生成随机的时长(s)
     * 为了后期数据的分析方便, 我们格式化通话时长的字符串的长度为4位
     * <p>
     * 比如: 12 -> "0012"
     * 140 -> '014'
     * 1500 -> '1500'
     *
     * @return
     */
    private String produceRandomDuration() {
        int duration = Util.randomInt(5, 6000);
        DecimalFormat format = new DecimalFormat("0000");
        return format.format(duration);
    }

    /**
     * 生成随机的时间戳
     *
     * @return
     */
    private long produceRandomTime() {
        // 为了业务需求, 我们只生成2018-1-1 到 2020-1-1之间的任意时间戳
        Calendar from = Calendar.getInstance();
        from.set(2018, 0, 1, 0, 0, 0);

        Calendar to = Calendar.getInstance();
        to.set(2020, 0, 1, 0, 0, 0);

        return Util.randomLong(from.getTimeInMillis(), to.getTimeInMillis());
    }

    /**
     * 随机生成两个电话
     *
     * @return
     */
    private String[] produceCalls() {
        List<String> phones = Data.phones;
        String[] calls = new String[2];
        // 随机生成两个电话号码: 生成两个不等的下标, 然后从集合中取出电话号码
        int index1 = Util.randomInt(0, phones.size() - 1);
        int index2;
        while (true) {
            index2 = Util.randomInt(0, phones.size() - 1);
            if (index1 != index2) break;
        }
        calls[0] = phones.get(index1);
        calls[1] = phones.get(index2);
        return calls;
    }

    public String getCall1() {
        return call1;
    }

    public void setCall1(String call1) {
        this.call1 = call1;
    }

    public String getCall2() {
        return call2;
    }

    public void setCall2(String call2) {
        this.call2 = call2;
    }

    public long getStartTime() {
        return startTime;
    }

    public void setStartTime(long startTime) {
        this.startTime = startTime;
    }

    public String getDuration() {
        return duration;
    }

    public void setDuration(String duration) {
        this.duration = duration;
    }

    public int getFlag() {
        return flag;
    }

    public void setFlag(int flag) {
        this.flag = flag;
    }

    /**
     * 返回的就是将来每条记录的格式
     * call1,startTime,call2,duration,flag
     * <p>
     * 输出字符串的时候, 我们把startTime 换成 2018-02-03 10:22:10 这样的格式
     *
     * @return
     */
    @Override
    public String toString() {
        return this.call1 + ","
                + dateFormatter.format(new Date(this.startTime)) + ","
                + this.call2 + ","
                + this.duration + ","
                + this.flag;
    }
}

类: com.atguigu.telecom.dataproducer.ProductLog

主类

package com.atguigu.telecom.dataproducer;

import com.atguigu.telecom.dataproducer.bean.CallRecord;

import java.io.*;


public class ProductLog {

    /**
     * 把日志(通话记录)写入本地磁盘
     * <p>
     * 一条通话记录, 我们向磁盘中写入一次
     * <p>
     * 通过循环的方式, 持续的写入到文件中.
     */
    public void writLogs() throws IOException, InterruptedException {
        String filePath = "/opt/module/telecom/calls.csv";

        FileWriter writer = new FileWriter(filePath);
        while (true) {
            writer.write(produceOneLog() + "\n");
            writer.flush();
            Thread.sleep(1000);
        }
    }

    /**
     * 随机产生一条通话记录
     * <p>
     * 通话记录的字符串表示形式
     */
    public String produceOneLog() {
        CallRecord record = new CallRecord();
        return record.toString();
    }

    public static void main(String[] args) throws IOException, InterruptedException {
        ProductLog productLog = new ProductLog();
        productLog.writLogs();
    }

}

工具类Util

package com.atguigu.telcom.util;

import sun.applet.Main;

import java.util.Random;

public class Util {
    /**
     * 随机数生成器对象
     */
    private static Random r = new Random();

    /**
     * 生成一个大于等于form, 小于等于to的随机int整数  [from, to]
     * from 必须小于等于 to
     *
     * @param from
     * @param to
     */
    public static int randomInt(int from, int to) {
        if (from > to) throw new RuntimeException("参数错误, " + from + " 大于" + to + "要求from必须小于等于to");
        return r.nextInt(to - from + 1) + from;
    }

    public static long randomLong(long from, long to) {
        if (from > to) throw new RuntimeException("参数错误, " + from + " 大于" + to + "要求from必须小于等于to");

        return Math.abs(r.nextLong()) % (to - from + 1) + from;

    }
}

注意:

  • 为了在其他项目中使用方便, 可以把工具类放入在专门的子项目中.

3.1.3 打包到 Linux 进行测试

创建目录/opt/module/telecom

把本项目下用到的文件都放在此目录下.

把打包好的文件copy到该目录下.

执行 jar 包文件下的主类:

java -Djava.ext.dirs=/opt/module/telecom -cp producer.jar com.atguigu.telecom.dataproducer
.ProductLog

说明:

  • 如果依赖了其他的子项目则需要添加参数:-Djava.ext.dirs=/opt/module/telecom, 如果没有则不需要添加

  • 如果有依赖其他的子项目, 也需要把其他的子项目打包放在-Djava.ext.dirs指定的目录下.

  • -cp 等价于-classpath


3.1.4 编写 shell 脚本

为了以后生成数据方便, 则可以把上面的命令放在脚本内.

cd /opt/module/telecom
vim producer.sh

# 上面的指令放入文件中
java -Djava.ext.dirs=/opt/module/telecom -cp producer.jar com.atguigu.telecom.dataproducer
.ProductLog 

# 修改文件权限
chmod 777 producer.sh

# 执行脚本
./producer.sh
Copyright © 尚硅谷大数据 2019 all right reserved,powered by Gitbook
该文件最后修订时间: 2018-12-27 20:11:51

results matching ""

    No results matching ""