Etl目标 

解析我们收集的日志数据,将解析后的数据保存到hbase中。这里选择hbase来存储数据的主要原因就是:

hbase的宽表结构设计适合我们的这样多种数据格式的数据存储(不同event有不同的存储格式)。

在etl过程中,我们需要将我们收集得到的数据进行处理,包括ip地址解析、userAgent解析、服务器时间解析等。

在我们本次项目中ip解析采用的是纯真ip数据库,官网是http://www.cz88.net/
另外:ip解析可以采用淘宝提供的ip接口来进行解析
地址:http://ip.taobao.com/
接口:http://ip.taobao.com/service/getIpInfo.php?ip=[ip地址字串]

ETL存储
etl的结果存储到hbase中,由于考虑到不同事件有不同的数据格式,所以我们将最终etl的结果保存到hbase中,我们使用单family的数据格式,rowkey的生产模式我们采用timestamp+uuid.crc编码的方式。hbase创建命令:create 'event_logs', 'info'

步骤如下:

1. 修改pom文件,添加hadoop和hbase依赖

<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>cz.mallat.uasparser</groupId>
<artifactId>uasparser</artifactId>
<version>0.6.2</version>
</dependency>
<!-- hadoop start -->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.2</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-common</artifactId>
<version>2.7.2</version>
</dependency>
<!-- hadoop end -->
<!-- hbase start -->
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.1.3</version>
</dependency> <dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client </artifactId>
<version>1.1.3</version>
</dependency> <!-- hbase end -->
<!-- mysql start -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
</dependency>
<!-- mysql end -->
<dependency>
<groupId>cz.mallat.uasparser</groupId>
<artifactId>uasparser</artifactId>
<version>0.6.1</version>
</dependency> <dependency>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
<version>1.8</version>
<scope>system</scope>
<systemPath>${JAVA_HOME}/lib/tools.jar</systemPath>
</dependency>
<dependency>
<groupId>org.cloudera.htrace</groupId>
<artifactId>htrace-core</artifactId>
<version>2.04</version>
</dependency>
<dependency>
<groupId>org.apache.htrace</groupId>
<artifactId>htrace-core</artifactId>
<version>3.1.0-incubating</version>
</dependency>
<dependency>
<groupId>org.apache.htrace</groupId>
<artifactId>htrace-core4</artifactId>
<version>4.0.1-incubating</version>
</dependency>
</dependencies>

2. 添加LoggerUtil类,中间设计到EventLogConstant常量类和TimeUtil工具类
LoggerUtil主要作用就是解析日志,返回一个map对象

