zoukankan      html  css  js  c++  java
  • 关于.Net Core使用Elasticsearch(俗称ES)、Kibana的研究说明

    关于ElasticSearch

    Elasticsearch是一个分布式的开源搜索和分析引擎,适用于所有类型的数据,包括文本、数字、地理空间、结构化和非结构化数据。Elasticsearch 在 Apache Lucene 的基础上开发而成,由 Elasticsearch N.V.(即现在的 Elastic)于 2010 年首次发布。Elasticsearch 以其简单的 REST 风格 API、分布式特性、速度和可扩展性而闻名,是 Elastic Stack 的核心组件;Elastic Stack 是适用于数据采集、充实、存储、分析和可视化的一组开源工具。人们通常将 Elastic Stack 称为 ELK Stack(代指 ElasticsearchLogstashKibana),目前 Elastic Stack 包括一系列丰富的轻量型数据采集代理,这些代理统称为 Beats,可用来向 Elasticsearch 发送数据。

    ElasticSearch用途

    • 应用程序搜索
    • 网站搜索
    • 企业搜索
    • 日志处理和分析
    • 基础设施指标和容器监测
    • 应用程序性能监测
    • 地理空间数据分析和可视化
    • 安全分析
    • 业务分析

    工作原理

    原始数据会从多个来源(包括日志、系统指标和网络应用程序)输入到 Elasticsearch 中。数据采集指在 Elasticsearch 中进行索引之前解析、标准化并充实这些原始数据的过程。这些数据在 Elasticsearch 中索引完成之后,用户便可针对他们的数据运行复杂的查询,并使用聚合来检索自身数据的复杂汇总。在 Kibana 中,用户可以基于自己的数据创建强大的可视化,分享仪表板,并对 Elastic Stack 进行管理。

    索引是什么

    Elasticsearch 索引指相互关联的文档集合。Elasticsearch 会以 JSON 文档的形式存储数据。每个文档都会在一组键(字段或属性的名称)和它们对应的值(字符串、数字、布尔值、日期、数值组、地理位置或其他类型的数据)之间建立联系。

    Elasticsearch 使用的是一种名为倒排索引的数据结构,这一结构的设计可以允许十分快速地进行全文本搜索。倒排索引会列出在所有文档中出现的每个特有词汇,并且可以找到包含每个词汇的全部文档。

    在索引过程中,Elasticsearch 会存储文档并构建倒排索引,这样用户便可以近实时地对文档数据进行搜索。索引过程是在索引 API 中启动的,通过此 API 您既可向特定索引中添加 JSON 文档,也可更改特定索引中的 JSON 文档。

    为何使用Elasticsearch

    Elasticsearch很快。 由于Elasticsearch 是在 Lucene 基础上构建而成的,所以在全文本搜索方面表现十分出色。Elasticsearch 同时还是一个近实时的搜索平台,这意味着从文档索引操作到文档变为可搜索状态之间的延时很短,一般只有一秒。因此,Elasticsearch 非常适用于对时间有严苛要求的用例,例如安全分析和基础设施监测。

    Elasticsearch具有分布式的本质特征。 Elasticsearch 中存储的文档分布在不同的容器中,这些容器称为分片,可以进行复制以提供数据冗余副本,以防发生硬件故障。Elasticsearch 的分布式特性使得它可以扩展至数百台(甚至数千台)服务器,并处理 PB 量级的数据。

    Elasticsearch 包含一系列广泛的功能。 除了速度、可扩展性和弹性等优势以外,Elasticsearch 还有大量强大的内置功能(例如数据汇总和索引生命周期管理),可以方便用户更加高效地存储和搜索数据。

    Elastic Stack 简化了数据采集、可视化和报告过程。 通过与 Beats 和 Logstash 进行集成,用户能够在向 Elasticsearch 中索引数据之前轻松地处理数据。同时,Kibana 不仅可针对 Elasticsearch 数据提供实时可视化,同时还提供 UI 以便用户快速访问应用程序性能监测 (APM)、日志和基础设施指标等数据。

    Elasticsearch免费吗

    用户可以基于 Apache 2 许可证免费使用 Elasticsearch 的开源功能。用户还可基于 Elastic 许可证使用更多免费功能,同时通过付费订阅服务,可以获取支持并使用诸如 Alerting 和 Machine Learning 等高级功能。

    Elasticsearch案例

    • 2013年初,GitHub抛弃了Solr,采取ElasticSearch 来做PB级的搜索。 “GitHub使用ElasticSearch搜索20TB的数据,包括13亿文件和1300亿行代码”
    • 维基百科:启动以elasticsearch为基础的核心搜索架构
    • SoundCloud:“SoundCloud使用ElasticSearch为1.8亿用户提供即时而精准的音乐搜索服务”
    • 百度:百度目前广泛使用ElasticSearch作为文本数据分析,采集百度所有服务器上的各类指标数据及用户自定义数据,通过对各种数据进行多维分析展示,辅助定位分析实例异常或业务层面异常。目前覆盖百度内部20多个业务线(包括casio、云分析、网盟、预测、文库、直达号、钱包、风控等),单集群最大100台机器,200个ES节点,每天导入30TB+数据
    • 新浪使用ES 分析处理32亿条实时日志
    • 阿里使用ES 构建挖财自己的日志采集和分析体系

    Elasticsearch正式分发包

    基于WSL2的Docker Desktop配置

    如果你打算将ES用于生产,你需要把Docker设置下vm.max_map_count参数

    在PS里面运行如下命令即可

    wsl -d docker-desktop
    sysctl -w vm.max_map_count=262144
    

    Elasticsearch安装By Docker On WSL2

    1. 基本安装

    docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:7.9.1
    

    2. 定制安装

    以下操作均在Ubuntu的Root用户下执行,如果切换Root,请su root

    1. 先以非挂载模式运行实例,后续再操作挂载,不然会有权限问题
    docker run --publish 9200:9200 --publish 9300:9300 -e "discovery.type=single-node" --name elasticsearch --restart always 
    elasticsearch:7.9.1
    

    这样得到一个临时的运行实例,访问http://localhost:9200查看是否成功,如果成功,继续后续操作。

    1. 创建挂载持久化数据的目录

    切换到你希望持久化的根目录,在这个目录底下创建关于elasticsearch两个关键目录dataconfig

    mkdir elasticsearch
    cd elasticsearch/
    mkdir data
    mkdir config
    

    以笔者为例,创建成功之后,最终会得到以下两个目录:

    • /home/username/elasticsearch/data/
    • /home/username/elasticsearch/config/
    1. 拷贝实例的当前Data和Config到我们创建的目录。
    docker cp elasticsearch:/usr/share/elasticsearch/data /home/username/elasticsearch/
    docker cp elasticsearch:/usr/share/elasticsearch/config /home/username/elasticsearch/
    

    它会自动把运行实例下dataconfig所有文件拷贝到我们创建的那两个对应文件夹中。

    1. 停止并删除之前的实例,重新创建带持久化挂载的实例。

    先删除

    docker stop elasticsearch
    docker rm elasticsearch
    

    再创建

    docker run --publish 9200:9200 --publish 9300:9300 -e "discovery.type=single-node" --name elasticsearch --restart always 
    --volume /home/username/elasticsearch/data/:/usr/share/elasticsearch/data 
    --volume /home/username/elasticsearch/config/:/usr/share/elasticsearch/config 
    elasticsearch:7.9.1
    
    • 绑定/usr/share/elasticsearch/data目录可以防止重启实例时数据丢失。
    • 绑定/usr/share/elasticsearch/config/目录可以防止重启实例时配置丢失。
    • 设置--restart always可以确保重启Docker时自动启动es。
    • 镜像直接使用elasticsearch:7.9.1将从Docker Hub搜索镜像并拉取。
    • 请务必将其中的username替换成你本地的用户名。
    • https://hub.docker.com/_/elasticsearch

    恭喜你,这时候就完美运行起来了。

    访问ES管理后台

    {
      "name" : "dc7593c4d74d",
      "cluster_name" : "docker-cluster",
      "cluster_uuid" : "YqAcQFMnRamIdLHBIRNVjg",
      "version" : {
        "number" : "7.9.1",
        "build_flavor" : "default",
        "build_type" : "docker",
        "build_hash" : "083627f112ba94dffc1232e8b42b73492789ef91",
        "build_date" : "2020-09-01T21:22:21.964974Z",
        "build_snapshot" : false,
        "lucene_version" : "8.6.2",
        "minimum_wire_compatibility_version" : "6.8.0",
        "minimum_index_compatibility_version" : "6.0.0-beta1"
      },
      "tagline" : "You Know, for Search"
    }
    

    看到类似返回,就说明运行成功了。

    Kibana安装By Docker On WSL2

    docker run --publish 5601:5601 --name kibana --restart always 
    --link elasticsearch:elasticsearch 
    kibana:7.9.1
    

    安装运行成功之后,可打开网址:http://localhost:5601

    ElasticSearch常见操作

    检查集群或节点健康情况

    http://localhost:9200/_cat/health?v

    • green:每个索引的primary shard和replica shard都是active状态的
    • yellow:每个索引的primary shard都是active状态的,但是部分replica shard不是active状态,处于不可用的状态
    • red:不是所有索引的primary shard都是active状态的,部分索引有数据丢失了

    查看所有的索引及统计

    http://localhost:9200/_cat/indices?v

    创建和删除索引

    • 创建索引
    PUT http://localhost:9200/yourindexname
    
    {
       "settings" : {
          "number_of_shards" : 3, # 分片数
          "number_of_replicas" : 1 # 副本数
       }
    }
    
    • 删除索引
    DELETE http://localhost:9200/yourindexname
    

    .Net Core的ES开发包

    ES主要是通过RESTFul风格的API来进行操作的,本身是没有提供UI界面,如果.Net Core程序想要操作,除了自己写相关的API调用代码,最方便的方法就是直接安装官方提供的Nuget包。

    这两的差异就是,说白了NEST就是基于Elasticsearch.Net来实现的。
    简单说呢,Elasticsearch.Net是粗狂豪放型,可以非常自由的组织API请求内容,任君发挥了
    NEST是结合.Net高级特性的高级语法糖封装,可以让你类LINQ的方式来编写你的代码。
    总结而言,还是推荐先用高级封装的NEST吧,这样能让问题更简单一些。

    Nuget安装到现有项目中

    搜索关键词安装即可

    NEST
    

    开始编写代码

    1. 连接ElasticSearch
    var singleNode = new Uri("http://localhost:9200");
    var connSettings = new ConnectionSettings(singleNode);
    var esClient = new ElasticClient(connSettings);
    

    如果是链接多Node节点。

    var nodes = new Uri[]
    {
        new Uri("http://esNode1:9200"),
        new Uri("http://esNode2:9200"),
        new Uri("http://esNode3:9200")
    };
    var pool = new StaticConnectionPool(nodes);
    var settings = new ConnectionSettings(pool);
    var client = new ElasticClient(settings);
    
    1. 写入新数据并自动创建索引
    /// <summary>
    /// 用户信息模型
    /// </summary>
    public class UserInfo
    {
        /// <summary>
        /// 名字
        /// </summary>
        public string Name { get; set; }
    }
    
    var userInfo = new UserInfo { Name = "demoname" };
    var nodeUri = new Uri("http://localhost:9200");
    var connSetting = new ConnectionSettings(nodeUri);
    var targetIndexName = "demo_indexname";
    var esClient = new ElasticClient(connSetting.DefaultIndex(targetIndexName));
    
    // 初始化索引
    esClient.IndexDocument(userInfo);
    // 或者 推荐异步语法糖写法
    var response = await esClient.IndexAsync(userInfo, idx => idx.Index(targetIndexName));
    

    以上动作,在最后一句才会发出HTTP API请求,内容如下:

    POST http://localhost:9200/demo_indexname/_doc
    
    {"name":"demoname"}
    

    值得注意,这里面demo_indexname是索引的名字,这里面_doc是索引的默认_type名字

    另外,你会发现,NEST框架已经自动把UserInfo模型的大写转成驼峰小写了,非常方便。

    1. 获取指定ID的文档数据
    // 获取指定文档Id的文档内容
    var targetIndexName = "demo_indexname";
    var response = await esClient.GetAsync<UserInfo>("rnwnnXQBwd6SZB0378vX", idx => idx.Index(targetIndexName)); 
    var userInfo = response.Source; 
    

    以上动作,发出HTTP API请求,内容如下:

    GET http://localhost:9200/demo_indexname/_doc/rnwnnXQBwd6SZB0378vX
    

    如果找不到就会返回404,这时候拿到的对象就是NULL,如果找得到就能正确返回对象数据。

    {
        "_index": "demo_indexname",
        "_type": "_doc",
        "_id": "rnwnnXQBwd6SZB0378vX",
        "_version": 1,
        "_seq_no": 0,
        "_primary_term": 1,
        "found": true,
        "_source": {
            "name": "demoname"
        }
    }
    
    1. 基于LINQ模式查询目标文档
    // 基于LINQ模式查询目标文档
    var response = await esClient.SearchAsync<UserInfo>(s => s
        .Index(targetIndexName) 
        .From(0)
        .Size(10)
        .Query(q => q.Term(t => t.Name, "demoname") || q.Match(m => m.Field(f => f.Name).Query("demoname"))
        )
    );
    var userinfos = response.Documents;
    

    POST http://localhost:9200/demo_indexname/_search?typed_keys=true
    
    {
        "from": 0,
        "query": {
            "bool": {
                "should": [
                    {
                        "term": {
                            "name": {
                                "value": "demoname"
                            }
                        }
                    },
                    {
                        "match": {
                            "name": {
                                "query": "demoname"
                            }
                        }
                    }
                ]
            }
        },
        "size": 10
    }
    
    {
        "took": 0,
        "timed_out": false,
        "_shards": {
            "total": 1,
            "successful": 1,
            "skipped": 0,
            "failed": 0
        },
        "hits": {
            "total": {
                "value": 1,
                "relation": "eq"
            },
            "max_score": 0.5753642,
            "hits": [
                {
                    "_index": "demo_indexname",
                    "_type": "_doc",
                    "_id": "rnwnnXQBwd6SZB0378vX",
                    "_score": 0.5753642,
                    "_source": {
                        "name": "demoname"
                    }
                }
            ]
        }
    }
    

    1. 带指定ID的创建对象
    /// <summary>
    /// 用户信息模型
    /// </summary>
    public class UserInfo
    {
        /// <summary>
        /// 键值
        /// </summary>
        public string Id { get; set; }
    
        /// <summary>
        /// 名字
        /// </summary>
        public string Name { get; set; }
    
        /// <summary>
        /// Person
        /// </summary>
        public PersonInfo Person { get; set; }
    }
    
    var userInfo = new UserInfo { Id = Guid.NewGuid().ToString(), Name = "3333" ,Person = new PersonInfo { Phone= "188****4444" } };
    .....
    // 带指定ID的创建对象
    var response = await esClient.CreateAsync(userInfo, idx => idx.Index(targetIndexName));
    var isSuccess = response.Result == Result.Created;
    

    PUT http://localhost:9200/demo_indexname/_create/1aa9de3f-f610-48d4-93fb-be41fbfd8987
    
    {"id":"1aa9de3f-f610-48d4-93fb-be41fbfd8987","name":"3333","person":{"phone":"188****4444"}}
    

    查询带子结构文档

    // 基于LINQ模式查询带子结构文档
    var response = await esClient.SearchAsync<UserInfo>(s => s
        .Index(targetIndexName)
        .From(0)
        .Size(10)
        .Query(q => q.Term(t => t.Person.Phone, "188xxxxx0637"))
    );
    var userinfos = response.Documents;
    
    POST http://localhost:9200/demo_indexname/_search?typed_keys=true HTTP/1.1
    
    {
        "from": 0,
        "query": {
            "term": {
                "person.phone": {
                    "value": "188xxxxx0637"
                }
            }
        },
        "size": 10
    }
    

    附录

  • 相关阅读:
    java json 库之 jackson
    java 多线程
    golang slice 和 string 重用
    golang 字节对齐
    golang 并发编程之生产者消费者
    golang 设计模式之选项模式
    golang aws-sdk-go 之 s3 服务
    markdown 一个优雅的写作工具
    常见句型、用法
    hg
  • 原文地址:https://www.cnblogs.com/craigtaylor/p/13676564.html
Copyright © 2011-2022 走看看