Elastic_Stack

搜索引擎基础

倒排索引

  • 常见搜索引擎

    • 网页搜索引擎:百度、谷歌
    • 业务搜索引擎:商品搜索、订单搜索
    • 日志搜索引擎:运维查看日志
  • 应用:用户给定关键词,搜索引擎返回与关键词相关的结果

  • 关系:大数据与搜索引擎工程师之间的联系

    • 搜索引擎中的数据一般由大数据工程师实现存储
  • 过程

    • step1:用户提交搜索词

      大数据分析
      
    • step2:搜索引擎对用户的搜索词做分词

      • 搜索引擎会获取用户的输入,调用分词器对用户的搜索词进行分词
      • 大数据
      • 分析
    • step3:将爬取到的所有网页构建倒排索引,构建倒排索引库

      • 将每个网页中的每句话进行分词:将一个网页中所有词拆分出来

        网页编号 网页的内容 分词
        1 大数据工业化应用 大数据、工业化、应用
        2 大数据与人工智能的关系 大数据、人工智能、关系
        3 大数据的应用场景 大数据、应用、场景
      • 构建倒排索引

        网页 的id
        大数据 1,2,3
        应用 1,3
        人工智能 2
      • 假设用户搜索:大数据分析

        • 根据用户查询的分词从倒排索引中返回所有相关的网页的id
        • 根据算法对所有网页进行评分排名
        • 3:90
        • 2:85
        • 1:70
    • 倒排索引概念

      • 传统索引:根据id查询内容
      • 倒排索引:根据内容查id
    • step4:根据用户的关键词分词的结果到索引库中进行匹配,返回所有相关的词对应的所有网页

      Elastic_Stack_第1张图片

实现框架

  • 传统的存储检索工具
    • 如果搜索引擎通过MySQL或者Oracle来实现能不能满足需求?

      • 答案:不能
    • 查询的条件:走索引的问题

      • 需求:不论用户查询的是什么内容,都要走索引返回
      • SQL:like %keyword%
    • 数据量的负载:关系型数据库的负载量很小

    • 需求:专业的搜索引擎工具

  • 专业的搜索引擎工具
    • Lucene:最早的搜索引擎工具
      • 优点:奠定了整个搜索索引的框架和核心思想
      • 缺点:不好用,接口比较复杂,学习和使用成本比较高,现在几乎看不到
    • Solr:基于Lucene封装的一个工具
      • 优点:接口相对变的好用了
      • 缺点:数据量大的情况,性能会差一些,高度依赖于ZK
    • ElasticSearch:吸收了前两代工具的特点,解决了对应的缺点问题
      • 接口【JSON】友好,主攻搜索引擎实现,其他所有的功能都通过插件来实现,自己实现集群管理
      • 自己实现了类似于ZK的分布式管理机制:Zen-discovery机制

ElasticSearch

介绍

  • 基本介绍
    • ELK官网:https://www.elastic.co/
    • ELK官网文档:https://www.elastic.co/guide/index.html
    • ELK中文手册:https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html
    • ELK中文社区:https://elasticsearch.cn/
  • Elastic Stack
    • ElasticSearch:搜索引擎工具
      • 分布式存储、分布式分析计算
    • Logstash:用于实现数据采集
      • 重量级数据采集,功能比Flume多:文件、数据流、数据库
      • 采集的功能非常完善,后来更新发展侧重数据ETL
    • Kibana:可视化工具
      • 专门为ES设计的可视化工具,搭配ES使用
    • Beats:轻量级数据采集
      • 功能相对来说单一化,简单并且性能很好
  • 应用
    • 所有搜索引擎的场景都可以使用Elastic Stack来实现
    • 主要的应用场景:日志搜索引擎
  • 特点
    • 分布式:高可用、扩展性、高性能的
    • NRT:近实时的分布式存储系统,数据从写入到读取之间会有1s延迟
    • 全文索引:任何东西写入ES以后,都会自动创建索引
  • 区别
    • Redis:实时、内存、NoSQL数据库、大数据量缓存
    • Hbase:实时、内存+磁盘、NoSQL数据库、大数据量持久性
    • Kafka:实时、内存+磁盘、消息队列、大数据量缓存
    • ES:近实时,内存+磁盘、全文索引、大数据量持久化

存储概念

  • Index:索引库

    • 所有数据都是存储在index中,用于构建索引的
    • 类似于:数据库或者命名空间的概念
      • Redis:数据库
      • Hbase:命名空间
  • Type:索引类型/索引表

    • 注意:ES从7.0开始取消了这个Type结构

      • 6.0开始:每个Index库只允许有一个Type
    • 问题:ES中的所有数据在物理上是直接存储在index中,但是逻辑上数据写入某个Index的某个Type中

      • 对于index而言,如果列名相同,会被认为是同一列数据

      • index:索引库:person

        • type:索引表:Sudent

          sid		sname		age[string]
          
        • type:索引表:Teacher

          tid		tname		age[int]
          
  • Document:文档

    • 就是ES中存储的一条数据,每一条数据就是一个document
    • 构成:documentId + data【每一列构成】
      • docuementId:可以自己指定,唯一标识一行,如果不给,ES会自动生成一个
        • 功能
          • 作为主键,唯一标识一行,有主键索引
          • 决定分区的规则:按照documentID的Hash值取余分区个数
    • 类比于Hbase
      • document = Rowkey的数据
      • documentID = rowkey
      • data【fields】 = Cell[]
    • document作为ES中的存储/索引的最小单元
      • 写:按照整个document写入
      • 读:索引返回的是整个document的数据
  • Fields:数据列

    • 就是以前讲的column,每一列的数据
    • 构建索引的最小单元:列,可以指定哪些列构建索引,哪些列不构建索引
  • Shard:分片

    • 就是分区,shards是index级别的,一个index可以有多个shard分区,不同的shard分区分布在不同的节点上
    • 默认每个index有1个分片
  • Replicas:副本

    • 为了保证分片高可用性,默认每个分片有1个副本,分片 + 副本不允许超过机器个数
    • 注意:副本的个数不包含分片,如果1个分片有2个副本,总共存储了3份
    • 角色
      • leader:对外提供读写
      • follower:与leader同步数据,如果leader故障,follower会选举新的leader
  • Mapping:列的映射

    • 用于实现对于整个Index的列的管理:实现列的定义
    • 列:名称、类型、是否构建索引、是否进行分词
  • Setting:配置管理

    • 用于管理index的架构配置管理

    • 管理分片和副本

      • 默认:一个分片、一个副本
      • 实现:多个分片多个副本
概念 HDFS Hbase Kafka ES
第一层划分 目录 NameSpace - Index
第二层划分 文件 Table Topic Type【7.0开始没有这个结构】
存储分区 分块:Block 范围:Region 分区:Partition Shard
分区安全 副本机制 WAL+副本机制【就近原则】 副本机制【leader+Follower】 副本机制:Replicas【leader+Follower】
存储单元 -【行】 Rowkey+Store【列】 Segment【行:offset】 docld+ data[fields]
架构 主从架构:NN、DN 主从架构:Master、RegionServer 主从架构:Crontroler、Broker 主从架构:Master + Worker
HA 两个NN + ZK 两个Master+ZK ZK Zen-discovery

Elastic_Stack_第2张图片

小结

  • index :索引库 (类似于数据库的概念)
  • Type:索引类型/索引表
  • Document:文档 (ES中存储的一条数据) = documentld(唯一标识符)+date
  • Fields :数据列 (构建索引的最小单元:列)
  • Shard :分片(分区,它是index级别的,一个index可以有多个shard分区)
  • Repliacs :副本
  • Mapping : 列的映射 (用于实现对整个index的列的管理 列 : 名称,类型,是否构建索引,是否进行分词)
  • Setting :配置管理 (用于管理index的架构)

ES部署