package com.kk.etl.util;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map; import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger; import com.kk.common.EventLogConstants;
import com.kk.etl.util.IpSeekerExt.RegionInfo;
import com.kk.etl.util.UserAgentUtil.UserAgentInfo;
import com.kk.util.TimeUtil;
public class LoggerUtils {
private static final Logger logger = Logger.getLogger(LoggerUtils.class);
private static IpSeekerExt ipSeekerExt = new IpSeekerExt(); /**
* 处理日志数据logText,返回处理结果map集合<br/>
* 如果logText没有指定数据格式,那么直接返回empty的集合
*
* @param logText
* @return
*/
public static Map<String, String> handleLog(String logText) {
Map< String, String> clientInfo=new HashMap<String,String>();
if (logText!=null&&!logText.isEmpty()) {
String[] splits=logText.split(EventLogConstants.LOG_SEPARTIOR);
if (splits.length==4) {
// 日志格式为: ip^A 服务器时间^A host^A 请求参数
clientInfo.put(EventLogConstants.LOG_COLUMN_NAME_IP, splits[0].trim()); // 设置ip
//设置服务器时间
clientInfo.put(EventLogConstants.LOG_COLUMN_NAME_SERVER_TIME, String.valueOf(TimeUtil.parseNginxServerTime2Long(splits[1].trim())));
int index=splits[3].indexOf("?");
if (index > -1) {
String requestBody = splits[3].substring(index + 1); // 获取请求参数,也就是我们的收集数据
// 处理请求参数
handleRequestBody(requestBody, clientInfo);
// 处理userAgent
handleUserAgent(clientInfo);
// 处理ip地址
handleIp(clientInfo);
}else {
// 数据格式异常
clientInfo.clear();
} } }
return clientInfo;
}
/**
* 处理请求参数
*
* @param requestBody
* @param clientInfo
*/
private static void handleRequestBody(String requestBody, Map<String, String> clientInfo) { String[] requestParams=requestBody.split("&");
for (String param : requestParams) {
if (param!=null&&!param.isEmpty()) {
int index= param.indexOf("=");
if (index < 0) {
logger.warn("没法进行解析参数:" + param + ", 请求参数为:" + requestBody);
continue;
} try {
String p1=param.substring(0,index);
String p2 = URLDecoder.decode(param.substring(index + 1), "utf-8");
if (StringUtils.isNotBlank(p1) && StringUtils.isNotBlank(p2)) {
clientInfo.put(p1, p2);
}
} catch (UnsupportedEncodingException e) {
logger.warn("解码操作出现异常", e);
continue;
} } else { } }
} /**
* 处理ip地址
*
* @param clientInfo
*/
private static void handleIp(Map<String,String> clientInfo) {
if (clientInfo.containsKey(EventLogConstants.LOG_COLUMN_NAME_IP)) {
String ip = clientInfo.get(EventLogConstants.LOG_COLUMN_NAME_IP);
RegionInfo info = ipSeekerExt.analyticIp(ip);
if (info!= null) {
if (info.getCountry().equals("unknown")&&info.getCity().equals("unknown")&&info.getProvince().equals("unknown")) {
info.setCountry("中国");
info.setProvince("广东省");
info.setCity("广州市");
}
clientInfo.put(EventLogConstants.LOG_COLUMN_NAME_COUNTRY, info.getCountry());
clientInfo.put(EventLogConstants.LOG_COLUMN_NAME_PROVINCE, info.getProvince());
clientInfo.put(EventLogConstants.LOG_COLUMN_NAME_CITY, info.getCity());
}
}
} /**
* 处理浏览器的userAgent信息
*
* @param clientInfo
*/
private static void handleUserAgent(Map<String, String> clientInfo) {
if (clientInfo.containsKey(EventLogConstants.LOG_COLUMN_NAME_USER_AGENT)) {
UserAgentInfo info = UserAgentUtil.analyticUserAgent(clientInfo.get(EventLogConstants.LOG_COLUMN_NAME_USER_AGENT));
if (info != null) {
clientInfo.put(EventLogConstants.LOG_COLUMN_NAME_OS_NAME, info.getOsName());
clientInfo.put(EventLogConstants.LOG_COLUMN_NAME_OS_VERSION, info.getOsVersion());
clientInfo.put(EventLogConstants.LOG_COLUMN_NAME_BROWSER_NAME, info.getBrowserName());
clientInfo.put(EventLogConstants.LOG_COLUMN_NAME_BROWSER_VERSION, info.getBrowserVersion());
}
}
} }

EventLogConstants主要作用就是描述hbase的event_logs表的信息(表名,列簇名,列名)以及日志收集的日志中的数据参数name。其实列名和name是一样的。

package com.kk.common;

import java.nio.ByteBuffer;

