ELK服务应用日志分析&Mysql大数据

前言

ELK 协议栈介绍及体系结构
ELK 其实并不是一款软件,而是一整套解决方案,是三个软件产品的首字母缩写,Elasticsearch,Logstash 和 Kibana。这三款软件都是开源软件,通常是配合使用,而且又先后归于 Elastic.co 公司名下,故被简称为 ELK 协议栈,见图 1。

在这里插入图片描述图 1.ELK 协议栈

Elasticsearch

Elasticsearch 是一个实时的分布式搜索和分析引擎,它可以用于全文搜索,结构化搜索以及分析。它是一个建立在全文搜索引擎 Apache Lucene 基础上的搜索引擎,使用 Java 语言编写。目前,最新的版本是 2.1.0。

主要特点

  • 实时分析
  • 分布式实时文件存储,并将每一个字段都编入索引
  • 文档导向,所有的对象全部是文档
  • 高可用性,易扩展,支持集群(Cluster)、分片和复制(Shards 和 Replicas)。见图 2 和图 3
  • 接口友好,支持 JSON
    在这里插入图片描述
    图 2. 集群
    图 3. 分片和复制
    图 3. 分片和复制

Logstash

Logstash 是一个具有实时渠道能力的数据收集引擎。使用 JRuby 语言编写。其作者是世界著名的运维工程师乔丹西塞 (JordanSissel)。目前最新的版本是 2.1.1。

主要特点

  • 几乎可以访问任何数据

  • 可以和多种外部应用结合

  • 支持弹性扩展
    它由三个主要部分组成,见图 4:

  • Shipper-发送日志数据

  • Broker-收集数据,缺省内置 Redis

  • Indexer-数据写入
    在这里插入图片描述
    图 4.Logstash 基本组成

Kibana

Kibana 是一款基于 Apache 开源协议,使用 JavaScript 语言编写,为 Elasticsearch 提供分析和可视化的 Web 平台。它可以在 Elasticsearch 的索引中查找,交互数据,并生成各种维度的表图。

ELK 协议栈体系结构

完整的 ELK 协议栈体系结构见图 5。基本流程是 Shipper 负责从各种数据源里采集数据,然后发送到 Broker,Indexer 将存放在 Broker 中的数据再写入 Elasticsearch,Elasticsearch 对这些数据创建索引,然后由 Kibana 对其进行各种分析并以图表的形式展示。

在这里插入图片描述
图 5.ELK 协议栈体系结构
ELK 三款软件之间互相配合使用,完美衔接,高效的满足了很多场合的应用,并且被很多用户所采纳,诸如路透社,脸书(Facebook),StackOverFlow 等等

来源 https://www.ibm.com/developerworks/cn/opensource/os-cn-elk/index.html

服务搭建

参考 https://blog.csdn.net/cacacai/article/details/104717483

ELK服务应用

和spring boot结合搭建日志分析系统

一定定定要
需要提前spring-boot中开启通过logback来配置日志输出
https://blog.csdn.net/cacacai/article/details/104722041

配置好logback输出日志模板后开始下面的操作
在spring-boot项目中添加依赖Logstash

<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>6.1</version>
</dependency>

前文中配置好日志输出模板 resource/logback-spring.xml中添加
需要把其中的ip修改为搭建了ELK服务中的kibana的 可访问IP和端口。

<!--输出到 logstash的 appender-->
<appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
    <destination>192.168.33.10:4560</destination>
    <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
......
<root level="info">
    ......
    <appender-ref ref="logstash" />
</root>

回到Kibana管理界面创建Kinaba Index Patterns:
在这里插入图片描述
在Index pattern里输入我们在logstash配置文件logstash.conf里output.index指定的值cai-logstash-*
在这里插入图片描述
点击Next Step,在下拉框里选择@timestamp
在这里插入图片描述
最后点击Create Index Pattern按钮完成创建。
在这里插入图片描述
日志输出分析搭建完成,kibana更多的玩法可以自己主动去探索。

重点:多看英文,多思考。

转储Mysql数据到elasticsearch中