需求及环境准备

  • 安装需求

    • ElasticSearch:分布式搜索引擎,三台都有,先装一台,然后分发
      • 没有可视化界面
    • ElasticSearch-Head:可视化ES工具,第一台机器安装即可
      • 类似于Kafka-eagle
    • IK分词器:中文解析分词器 ,三台都有
      • 默认只带英文分词器,普通中文分词器比较鸡肋,使用IK中文分词器
  • 环境准备

    • 严禁使用root用户
    • 先用root用户在三台机器创建ES的用户

      useradd itcast
      passwd itcast
      
      • 设置密码:itcast
    • 三台机器创建ES的目录

      mkdir -p /export/server/es
      chown -R itcast:itcast /export/server/es
      
    • 三台机器配置itcast用户的sudo权限

      visudo
      
      100行左右添加: 
      itcast    ALL=(ALL)       NOPASSWD: ALL
      
      • 以后使用管理员的命令时要加上sudo

        sudo vim /etc/profile
        
    • 三台机器使用itcast用户连接登录

    • 三台机器配置免秘钥

      ssh-keygen -t rsa
      ssh-copy-id node1
      ssh-copy-id node2
      ssh-copy-id node3
      
    • 修改三台资源配置

      sudo vi /etc/security/limits.conf
      #在文件的末尾添加以下内容,*号不能去掉
      
      * soft nofile 65536
      * hard nofile 131072
      * soft nproc 4096
      * hard nproc 4096
      
      sudo sed -i '/^#DefaultLimitNOFILE=/aDefaultLimitNOFILE=4096' /etc/systemd/system.conf 
      sudo sed -i '/^#DefaultLimitNPROC=/aDefaultLimitNPROC=4096' /etc/systemd/system.conf 
      
      #临时设置
      sudo  sysctl -w vm.max_map_count=262144
      #永久设置
      sudo vim /etc/sysctl.d/99-sysctl.conf
      #添加这一行
      vm.max_map_count=262144
      #检查是否成功
      sudo sysctl -a | grep "vm.max_map_count"
      
    • 断开重连所有会话

ES的分布式安装

  • 上传安装包到第一台机器的家目录下

    cd ~
    rz
    tar -zxvf elasticsearch-7.6.1-linux-x86_64.tar.gz -C /export/server/es/
    cd /export/server/es/
    
  • 修改核心配置文件

    cd /export/server/es/elasticsearch-7.6.1/
    vim config/elasticsearch.yml
    
    cluster.name: itcast-es
    node.name: node1
    path.data: /export/server/es/elasticsearch-7.6.1/data
    path.logs: /export/server/es/elasticsearch-7.6.1/log
    network.host: node1
    http.port: 9200
    discovery.seed_hosts: ["node1", "node2", "node3"]
    cluster.initial_master_nodes: ["node1", "node2"]
    bootstrap.system_call_filter: false
    bootstrap.memory_lock: false
    http.cors.enabled: true
    http.cors.allow-origin: "*"
    
  • 修改JVM配置文件

    vim config/jvm.options 
    
    #22-23行:根据虚拟机的情况,给一半内存
    -Xms2g
    -Xmx2g
    
  • 分发

    cd /export/server/es/
    scp -r elasticsearch-7.6.1/ node2:$PWD
    scp -r elasticsearch-7.6.1/ node3:$PWD
    
  • 修改node2和node3的配置文件中的主机名

  • 启动三台ES

    cd /export/server/es/elasticsearch-7.6.1/
    /export/server/es/elasticsearch-7.6.1/bin/elasticsearch >>/dev/null 2>&1 &
    
    访问端口:9200
    

    Elastic_Stack_第3张图片

es-head的安装

  • 安装依赖环境

    • 下载node js

      cd ~
      wget https://npm.taobao.org/mirrors/node/v8.1.0/node-v8.1.0-linux-x64.tar.gz
      tar -zxvf node-v8.1.0-linux-x64.tar.gz -C /export/server/es/
      
    • 创建链接

      sudo ln -s /export/server/es/node-v8.1.0-linux-x64/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm
      sudo ln -s /export/server/es/node-v8.1.0-linux-x64/bin/node /usr/local/bin/node
      
    • 修改环境变量

      sudo vim /etc/profile
      
      export NODE_HOME=/export/server/es/node-v8.1.0-linux-x64
      export PATH=:$PATH:$NODE_HOME/bin
      
      source /etc/profile
      
    • 检查是否安装成功

      node -v
      npm -v
      
  • 上传到第一台机器解压

    cd ~
    rz
    tar -zxvf elasticsearch-head-compile-after.tar.gz -C /export/server/es/
    
  • 修改配置文件

    • 修改Gruntfile.js

      cd /export/server/es/elasticsearch-head
      vim Gruntfile.js
      
      #93行:ES-head的服务地址和端口
      hostname: 'node1',
      
    • 修改app.js

      cd /export/server/es/elasticsearch-head/_site
      vim app.js
      
      #4354行
      this.base_uri = this.config.base_uri || this.prefs.get("app-base_uri") || "http://node1:9200";
      
  • 启动

    cd /export/server/es/elasticsearch-head/node_modules/grunt/bin/
    #后台启动
    ./grunt server >/dev/null 2>&1 &
    #查看进程
    netstat -atunlp | grep 9100
    
  • 访问:http://node1:9100/

Elastic_Stack_第4张图片

IK分词器的安装

  • 创建插件目录

    mkdir -p /export/server/es/elasticsearch-7.6.1/plugins/ik
    
  • 上传解压

    cd /export/server/es/elasticsearch-7.6.1/plugins/ik/
    rz
    unzip elasticsearch-analysis-ik-7.6.1.zip 
    rm -rf elasticsearch-analysis-ik-7.6.1.zip 
    
  • 分发

    cd /export/server/es/elasticsearch-7.6.1/plugins
    scp -r ik/ node2:$PWD
    scp -r ik/ node3:$PWD
    
  • 重启ES

    • 直接Kill

      /export/server/es/elasticsearch-7.6.1/bin/elasticsearch >>/dev/null 2>&1 &
      

VSCode集成ES插件

创建一个文件,要以.es结尾,用VScode打开这个文件

Elastic_Stack_第5张图片

Elastic_Stack_第6张图片

配置一下ES服务端地址

Elastic_Stack_第7张图片

Elastic_Stack_第8张图片

Elastic_Stack_第9张图片

  • 测试分词器

    --标准分词
    post _analyze 
    {
           
        "analyzer":"standard", 
        "text":"我爱你中国"
    }
    --IK分词
    post _analyze 
    {
           
        "analyzer":"ik_max_word", 
        "text":"我爱你中国"
    }
    

RESTful API

背景及索引构建

  • 背景

    • 基于招聘网站中招聘信息的数据检索
    • 需求:用户可以根据地区、经验、岗位名称、岗位描述等信息快速的检索用户想要的招聘数据
    • RestFul API
      • 主要应用于DDL
        • 创建、删除、修改索引库
      • ES基于基于HTTP请求方式来实现的
        • PUT:插入或者更新数据
        • POST:可以做各种写入操作请求
        • GET:读取数据
        • DELETE:用于删除
        • 都以JSON形式来实现数据的请求管理
  • 索引库管理

    • 列举

      GET _cat/indices
      
      • 查询当前所有的索引库

        • SQL:show databases
    • 创建job_idx索引库

      PUT /job_idx
      {
               
          "mappings": {
               
              "properties" : {
               
                  "area": {
                "type": "text", "store": true, "analyzer": "ik_max_word"},
                  "exp": {
                "type": "text", "store": true, "analyzer": "ik_max_word"},
                  "edu": {
                "type": "keyword", "store": true},
                  "salary": {
                "type": "keyword", "store": true},
                  "job_type": {
                "type": "keyword", "store": true},
                  "cmp": {
                "type": "text", "store": true, "analyzer": "ik_max_word"},
                  "pv": {
                "type": "keyword", "store": true},
                  "title": {
                "type": "text", "store": true, "analyzer": "ik_max_word"},
                  "jd": {
                "type": "text", "store": true, "analyzer": "ik_max_word"}
              }
          },
        "settings" : {
               
              "number_of_shards":5,
            "number_of_replicas" : 1
          }
      }
      
    • 运行结果

      Elastic_Stack_第10张图片

      Elastic_Stack_第11张图片

    • 查看

      GET /job_idx/_mapping
      GET /job_idx/_settings
      
    • 删除

      delete /job_idx
      