/**
*
* @author hzk
*@parm
*
*/
public class EventLogConstants { /**
* 事件枚举类。指定事件的名称
*
* @author gerry
*
*/
public static enum EventEnum {
LAUNCH(1, "launch event", "e_l"), // launch事件,表示第一次访问
PAGEVIEW(2, "page view event", "e_pv"), // 页面浏览事件
CHARGEREQUEST(3, "charge request event", "e_crt"), // 订单生产事件
CHARGESUCCESS(4, "charge success event", "e_cs"), // 订单成功支付事件
CHARGEREFUND(5, "charge refund event", "e_cr"), // 订单退款事件
EVENT(6, "event duration event", "e_e") // 事件
; public final int id; // id 唯一标识
public final String name; // 名称
public final String alias; // 别名,用于数据收集的简写 private EventEnum(int id, String name, String alias) {
this.id = id;
this.name = name;
this.alias = alias;
} /**
* 获取匹配别名的event枚举对象,如果最终还是没有匹配的值,那么直接返回null。
*
* @param alias
* @return
*/
public static EventEnum valueOfAlias(String alias) {
for (EventEnum event : values()) {
if (event.alias.equals(alias)) {
return event;
}
}
return null;
}
} /**
* 表名称
*/
public static final String HBASE_NAME_EVENT_LOGS = "event_logs"; /**
* event_logs表的列簇名称
*/
public static final String EVENT_LOGS_FAMILY_NAME = "info"; /**
* 日志分隔符
*/
public static final String LOG_SEPARTIOR = "\\^A"; /**
* 用户ip地址
*/
public static final String LOG_COLUMN_NAME_IP = "ip"; /**
* 服务器时间
*/
public static final String LOG_COLUMN_NAME_SERVER_TIME = "s_time"; /**
* 事件名称
*/
public static final String LOG_COLUMN_NAME_EVENT_NAME = "en"; /**
* 数据收集端的版本信息
*/
public static final String LOG_COLUMN_NAME_VERSION = "ver"; /**
* 用户唯一标识符
*/
public static final String LOG_COLUMN_NAME_UUID = "u_ud"; /**
* 会员唯一标识符
*/
public static final String LOG_COLUMN_NAME_MEMBER_ID = "u_mid"; /**
* 会话id
*/
public static final String LOG_COLUMN_NAME_SESSION_ID = "u_sd";
/**
* 客户端时间
*/
public static final String LOG_COLUMN_NAME_CLIENT_TIME = "c_time";
/**
* 语言
*/
public static final String LOG_COLUMN_NAME_LANGUAGE = "l";
/**
* 浏览器user agent参数
*/
public static final String LOG_COLUMN_NAME_USER_AGENT = "b_iev";
/**
* 浏览器分辨率大小
*/
public static final String LOG_COLUMN_NAME_RESOLUTION = "b_rst";
/**
* 当前url
*/
public static final String LOG_COLUMN_NAME_CURRENT_URL = "p_url";
/**
* 前一个页面的url
*/
public static final String LOG_COLUMN_NAME_REFERRER_URL = "p_ref";
/**
* 当前页面的title
*/
public static final String LOG_COLUMN_NAME_TITLE = "tt";
/**
* 订单id
*/
public static final String LOG_COLUMN_NAME_ORDER_ID = "oid";
/**
* 订单名称
*/
public static final String LOG_COLUMN_NAME_ORDER_NAME = "on";
/**
* 订单金额
*/
public static final String LOG_COLUMN_NAME_ORDER_CURRENCY_AMOUNT = "cua";
/**
* 订单货币类型
*/
public static final String LOG_COLUMN_NAME_ORDER_CURRENCY_TYPE = "cut";
/**
* 订单支付金额
*/
public static final String LOG_COLUMN_NAME_ORDER_PAYMENT_TYPE = "pt";
/**
* category名称
*/
public static final String LOG_COLUMN_NAME_EVENT_CATEGORY = "ca";
/**
* action名称
*/
public static final String LOG_COLUMN_NAME_EVENT_ACTION = "ac";
/**
* kv前缀
*/
public static final String LOG_COLUMN_NAME_EVENT_KV_START = "kv_";
/**
* duration持续时间
*/
public static final String LOG_COLUMN_NAME_EVENT_DURATION = "du";
/**
* 操作系统名称
*/
public static final String LOG_COLUMN_NAME_OS_NAME = "os";
/**
* 操作系统版本
*/
public static final String LOG_COLUMN_NAME_OS_VERSION = "os_v";
/**
* 浏览器名称
*/
public static final String LOG_COLUMN_NAME_BROWSER_NAME = "browser";
/**
* 浏览器版本
*/
public static final String LOG_COLUMN_NAME_BROWSER_VERSION = "browser_v";
/**
* ip地址解析的所属国家
*/
public static final String LOG_COLUMN_NAME_COUNTRY = "country";
/**
* ip地址解析的所属省份
*/
public static final String LOG_COLUMN_NAME_PROVINCE = "province";
/**
* ip地址解析的所属城市
*/
public static final String LOG_COLUMN_NAME_CITY = "city"; /**
* 定义platform
*/
public static final String LOG_COLUMN_NAME_PLATFORM = "pl"; }

