实时日志监控系统-全览

大数据处理,大致可以分为两大模块:

  1. 离线数据处理:比如说电商、运营商出现的大批量的日志,可以由flume、sqoop或者其他路径,导入到HDFS中,然后经过数据清洗,使用Hive进行分析和处理,对于优化服务器资源等有很好的作用;个人觉得,支付宝的年账单就是离线数据处理的应用之处了。
  2. 实时数据处理:对于有些业务需要,可能第二天或者更晚的时候进行分析无关紧要,但对于一些高频的金融交易来说,实时性就太重要了,还有一些如百度搜索的top10,新浪微博的微博热点等等,如果等到第二天处理,那这些新闻也没什么吸引的价值了。

所以,纵观来说,离线数据处理和实时数据处理撑起了大数据处理的一片天,本文将介绍本人亲自负责并予以实施的日志监控项目,麻雀虽小,五脏俱全。

主要模块

  1. 日志收集模块
  2. 日志处理模块

主要工具

  1. flume:用于日志的收集,堪称是业内最好的日志收集工具,支持多种日志收集的渠道,同时支持诸多的日志收集存放地,功能强大;官方链接:flume官网
  2. kafka:消息缓冲队列,大数据处理中常用的缓冲队列,用于数据爆炸的时候,避免拖垮后续的处理逻辑,将消息先存放到队列中,延迟一定的时间进行处理。
  3. log4j:我们在Tomcat服务器上部署的业务系统,需要指定flume-appender,因此需要使用到log4j。
  4. SparkStreaming:在第一版本中,由于实时性不是很强,因此使用该工具予以处理,其处理日志会有一定的延迟,但吞吐量较大。
  5. MySql:用于读取配置数据,已经将配置数据全部迁移到zookeeper上。
  6. Spring boot:构建数据配置服务,方便用户配置自己的日志数据,比如邮件发给何人,短信发给何人,都可以自由指定。
  7. zookeeper:数据配置中心,在本项目用途中,主要是用于配置数据的管理,官方链接:zookeeper官网

1:日志收集模块

在日志收集模块中,针对我们自身的业务,可以分为两大部分:

  1. Nginx日志和数据库运行日志:首先是Nginx,作为业内比较强大的负责均衡工具,其性能比较优良,我们在日常的服务中,也是使用该工具来进行负载均衡的功能实现;插播一句,业内另一比较强大的负载均衡工具是淘宝的章文嵩博士开发的LVS,对于访问量不是很大的网站,使用Nginx完全可以实现功能;为了能够准确处理出错的日志,我们对日志格式进行了一定的定义,类似下图:实时日志监控系统-全览_第1张图片实时日志监控系统-全览_第2张图片
  2. 对于Tomcat类型的服务,选择使用log4j内置的flume-appender方式来实现,具体配置可以参考官网:https://logging.apache.org/log4j/2.x/manual/appenders.html#FlumeAppender;其中有很详细的flume-appender配置,在日志中配置合理,每一条日志都会按照相应的格式,作为flume收集日志的来源。

对于收集到的日志,统一采用kafkaSink的方式,输送到后续的kafka中,以备后续的处理。

关于日志的收集,在处理过程中有几点收获:

  1. 对于flume的收集渠道有了更加深入的理解,flume不愧是强大的工具,支持的收集渠道非常多,而且支持的类型也很多,我们在收集nginx日志的时候,配置的type为exec,即命令执行方式,其会执行该命令,把需要监控的日志实时进行读取,配置如下:
    a1.sources = r1
    a1.channels = c1
    a1.sources.r1.type = exec
    a1.sources.r1.command = tail -F /var/log/secure
    a1.sources.r1.channels = c1
  2. 对于tail命令,支持同时读取多个日志文件,会统一把这些日志输送到同一个源,输送到目的地。
  3. 拦截器的使用:有时候,收集到的日志并不是完全如我们的意愿,这时候,拦截器就派上了用场,我们在plugins.d目录下,部署了自己的jar包,用于拦截读取到的日志,进行第二步骤的处理;而且拦截器支持链式,即多个拦截器会依次处理收集到的日志。