插入/更新/删除 DDL操作

  • 插入

    put /index/_doc/doc_id
    {
    	JSON:每一列的数据
    }
    
    PUT /job_idx/_doc/29097
    {
           
        "area": "深圳-南山区",
        "exp": "1年经验",
        "edu": "大专以上",
        "salary": "6-8千/月",
        "job_type": "实习",
        "cmp": "乐有家",
        "pv": "61.6万人浏览过  / 14人评价  / 113人正在关注",
        "title": "桃园 深大销售实习 岗前培训",
        "jd": "薪酬待遇】 本科薪酬7500起 大专薪酬6800起 以上无业绩要求,同时享有业绩核算比例55%~80% 人均月收入超1.3万 【岗位职责】 1.爱学习,有耐心: 通过公司系统化培训熟悉房地产基本业务及相关法律、金融知识,不功利服务客户,耐心为客户在房产交易中遇到的各类问题; 2.会聆听,会提问: 详细了解客户的核心诉求,精准匹配合适的产品信息,具备和用户良好的沟通能力,有团队协作意识和服务意识; 3.爱琢磨,善思考: 热衷于用户心理研究,善于从用户数据中提炼用户需求,利用个性化、精细化运营手段,提升用户体验。 【岗位要求】 1.18-26周岁,自考大专以上学历; 2.具有良好的亲和力、理解能力、逻辑协调和沟通能力; 3.积极乐观开朗,为人诚实守信,工作积极主动,注重团队合作; 4.愿意服务于高端客户,并且通过与高端客户面对面沟通有意愿提升自己的综合能力; 5.愿意参加公益活动,具有爱心和感恩之心。 【培养路径】 1.上千堂课程;房产知识、营销知识、交易知识、法律法规、客户维护、目标管理、谈判技巧、心理学、经济学; 2.成长陪伴:一对一的师徒辅导 3.线上自主学习平台:乐有家学院,专业团队制作,每周大咖分享 4.储备及管理课堂: 干部训练营、月度/季度管理培训会 【晋升发展】 营销【精英】发展规划:A1置业顾问-A6资深置业专家 营销【管理】发展规划:(入职次月后就可竞聘) 置业顾问-置业经理-店长-营销副总经理-营销副总裁-营销总裁 内部【竞聘】公司职能岗位:如市场、渠道拓展中心、法务部、按揭经理等都是内部竞聘 【联系人】 黄媚主任15017903212(微信同号)"
    }
    
    {
        "_index": "job_idx",
        "_type": "_doc",
        "_id": "29097",
        "_version": 1,
        "result": "created",
        "_shards": {
            "total": 2,
            "successful": 2,
            "failed": 0
        },
        "_seq_no": 0,
        "_primary_term": 1
    }
    
  • 更新

    POST /job_idx/_update/29097
    {
           
        "doc": {
           
            "salary": "15-20千/月"
        }
    }
    
  • 删除

    DELETE /job_idx/_doc/29097
    
  • BulkLoad

    • 上传文件到Linux中

      Elastic_Stack_第12张图片

    • 执行批量加载

      curl -H "Content-Type: application/json" -XPOST "node1:9200/job_idx/_bulk?pretty&refresh" --data-binary "@job_info.json"
      

查询 DML操作

  • 查询方式

    • 根据doc_id进行查询:类似于MySQL中根据主键进行查询
      • 类似于Hbase中基于rowkey查询
    • 使用查询器进行查询:各种查询器满足各种查询需求
      • QueryBuilder:Match、Term、Range
  • doc_id查询

    GET /job_idx/_search
    {
           
        "query": {
           
            "ids": {
           
                "values": ["46313"]
            }
        }
    }
    
  • 关键词查询

    • 职位描述中包含销售的数据

      GET  /job_idx/_search 
      {
          "query": {
              "match": {
                  "jd": "销售"
              }
          }
      }
      
    • 默认只显示10条,如果要显示多条,可以通过size标签实现

      GET  /job_idx/_search 
      {
          "query": {
              "match": {
                  "jd": "销售"
              }
          },
          "size":"100"
      }
      
    • 职位描述及岗位名称中包含销售

      GET  /job_idx/_search
      {
               
          "query": {
               
              "multi_match": {
               
                  "query": "销售",
                  "fields": ["title", "jd"]
              }
          }
      }
      
      • multi_match:多列字符串匹配查询器
  • 分页查询

    • from and size

      GET  /job_idx/_search
      {
               
          "from": 0,
          "size": 5,
          "query": {
               
              "multi_match": {
               
                  "query": "销售",
                  "fields": ["title", "jd"]
              }
          }
      }
      
      • 浅分页:类似于SQL中的limit M,N
        • from:从第几条开始显示,第一条的值为0
        • size:每一页的显示几条
      • 每次必须手动修改from的值
    • scroll

      • 深分页:第一次查询,会将整个所有数据放在内存中,从第二次开始就从内存中来自动遍历每页的数据

        • scroll = 1m :做深分页,将查询的结果保存在内存中1分钟
      • size:每一页显示多少条

      • 优点:自动进行翻页

      • 第一次

        GET /job_idx/_search?scroll=1m
        {
                   
            "query": {
                   
                "multi_match": {
                   
                  "query": "销售",
                    "fields": ["title", "jd"]
              }
            },
            "size": 100
        }
        
      • 第二次开始

        GET _search/scroll?scroll=1m
        {
                   
            "scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAAdFmZSaUhmZWJvVHphVTA0VWFXWGctLUEAAAAAAAAAGhZ2ZmYya3k4ZVNuNnAzbDBOTEttUV9BAAAAAAAAAB4WZlJpSGZlYm9UemFVMDRVYVdYZy0tQQAAAAAAAAAbFnZmZjJreThlU242cDNsME5MS21RX0EAAAAAAAAAHBZ2ZmYya3k4ZVNuNnAzbDBOTEttUV9B"
        }
        

Java API

环境准备

  • JobDetail:Java Bean对象,用于存储每一个职位的信息

    • 整个ES的读写都是以JSON形式实现的,不方便处理
    • 将数据封装在JobDetail对象,方便处理数据
  • JobFullTextService:封装的所有增删改查的接口,定义所有方法以及返回值

  • JobFullTextServiceImpl:真正的实现类,实现了所有增删改查的方法

  • JobFullTextServiceTest:测试工具类,用于测试实现类的方法是否可用

插入数据

  • 构建连接对象 RestHighLevelClient
restHighLevelClient = new RestHighLevelClient(RestClient.builder(
                new HttpHost("node1", 9200, "http")
                , new HttpHost("node2", 9200, "http")
                , new HttpHost("node3", 9200, "http")
        ));
  • 写入数据*

    • 调用index方法来写入ES

    • 构建IndexRequest对象

      • IndexRequest:写入数据必须构建的对象
        • .id:设置写入的doc_id
        • .source:设置写入的数据列
      //往ES中写入一条数据:JSON格式的数据
          @Override
          public void add(JobDetail jobDetail)  {
               
              //构建了一个索引请求器对象,用于写入,制定了请求器请求的索引库的名称
              IndexRequest indexRequest = new IndexRequest(JOB_IDX_NAME);
              //封装数据到请求器中:doc_Id + JSON数据
              //从参数的对象中获取id作为docId
              indexRequest.id(jobDetail.getId() +"");
              //将JavaBean对象转换为JSON字符串
              String jsonString = JSON.toJSONString(jobDetail);
              //将JSON数据加载到请求器中,指定数据为JSON格式
              indexRequest.source(jsonString, XContentType.JSON);
              try {
               
                  //客户端连接调用index方法实现写入:第一个参数是索引请求器
                  restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
              } catch (IOException e) {
               
                  e.printStackTrace();
              }
      }
      

小结

  • RestHighLevelClient :客户端连接对象

    • .index:写入方法 (第一个参数是索引请求器,RequestOptions.DEFAULT)
  • IndexRequst :写入请求器对象 new该对象的时候里面放索引库名称

    • .id (里面放docid,类型为string )
    • .source (指定数据格式 ,数据 )