在mysql等数据库中在大数据量30w+下,通过like去匹配多个字段检索是非常耗时的。通过logstash把数据导向elasticsearch中,就可以快速海量的数据检索了。

和搭建ELK服务不一样的是,需要修改logstash的配置文件来定义数据库连接和索引建立,所以一般是不需要kabbi服务的。

步骤

  1. 在搭建ELK服务过程中https://blog.csdn.net/cacacai/article/details/104717483,需要修改logstash.yml文件来添加MySQL数据导管jdbc。
input{
     jdbc {
         # 驱动类名
         jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
         # mysql 数据库链接,blog为数据库名,根据自己实际情况修改
         jdbc_connection_string => "jdbc:mysql://192.168.33.16:3306/cai_cloud_base?useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=CST"
         # 连接数据库用户名
         jdbc_user => "root"
         # 连接数据库密码
         jdbc_password => "123456"
         # 是否启用分页
         jdbc_paging_enabled => "true"
         jdbc_page_size => "50000" 
         # 设置监听间隔  各字段含义(由左至右) 分、时、天、月、年,全部为*默认含义为每分钟都更新
         # 每天凌晨1:00自动执行   "0 1 * * *"
         schedule => "* * * * *"
         # 定义执行sql文路径及名称
         # statement_filepath => "/home/logstash/blog.sql"
         # 直接写sql语句用这个,t_user为需要把数据导向elasticsearch
         statement => "select * from t_user"
         type => "jdbc"
       }
	tcp {
	    mode => "server"
	    host => "0.0.0.0"
	    port => 4560
	    codec => json_lines
	    type => "tcp"
	  }
       
}

filter{
    json{
        source => "message"
        remove_field => ["message"]
    }
}
#output插件配置
output{
    if [type]=="jdbc" {
      elasticsearch {
        #ES索引名称(自己定义的)
        index => "faq_index"
        #自增ID编号
        document_id => "%{USERNAME}"
		hosts => ["es:9200"]
      }
   }
   if[type]=="tcp"{
	    elasticsearch {
	    hosts => "es:9200"
	    index => "febs-logstash-%{+YYYY.MM.dd}"
	      }
     }
 }

  1. 然后连接容器安装插件
docker exec -it logstash bash
#安装插件
bin/logstash-plugin install logstash-input-jdbc
# 下载jdbc驱动 
curl -L https://maven.aliyun.com/repository/public/mysql/mysql-connector-java/8.0.11/mysql-connector-java-8.0.11.jar -o /usr/share/logstash/logstash-core/lib/jars/mysql-connector-java-8.0.11.jar
  1. 单独重启logstash服务
docker stop logstash
docker start logstash

在浏览器中安装elasticsearch-head插件,连接elasticsearch,就可以看到数据情况。
在这里插入图片描述

和spring boot结合进行数据查询

参考例子

  1. 添加依赖
<!--elasticsearch 集成-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
  1. 配置文件application.yml中添加连接服务参数
spring:
	data:
	    elasticsearch:
	      cluster-nodes: 192.168.22.11:9300
	      cluster-name: elasticsearch
  1. 根据elasticsearch中的索引建立实体类

Spring Data通过注解来声明字段的映射属性,有下面的三个注解:

  • @Document 作用在类,标记实体类为文档对象,一般有两个属性
    • indexName:对应索引库名称
    • type:对应在索引库中的类型
    • shards:分片数量,默认5
    • replicas:副本数量,默认1
  • @Id 作用在成员变量,标记一个字段作为id主键
  • @Field 作用在成员变量,标记为文档的字段,并指定字段映射属性:
    • type:字段类型,取值是枚举:FieldType
    • index:是否索引,布尔类型,默认是true
    • store:是否存储,布尔类型,默认是false
    • analyzer:分词器名称

@Data 为Lombok插件,因此省略了get set方法
@Column 为映射对应的字段