TimeUtil主要作用解析服务器时间以及定义rowkey中的timestamp时间戳格式。

package com.kk.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern; import org.apache.commons.lang.StringUtils; import com.kk.common.DateEnum; public class TimeUtil { /**
* 将nginx服务器时间转换为时间戳,如果说解析失败,返回-1
*
* @param input
* @return
*/
public static long parseNginxServerTime2Long(String input) {
Date date = parseNginxServerTime2Date(input);
return date == null ? -1L : date.getTime();
} /**
* 将nginx服务器时间转换为date对象。如果解析失败,返回null
*
* @param input
* 格式: 1449410796.976
* @return
*/
public static Date parseNginxServerTime2Date(String input) {
if (StringUtils.isNotBlank(input)) {
try {
long timestamp = Double.valueOf(Double.valueOf(input.trim()) * 1000).longValue();
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(timestamp);
return calendar.getTime();
} catch (Exception e) {
// nothing
}
}
return null;
} /**
* 判断输入的参数是否是一个有效的时间格式数据
*
* @param input
* @return
*/
public static boolean isValidateRunningDate(String input) {
Matcher matcher = null;
boolean result = false;
String regex = "[0-9]{4}-[0-9]{2}-[0-9]{2}";
if (input != null && !input.isEmpty()) {
Pattern pattern = Pattern.compile(regex);
matcher = pattern.matcher(input);
}
if (matcher != null) {
result = matcher.matches();
}
return result;
}
public static final String DATE_FORMAT = "yyyy-MM-dd"; /**
* 获取昨日的日期格式字符串数据
*
* @return
*/
public static String getYesterday() {
return getYesterday(DATE_FORMAT);
} /**
* 获取对应格式的时间字符串
*
* @param pattern
* @return
*/
public static String getYesterday(String pattern) {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR, -1);
return sdf.format(calendar.getTime());
} /**
* 将yyyy-MM-dd格式的时间字符串转换为时间戳
*
* @param input
* @return
*/
public static long parseString2Long(String input) {
return parseString2Long(input, DATE_FORMAT);
}
/**
* 将指定格式的时间字符串转换为时间戳
*
* @param input
* @param pattern
* @return
*/
public static long parseString2Long(String input, String pattern) {
Date date = null;
try {
date = new SimpleDateFormat(pattern).parse(input);
} catch (ParseException e) {
throw new RuntimeException(e);
}
return date.getTime();
} /**
* 将时间戳转换为yyyy-MM-dd格式的时间字符串
* @param input
* @return
*/
public static String parseLong2String(long input) {
return parseLong2String(input, DATE_FORMAT);
} /**
* 将时间戳转换为指定格式的字符串
*
* @param input
* @param pattern
* @return
*/
public static String parseLong2String(long input, String pattern) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(input);
return new SimpleDateFormat(pattern).format(calendar.getTime());
} /**
* 从时间戳中获取需要的时间信息
*
* @param time
* 时间戳
* @param type
* @return 如果没有匹配的type,抛出异常信息
*/
public static int getDateInfo(long time, DateEnum type) {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(time);
if (DateEnum.YEAR.equals(type)) {
// 需要年份信息
return calendar.get(Calendar.YEAR);
} else if (DateEnum.SEASON.equals(type)) {
// 需要季度信息
int month = calendar.get(Calendar.MONTH) + 1;
if (month % 3 == 0) {
return month / 3;
}
return month / 3 + 1;
} else if (DateEnum.MONTH.equals(type)) {
// 需要月份信息
return calendar.get(Calendar.MONTH) + 1;
} else if (DateEnum.WEEK.equals(type)) {
// 需要周信息
return calendar.get(Calendar.WEEK_OF_YEAR);
} else if (DateEnum.DAY.equals(type)) {
return calendar.get(Calendar.DAY_OF_MONTH);
} else if (DateEnum.HOUR.equals(type)) {
return calendar.get(Calendar.HOUR_OF_DAY);
}
throw new RuntimeException("没有对应的时间类型:" + type);
} /**
* 获取time指定周的第一天的时间戳值
*
* @param time
* @return
*/
public static long getFirstDayOfThisWeek(long time) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(time);
cal.set(Calendar.DAY_OF_WEEK, 1);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTimeInMillis();
}
}

