市场项目交接文档初稿

33 篇文章 0 订阅
27 篇文章 1 订阅

市场项目交接文档初稿

市场项目交接,一个需求的解决逻辑

​一、首先拿到需求

分析一下需求,需要提数的内容

  需求移除需求

​二、从web层入手,找到mongodb中的表。

我们获取到php的代码,从源头解决该需求;因为php的关系,我们可以直接通过url地址确定到代码段。

首先我们根据页面,查到:推送部署,eclipse使用ctrl-h。

  移除点击此处添加图片说明文字

​一下子就定位到哪个php页面了,可以看到这个页面的表单名称:

  移除点击此处添加图片说明文字

​然后根据需求,我们需要: 查询具体内容:message用户数(UV)、展现用户数(UV)、下载用户数(UV)、安装用户数(UV)四个数据维度成功的手机型号、分辨率、android版本

那,php代码里,这里可以确定字段名称和字段的变量。

  移除点击此处添加图片说明文字

​接下来干嘛咧,没错,就是通过数据源找到数据库连接配置和语句。我之前的php代码增删改查文档派上了用场,我们来看一下这段代码:

  移除点击此处添加图片说明文字

​还等什么,点进去看看何方神圣。

然后这段代码里面,我们看到了数组结果变成result变量的过程,代码在这里

  移除点击此处添加图片说明文字

​可以,那我们是现在确定了是这张表。数据的最终来源于这张表:push_detail_statistics_deploy

ok,那我们看一下架构图,发现其实mongodb的数据来自于后台hive的推送,那么推送的过程是写到定时器里面了,定时器每天定时来做推送。所以,接下来,我们要去查看推送的定时器,推送了啥。

请注意,因为服务器发布在不同的集群节点上,所以,这里我们需要去询问服务器集群的定时器crontable位置。

三、从crontable中找到定时推送的python代码

我们来查看从hive到mongodb的过程。拿到了服务器集群的地址和用户名密码,192.168.0.141,root,密码:好运,走起,我们去看看。嗯,很轻松就找到了

  移除点击此处添加图片说明文字

​关于crontable的用法,我现在就粘贴一部分demo:

设置系统计划任务

修改/etc/crontab

添加对应的操作

* * * */1 * root reboot      每个月重启一次

第一个* 表示 “分钟”  0~59    可单个指定,用逗号分开  "*/2" 表示每两分钟运行一次

第二个* 表示 “小时”  0~23    可单个指定,用逗号分开  "*/1" 表示每个小时运行一次

第三个* 表示 “天数”  0~30    可单个指定,用逗号分开  "*/4" 表示这个月每4天运行一次

第四个* 表示 “月数”  1~12    可单个指定,用逗号分开  "*/3" 表示每个季度运行一次

第五个* 表示 “星期几”  0~6    单个指定,用逗号分开

具体实例:

23  6  5  12  *    root  rebooot          表示,在每年的12月5号的早晨6点23分00秒以root用户执行一次重启

5  */1  1  1  *   root  sh /etc/happy.sh         表示每年的元旦那天,每个小时的5分00秒执行一次happy.sh的脚本

好的,继续,切换用户 

su hdfs

crontab -l

查看到定时任务。

  移除点击此处添加图片说明文字

​现在我们要找到那个定时任务,将后台hive向mongodb里面导的脚本,找出来。注意这里定时脚本很多,也符合crontab的开发方式,是哪一个呢,很尴尬,注释是后来加上的,本来都没有写注释,很难受。

然后我们了解到,这个sh是做市场的任务计算的,将hive表的数据往mongodb里面推,也是在这里做的。

  移除点击此处添加图片说明文字

​咱们决定去看看这个脚本:

more /etl/tools/etl-python/push/push_report.sh

  移除点击此处添加图片说明文字

​好了,那现在搞清楚了,这两段红色部分,先看下面这部分,是干嘛的呢,是做同步用的,还记得前文说的mongodb数据吗,那mongodb从哪里来的呢,其实就是从后台的hive同步过去的,我们可以看一下,主要这里看《推送部署统计查询》的部分,而不是静默的。

那我们看下面这个部分,我举其中一个例子。

cd /etl/tools/etl-python/push;

/opt/cloudera/parcels/CDH/lib/hadoop/bin/hadoop jar /home/OTAtest/ToMongo-0.0.1-SNAPSHOT-jar-with-dependencies.jar push_detail_statistics_deploy $yesday_date $yesday_date   > ./hive2Mongo.log

第一步进入目录,第二步咱们将hadoop里面的jar包执行, 给入的参数main类,还有两个时间参数。这个模块还好,后期,咱们也会去看这个jar包里面到底做什么的,那现在的业务是提数,所以这里我们也不纠结,接着看上面两个。