查询数据

  • 调用get方法来查询某个docid对应的数据

  • 构建GetRequest对象

    • GetRequest:根据DOCID进行请求,读取某个Doc的数据
    //用于通过docId来查找数据
        @Override
        public JobDetail findById(long id) throws IOException {
           
            //构建一个Get请求器对象
            GetRequest getRequest = new GetRequest(JOB_IDX_NAME);
            //指定get请求器的docid
            getRequest.id(id+"");
            //客户端连接对象调用get方法来获取某个docId对应的数据:传递get请求器
            GetResponse documentFields = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
            //将每一列对应的数据取出转换为JSON String
            String sourceAsString = documentFields.getSourceAsString();
            //将JSONString转换为JavaBean独享
            JobDetail jobDetail = JSON.parseObject(sourceAsString, JobDetail.class);
            return jobDetail;
        }
    

小结

  • GetRequest :get请求器对象,new的时候里面放索引库名称
    • .id :指定请求器的docid
  • RestHighLevelClient :客户端连接对象
    • .get :按照docId进行查询(第一个参数为get请求器,RequestOptions.DEFAULT)返回的对象类型为GetResponse
    • 在使用GetResponse对象的getSourceAsString方法,将数据转换为Json数据
  • 使用JSON的parseObject方法把json数据转为javebean对象

更新与删除

  • 更新数据

    • 调用update方法来实现

    • 构建UpdateRequest对象

      • UpdateRequest:用于构建一个更新的请求
      //实现更新,将新的数据替换老的数据
          @Override
          public void update(JobDetail jobDetail) throws IOException {
               
              //todo:1-先判断是否存在
              GetRequest getRequest = new GetRequest(JOB_IDX_NAME);
              getRequest.id(jobDetail.getId()+"");
              //判断是否存在
              boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
              //如果不存在,方法调用结束
              if(!exists) return;
              //构建更新请求器:索引库名 + 指定更新的docid
              UpdateRequest updateRequest = new UpdateRequest(JOB_IDX_NAME,jobDetail.getId()+"");
              //更新请求器加载新数据
              updateRequest.doc(JSON.toJSONString(jobDetail),XContentType.JSON);
              //调用客户端连接中的更新方法
              restHighLevelClient.update(updateRequest,RequestOptions.DEFAULT);
          }
      
  • 删除数据

    • 调用delete方法来实现

    • 构建DeleteRequeset对象

      • DeleteRequest:用于实现删除请求
      //定义删除功能
          @Override
          public void deleteById(long id) throws IOException {
               
              //构建删除请求器
              DeleteRequest deleteRequest = new DeleteRequest(JOB_IDX_NAME);
              //添加指定删除的docid
              deleteRequest.id(id+"");
              //调用删除方法
              restHighLevelClient.delete(deleteRequest,RequestOptions.DEFAULT);
          }
      

小结:

  • 更新 :更新得先判断是否存在该数据,
    • GetRequest :请求器对象 (索引库名称)
      • .id :(指定请求器的docid )
    • RestHighLevelClient:客户端连接对象
      • .exists : 判断是否存在的方法 (第一参数是GetRequest对象,RequestOptions.DEFAULT)返回一个布尔值
      • .update(UpdateRequest):实现更新操作
    • UpdateRequest:更新请求器对象 (索引库名称,更新的docId要string类型)
      • .doc(JSON字符串的更新数据)
  • 删除
    • RestHighLevelClient:客户端连接对象
      • .delete(DeleteRequest):实现删除操作
    • DeleteRequest:删除请求器
      • .id(docId)

关键词查询

  • 调用search方法

       @Override
        public List<JobDetail> searchByKeywords(String keywords) throws IOException {
           
            //todo:1-构建返回值对象
            List<JobDetail> lists = new ArrayList<>();
            //todo:2-根据搜索词查询符合的数据
            //构建Search的请求器
            SearchRequest searchRequest = new SearchRequest();
            //构建条件的建造器
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            //构建一个符合需求的查询器
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keywords, "jd", "title");
    //        MatchQueryBuilder c1 = QueryBuilders.matchQuery(keywords, "jd");//关键词单列查询器,模糊查询,做分词
    //        TermQueryBuilder c2 = QueryBuilders.termQuery(keywords, "jd");//关键词单列查询器,精准查询,不做分词
    //        RangeQueryBuilder c3 = QueryBuilders.rangeQuery("age").gt("18").lt("30");//范围查询
    //        QueryBuilders.boolQuery().must(c2).must(c3).should(c1); //must代表并列,should表示或者,多条件组合查询
            //条件建造器加载查询器
            searchSourceBuilder.query(multiMatchQueryBuilder).size(100);
            //加载查询的条件
            searchRequest.source(searchSourceBuilder);
            //调用客户端连接的search方法,实现查询
            SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHit[] hits = search.getHits().getHits();
            for (SearchHit hit : hits) {
           
                //获取每一条数据,将数据的内容转换为JAVA Bean
                JobDetail jobDetail = JSON.parseObject(hit.getSourceAsString(), JobDetail.class);
                //将docId设置为id
                jobDetail.setId(Long.parseLong(hit.getId()));
                lists.add(jobDetail);
            }
          //todo:3-实现返回
            return lists;
        }
    
  • 重点内容:查询器的构建

    • QueryBuilders

小结

  • 构建返回值对象 List

  • RestHighLevelClient:客户端连接对象

  • SearchRequset :构建Search 请求器

  • SearchSourceBuilder : 构建条件的建造器

  • QueryBuilders.multiMatchQuery :构建一个符合需求的查询器

    • 查询器构建

      QueryBuilders.multiMatchQuery:多列模糊匹配查询
      QueryBuilders.matchQuery:单列模糊匹配
      QueryBuilders.termQuery:单列精准匹配
      QueryBuilders.rangeQuery:范围匹配
      QueryBuilders.boolQuery:条件查询器
      
  • 条件建造器加载查询器 => 建造器对象.query(查询器).size(查询显示数)

  • 加载查询条件 => 请求器对象.source(建造器对象)

  • 调用客户端的search方法 => 客户端对象.search(请求器对象,RequestOptions.DEFAULT) =>返回值为SearchResponse对象

  • 在调用SearchResponse对象的getHits方法,在调用getHits方法,得到所有数据内容为hit

  • 使用for循环遍历,使用JSON的parseObject方法 (在使用hit.getSourceAsString,javabean.class)

  • 在添加到List集合里

分页查询

浅分页查询

  • from and size

    /**
         * 实现浅分页查询
         * @param keywords :查询的关键词
         * @param pageNum : 就是从第几条开始查询,from
         * @param pageSize :每页显示几条,size
         * @return
         * @throws IOException
         */
        @Override
        public Map<String, Object> searchByPage(String keywords, int pageNum, int pageSize) throws IOException {
           
            SearchRequest searchRequest = new SearchRequest();
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keywords, "jd", "title");
            //建造器中指定查询器,指定from和size
            searchSourceBuilder.query(multiMatchQueryBuilder)
                    .from(pageNum)
                    .size(pageSize);
            searchRequest.source(searchSourceBuilder);
            SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            //取的是第一层hits
            SearchHits hits = search.getHits();
            List<JobDetail> lists = new ArrayList<>();
            for (SearchHit hit : hits) {
           
                JobDetail jobDetail = JSON.parseObject(hit.getSourceAsString(), JobDetail.class);
                jobDetail.setId(Long.parseLong(hit.getId()));
                lists.add(jobDetail);
            }
            //构建返回值:返回值为Map集合
            Map<String, Object> result = new HashMap<>();
            //第一条数据:Key:total,Value:返回值的总条数
            result.put("total", hits.getTotalHits().value);
            //第二条数据:Key:content,Value:每条数据的List集合
            result.put("content", lists);
    
            return result;
        }
    
  • 深分页查询

    • 分为两步
    • step1:第一次运行
    • step2:第二次开始
    /**
         * 测试深分页
         * @param keywords :查询关键词
         * @param scrollId : 内存数据的id,第一次是没有,第二次开始根据这个id进行自动分页查询
         * @param pageSize :每页的大小
         * @return
         * @throws IOException
         */
        @Override
        public Map<String, Object> searchByScrollPage(String keywords, String scrollId, int pageSize) throws IOException {
           
            //构建返回值
            Map<String, Object> result = new HashMap<>();
            List<JobDetail> jobList = new ArrayList<>();
    
            try {
           
                SearchResponse searchResponse = null;
                //如果为null,这是第一次请求
                if(scrollId == null) {
           
                    // 1. 创建搜索请求
                    SearchRequest searchRequest = new SearchRequest(JOB_IDX_NAME);
                    // 2. 构建查询条件
                    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
                    searchSourceBuilder.query(QueryBuilders.multiMatchQuery(keywords, "title", "jd"));
                    // 3. 设置分页大小
                    searchSourceBuilder.size(pageSize);
                    // 4. 设置查询条件、并设置滚动快照有效时间
                    searchRequest.source(searchSourceBuilder);
                    //指定数据在内存中放置时间
                    searchRequest.scroll(TimeValue.timeValueMinutes(1));
                    // 5. 发起请求
                    //提交查询器
                    searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
                }
                //不是第一次,直接根据scrollid来查询
                else {
           
                    //构建深分页查询器,传递scrollid
                    SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
                    searchScrollRequest.scroll(TimeValue.timeValueMinutes(1));
                    //调用scroll实现深分页
                    searchResponse = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT);
                }
    
                // 6. 迭代响应结果
                SearchHits hits = searchResponse.getHits();
                for (SearchHit hit : hits) {
           
                    JobDetail jobDetail = JSONObject.parseObject(hit.getSourceAsString(), JobDetail.class);
                    jobDetail.setId(Long.parseLong(hit.getId()));
                    jobList.add(jobDetail);
                }
                //第一条数据:数据的呃逆荣
                result.put("content", jobList);
                //第二条数据;scrollid
                result.put("scroll_id", searchResponse.getScrollId());
    
            }
            catch (IOException e) {
           
                e.printStackTrace();
            }
    
            return result;
    
        }
    