3. 编写mapper类和runner类

mapper

package com.kk.etl.util.mr.ald;

import java.io.IOException;
import java.util.Map;
import java.util.zip.CRC32; import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.log4j.Logger; import com.kk.common.EventLogConstants;
import com.kk.common.EventLogConstants.EventEnum;
import com.kk.etl.util.LoggerUtils; /**
* 自定义数据解析map类
*
* @author gerry
*
*/
public class AnalyserLogDataMapper extends Mapper<Object, Text, NullWritable, Put> { private final Logger logger = Logger.getLogger(AnalyserLogDataMapper.class);
private int inputRecords, filterRecords, outputRecords; // 主要用于标志,方便查看过滤数据
private byte[] family = Bytes.toBytes(EventLogConstants.EVENT_LOGS_FAMILY_NAME);
private CRC32 crc32 = new CRC32(); @Override
protected void map(Object key, Text value, Context context) throws IOException, InterruptedException {
this.inputRecords++;
this.logger.debug("Analyse data of :" + value); try {
// 解析日志
Map<String, String> clientInfo = LoggerUtils.handleLog(value.toString()); // 过滤解析失败的数据
if (clientInfo.isEmpty()) {
this.filterRecords++;
return;
} // 获取事件名称
String eventAliasName = clientInfo.get(EventLogConstants.LOG_COLUMN_NAME_EVENT_NAME);
EventEnum event = EventEnum.valueOfAlias(eventAliasName);
switch (event) {
case LAUNCH:
case PAGEVIEW:
case CHARGEREQUEST:
case CHARGEREFUND:
case CHARGESUCCESS:
case EVENT:
// 处理数据
this.handleData(clientInfo, event, context);
break;
default:
this.filterRecords++;
this.logger.warn("该事件没法进行解析,事件名称为:" + eventAliasName);
}
} catch (Exception e) {
this.filterRecords++;
this.logger.error("处理数据发出异常,数据:" + value, e);
}
} @Override
protected void cleanup(Context context) throws IOException, InterruptedException {
super.cleanup(context);
logger.info("输入数据:" + this.inputRecords + ";输出数据:" + this.outputRecords + ";过滤数据:" + this.filterRecords);
} /**
* 具体处理数据的方法
*
* @param clientInfo
* @param context
* @param event
* @throws InterruptedException
* @throws IOException
*/
private void handleData(Map<String, String> clientInfo, EventEnum event, Context context) throws IOException, InterruptedException {
String uuid = clientInfo.get(EventLogConstants.LOG_COLUMN_NAME_UUID);
String memberId = clientInfo.get(EventLogConstants.LOG_COLUMN_NAME_MEMBER_ID);
String serverTime = clientInfo.get(EventLogConstants.LOG_COLUMN_NAME_SERVER_TIME);
if (StringUtils.isNotBlank(serverTime)) {
// 要求服务器时间不为空
clientInfo.remove(EventLogConstants.LOG_COLUMN_NAME_USER_AGENT); // 浏览器信息去掉
String rowkey = this.generateRowKey(uuid, memberId, event.alias, serverTime); // timestamp
// +
// (uuid+memberid+event).crc
Put put = new Put(Bytes.toBytes(rowkey));
for (Map.Entry<String, String> entry : clientInfo.entrySet()) {
if (StringUtils.isNotBlank(entry.getKey()) && StringUtils.isNotBlank(entry.getValue())) {
put.add(family, Bytes.toBytes(entry.getKey()), Bytes.toBytes(entry.getValue()));
}
}
context.write(NullWritable.get(), put);
this.outputRecords++;
} else {
this.filterRecords++;
}
} /**
* 根据uuid memberid servertime创建rowkey
*
* @param uuid
* @param memberId
* @param eventAliasName
* @param serverTime
* @return
*/
private String generateRowKey(String uuid, String memberId, String eventAliasName, String serverTime) {
StringBuilder sb = new StringBuilder();
sb.append(serverTime).append("_");
this.crc32.reset();
if (StringUtils.isNotBlank(uuid)) {
this.crc32.update(uuid.getBytes());
}
if (StringUtils.isNotBlank(memberId)) {
this.crc32.update(memberId.getBytes());
}
this.crc32.update(eventAliasName.getBytes());
sb.append(this.crc32.getValue() % 100000000L);
return sb.toString();
} }