好了,我们随便来看看这两个java包里面到底做了什么:

我想了解一下到底,hive是怎么to mongodb的,都在这里解释。


说到上面两个的话,我们就得先去hive里,看看这两个报表同步的原始表了。

四、hive表中的数据算法追踪。

那我们来看看前台业务的点击按钮下,触发的两张表,那我们着重看需求的这张表,推送部署统计查询:

看一下这张表的大概。​

  移除点击此处添加图片说明文字

看下这种数据格式:

  移除点击此处添加图片说明文字

​如果字段不是很清楚,因为没有文档,我们应该怎么看呢,对了,对照php的字段类型。

  移除点击此处添加图片说明文字

​嗯,与字段对比发现pv、uv的前面首字母被小写了,嗯,好,那这个关系找到了,我们看下需求:

  移除点击此处添加图片说明文字

文字版本是:

   请帮忙查询卓易市场锐嘉科渠道push用户相关信息,具体查询条件如下:

查询push案件:卡牛信用管家(com.mymoney.sms)

查询时间段:2017年7月8日-2017年7月11日

push--锐嘉科渠道号:bshrjke01(z001@bshrjke01)

​好的,我们写一下sql语句,查询这个渠道好的四个指标。

  移除点击此处添加图片说明文字

这个其实不是他最后要的,所以我们还得接着往下看的。​

好的,其实这个sql语句我们查和不查好像也没有什么意义的,因为,需求要的是具体的内容,所以,我们需要去看一下,这四个指标是如何算的,那,到这里,我们还得追刚才我们看到的crontab里面找到的脚本里的第一个红框框,接着去找他的出处。那下面,我们就带着这4个结果,去找手机型号、分辨率、安卓版本了。

五、python脚本解读,找到计算的各个指标的逻辑。

那,我们看

/usr/bin/python /etl/tools/etl-python/push/push_detail_statistics_deploy.py $yesday_date $yesday_date

其实就是这个:

  移除点击此处添加图片说明文字

​那我们打开看看,这个python脚本。

豁然开朗了没有?

  移除点击此处添加图片说明文字

​开朗了吧,我粘贴一下代码,因为notepad的高亮不是很清楚,那我们就这主要做的两个sql拿出来粘贴一下。

记住指标是这四个,message用户数、展现用户数、下载用户数、安装用户数

分别对应的字段为:udcnt,s_uv,d_uv,i_uv这四个用户数的算法。

我们就把分析的sql在下方用红色高亮出来吧。

use push_report;set hive.auto.convert.join=false; insert overwrite table push_detail_statistics_deploy partition(pt ='"""+date+"""')

select t3.package_name,t3.channel_id,udcnt,

s_pv,

s_uv,

c_pv,

c_uv,

d_pv,

d_uv,

i_pv,

i_uv from

(select package_name,trim(channel_id) as channel_id,count(imsi) as s_pv,count(distinct imsi) as s_uv from oz_log.cap_data_market_rec_mt where action = 3 and source1 = 3 and pt='"""+date+"""' and package_name<>'null' group by package_name,channel_id)t3 left outer join

(select package_name,trim(channel_id) as channel_id,count(imsi) as c_pv,count(distinct imsi)as c_uv from oz_log.cap_data_market_rec_mt where action = 4 and source1 = 3 and pt='"""+date+"""' and package_name<>'null' group by package_name,channel_id)t4 on

(t3.package_name=t4.package_name  and t3.channel_id=t4.channel_id) left outer join

(select file_name,trim(channel_id) as channel_id,count(imsi) as d_pv,count(distinct imsi) as d_uv from oz_log.cap_data_download_rec_mt where result=3 and source1 = 3 and pt='"""+date+"""' and file_name<>'null' group by file_name,channel_id)t6 on

(t3.package_name=t6.file_name  and t3.channel_id=t6.channel_id) left outer join

(select package_name,trim(channel_id) as channel_id,count(imsi) as i_pv,count(distinct imsi) as i_uv from oz_log.cap_data_market_rec_mt where source1 = 3 and action = 6 and imsi is not null 

and pt='"""+date+"""' group by package_name,channel_id)t8 on

(t3.package_name=t8.package_name  and t3.channel_id=t8.channel_id) left outer join

(select package_name,trim(channel_id) as channel_id,count(distinct imsi) as udcnt from oz_log.cap_appstore_push_rec_mt t1 join  oz_log.cap_resource_info t2 on push_apk=res_apk_id where t1.pt='"""+date+"""' and t2.pt='"""+date+"""' and imsi is not null  group by package_name,channel_id)t2 