2:日志处理模块

对于收集到的日志的处理,我们采用的是Spark-Streaming工具,将其与kafka对接,对于收集到的每一条数据进行处理:

public void startTask() {		
		//新建sparkConf
		SparkConf conf = new SparkConf().setAppName(ConfigUtils.SPARK_APPNAME);
		conf.setMaster("local[4]");// 本地多线程调用
		// conf.setMaster(ConfigUtils.SPARK_MASTER);//集群调用
		//制作StreamingContext
		JavaStreamingContext jsc = new JavaStreamingContext(conf,
				Durations.seconds(Long.valueOf(ConfigUtils.SPARK_DURATIONS)));
		Map kafaParameters = new HashMap();
		//部署kafka机器的ip及端口号
		kafaParameters.put("metadata.broker.list", ConfigUtils.KAFKA_BROKER);
		//消费组的groupId
		kafaParameters.put("group.id", ConfigUtils.KAFKA_GROUPID);
		kafaParameters.put("fetch.message.max.bytes", ConfigUtils.KAFKA_FETCH_MAX);
		kafaParameters.put("num.consumer.fetchers", ConfigUtils.KAFKA_FETCH_NUM);
		Set topics = new HashSet();
		topics.add(ConfigUtils.KAFKA_TOPIC);
		try {
			//指定直连,消费kafka某个topic内的数据
			JavaPairInputDStream lines = KafkaUtils.createDirectStream(jsc, String.class, String.class,
					StringDecoder.class, StringDecoder.class, kafaParameters, topics);
			JavaDStream words = lines.flatMap(new FlatMapFunction, String>() {
				public Iterator call(Tuple2 tuple) throws Exception {
					// log.info("接收kafka数据:" + tuple._2);
					return Arrays.asList(tuple._2.split(SPACE.pattern())).iterator();
				}
			});
			words.foreachRDD(new VoidFunction2, Time>() {
				public void call(JavaRDD word, Time arg1) throws Exception {
					// TODO Auto-generated method stub
					process(word);
				}
			});
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

这里,主要是将SparkStreaming与kafka对接起来的实现,需要指定消费组的group id,需要指定消费的topic,指定消费的机器,最重要的一步就是创建接下来需要进行处理的JavaRDD,其实,spark最核心的概念就是rdd的处理,其SparkStreaming,实际上处理的也就是一段时间内产生的RDD而已。

对于上述的代码中一些问题予以优化下:

try {
			JavaPairInputDStream lines = KafkaUtils
					.createDirectStream(jsc, String.class, String.class,
							StringDecoder.class, StringDecoder.class,
							kafaParameters, topics);
			lines.foreachRDD(new VoidFunction>() {
				@Override
				public void call(JavaPairRDD t)
						throws Exception {
					t.foreachPartition(new VoidFunction>>() {
						@Override
						public void call(Iterator> t)
								throws Exception {
							while (t.hasNext()) {
								String res = t.next()._2;
								try {
									// 这里,很重要的一点是,到底要不要输出日志
									if (flag) {
										log.info("read kafka message:" + res);
									}
									process(res);
								} catch (Exception e) {
									log.info(res + "------处理异常------"
											+ getExeptionMessage(e));
								}
							}
						}
					});
				}
			});
		} catch (Exception e) {
			e.printStackTrace();
		}
更新了其中的算子,争取能够提高效率:

接下来的处理,则是对收集到的日志,进行自己的处理,在此处不予赘述。

项目总结:本项目其实难度并不大,重点在于拦截器的设置,kafka集群的搭建,后续处理的完善,以及如何形成spark与kafka数据的对接等方面。

你可能感兴趣的:(大数据,Spark)