总结

插入:

  • RestHighLevelClient :客户端连接对象
    • .index:写入方法 (第一个参数是索引请求器,RequestOptions.DEFAULT)
  • IndexRequst :写入请求器对象 new该对象的时候里面放索引库名称
    • .id (里面放docid,类型为string )
    • .source (指定数据格式 ,数据 )

查询:

  • GetRequest :get请求器对象,new的时候里面放索引库名称
    • .id :指定请求器的docid
  • RestHighLevelClient :客户端连接对象
    • .get :按照docId进行查询(第一个参数为get请求器,RequestOptions.DEFAULT)返回的对象类型为GetResponse
    • 在使用GetResponse对象的getSourceAsString方法,将数据转换为Json数据
  • 使用JSON的parseObject方法把json数据转为javebean对象

更新 :更新得先判断是否存在该数据,

  • GetRequest :请求器对象 (索引库名称)
    • .id :(指定请求器的docid )
  • RestHighLevelClient:客户端连接对象
    • .exists : 判断是否存在的方法 (第一参数是GetRequest对象,RequestOptions.DEFAULT)返回一个布尔值
    • .update(UpdateRequest):实现更新操作
  • UpdateRequest:更新请求器对象 (索引库名称,更新的docId要string类型)
    • .doc(JSON字符串的更新数据)

删除:

  • RestHighLevelClient:客户端连接对象
    • .delete(DeleteRequest):实现删除操作
  • DeleteRequest:删除请求器
    • .id(docId)

关键词查询:(别人)

  • RestHighLevelClient:客户端连接对象

    • .search(SearchRequest)
  • SearchRequest:查询请求器

    • .source(SearchSourceBuilder):设置查询建造器
  • SearchSourceBuilder:查询建造器

    • .query(查询器):根据不同的需求设置不同的查询器
  • 查询器构建

    QueryBuilders.multiMatchQuery:多列模糊匹配查询
    QueryBuilders.matchQuery:单列模糊匹配
    QueryBuilders.termQuery:单列精准匹配
    QueryBuilders.rangeQuery:范围匹配
    QueryBuilders.boolQuery:条件查询器
    

    (自己总结)

  • 构建返回值对象 List

  • RestHighLevelClient:客户端连接对象

  • SearchRequset :构建Search 请求器

  • SearchSourceBuilder : 构建条件的建造器

  • QueryBuilders :查询器

    • .multiMatchQuery :构建一个符合需求的查询器

    • 查询器构建

      QueryBuilders.multiMatchQuery:多列模糊匹配查询
      QueryBuilders.matchQuery:单列模糊匹配
      QueryBuilders.termQuery:单列精准匹配
      QueryBuilders.rangeQuery:范围匹配
      QueryBuilders.boolQuery:条件查询器
      
  • 条件建造器加载查询器 => 建造器对象.query(查询器).size(查询显示数)

  • 加载查询条件 => 请求器对象.source(建造器对象)

  • 调用客户端的search方法 => 客户端对象.search(请求器对象,RequestOptions.DEFAULT) =>返回值为SearchResponse对象

  • 在调用SearchResponse对象的getHits方法,在调用getHits方法,得到所有数据内容为hit

  • 使用for循环遍历,使用JSON的parseObject方法 (在使用hit.getSourceAsString,javabean.class)

  • 在添加到List集合里

存储原理

基本角色

  • Master:主节点,负责接收客户端请求,实现集群管理和数据存储的功能

  • Worker:从节点,负责存储数据,接收客户端请求

  • Coordinator Node:中心调度节点

    • 谁接收的客户端请求,谁就是中心调度节点
    • 负责接收请求,转发处理请求,返回结果给客户端
  • 举例

    • 三台机器:node1,node2,node3
    • index:两个分区,每个分区有两个副本
      • shard0:p0 , r0 ,r0
      • shard1:p1,r1,r1

写入流程

Elastic_Stack_第13张图片

  • step1:客户端请求第一条提交写入一条数据,第一台机器作为中心调度节点
    • 解析这个请求,判断是否是一个DDL请求,如果是,转发给Master
    • 如果不是,就根据分片规则,查询元数据,将这个请求转发给对应分区所在的primary shard【Leader副本】的节点
  • step2:中心调度节点发现这条数据要写入0分区,0分区的主副本在第三台,转发给第三台机器
  • step3:第三台机器接受写入请求,将数据写入p0,p0会将这个写入广播给所有r0,超过半数的r0写入成功,就直接返回写入成功
  • step4:第三台机器将写入结果返回给中心调度节点,中心调度节点将结果返回给客户端

读取流程

Elastic_Stack_第14张图片

  • step1:客户端请求第三台提交读取一条数据,第三台机器作为中心调度节点

  • 读的方式一:直接读取某个docid的数据

    • 中心调度节点根据分片规则,将这个请求转发给对应的分区所在的节点
    • 对应分区查询这个docid对应的数据进行返回
  • 读到方式二:根据search来做query查询

    • 查询这个索引库所有分区的数据,将符合条件的结果全部返回到中心调度节点
    • 中心调度节点根据评分降序排序返回给客户端

物理存储

  • step1:先将数据写入分区对应buffer,内存区域,用于缓冲,这时候没有索引,数据不能被读取
  • step2:每隔1s,会将每个分区的buffer数据写入os cache
    • 会构建索引
    • 写入操作会记录在translog:也是存储在内存中,每隔一段时候同步磁盘
    • 现在的数据可以被读取到
  • step3:每隔半小时,将数据flush到磁盘文件:segment文件中
  • step4:将segment进行合并,将所有被标记删除和更新的数据真正的删除,产生新的segment

Elastic_Stack_第15张图片

ES SQL

需求

需求

对于所有ES中存储的数据,需要进行增删改查的处理的需求,使用SQL最方便能快速的进行开发和上手

设计

  • 所有ES的概念可以映射一张分布式的表的概念
  • 缺点
    • 没有免费或者开源的SQL开发工具
    • SQL语法支持的并不全面