on (t3.channel_id=t2.channel_id and t3.package_name=t2.package_name )

 group by t3.package_name,t3.channel_id,udcnt,s_pv,s_uv,c_pv,c_uv,d_pv,d_uv,i_pv,i_uv;

好了,这么看很难受吧,我截图吧:

  移除点击此处添加图片说明文字

​干哈的呢?这就是udcnt的算法了,显示查出来t3表然后用t3表和我们的t1t2关联表来关联,就查到了这个message用户数。而且,这个是由oz_log库算出来的结果,那我们就把这个sql场景还原。

咱们整理出来,其实就是这么算的

  移除点击此处添加图片说明文字

​oz_log.cap_appstore_push_rec_mt t1   join  oz_log.cap_resource_info t2

两表关联得到message用户数

现在咱们就去看看这两张表。

大致分析了一下这两张表,我们可以看一下大概的内容,因为没有文档,只能通过数据和名字来了解了。

  移除点击此处添加图片说明文字

​这个应该就是分辨率了。那我们再看一下需求吧,手机型号、分辨率、android版本

那么最后的sql可以这么写,来筛选维度:

select imei,imsi,package_name,channel_id,hstype,screen_width,screen_height,version_code  from oz_log.cap_appstore_push_rec_mt t1 

  join  oz_log.cap_resource_info t2 on push_apk=res_apk_id where t1.pt between '2017-07-08' and '2017-07-11' and t2.pt between '2017-07-08' and '2017-07-11' 

  and imsi is not null and channel_id='z001@bshrjke01'

当然关于是否去重,我就不清楚了,反正,就把数据导出来把。查询完了,我们发现需求分析错了,从新分析一下,他要的可能是四个字段值,

  移除点击此处添加图片说明文字

​那,这个就是他最后要的东西了,咱们解决了一个,还有另一个,分辨率的维度,我们检测一下,分析一下。其实这些统计也就是想看看哪些型号,哪些用户数多,哪些分辨率的对用户数的影响,

  移除点击此处添加图片说明文字

​那最后,这个就是我们说的,这个所有需求的结果,我们发送给运营人员了。那,这边就慢慢的接入市场项目了。

继续解决遗留的一个问题,就是那个java文件到底做了什么,我们首先下载一个反编译工具,看一下,反编译的内容,可以看到,这里有个udf,粘贴一下,咱们写过复杂的,这种简单的udf,直接带过了,粘贴一下代码,然后看一下.

package com.ToMongo;


import org.apache.hadoop.hive.ql.exec.UDF;

import org.apache.log4j.Logger;

import redis.clients.jedis.Jedis;


public class Udf_mst_dict extends UDF

{

  private static final Logger log = Logger.getLogger(Udf_mst_dict.class.getName());


  private final String REDIS_IP = "192.168.3.48";

  private final int REDIS_PORT = 6379;

  private final int REDIS_DB_1 = 41;


  private Jedis jedis_db_1 = null;


  public Udf_mst_dict() {

    this.jedis_db_1 = new Jedis("192.168.3.48", 6379);

    this.jedis_db_1.select(41);

  }


  public String evaluate(String app_id, String name, String type)

  {

    if (name == null) {

      return "00000000";

    }

    if ("0".equals(type)) {

      String ch_cd = this.jedis_db_1.hget(app_id + name, "mst_ch");

      return ch_cd;

    }if ("1".equals(type)) {

      String ver_cd = this.jedis_db_1.hget(app_id + name, "mst_ver");

      return ver_cd;

    }

    log.error("字典表中出现无效");

    return "无效类型";

  }

}​

那我们看,它引入了redis,从redis里面拿了一些东西出来,这里声明redis的ip端口和库名称。

我们看,这个evaluate声明了三个参数,如果name为null返回好多个0,如果类型是0,我们把redis里面的appid和name和mst渠道取出来,用字符串返回,如果为1的类型,返回mst的版本拼接的字符出去了。这个udf搞这件事情的,在脚本里面一定有用的。

另外一个类,ImportDataToMongo类,​代码比较多,我就不解释太多了,直接粘贴一些代码,然后大致说一下吧。

package com.ToMongo;


import com.mongodb.MongoClient;

import com.mongodb.client.MongoCollection;

import com.mongodb.client.MongoDatabase;

import java.io.BufferedInputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.InputStream;

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.ResultSet;

import java.sql.ResultSetMetaData;

import java.sql.Statement;

import java.text.SimpleDateFormat;

import java.util.Calendar;

import java.util.Date;

import java.util.Properties;

import org.apache.log4j.Logger;

import org.bson.Document;


public class ImportDataToMongo

{

  protected static Logger logger = Logger.getLogger(ImportDataToMongo.class);

  private static String driverName = "org.apache.hive.jdbc.HiveDriver";


