毕业设计项目:基于Flume+Spark+Flask的分布式实时日志分析与入侵检测系统的设计与实现

项目源码:https://download.csdn.net/download/zy_dreamer/88038658

项目内容截图:

项目效果:

 

 架构图:

 

 

 

简介

本项目是一个整合了web日志聚合、分发、实时分析、入侵检测、数据存储与可视化的日志分析解决方案。聚合采用Apache Flume,分发采用Apache Kafka,实时处理采用Spark Streaming,入侵检测采用Spark MLlib,数据存储使用HDFS与Redis,可视化采用Flask、SocketIO、Echarts、Bootstrap。

本文下述的使用方法均面向单机伪分布式环境,你可以根据需求进行配置上的调整以适应分布式部署。

本系统各模块由个人独立开发,期间参考了一些有价值的文献与资料。本系统还是个人的本科毕业设计。

获得的奖项:2019年全国大学生计算机设计大赛安徽省二等奖、2019年安徽省信息安全作品赛二等奖。

项目结构

  • flask:Flask Web后端

  • spark:日志分析与入侵检测的实现

  • flume:Flume配置文件

  • log_gen:模拟日志生成器

  • datasets:测试日志数据集

  • images:README的图片

依赖与版本

  • 编译与Web端需要用到的:

    • Java 8, Scala 2.11.12, Python 3.8 (包依赖见requirements), sbt 1.3.8

  • 计算环境中需要用到的:

    • Java 8, Apache Flume 1.9.0, Kafka 2.4, Spark 2.4.5, ZooKeeper 3.5.7, Hadoop 2.9.2, Redis 5.0.8

使用说明

在开始之前,你需要修改源码或配置文件中的IP为你自己的地址。具体涉及到flume配置文件、Spark主程序、Flask Web后端。

编译Spark应用

在安装好Java8与Scala11的前提下,在spark目录下,初始化sbt

sbt

退出sbt shell并使用sbt-assembly对Spark项目进行编译打包:

sbt assembly

然后将生成的jar包重命名为logvision.jar

环境准备

你需要一个伪分布式环境(测试环境为CentOS 7),并完成了所有对应版本组件依赖的配置与运行。 使用flume目录下的standalone.conf启动一个Flume Agent。 将datasets文件夹中的learning-datasets提交如下路径:

/home/logv/learning-datasets

datasets文件夹中的access_log提交如下路径:

/home/logv/access_log

入侵检测模型训练与测试

提交jar包至Spark集群并执行入侵检测模型的生成与测试:

spark-submit --class learning logvision.jar

你将可以看到如下结果: 两个表格分别代表正常与异常数据集的入侵检测结果,下面四个表格可用于判断识别准确率。如图中所示250条正常测试数据被检测为250条正常,识别率100%;250条异常测试数据被检测为240条异常,10条正常,准确率96%。

启动可视化后端

flask目录下执行如下命令,下载依赖包:

pip3 install -r requirements.txt

启动Flask Web:

python3 app.py

启动实时日志生成器

log_gen中的实时日志生成器可根据传入参数(每次写入行数、写入间隔时间)将样本日志中的特定行块追加至目标日志中,以模拟实时日志的生成过程,供后续实时处理。

java log_gen [日志源] [目标文件] [每次追加的行数] [时间间隔(秒)]

提交至环境,编译并运行,每2秒将/home/logv/access_log文件中的5行追加至/home/logSrc中:

javac log_gen.java
java log_gen /home/logv/access_log /home/logSrc 5 2

启动分析任务

提交jar包至Spark集群并执行实时分析任务:

spark-submit --class streaming logvision.jar

查看可视化结果

至此你已经完成了后端组件的配置,通过浏览器访问Web端主机的5000端口可以查看到实时日志分析的可视化结果

Flask-python依赖:

Flask_SocketIO==4.3.0
requests==2.23.0
kafka_python==2.0.1
redis==3.5.0
flask==1.1.2

部分代码演示:

import ast
import time
from kafka import KafkaConsumer
import redis
import requests

from threading import Lock, Thread
from flask import Flask, render_template, session, request
from flask_socketio import SocketIO, emit

async_mode = None
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app, async_mode=async_mode)

thread = None
thread_lock = Lock()

# 配置项目
time_interval = 1
kafka_bootstrap_servers = "10.0.0.222:9092"
redis_con_pool = redis.ConnectionPool(host='10.0.0.222', port=6379, decode_responses=True)