测试

  • 查询所有职位信息

    GET /_sql?format=txt
    {
           
        "query": "SELECT * FROM job_idx limit 1"
    }
    
  • 转换为DSL

    • DSL:将SQL语法转换成每个独立的关键字
    GET /_sql/translate
    {
           
        "query": "SELECT * FROM job_idx limit 1"
    }
    
  • scroll分页

    --第一次
    GET /_sql?format=json
    {
           
        "query": "SELECT * FROM job_idx",
        "fetch_size": 10
    }
    --第二次开始
    GET /_sql?format=json
    {
           
        "cursor": "5/WuAwFaAXPkAURuRjFaWEo1VkdobGJrWmxkR05vQlFBQUFBQUFBQUF5RmpoSGVUUnZhWHBQVW5BMlpVbEhkV1JqVDNKYVdtY0FBQUFBQUFBQVB4WjZiM2xXTlhrNWIxRkVWMHR6ZVhGNVZsWlpNMmhSQUFBQUFBQUFBREVXT0VkNU5HOXBlazlTY0RabFNVZDFaR05QY2xwYVp3QUFBQUFBQUFCQUZucHZlVlkxZVRsdlVVUlhTM041Y1hsV1Zsa3phRkVBQUFBQUFBQUFNeFpYWTBWclRFaENTVkk1ZVVKYVNFeE5aR1ZKY1hKbv8PCQFmBGFyZWEBBGFyZWEBBHRleHQAAAABZgNjbXABA2NtcAEEdGV4dAAAAAFmA2VkdQEDZWR1AQdrZXl3b3JkAQAAAWYDZXhwAQNleHABBHRleHQAAAABZgJqZAECamQBBHRleHQAAAABZghqb2JfdHlwZQEIam9iX3R5cGUBB2tleXdvcmQBAAABZgJwdgECcHYBB2tleXdvcmQBAAABZgZzYWxhcnkBBnNhbGFyeQEHa2V5d29yZAEAAAFmBXRpdGxlAQV0aXRsZQEEdGV4dAAAAAL/AQ=="
    }
    --关闭
    POST /_sql/close
    {
           
        "cursor": "5/WuAwFaAXPkAURuRjFaWEo1VkdobGJrWmxkR05vQlFBQUFBQUFBQUF5RmpoSGVUUnZhWHBQVW5BMlpVbEhkV1JqVDNKYVdtY0FBQUFBQUFBQVB4WjZiM2xXTlhrNWIxRkVWMHR6ZVhGNVZsWlpNMmhSQUFBQUFBQUFBREVXT0VkNU5HOXBlazlTY0RabFNVZDFaR05QY2xwYVp3QUFBQUFBQUFCQUZucHZlVlkxZVRsdlVVUlhTM041Y1hsV1Zsa3phRkVBQUFBQUFBQUFNeFpYWTBWclRFaENTVkk1ZVVKYVNFeE5aR1ZKY1hKbv8PCQFmBGFyZWEBBGFyZWEBBHRleHQAAAABZgNjbXABA2NtcAEEdGV4dAAAAAFmA2VkdQEDZWR1AQdrZXl3b3JkAQAAAWYDZXhwAQNleHABBHRleHQAAAABZgJqZAECamQBBHRleHQAAAABZghqb2JfdHlwZQEIam9iX3R5cGUBB2tleXdvcmQBAAABZgJwdgECcHYBB2tleXdvcmQBAAABZgZzYWxhcnkBBnNhbGFyeQEHa2V5d29yZAEAAAFmBXRpdGxlAQV0aXRsZQEEdGV4dAAAAAL/AQ=="
    }
    
  • 条件检索

    GET /_sql?format=txt
    {
        "query": "select * from job_idx where MATCH(title, 'hadoop') or MATCH(jd, 'hadoop') limit 10"
    }
    

订单统计实现

  • 创建索引

    PUT /order_idx/
    {
           
        "mappings": {
           
            "properties": {
           
                "id": {
           
                    "type": "keyword",
                    "store": true
                },
                "status": {
           
                    "type": "keyword",
                    "store": true
                },
                "pay_money": {
           
                    "type": "double",
                    "store": true
                },
                "payway": {
           
                    "type": "byte",
                    "store": true
                },
                "userid": {
           
                    "type": "keyword",
                    "store": true
                },
                "operation_date": {
           
                    "type": "date",
                    "format": "yyyy-MM-dd HH:mm:ss",
                    "store": true
                },
                "category": {
           
                    "type": "keyword",
                    "store": true
                }
            }
        }
    }
    
    
  • 上传并导入测试数据

    curl -H "Content-Type: application/json" -XPOST "node1:9200/order_idx/_bulk?pretty&refresh" --data-binary "@order_data.json"
    
  • 基于DSL:统计每种支付方式的订单个数

    GET /order_idx/_search
    {
           
        "size": 0,
        "aggs": {
           
            "group_by_state": {
           
                "terms": {
           
                    "field": "payway"
                }
            }
        }
    }
    
  • 基于SQL:统计每种支付方式的订单个数

    GET /_sql?format=txt
    {
           
        "query": "select payway, count(*) as order_cnt from order_idx group by payway"
    }
    
  • 基于SQL:统计每个用户的订单个数和订单总金额

    GET /_sql?format=txt
    {
           
        "query": "select userid, count(1) as cnt, sum(pay_money) as total_money from order_idx group by userid"
    }
    

FileBeat

介绍

介绍:Beats:Elastic Stack中的一个组件,轻量级的数据采集的工具

  • FileBeat:专门用于实现数据文件采集的工具
    • 采集文件数据,类似于Flume:exec、taildir
  • PackageBeat:专门用于实现网络监听流量采集工具
    • 对网络数据传输的采集
  • MetricBeat:专门用于收集服务器性能指标的采集工具
    • CPU利用率、内存利用率
  • HeartBeat:专门用于监听服务可用性的信息采集工具
  • WindowsBeat:专门用于监听windows系统中产生的事件信息的采集工具

Elastic_Stack_第16张图片

应用:专门用于实现轻量级的文件采集

功能:实现基于文件的数据采集

特点 FileBeat Flume Logstash
功能 只能采集文件 采集文件和网络端口 文件、数据库、HDFS、Redis、网络
组件 Input、Output Source、Channel、SInk Input、Filter、Output
语言 Go Java Jruby
优点 轻量级,消耗资源最少 对于大数据开发接口的完美支持,性能比较好 全场景
缺点 功能比较少 特殊需求需要自定义开发,占用的资源比较多 性能比价差,占用资源非常多,设计关注于数据的过滤处理
应用 专门用于文件采集 大数据系统中的网络和文件采集 专门用于ES的数据采集,实现ETL,搭配Beats使用

对于大数据工程而言

  • 如果只采集文件,需要低消耗高性能:filebeat
  • 如果采集分布式网络端口或者文件比较多,比较复杂:Flume

安装以及开发规则

  • 安装

    • 使用文档:https://www.elastic.co/guide/en/beats/filebeat/current/index.html

    • 上传

      cd ~
      rz
      
    • 解压

      tar -zxvf filebeat-7.6.1-linux-x86_64.tar.gz -C /export/server/es/
      
  • 开发规则

    • 基本组件:所有的FileBeat的程序都有两个部分Input和Output组成

    • step1:开发一个配置文件

      • 定义Input:读取哪些文件的数据

        Elastic_Stack_第17张图片

      • 定义Output:将读取到的数据发送到哪个目标地中
        Elastic_Stack_第18张图片

    • step2:运行文件即可

      • filebeat运行开发的配置文件即可

案例

  • 需求
    • 采集/home/itcast目录下所有以server.log开头的日志文件数据写入ES中

    • 数据源