runner

package com.kk.etl.util.mr.ald;
import java.io.File;
import java.io.IOException; import java.io.IOException; import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.mapreduce.TableMapReduceUtil;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.mapred.JobConf;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;
import org.apache.log4j.Logger; import com.kk.common.EventLogConstants;
import com.kk.common.GlobalConstants;
import com.kk.util.EJob;
import com.kk.util.TimeUtil; import cz.mallat.uasparser.*; public class AnalyserLogDataRunner implements Tool{
private static final Logger logger = Logger.getLogger(AnalyserLogDataRunner.class);
private Configuration conf = null; public static void main(String[] args) {
try {
ToolRunner.run(new Configuration(), new AnalyserLogDataRunner(), args);
} catch (Exception e) {
logger.error("执行日志解析job异常", e);
throw new RuntimeException(e);
}
} @Override
public void setConf(Configuration conf) {
this.conf = HBaseConfiguration.create(conf);
} @Override
public Configuration getConf() {
return this.conf;
} @Override
public int run(String[] args) throws Exception {
Configuration conf = this.getConf();
this.processArgs(conf, args);
conf.set("fs.defaultFS","hdfs://hadoop-001:9000");
conf.set("mapreduce.framework.name","yarn");
conf.set("yarn.resourcemanager.hostname","hadoop-002"); Job job = Job.getInstance(conf, "analyser_logdata" ); // 设置本地提交job,集群运行,需要代码
File jarFile = EJob.createTempJar("target/classes");
JobConf jobConf=(JobConf)job.getConfiguration();
jobConf.setJar(jarFile.toString());
// 设置本地提交job,集群运行,需要代码结束 job.setJarByClass(AnalyserLogDataRunner.class);
job.setMapperClass(AnalyserLogDataMapper.class);
job.setMapOutputKeyClass(NullWritable.class);
job.setMapOutputValueClass(Put.class); // 设置reducer配置
// 1. 集群上运行,打成jar运行(要求addDependencyJars参数为true,默认就是true)
conf.set("addDependencyJars", "true");
TableMapReduceUtil.initTableReducerJob(EventLogConstants.HBASE_NAME_EVENT_LOGS, null, job);
// 2. 本地运行,要求参数addDependencyJars为false
//
// conf.set("addDependencyJars", "false");
// TableMapReduceUtil.initTableReducerJob(EventLogConstants.HBASE_NAME_EVENT_LOGS,
// null, job, null, null, null, null, false);
job.setNumReduceTasks(0); // 设置输入路径
this.setJobInputPaths(job);
return job.waitForCompletion(true) ? 0 : -1;
} /**
* 处理参数
*
* @param conf
* @param args
*/
private void processArgs(Configuration conf, String[] args) {
String date = null;
for (int i = 0; i < args.length; i++) {
if ("-d".equals(args[i])) {
if (i + 1 < args.length) {
date = args[++i];
break;
}
}
} // 要求date格式为: yyyy-MM-dd
if (StringUtils.isBlank(date) || !TimeUtil.isValidateRunningDate(date)) {
// date是一个无效时间数据
date = TimeUtil.getYesterday(); // 默认时间是昨天
}
conf.set(GlobalConstants.RUNNING_DATE_PARAMES, date);
} /**
* 设置job的输入路径
*
* @param job
*/
private void setJobInputPaths(Job job) {
Configuration conf = job.getConfiguration();
FileSystem fs = null;
try {
fs = FileSystem.get(conf);
String date = conf.get(GlobalConstants.RUNNING_DATE_PARAMES);
Path inputPath = new Path("/logs/nginx/" + TimeUtil.parseLong2String(TimeUtil.parseString2Long(date), "MM/dd"));//+"/BF-01.1553792641215");
if (fs.exists(inputPath)) {
FileInputFormat.addInputPath(job, inputPath);
} else {
throw new RuntimeException("文件不存在:" + inputPath);
}
} catch (IOException e) {
throw new RuntimeException("设置job的mapreduce输入路径出现异常", e);
} finally {
if (fs != null) {
try {
fs.close();
} catch (IOException e) {
// nothing
}
}
}
} }