# 页面路由与对应页面的ws接口
# 系统时间
@socketio.on('connect', namespace='/sys_time')
def sys_time():
    def loop():
        while True:
            socketio.sleep(time_interval)
            current_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
            socketio.emit('sys_time',
                          {'data': current_time},
                          namespace='/sys_time')

    socketio.start_background_task(target=loop)


# 欢迎页面
@app.route('/')
@app.route('/welcome')
def welcome():
    return render_template('index.html', async_mode=socketio.async_mode)


# 实时日志流
@socketio.on('connect', namespace='/log_stream')
def log_stream():
    def loop():
        socketio.sleep(time_interval)
        consumer = KafkaConsumer("raw_log", bootstrap_servers=kafka_bootstrap_servers)
        cache = ""
        for msg in consumer:
            cache += bytes.decode(msg.value) + "\n"
            if len(cache.split("\n")) == 25:
                socketio.emit('log_stream',
                              {'data': cache},
                              namespace='/log_stream')
                cache = ""

    socketio.start_background_task(target=loop)


# 实时日志分析页面
@app.route('/analysis')
def analysis():
    return render_template('analysis.html', async_mode=socketio.async_mode)


# 实时计数器
@socketio.on('connect', namespace='/count_board')
def count_board():
    def loop():
        while True:
            socketio.sleep(time_interval)
            redis_con = redis.Redis(connection_pool=redis_con_pool)
            res = redis_con.zrange("statcode", 0, 40, withscores=True)

            # 总请求数(日志行数)
            host_count = redis_con.zscore("line", "count")

            # 成功请求数(状态码属于normal的个数)
            normal = ["200", "201", "202", "203", "204", "205", "206", "207"]
            success_count = 0
            for i in res:
                if i[0] in normal:
                    success_count += int(i[1])

            # 其他请求数(其他状态码个数)
            other_count = 0
            for i in res:
                other_count += int(i[1])
            other_count -= success_count

            # 访客数(不同的IP个数)
            visitor_count = redis_con.zcard("host")

            # 资源数(不同的url个数)
            url_count = redis_con.zcard("url")

            # 流量大小(bytes的和,MB)
            traffic_sum = int(redis_con.zscore("traffic", "sum"))

            # 日志大小(MB)
            log_size = int(redis_con.zscore("size", "sum"))

            socketio.emit('count_board',
                          {'host_count': host_count,
                           'success_count': success_count,
                           'other_count': other_count,
                           'visitor_count': visitor_count,
                           'url_count': url_count,
                           'traffic_sum': traffic_sum,
                           'log_size': log_size},
                          namespace='/count_board')

    socketio.start_background_task(target=loop)


# 实时热门位置
@socketio.on('connect', namespace='/hot_geo')
def hot_geo():
    def loop():
        while True:
            socketio.sleep(2)
            redis_con = redis.Redis(connection_pool=redis_con_pool)
            res = redis_con.zrevrange("host", 0, 50, withscores=True)
            data = []

            for i in res:
                # 调用接口获取地理坐标
                req = requests.get("http://api.map.baidu.com/location/ip",
                                   {'ak': '0jKbOcwqK7dGZiYIhSai5rsxTnQZ4UQt',
                                    'ip': i[0],
                                    'coor': 'bd09ll'})
                body = eval(req.text)

                # 仅显示境内定位
                if body['status'] == 0:
                    coor_x = body['content']['point']['x']
                    coor_y = body['content']['point']['y']

                    data.append({"name": i[0], "value": [coor_x, coor_y, i[1]]})

            socketio.emit('hot_geo',
                          {'data': data},
                          namespace='/hot_geo')

    socketio.start_background_task(target=loop)


# 实时热门资源排名
@socketio.on('connect', namespace='/hot_url')
def hot_url():
    def loop():
        while True:
            socketio.sleep(time_interval)
            redis_con = redis.Redis(connection_pool=redis_con_pool)
            res = redis_con.zrevrange("url", 0, 9, withscores=True)
            data = []
            no = 1

            for i in res:
                data.append({"no": no, "url": i[0], "count": i[1]})
                no += 1

            socketio.emit('hot_url',
                          {'data': data},
                          namespace='/hot_url')

    socketio.start_background_task(target=loop)