  private static String mongo_url = "192.168.0.141";

  private static int port = 27010;


  public static void main(String[] args) { logger.info("usage hadoop jar  <table name> <start>  <end>");

    String table_name = args[0];

    String dt_start = "";

    String dt_end = "";

    if (args.length > 2) {

      dt_start = args[1];

      dt_end = args[2];

    }


    if ((table_name == "") || (table_name == null)) {

      logger.error("please input table name...");

    }

    if ((dt_start == "") || (dt_start == null)) {

      Calendar cal = Calendar.getInstance();

      cal.add(5, -1);

      dt_start = new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime());

    }

    if ((dt_end == "") || (dt_end == null)) {

      dt_end = new SimpleDateFormat("yyyy-MM-dd").format(new Date());

    }

    Properties prop = new Properties();

    String dir = System.getProperty("user.dir");

    String reg = "^[0-9]{4}-[0-9]{2}-[0-9]{2}$";

    if ((dt_start.trim().matches(reg)) && (dt_end.trim().matches(reg)))

      try {

        logger.info("start import data..............." + dt_start + ":" + dt_end);

        InputStream in = new BufferedInputStream(new FileInputStream(dir + File.separator + "database.properties"));

        prop.load(in);

        String db_name = prop.getProperty("db_name");

        String hive_name = prop.getProperty("hive_name");

        String hive_port = prop.getProperty("hive_port");

        if ((db_name == null) || (db_name == "")) {

          logger.error("your db_name isn't exists please check");

        }

        if ((hive_name == null) || (hive_name == "")) {

          logger.error("your hive_name isn't exists please check");

        }

        if ((hive_port == null) || (hive_port == "")) {

          logger.error("your hive_port isn't exists please check");

        }

        in.close();

        Class.forName(driverName);

        Connection con = DriverManager.getConnection("jdbc:hive2://" + hive_name + ":" + hive_port, "root", "goodluck");


        String sql = "select * from " + db_name + "." + table_name + " where pt<='" + dt_end + "' and pt>='" + dt_start + "'";

        logger.info(sql);

        Statement stmt = con.createStatement();

        ResultSet res = stmt.executeQuery(sql);

        MongoClient client = getClient(mongo_url, port);

        MongoDatabase db = client.getDatabase(db_name);

        MongoCollection col = db.getCollection(table_name);

        col.deleteMany(new Document("pt", new Document("$gte", dt_start).append("$lt", dt_end)));

        ResultSetMetaData meta = res.getMetaData();

        int index = 0;

        while (res.next()) {

          Document doc = new Document();

          for (int i = 1; i <= meta.getColumnCount(); i++) {

            String coln = meta.getColumnName(i).replaceAll(table_name + ".", "").trim();

            Object obj = res.getObject(i);

            if ((obj == null) || (obj == "null")) {

              if (meta.getColumnTypeName(i).toLowerCase() == "int")

                obj = Integer.valueOf(0);

              else if (meta.getColumnTypeName(i).toLowerCase() == "string") {

                obj = " ";

              }

            }

            doc.append(coln, res.getObject(i));

          }

          index++;

          col.insertOne(doc);

        }

        logger.info("import success " + index + "  lines into " + db_name + "." + table_name);

      } catch (Exception e) {

        logger.error(e.getMessage());

        System.exit(1);

      } }


  public static MongoClient getClient(String url, int port)

  {

    MongoClient client = null;

    try {

      client = new MongoClient("192.168.0.141", 27010);

    }

    catch (Exception e) {

    }

    return client;

  }

}​

我带你们看一下,就是首先呢,我们声明驱动名称,是hive的,因为要hive搬到mongodb,所以,还要声明mongo的url和端口。而且,我们通过脚本看到了,是通过hadoop集群来执行这个jar的,并且,当时给了三个参数,看来在这里都明朗了,表名,开始时间,结束时间,都是昨天。

然后我们if-else判断一下,这里吧时间都重新校验了一下,时间也用了正则校验了。

  移除点击此处添加图片说明文字

​好的,接着,我们看到了如果开始时间和结束时间都匹配的话,我们进入导入部分,读取配置文件,配置文件里面放了

InputStream in = new BufferedInputStream(new FileInputStream(dir + File.separator + "database.properties"));

        prop.load(in);

        String db_name = prop.getProperty("db_name");

        String hive_name = prop.getProperty("hive_name");

        String hive_port = prop.getProperty("hive_port");

​嗯,然后我们关闭流,创建hive的jdbc,拼写sql语句,jdbc这种形式,将所查到的结果集循环遍历然后col.insertOne(doc)。好了,就这么个东西,把一切都高通了,就gg了。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值