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);
        }

    }

效果
在這裏插入圖片描述

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