# 实时热门IP排名
@socketio.on('connect', namespace='/hot_ip')
def hot_ip():
    def loop():
        while True:
            socketio.sleep(time_interval)
            redis_con = redis.Redis(connection_pool=redis_con_pool)
            res = redis_con.zrevrange("host", 0, 13, withscores=True)
            data = []
            no = 1

            for i in res:
                # 调用接口获取地理坐标
                req = requests.get("http://api.map.baidu.com/location/ip",
                                   {'ak': '0jKbOcwqK7dGZiYIhSai5rsxTnQZ4UQt',
                                    'ip': i[0],
                                    'coor': 'bd09ll'})
                body = eval(req.text)

                # 仅显示境内定位
                if body['status'] == 0:
                    address = body['content']['address']

                    data.append({"no": no, "ip": i[0], "address": address, "count": i[1]})
                    no += 1

            socketio.emit('hot_ip',
                          {'data': data},
                          namespace='/hot_ip')

    socketio.start_background_task(target=loop)


# 实时状态码比例
@socketio.on('connect', namespace='/status_code_pie')
def status_code_pie():
    def loop():
        while True:
            socketio.sleep(time_interval)
            redis_con = redis.Redis(connection_pool=redis_con_pool)
            res = redis_con.zrevrange("statcode", 0, 100, withscores=True)
            data = []
            legend = []

            for i in res:
                if i[0] != 'foo':
                    data.append({"value": i[1], "name": i[0]})
                    legend.append(i[0])

            socketio.emit('status_code_pie',
                          {'legend': legend, 'data': data},
                          namespace='/status_code_pie')

    socketio.start_background_task(target=loop)


# 实时请求方式比例
@socketio.on('connect', namespace='/req_method_pie')
def req_method_pie():
    def loop():
        while True:
            socketio.sleep(time_interval)
            redis_con = redis.Redis(connection_pool=redis_con_pool)
            res = redis_con.zrevrange("reqmt", 0, 100, withscores=True)
            data = []
            legend = []

            for i in res:
                if i[0] != 'foo':
                    data.append({"value": i[1], "name": i[0]})
                    legend.append(i[0])

            socketio.emit('req_method_pie',
                          {'legend': legend, 'data': data},
                          namespace='/req_method_pie')

    socketio.start_background_task(target=loop)


# 实时请求计数(按时间顺序)
@socketio.on('connect', namespace='/req_count_timeline')
def req_count_timeline():
    def loop():
        while True:
            socketio.sleep(time_interval)
            redis_con = redis.Redis(connection_pool=redis_con_pool)
            res = dict(redis_con.zrange("datetime", 0, 10000000, withscores=True))
            data = []
            date = []

            # 按时间排序
            for i in sorted(res):
                datetime = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(i) / 1000))
                data.append(res[i])
                date.append(datetime)

            socketio.emit('req_count_timeline',
                          {"data": data, "date": date},
                          namespace='/req_count_timeline')

    socketio.start_background_task(target=loop)


# IP请求数排序
@socketio.on('connect', namespace='/ip_ranking')
def timestamp_count_timeline():
    def loop():
        while True:
            socketio.sleep(time_interval)
            redis_con = redis.Redis(connection_pool=redis_con_pool)
            res = redis_con.zrevrange("host", 0, 50, withscores=True)
            ip = []
            count = []

            for i in res:
                ip.append(i[0])
                count.append(i[1])

            socketio.emit('ip_ranking',
                          {"ip": ip, "count": count},
                          namespace='/ip_ranking')

    socketio.start_background_task(target=loop)


@app.route('/id')
def id():
    return render_template("id.html", async_mode=socketio.async_mode)


# 异常请求计数
@socketio.on('connect', namespace='/bad_count')
def bad_count():
    def loop():
        while True:
            socketio.sleep(time_interval)
            redis_con = redis.Redis(connection_pool=redis_con_pool)
            res = int(redis_con.zscore("bad", "bad"))

            socketio.emit('bad_count',
                          {"data": res},
                          namespace='/bad_count')

    socketio.start_background_task(target=loop)


# 正常请求计数
@socketio.on('connect', namespace='/good_count')
def bad_count():
    def loop():
        while True:
            socketio.sleep(time_interval)
            redis_con = redis.Redis(connection_pool=redis_con_pool)
            res = int(redis_con.zscore("good", "good"))

            socketio.emit('good_count',
                          {"data": res},
                          namespace='/good_count')

    socketio.start_background_task(target=loop)