[

  • 开发

    • 创建配置文件

      cd /export/server/es/filebeat-7.6.1-linux-x86_64/
      mkdir apps
      vim apps/logfile_to_es.filebeat
      
    • 定义input和output

      filebeat.inputs:
      - type: log
        enabled: true
        paths:
          - /home/itcast/server.log.*
      
      output.elasticsearch:
          hosts: ["node1:9200", "node2:9200", "node3:9200"]
      
  • 上传数据

    • 上传测试文件到/home/itcast目录下
  • 测试运行

    • 修改权限

      chmod go-w /export/server/es/filebeat-7.6.1-linux-x86_64/apps/logfile_to_es.filebeat 
      
    • 运行

      ./filebeat -c apps/logfile_to_es.filebeat -e
      
      • -c:指定要运行的配置文件
      • -e:表示执行
  • 查看结果

  • 查看数据

    GET /filebeat-7.6.1-2021.07.03-000001/_search
    
  • 实时导入数据:添加新的文件,观察是否能实时的采集

小结

  • 实现FileBeatas案例的测试开发
  • 注意:filebeat会自动记录采集的偏移量

解决多行合并问题

导入错误数据

  • 导入

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0KigGZEY-1625579351323)(Elastic_Stack.assets/image-20210703102356129.png)]

  • 查看

    GET /filebeat-7.6.1-2021.07.03-000001/_search
    {
        "query": {
            "match": {
                "log.file.path": "/home/itcast/server.log.error"
            }
        }
    }
    
  • 问题

    • 错误日志被拆分为多行记录在ES中

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q3TNLeY2-1625579351324)(Elastic_Stack.assets/image-20210603102101286.png)]

    • 原因:filebeat将文件中每一行作为ES中的一行

    • 实际需求:需要将错误日志的多行作为一条错误信息存储在ES中

  • 解决

    • 将多行错误日志解析时合并为一行
  • 开发

    vim apps/logfile_regex_to_es.filebeat
    
    filebeat.inputs:
    - type: log
      enabled: true
      paths:
        - /home/itcast/server.log.*
      multiline.pattern: '^\['
      multiline.negate: true
      multiline.match: after
    
    output.elasticsearch:
        hosts: ["node1:9200", "node2:9200", "node3:9200"]
    
    • multiline.pattern: ‘^[’:中括号开头的数据作为新的一行
    • multiline.negate: true:不符合的行进行合并
    • multiline.match: after:合并在上一行的后面
  • 测试运行

    • 删除FileBeat的元数据

      rm -rf data/registry/filebeat/*
      
    • 删除之前的数据ES中的数据

      delete /filebeat-7.6.1-2021.07.03-000001
      
    • 运行

      chmod go-w /export/server/es/filebeat-7.6.1-linux-x86_64/apps/logfile_regex_to_es.filebeat
      
      ./filebeat -c apps/logfile_regex_to_es.filebeat -e
      
    • 查看ES

      GET /filebeat-7.6.1-2021.06.03-000001/_search
      {
          "query": {
              "match": {
                  "log.file.path": "/home/itcast/server.log.error"
              }
          }
      }
      

Logstash

介绍

  • 介绍:ELK中的L组件:用于实现各种场景下数据采集的功能
  • 功能:全场景下的数据采集:核心特点,注重于数据的转换处理
  • 应用
    • FileBeat:对数据进行采集
    • |
    • Logstash:对FileBeat传递过来的数据进行处理,ETL
    • |
    • ElasticSearch:实现数据的存储和分析
    • |
    • Kibana:实现可视化报表和可视化分析
  • 特点
    • 优点:功能全面,可以实现数据的预处理,支持全场景的数据采集
    • 缺点:侧重于数据处理,性能较差,开发接口不友好,现在一般不用于做数据采集工具

安装及开发规则

  • 安装

    • 上传

      cd ~
      rz
      
    • 解压

      unzip logstash-7.6.1.zip -d /export/server/es/
      cd /export/server/es/logstash-7.6.1/
      
  • 开发规则

    • 文档:https://www.elastic.co/guide/en/logstash/current/index.html
    • 基本规则:所有的Logstash的程序都可以有三个部分Input、Filter【可选】、Output组成
    • step1:先开发一个文件,定义Input、Filter、Output
      • Input:定义从什么地方读取
        • https://www.elastic.co/guide/en/logstash/current/input-plugins.html
      • Filter:可选的,实现对Input的数据进行预处理
        • https://www.elastic.co/guide/en/logstash/current/filter-plugins.html
      • Output:定义将数据保存到什么地方
        • https://www.elastic.co/guide/en/logstash/current/output-plugins.html
    • step2:提交运行文件即可

采集stdin输出到stdout (监听用户的输入)

  • 需求:监听用户的输入,将用户的输入在命令行直接输出

  • 开发

    • 编辑文件

      cd /export/server/es/logstash-7.6.1/
      mkdir apps
      vim apps/stdin-stdout.json
      
    • 写入配置

      input { 
          stdin { } 
      } 
      
      output {
          stdout {} 
      }
      
  • 测试

    bin/logstash -f apps/stdin-stdout.json 
    
    • 选项:三个选项只能使用一个
      • -f:指定运行某个文件
      • -e:在命名行执行代码
      • -t:测试代码的语法是否正确
  • 结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sdZMqThc-1625579351324)(Elastic_Stack.assets/image-20210603104331618.png)]

采集文件输出搭配stdout

  • 需求:实现Logstash采集文件输出到stdout

  • 开发

    vim apps/input-file-test.json 
    
    input{
        file{
            path => "/home/itcast/tomcat.log"
            type => "log"
            start_position => "beginning"
        }
    }
    output{
            stdout{
    			codec=>rubydebug
            }
    }
    
  • 测试

    bin/logstash -f apps/input-file-test.json
    
  • 结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XJvTrUUh-1625579351325)(Elastic_Stack.assets/image-20210603104848220.png)]

采集MySQL输出到stdout

  • 需求:Logstash采集MySQL输出到stdout

    • 创建MySQL数据表

      create database test;
      use test;
      CREATE TABLE `wcresult` (
        `id` int(11) NOT NULL AUTO_INCREMENT,
        `word` varchar(100) NOT NULL,
        `number` int(11) NOT NULL,
        PRIMARY KEY (`id`)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
      
    • 插入数据

      insert into wcresult values(null,'hadoop',8);
      insert into wcresult values(null,'hive',16);
      
  • 全量开发

    vim apps/input-jdbc1.json
    
    input {
      jdbc {
        jdbc_driver_library => "/home/itcast/mysql-connector-java-5.1.38.jar"
        jdbc_driver_class => "com.mysql.jdbc.Driver"
        jdbc_connection_string => "jdbc:mysql://node3:3306/test"
        jdbc_user => "root"
        jdbc_password => "123456"
        schedule => "*/1 * * * *"
        statement => "SELECT * from wcresult where number > 10;"
      }
    }
    
    output{
            stdout{
            codec=>rubydebug
            }
    }
    
  • 全量测试

    bin/logstash -f apps/input-jdbc1.json 
    
  • 增量开发

    vim apps/input-jdbc2.json
    
    input {
      jdbc {
        jdbc_driver_library => "/home/itcast/mysql-connector-java-5.1.38.jar"
        jdbc_driver_class => "com.mysql.jdbc.Driver"
        jdbc_connection_string => "jdbc:mysql://node3:3306/test"
        jdbc_user => "root"
        jdbc_password => "123456"
        use_column_value => true
        tracking_column => "id"
        schedule => "*/1 * * * *"
        statement => "SELECT * from wcresult where number > 10 and id > :sql_last_value;"
      }
    }
    
    output{
            stdout{
            codec=>rubydebug
            }
    }
    
  • 增量测试

    rm -rf /home/itcast/.logstash_jdbc_last_run
    bin/logstash -f apps/input-jdbc2.json 
    