@Data
@Table(name = "t_faq")
@Document(indexName = "faq_index",type = "doc",shards = 5, replicas = 0)
public class FAQ implements Serializable {
    private static final long serialVersionUID = -4852732623765810959L;
    @Column(name = "number")
    @Id
    @org.springframework.data.annotation.Id
    private String number;

    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", timezone = "GMT+8")
    @Column(name = "time")
    private Date time;

    @Column(name = "version")
    private String version;

    @Field(type = FieldType.Text)
    @Column(name = "title")
    private String title;

    @Field(type = FieldType.Text)
    @Column(name = "description")
    private String description;

    @Field(type = FieldType.Text)
    @Column(name = "finalResult")
    private String finalresult;
}
  1. 搜索服务接口
/**
*question 检索词,page构造分页
**/
    public Map<String, Object> findAllHighLight(String question, Pageable page) {
        //构造搜索字段  这是一个关键词在多个字段上进行检索
        Map<String, Object> data = new HashMap<>();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .should(matchQuery("description", question))
                .should(matchQuery("finalresult", question))
                .should(matchQuery("number", question))
                .should(matchQuery("title", question));
        NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .withPageable(page)
                .withHighlightFields(//设置需要高亮的字段。返回数据自动对匹配的字段高亮
                        new HighlightBuilder.Field("finalresult").preTags("<span style='color:blue'>").postTags("</span>"),
                        new HighlightBuilder.Field("description").preTags("<span style='color:blue'>").postTags("</span>"),
                        new HighlightBuilder.Field("title").preTags("<span style='color:blue'>").postTags("</span>")).build();
                        //分页获取数据
        elasticsearchTemplate.queryForPage(nativeSearchQuery, FAQ.class, new SearchResultMapper() {
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
                ArrayList<FAQ> list = new ArrayList<>();
                SearchHits hits = response.getHits();
                data.put("total", hits.totalHits);
                for (SearchHit searchHit : hits) {
                    if (hits.getHits().length <= 0) {
                        return null;
                    }
                    Map<String, Object> sourceAsMap = searchHit.getSourceAsMap();
                    String description = (String) sourceAsMap.get("description");
                    String finalResult = (String) sourceAsMap.get("finalresult");
                    String title = (String) sourceAsMap.get("title");
                    String number = (String) sourceAsMap.get("number");
                    String version = (String) sourceAsMap.get("version");
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                    Date time = null;
                    try {
                        time = sdf.parse(((String) sourceAsMap.get("time")).substring(0, 10));
                    } catch (ParseException e) {
                        e.printStackTrace();
                    }
                    FAQ faq = new FAQ();
                    HighlightField field = searchHit.getHighlightFields().get("description");
                    if (field == null) {
                        faq.setDescription(description);
                    } else {
                        faq.setDescription(field.fragments()[0].toString());
                    }
                    field = searchHit.getHighlightFields().get("title");
                    if (field == null) {
                        faq.setTitle(title);
                    } else {
                        faq.setTitle(field.fragments()[0].toString());
                    }
                    field = searchHit.getHighlightFields().get("version");
                    if (field == null) {
                        faq.setVersion(version);
                    } else {
                        faq.setVersion(field.fragments()[0].toString());
                    }
                    field = searchHit.getHighlightFields().get("finalresult");
                    if (field == null) {
                        faq.setFinalresult(finalResult);
                    } else {
                        faq.setFinalresult(field.fragments()[0].toString());
                    }
                    faq.setNumber(number);
                    faq.setTime(time);
                    list.add(faq);
                }
                if (list.size() > 0) {
                    data.put("rows", list);
                }
                return null;
            }
        });

        return data;
    }
  1. 数据接口控制器controller方法
    @RequestMapping(value = "morelist")
    @ResponseBody
    public Map<String, Object> moreFaqList(QueryRequest request, FAQ faq) {
        //手动分页
        if (faq.getTitle().equals("")) {
        	//通过mysql数据
            return faqService.findInMysqlByPage(request.getPageNum(),request.getPageSize());
        }else{
           //通过elasticsearch 获取数据
            Pageable page = PageRequest.of(request.getPageNum()-1,request.getPageSize());
            return faqService.findAllHighLight(faq.getTitle(),page);
        }

    }

效果
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章