# 正常请求地理标记
@socketio.on('connect', namespace='/good_geo')
def good_geo():
    def loop():
        while True:
            socketio.sleep(time_interval)
            consumer = KafkaConsumer("good_result", bootstrap_servers=kafka_bootstrap_servers)
            data = []

            for msg in consumer:
                result = ast.literal_eval(bytes.decode(msg.value))
                for record in result:
                    if record['host'] != "foo":
                        # 调用接口获取地理坐标
                        req = requests.get("http://api.map.baidu.com/location/ip",
                                           {'ak': '0jKbOcwqK7dGZiYIhSai5rsxTnQZ4UQt',
                                            'ip': record['host'],
                                            'coor': 'bd09ll'})
                        body = eval(req.text)
                        # 仅显示境内定位
                        if body['status'] == 0:
                            coor_x = body['content']['point']['x']
                            coor_y = body['content']['point']['y']
                            datetime = time.strftime("%Y-%m-%d %H:%M:%S",
                                                     time.localtime(int(record['timestamp']) / 1000))

                            data.append({"name": record['host'], "value": [coor_x, coor_y,
                                                                           record['url'],
                                                                           datetime,
                                                                           record['req_method'],
                                                                           record['protocol'],
                                                                           record['status_code']]})
                            socketio.emit('good_geo',
                                          {"data": data},
                                          namespace='/good_geo')

    socketio.start_background_task(target=loop)

说明:当前文章或代码如侵犯了您的权益,请私信作者删除!

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: flume+spark+hive+spark sql离线分析系统是一种基于大数据技术的离线数据分析系统。其中,flume用于数据采集和传输,spark用于数据处理和计算,hive用于数据存储和管理,spark sql用于数据查询和分析。通过这个系统,可以实现对大量数据的高效处理和分析,为企业决策提供有力的支持。 ### 回答2: flume spark hive spark sql离线分析系统是一种数据处理系统。该系统可以用于处理大量的数据,生成相应的报告和分析。博客文章有详细讨论。 flume是一个分布式日志采集系统,它可以将数据从不同的地方采集并传输到所需的位置。它可以采集不同的日志数据,包括web日志、服务器日志、应用程序日志等。flume是一个可扩展的系统,可以用于处理大量的数据。 spark是一个强大的分布式计算引擎,它允许用户在大规模的数据集上进行高性能计算。spark可以快速地处理大量的数据,并支持多种编程语言,例如Java、Python和Scala等。spark还提供了可视化编程工具,例如RDD(弹性分布式数据集)来支持数据处理和分析等任务。 hive是一个基于Hadoop的数据仓库系统,它可以将结构化的数据存储在Hadoop的HDFS文件系统中。hive提供了类SQL的查询语言,例如HQL,并支持复杂查询和数据分析任务。hive还提供了很多插件,使用户可以轻松地将数据导入和导出到不同的数据源中。 spark sql是spark的一部分,它提供了SQL查询和数据分析功能。spark sql的灵活性和可扩展性使其非常适合处理大数据量的数据,包括结构化数据和半结构化数据。 综上所述,flume spark hive spark sql离线分析系统是一个可以用于处理大量的数据的系统,它由flumespark、hive以及spark sql等组成部分。该系统可以帮助用户轻松地采集、存储、分析和报告大量的数据,有着非常广泛的应用。 ### 回答3: FlumeSpark、Hive、Spark SQL四个工具都是用于离线分析系统的。 Flume是由Apache基金会开发的开源数据采集系统,用于收集、聚合和移动大量数据。Flume可以实现数据的采集、压缩、持久化和转发,从而实现数据流水线。Flume可以将数据从不同来源收集到不同的目标,支持多种数据源,包括文件、HTTP、数据库等。Flume可以使数据收集更加高效和可靠。 Spark是一种快速、通用的计算引擎,用于大规模数据处理。Spark支持分布式计算,可以在数百台计算机上并行运行。Spark是用Java、Scala或Python编写的,可以处理数据,并提供先进的机器学习和图形处理功能。Spark具有内存计算和多种处理任务的灵活性,可以用于各种大规模数据处理的场景中。 Hive是面向Hadoop的数据仓库软件,提供了一个类似SQL的查询语言,用于查询和分析大规模数据。Hive将数据以表格的形式组织和存储,并通过SQL语言进行查询和分析。Hive可以用于各种数据仓库的管理,包括文件、HDFS、HBase等。 Spark SQL是在Spark引擎之上构建的结构化数据处理系统,提供了一种基于SQL的编程接口。Spark SQL可以将结构化数据与RDD集成在一起,可以使用Spark的内存计算引擎和流式处理引擎进行大规模的数据分析。Spark SQL可以在SQL查询中使用自己的数据格式,从而实现高效的数据处理和分析。 综上所述,FlumeSpark、Hive、Spark SQL这四个工具是离线分析系统中的重要组成部分,可以实现数据采集、数据处理和数据分析。在大数据分析的过程中,这些工具为数据科学家提供了丰富的选项,从而可以更好地处理数据,加快分析速度并获得更深入的见解。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

白话机器学习

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值