4. 添加环境变量文件,core-site.xml hbase-site.xml log4j.properties 根据不同的运行情况,修改源码将修改后的源码放到代码中。

5. 添加pom编译代码,并进行测试
本地运行测试: 需要注意的就是windows环境可能会导致出现access方法调用异常,需要修改nativeio这个java文件。
使用TableMapReduceUtil的时候如果出现异常:


*****/htrace-core-2.04.jar from hdfs://***/htrace-core-2.04.jar is not a valid DFS filename.


就需要将addDependencyJars参数设置为false。
本地提交job,集群运行测试:
本地需要知道提交的job是需要提交到集群上的,所以需要指定两个参数mapreduce.framework.name和yarn.resourcemanager.address,value分别为yarn和hh:8032即可,但是可能会出现异常信息,此时需要将参数mapreduce.app-submission.cross-platform设置为true。

参数设置:


mapreduce.framework.name=yarn
yarn.resourcemanager.address=hadoop-001:8032
mapreduce.app-submission.cross-platform=true


目录结构如下

异常:
1. Permission denied: user=gerry, access=EXECUTE, inode="/tmp":hadoop:supergroup:drwx------
解决方案:执行


hdfs dfs -chmod -R 777 /tmp


2. Stack trace: ExitCodeException exitCode=1: /bin/bash: line 0: fg: no job control
解决方案:


添加mapreduce.app-submission.cross-platform=true


3. ExitCodeException exitCode=1:
解决方案:


habse指定输出reducer的时候必须给定addDependencyJars参数为true。


4. Class com.beifeng.etl.mr.ald.AnalyserLogDataMapper not found
解决方案:


引入EJob.java文件,然后再runner类中添加代码
File jarFile = EJob.createTempJar("target/classes");
((JobConf) job.getConfiguration()).setJar(jarFile.toString());


集群提交&运行job测试:

大数据离线分析平台 用户数据Etl的更多相关文章

  1. 大数据离线分析平台 JavaSDK数据收集引擎编写

    JavaSDK设计规则 JavaSDK提供两个事件触发方法,分别为onChargeSuccess和onChargeRefund.我们在java sdk中通过一个单独的线程来发送线程数据,这样可以减少对 ...

  2. 大数据离线分析平台 JSSDK数据收集引擎编写

    JsSDK设计规则在js sdk中我们需要收集launch.pageview.chargeRequest和eventDuration四种数据,所以我们需要在js中写入四个方法来分别收集这些数据,另外我 ...

  3. mapReduce 大数据离线分析

    数据分析一般分为两种,一种是在线一种是离线 流程: 一般都是对于日志文件的采集和分析 场景实例(某个电商网站产生的用户访问日志(access.log)进行离线处理与分析的过程) 1.需求: 基于Map ...

  4. 大快DKH大数据智能分析平台监控参数说明

    2018年国内大数据公司50强榜单排名已经公布了出来,大快以黑马之姿闯入50强,并摘得多项桂冠.Hanlp自然语言处理技术也荣膺了“2018中国数据星技术”奖.对这份榜单感兴趣的可以找一下看看.本篇承 ...

  5. 快速构建大数据存储分析平台-ELK平台安装

    一.概述 ELK是由Elastic公司开发的Elasticsearch.Logstash.Kibana三款开源软件的缩写(但不限于这三款软件). 为什么使用ELK? 在目前流行的微服务架构中,一个大型 ...

  6. 从Excel读取数据,然后分析相似的数据,多线程处理(多线程比较相似的字符串,统计出相似的数量及字符串)

    之前的jar包有问题,现已修改. 需要的jar包,已修改 自己去Maven中央仓库下载jar包. excel数据: 直接上代码. 程序再度优化了一遍.之后如果想再度精准,可能需要建模,最近没空继续做了 ...

  7. 分布式大数据系统离线分析技术解决方案(spark2.x)

    一.sark2.x新型的架构系统

  8. python实现人人网用户数据爬取及简单分析

    这是之前做的一个小项目.这几天刚好整理了一些相关资料,顺便就在这里做一个梳理啦~ 简单来说这个项目实现了,登录人人网并爬取用户数据.并对用户数据进行分析挖掘,终于效果例如以下:1.存储人人网用户数据( ...

  9. 基于Hadoop技术实现的离线电商分析平台(Flume、Hadoop、Hbase、SpringMVC、highcharts)

    离线数据分析平台是一种利用hadoop集群开发工具的一种方式,主要作用是帮助公司对网站的应用有一个比较好的了解.尤其是在电商.旅游.银行.证券.游戏等领域有非常广泛,因为这些领域对数据和用户的特性把握 ...

随机推荐

  1. linux的python版本升级

    可利用Linux自带下载工具wget下载,如下所示:     # wget http://www.python.org/ftp/python/2.7.3/Python-2.7.13.tgz 下载完成后 ...

  2. springmvc跳转到自定义404页面的三种方法

    有时候我们并不想跳转到系统自定义的错误页面中,那么我们需要自定义页面并且实现它的跳转 有三种方法可以实现 方法一:最简单的实现,也是最快的 在<web-app>节点下配置 代码如下: &l ...

  3. 解决The resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers.

    SpringMVC中当在浏览器中输入对应的MappingUrl时,报The resource identified by this request is only capable of generat ...

  4. django面试四

    Django的优点 功能完善.要素齐全:自带大量常用工具和框架(比如分页,auth,权限管理), 适合快速开发企业级网站. 完善的文档:经过十多年的发展和完善,Django有广泛的实践案例和完善的在线 ...

  5. springboot (spring mvc)集成swagger

    最近用springboot构建rest接口,考虑到最方便的验证接口,想到了引入swagger. 基本的步骤大致如下: 1.pom中引入swagger依赖: <dependency> < ...

  6. Linux fdisk命令操作磁盘(添加、删除、转换分区等)

    创建分区1->查看原始分区sudo fdisk -l Disk /dev/sda: 21.5 GB, 21474836480 bytes255 heads, 63 sectors/track, ...

  7. 【leetcode】7-ReverseInteger

    problem: Reverse Integer 注意考虑是否越界: INT_MAX INT_MIN 32bits or 64bits 调整策略,先从简单的问题开始:

  8. 在 Mac 安装Docker

    https://blog.csdn.net/jpiverson/article/details/50685817 https://legacy.gitbook.com/book/yeasy/docke ...

  9. 句法分析工具 LTP HanLP

    参考:http://cslt.riit.tsinghua.edu.cn/mediawiki/images/e/e5/%E5%8F%A5%E6%B3%95%E5%B7%A5%E5%85%B7%E5%88 ...

  10. css有缝隙