采集FileBeats输出到ES

  • 需求:先用fileBeat采集文件,写入Logstash,再用Logstash写入ES

    • 数据格式

      90.224.57.84 						--ip地址
      - 
      - 
      [15/Apr/2020:00:27:19 +0800] 		--访问时间
      "POST 								--请求类型
      /report 							--请求页面路径
      HTTP/1.1" 							--协议
      404 								--返回状态
      21 									--返回字节大小
      "www.baidu.com" 					--请求地址
      "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.104 Safari/537.36 Core/1.53.4549.400 QQBrowser/9.7.12900"  --浏览器信息
      
  • 开发

    • step1:使用filebeat采集数据到Logstash

      cd /export/server/es/filebeat-7.6.1-linux-x86_64/
      vim apps/logfile_to_logstash.filebeat
      
      filebeat.inputs:
      - type: log
        enabled: true
        paths:
          - /home/itcast/access.*
        multiline.pattern: '^\d+\.\d+\.\d+\.\d+ '
        multiline.negate: true
        multiline.match: after
      
      output.logstash:
        enabled: true
        hosts: ["node1:45454"]
      
      chmod go-w apps/logfile_to_logstash.filebeat
      
    • step2:使用Logstash将数据采集到ES

      cd /export/server/es/logstash-7.6.1/
      vim apps/beats_to_es.json
      
      input {
          beats {
          port => 45454
      }
      }
      
      output {
          elasticsearch {
          hosts => [ "node1:9200","node2:9200","node3:9200"]
        }
      }
      
  • 测试

    • 删除元数据

      rm -rf /export/server/es/filebeat-7.6.1-linux-x86_64/data/registry/filebeat/*
      
    • 启动Logstash

      bin/logstash -f apps/beats_to_es.json
      
    • 启动Beats

      ./filebeat -c apps/logfile_to_logstash.filebeat -e
      
    • 上传测试数据access.log

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ea1eiWR8-1625579351325)(Elastic_Stack.assets/image-20210603112354181.png)]

  • 结果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xCB6r2kG-1625579351325)(Elastic_Stack.assets/image-20210603112342267.png)]

实现数据解析

  • Logstash插件

    • 列举所有插件

      bin/logstash-plugin list
      
    • GROK插件

      • 功能:正则匹配,从原始数据中将字段提取出来

      • 语法

        %{SYNTAX:SEMANTIC}
        
  • 需求:匹配日志中的所有常见字段,将每个字段提取写入ES

  • 开发

    vim apps/beats_grokall_console.json
    
    input {    beats {        port => 45454    }}filter {    grok {        match => {             "message" => "%{IP:ip} - - \[%{HTTPDATE:date}\] \"%{WORD:method} %{PATH:uri} %{DATA}\" %{INT:status} %{INT:length} \"%{DATA:reference}\" \"%{DATA:browser}\""         }    }   }output {    stdout {        codec => rubydebug  }}
    
  • 测试

    • 启动Logstash

      bin/logstash -f apps/beats_grokall_console.json
      
    • 删除元数据

      rm -rf /export/server/es/filebeat-7.6.1-linux-x86_64/data/registry/filebeat/*
      
    • 启动Filebeat

      ./filebeat -c apps/logfile_to_logstash.filebeat -e
      
  • 结果

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E54VMCYG-1625579351326)(Elastic_Stack.assets/image-20210603113021364.png)]

实现数据转换

  • 插件

    • mutate插件功能:实现字段添加、处理、重命名等
    • date插件功能:实现日期转换处理
  • 需求:转换日期格式并保留需要的字段写入ES

  • 开发

    vim apps/beats_mutate_date_es.json
    
    input {    beats {        port => 45454    }}filter {    grok {        match => {             "message" => "%{IP:ip} - - \[%{HTTPDATE:date}\] \"%{WORD:method} %{PATH:uri} %{DATA}\" %{INT:status:int} %{INT:length:int} \"%{DATA:reference}\" \"%{DATA:browser}\""         }    }    mutate {        enable_metric => "false"        remove_field => ["message", "log", "tags", "@timestamp", "input", "agent", "host", "ecs", "@version"]    }    date {        match => ["date","dd/MMM/yyyy:HH:mm:ss Z","yyyy-MM-dd HH:mm:ss"]        target => "date"    }}output {    stdout {        codec => rubydebug    }    elasticsearch {        hosts => ["node1:9200" ,"node2:9200" ,"node3:9200"]        index => "apache_web_log"    }}
    
  • 测试

    • 启动Logstash

      bin/logstash -f apps/beats_mutate_date_es.json
      
    • 删除元数据

      rm -rf /export/server/es/filebeat-7.6.1-linux-x86_64/data/registry/filebeat/*
      
    • 启动Filebeat

      ./filebeat -c apps/logfile_to_logstash.filebeat -e
      
  • 结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vR1Qn53T-1625579351326)(Elastic_Stack.assets/image-20210603113853249.png)]

其他案例

  • 采集用户输入输出到文件中

    • 编辑文件

      vim apps/output-file.json
      
      input {stdin{}}output {    file {        path => "/export/servers/es/logstash-6.0.0/usercase/datas/%{+YYYY-MM-dd}-%{host}.txt"        codec => line {            format => "%{message}"        }        flush_interval => 0    }}
      
    • 运行

      bin/logstash -f apps/output-file.json 
      
  • 采集用户输入输出到ES中

    • 编辑文件

      vim apps/output-es.json
      
      input {stdin{}}output {    elasticsearch {        hosts => ["node1:9200"]        index => "logstash-%{+YYYY.MM.dd}"    }}
      
    • 运行

      bin/logstash -f apps/output-es.json 
      
  • 采集文件的数据到Kafka中

    • 编辑文件

      vim apps/output-kafka.json
      
      input {        file{        path => "/home/es/tomcat.log"        type => "log"        start_position => "beginning"    }}output {    kafka {        topic_id => "bigdata"        bootstrap_servers => "node1:9092,node2:9092,node3:9092"        batch_size => 5   }}
      
    • 运行

      bin/logstash -f apps/output-kafka.json
      
  • 采集kafka数据到Es

    • 编辑文件

      vim apps/kafka-es.json
      
      input{
          kafka {
              group_id => "testLogstash"
              auto_offset_reset => "earliest"
              topics => ["bigdata"]
              bootstrap_servers => "node1:9092,node2:9092,node3:9092"
         }
      }
      output {
          elasticsearch {
              hosts => ["node1:9200"]
              index => "kakfatoes"
          }
      }
      
    • 运行

      bin/logstash -f apps/kafka-es.json
      

Kibana

介绍

  • 介绍:专门为ES设计的可视化工具

  • 功能:实现ES的可视化开发、查询、分析及数据报表的可视化

  • 应用:固定搭配ES来使用

安装

安装

  • 上传

    cd ~
    rz
    
  • 解压

    tar -zxvf kibana-7.6.1-linux-x86_64.tar.gz -C /export/server/es/
    cd /export/server/es/kibana-7.6.1-linux-x86_64/
    
  • 配置

    cd /export/server/es/kibana-7.6.1-linux-x86_64/
    vim config/kibana.yml
    
    # 7行:Kibana服务地址
    server.host: "node1"
    # 25行:修改显示名称
    server.name: "itcast-kibana"
    # 28行:修改es地址
    elasticsearch.hosts: ["http://node1:9200"]
    
  • 启动

    cd /export/server/es/kibana-7.6.1-linux-x86_64/
    bin/kibana >>/dev/null 2>&1 &
    
  • 查看

    #查看进程
    ps -ef | grep node
    #页面访问
    node1:5601
    

添加数据源及数据检索

添加数据源

[

指定条件检索

#查询包含zhihu的请求
*zhihu*
#查询页面不存在的请求
status : 404
#查询请求成功和不存在的请求
status: (404 or 200)
#查询方式为POST请求,并请求成功的日志
status: 200 and method: post
#查询方式为GET成功的请求,并且响应数据大于512的日志
status: 200 and method: get and length > 512
#查询请求成功的且URL为「/itcast.cn」开头的日志:注意:因为/为特殊字符,需要使用反斜杠进行转义
uri: "\/itcast.cn\/*"

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HvccFxdI-1625579351328)(Elastic_Stack.assets/image-20210202145857708.png)]

构建可视化报表

附录一:Maven依赖

<repositories>
    <repository>
        <id>aliyunid>
        <url>http://maven.aliyun.com/nexus/content/groups/public/url>
        <releases>
            <enabled>trueenabled>
        releases>
        <snapshots>
            <enabled>falseenabled>
            <updatePolicy>neverupdatePolicy>
        snapshots>
    repository>
repositories>

<dependencies>
    <dependency>
        <groupId>org.elasticsearch.clientgroupId>
        <artifactId>elasticsearch-rest-high-level-clientartifactId>
        <version>7.6.1version>
    dependency>
    <dependency>
        <groupId>org.apache.logging.log4jgroupId>
        <artifactId>log4j-coreartifactId>
        <version>2.11.1version>
    dependency>
    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>fastjsonartifactId>
        <version>1.2.62version>
    dependency>
    <dependency>
        <groupId>junitgroupId>
        <artifactId>junitartifactId>
        <version>4.12version>
        <scope>testscope>
    dependency>
    <dependency>
        <groupId>org.testnggroupId>
        <artifactId>testngartifactId>
        <version>6.14.3version>
        <scope>testscope>
    dependency>
dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.pluginsgroupId>
            <artifactId>maven-compiler-pluginartifactId>
            <version>3.1version>
            <configuration>
                <target>1.8target>
                <source>1.8source>
            configuration>
        plugin>
    plugins>
build>

你可能感兴趣的:(Elastic_Stack)