概述
1.Elastic Stack简介
2.Elasticsearch
2.1 简介 2.2 安装 2.2.1 版本说明 2.2.2 下载 2.2.3 单机版安装 2.2.4 elasticsearch-head 2.3 基本概念 2.4 RESTful API 2.4.1 创建非结构化数据 2.4.2 插入数据 2.4.3 更新数据 2.4.4 删除数据 2.4.5 搜索数据 2.4.6 DSL搜索 2.4.7 高亮显示 2.4.8 聚合
3. 核心详解
3.1 文档 3.2 查询响应 3.2.1 pretty 3.2.2 指定响应字段 3.3 判断文档是否存在 3.4 批量操作 3.4.1 批量查询 3.4.2 _bulk操作 3.5 分页 3.6 映射 3.7 结构化查询 3.7.1 term查询 3.7.2 terms查询 3.7.3 range查询 3.7.4 exists 查询 3.7.5 match查询 3.7.6 bool查询
4. 中文分词
4.1 什么是分词 4.2 分词api 4.3 中文分词
5. 全文搜索
5.1 构造数据 5.2 单词搜索 5.3 多词搜索 5.4 组合搜索 5.5 权重
6. Elasticsearch集群
6.1 集群节点 6.2 搭建集群 6.3 分片和副本 6.4 故障转移 6.4.1 将data节点停止 6.4.2 将master节点停止 6.5 分布式文档 6.5.1 路由 6.5.2 文档写操作 6.5.3 搜索文档(单个文档) 6.5.4 全文搜索
7. Java客户端
7.1 REST客户端 7.2 构造数据 7.3 REST低级客户端 7.3.1 创建工程 7.3.2 编写测试用例 7.4 REST高级客户端 7.4.1 引入依赖 7.4.2 编写测试用例
8. Elasticsearch、Filebeat、Kibana添加安全认证
------------------------------------------------------------------------------------------------------------------------------------
1.Elastic Stack简介
如果你没有听说过Elastic Stack,那你一定听说过ELK,实际上ELK是三款软件的简称,分别是Elasticsearch、Logstash、Kibana组成,在发展的过程中,又有新成员Beats的加入,所以就形成了Elastic Stack。所以说,ELK是旧的称呼,Elastic Stack是新的名字。
全系的Elastic Stack技术栈包括:
Elasticsearch
Elasticsearch 基于java,是个开源分布式搜索引擎,它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等。
Logstash
Logstash 基于java,是一个开源的用于收集,分析和存储日志的工具。
Kibana
Kibana 基于nodejs,也是一个开源和免费的工具,Kibana可以为 Logstash 和 ElasticSearch 提供的日志分析友好的Web 界面,可以汇总、分析和搜索重要数据日志。
Beats
Beats是elastic公司开源的一款采集系统监控数据的代理agent,是在被监控服务器上以客户端形式运行的数据收集器的统称,可以直接把数据发送给Elasticsearch或者通过Logstash发送给Elasticsearch,然后进行后续的数据分析活动。
Logstash和Beats在数据收集功能上有一定的重复,现在更多的是采用Beats来做数据采集,Logstash更多的做一些数据的处理,包括字符串分割、字符串提取,早期没有Beats的时候,使用Logstash做数据采集,现在有了Beats,就使用Beats,因为Beats功能更加强大。
Beats是一个综合的名字,它有一些子项目组成,由如下组成:
Packetbeat:是一个网络数据包分析器,用于监控、收集网络流量信息,Packetbeat嗅探服务器之间的流量,解析应用层协议,并关联到消息的处理,其支 持ICMP (v4 and v6)、DNS、HTTP、Mysql、PostgreSQL、Redis、MongoDB、Memcache等协议;
Filebeat:用于监控、收集服务器日志文件,其已取代 logstash forwarder;
Metricbeat:可定期获取外部系统的监控指标信息,其可以监控、收集 Apache、HAProxy、MongoDB、MySQL、Nginx、PostgreSQL、Redis、System、Zookeeper等服务;
Winlogbeat:用于监控、收集Windows系统的日志信息;
2.Elasticsearch
2.1 简介
ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口操作ES,也可以利用Java API。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。
官网:https://www.elastic.co/cn/elasticsearch/
2.2 安装
2.2.1 版本说明
Elasticsearch的发展是非常快速的,所以在ES5.0之前,ELK的各个版本都不统一,出现了版本号混乱的状态,所以从5.0开始,所有Elastic Stack中的项目全部统一版本号。目前最新版本是7.9.3,我们将基于这一版本进行学习。
2.2.2 下载
地址:https://www.elastic.co/cn/downloads/elasticsearch
2.2.3 单机版安装
将下载好的elasticsearch解压到指定目录:tar -zxvf elasticsearch-7.9.3-linux-x86_64.tar.gz -C /opt/
修改Linux系统的限制配置,将文件创建数修改为65536个,使用root用户修改。
elsearch hard nofile 65536
elsearch soft nproc 4096
elsearch hard nproc 4096
再次启动:bin/elasticsearch -d 说明-d是指后台启动
验证: curl http://localhost:9200 如下,表示单机版启动成功,如果启动还报错,根据报错提示修改一下配置。
2.2.4 elasticsearch-head
由于ES官方并没有为ES提供界面管理工具,仅仅是提供了后台的服务。elasticsearch-head是一个为ES开发的一个页面客户端工具,其源码托管于GitHub,地址为:https://github.com/mobz/elasticsearch-head
head提供了4种安装方式:
源码安装,通过npm run start启动(不推荐)
通过docker安装(推荐)
通过chrome插件安装(推荐)
通过ES的plugin方式安装(不推荐)
这里安装的谷歌插件,通过浏览器进行访问:
2.3 基本概念
2.4 RESTful API
在Elasticsearch中,提供了功能丰富的RESTful API的操作,包括基本的CRUD、创建索引、删除索引等操作。
2.4.1 创建非结构化数据
"settings": {
"index": {
"number_of_shards": "5",
"number_of_replicas": "0"
}
}
}
2.4.2 插入数据
#数据
{
"id": 1001,
"name": "张三",
"age": 20,
"sex": "男"
}
{
"id": 1002,
"name": "张三",
"age": 20,
"sex": "男"
}
2.4.3 更新数据
在Elasticsearch中,文档数据是不为修改的,但是可以通过覆盖的方式进行更新。
PUT /haoke/user/1001
{
"id": 1001,
"name": "张三",
"age": 21,
"sex": "女"
}
可以看到数据已经被覆盖了。
问题来了,可以局部更新吗? -- 可以的。
前面不是说,文档数据不能更新吗? 其实是这样的:在内部,依然会查询到这个文档数据,然后进行覆盖操作,步骤如下:
1. 从旧文档中检索JSON
2. 修改它
3. 删除旧文档
4. 索引新文档
示例:
#注意:这里多了_update标识 POST /haoke/user/1001/_update
{
"doc": {
"age": 23
}
}
2.4.4 删除数据
在Elasticsearch中,删除文档数据,只需要发起DELETE请求即可。
DELETE /haoke/user/1001
说明:删除一个文档也不会立即从磁盘上移除,它只是被标记成已删除。Elasticsearch将会在你之后添加更多索引的时候才会在后台进行删除内容的清理。
2.4.5 搜索数据
根据id搜索数据 GET /haoke/user/yAjcnHUBau-AZQnWpW8f
搜索全部数据 GET /haoke/user/_search (默认返回10条数据)
关键字搜素数据
#查询年龄等于20的用户
2.4.6 DSL搜索
#请求体 match只是查询的一种
{
"query": {
"match": {
"age": 20
}
}
}
现有数据:
#请求数据
{
"query": {
"bool": {
"filter": {
"range": {
"age": {
"gt": 30
}
}
},
"must": {
"match": {
"sex": "男"
}
}
}
}
}
POST /haoke/user/_search
#请求数据
{
"query": {
"match": {
"name": "张三 李四"
}
}
}
2.4.7 高亮显示
POST /haoke/user/_search
{
"query": {
"match": {
"name": "张三 李四"
}
},
"highlight": {
"fields": {
"name": {}
}
}
}
2.4.8 聚合
在Elasticsearch中,支持聚合操作,类似SQL中的group by操作。
POST /haoke/user/_search
{
"aggs": {
"all_interests": {
"terms": {
"field": "age"
}
}
}
}
从结果可以看出,年龄31的有2条数据,20的有2条数据。
3. 核心详解
3.1 文档
在Elasticsearch中,文档以JSON格式进行存储,可以是复杂的结构,如:
{
"_index": "haoke",
"_type": "user",
"_id": "1005",
"_version": 1,
"_score": 1,
"_source": {
"id": 1005,
"name": "孙七",
"age": 27,
"sex": "女",
"card": {
"card_number": "123456789"
}
}
}
提示:事实上,我们的数据被存储和索引在分片(shards)中,索引只是一个把一个或多个分片分组在一起的逻辑空间。然而,这只是一些内部细节——我们的程序完全不用关心分片。对于我们的程序而言,文档存储在索引(index)中。剩下的细节由Elasticsearch关心既可。
_type 在应用中,我们使用对象表示一些“事物”,例如一个用户、一篇博客、一个评论,或者一封邮件。每个对象都属于一个类(class),这个类定义了属性或与对象关联的数据。 user 类的对象可能包含姓名、性别、年龄和Email地址。
在关系型数据库中,我们经常将相同类的对象存储在一个表里,因为它们有着相同的结构。同理,在Elasticsearch中,我们使用相同类型(type)的文档表示相同的“事物”,因为他们的数据结构也是相同的。
每个类型(type)都有自己的映射(mapping)或者结构定义,就像传统数据库表中的列一样。所有类型下的文档被存储在同一个索引下,但是类型的映射(mapping)会告诉Elasticsearch不同的文档如何被索引。_type 的名字可以是大写或小写,不能包含下划线或逗号。我们将使用 blog 做为类型名。
_id id仅仅是一个字符串,它与 _index 和 _type 组合时,就可以在Elasticsearch中唯一标识一个文档。当创建一个文档,你可以自定义 _id ,也可以让Elasticsearch帮你自动生成(32位长度)。
3.2 查询响应
3.2.1 pretty
可以在查询url后面添加pretty参数,使得返回的json更易查看。
3.2.2 指定响应字段
在响应的数据中,如果我们不需要全部的字段,可以指定某些需要的字段进行返回
GET /haoke/user/1001?_source=id,name
如不需要返回元数据,仅仅返回原始数据,可以这样: GET /haoke/user/1001/_source
还可以这样: GET /haoke/user/1001/_source?_source=id,name
3.3 判断文档是否存在
如果我们只需要判断文档是否存在,而不是查询文档内容,那么可以这样:HEAD /haoke/user/1001 有返回200状态码,没有返回404状态码
3.4 批量操作
有些情况下可以通过批量操作以减少网络请求。如:批量查询、批量插入数据。
3.4.1 批量查询
POST /haoke/user/_mget
{
"ids": [
"1001",
"1003"
]
}
3.4.2 _bulk操作
POST /haoke/user/_bulk
在Elasticsearch中,支持批量的插入、修改、删除操作,都是通过_bulk的api完成的。请求格式如下:(请求格式不同寻常)
{ action: { metadata }}n
{ request body }n
{ action: { metadata }}n
{ request body }n
...
批量插入数据:
{"create":{"_index":"haoke","_type":"user","_id":2001}}
{"id":2001,"name":"name1","age": 20,"sex": "男"}
{"create":{"_index":"haoke","_type":"user","_id":2002}}
{"id":2002,"name":"name2","age": 20,"sex": "男"}
{"create":{"_index":"haoke","_type":"user","_id":2003}}
{"id":2003,"name":"name3","age": 20,"sex": "男"}
注意最后一行的回车。
{"delete":{"_index":"haoke","_type":"user","_id":2002}}
{"delete":{"_index":"haoke","_type":"user","_id":2003}}
整个批量请求需要被加载到接受我们请求节点的内存里,所以请求越大,给其它请求可用的内存就越小。有一个最佳的bulk请求大小。超过这个大小,性能不再提升而且可能降低。
最佳大小,当然并不是一个固定的数字。它完全取决于你的硬件、你文档的大小和复杂度以及索引和搜索的负载。
幸运的是,这个最佳点(sweetspot)还是容易找到的:试着批量索引标准的文档,随着大小的增长,当性能开始降低,说明你每个批次的大小太大了。开始的数量可以在1000~5000个文档之间,如果你的文档非常大,可以使用较小的批次。
通常着眼于你请求批次的物理大小是非常有用的。一千个1kB的文档和一千个1MB的文档大不相同。一个好的批次最好保持在5-15MB大小间。
3.5 分页
和SQL使用 LIMIT 关键字返回只有一页的结果一样,Elasticsearch接受 from 和 size 参数:
size: 结果数,默认10
from: 跳过开始的结果数,默认0
如果你想每页显示5个结果,页码从1到3,那请求如下:
GET /_search?size=5
GET /_search?size=5&from=5
GET /_search?size=5&from=10
应该当心分页太深或者一次请求太多的结果。结果在返回前会被排序。但是记住一个搜索请求常常涉及多个分片。每个分片生成自己排好序的结果,它们接着需要集中起来排序以确保整体排序正确。
GET /haoke/user/_search?size=1&from=2
在集群系统中深度分页
为了理解为什么深度分页是有问题的,让我们假设在一个有5个主分片的索引中搜索。当我们请求结果的第一页(结果1到10)时,每个分片产生自己最顶端10个结果然后返回它们给请求节点(requesting node),它再排序这所有的50个结果以选出顶端的10个结果。
现在假设我们请求第1000页——结果10001到10010。工作方式都相同,不同的是每个分片都必须产生顶端的10010个结果。然后请求节点排序这50050个结果并丢弃50040个!
你可以看到在分布式系统中,排序结果的花费随着分页的深入而成倍增长。这也是为什么网络搜索引擎中任何语句不能返回多于1000个结果的原因。
3.6 映射
前面我们创建的索引以及插入数据,都是由Elasticsearch进行自动判断类型,有些时候我们是需要进行明确字段类型的,否则,自动判断的类型和实际需求是不相符的。
自动判断的规则如下:
Elasticsearch中支持的类型如下:
string类型在ElasticSearch 旧版本中使用较多,从ElasticSearch 5.x开始不再支持string,由text和 keyword类型替代。
text类型,当一个字段是要被全文搜索的,比如Email内容、产品描述,应该使用text类型。设置text类型以后,字段内容会被分析,在生成倒排索引以前,字符串会被分析器分成一个一个词项。text类型的字段不用于排序,很少用于聚合。
keyword类型适用于索引结构化的字段,比如email地址、主机名、状态码和标签。如果字段需要进行过滤(比如查找已发布博客中status属性为published的文章)、排序、聚合。
keyword类型的字段只能通过精确值搜索到
官网7.4以上默认不在支持指定索引类型 : https://www.elastic.co/guide/en/elasticsearch/reference/current/removal-of-types.html#_typeless_apis_in_7_0
创建明确类型的索引:PUT /yz_test
{
"settings": {
"index": {
"number_of_shards": "2",
"number_of_replicas": "0"
}
},
"mappings": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "integer"
},
"mail": {
"type": "keyword"
},
"hobby": {
"type": "text"
}
}
}
}
查看映射:GET /yz_test/_mappings
插入数据:POST /yz_test/_bulk
{"index":{"_index":"yz_test"}}
{"name":"张三","age": 20,"mail": "111@qq.com","hobby":"羽毛球、乒乓球、足球"}
{"index":{"_index":"yz_test"}}
{"name":"李四","age": 21,"mail": "222@qq.com","hobby":"羽毛球、乒乓球、足球、篮球"}
{"index":{"_index":"yz_test"}}
{"name":"王五","age": 22,"mail": "333@qq.com","hobby":"羽毛球、篮球、游泳、听音乐"}
{"index":{"_index":"yz_test"}}
{"name":"赵六","age": 23,"mail": "444@qq.com","hobby":"跑步、游泳"}
{"index":{"_index":"yz_test"}}
{"name":"孙七","age": 24,"mail": "555@qq.com","hobby":"听音乐、看电影"}
3.7 结构化查询
3.7.1 term查询
term 主要用于精确匹配哪些值,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型):
{ "term": { "age": 26 }}
{ "term": { "date": "2014-09-01" }}
{ "term": { "public": true }}
{ "term": { "tag": "full_text" }}
示例:
POST /yz_test/_search
{
"query": {
"term": {
"age": 20
}
}
}
3.7.2 terms查询
"terms": {
"tag": [ "search", "full_text", "nosql" ]
}
}
POST /yz_test/_search
{
"query": {
"terms": {
"age": [20,21]
}
}
}
3.7.3 range查询
range 过滤允许我们按照指定范围查找一批数据:
{
"range": {
"age": {
"gte": 20,
"lt": 30
}
}
}
POST /yz_test/_search
{
"query": {
"range": {
"age": {
"gte": 20,
"lte": 22
}
}
}
}
3.7.4 exists 查询
exists 查询可以用于查找文档中是否包含指定字段或没有某个字段,类似于SQL语句中的 IS_NULL 条件
{
"exists": {
"field": "title"
}
}
这两个查询只是针对已经查出一批数据来,但是想区分出某个字段是否存在的时候使用。
示例:
POST /haoke/user/_search
{
"query": {
"exists": {
"field": "name"
}
}
}
3.7.5 match查询
match 查询是一个标准查询,不管你需要全文本查询还是精确查询基本上都要用到它。
如果你使用 match 查询一个全文本字段,它会在真正查询之前用分析器先分析 match 一下查询字符:
{
"match": {
"tweet": "About Search"
}
}
"query": {
"match": {
"age": "30"
}
},
"sort": [
{
"id": {
"order": "desc"
}
}
]
}
3.7.6 bool查询
"query": {
"bool": {
"must": {
"term": {
"folder": "inbox"
}
},
"must_not": {
"term": {
"tag": "spam"
}
},
"should": [
{
"term": {
"starred": true
}
},
{
"term": {
"unread": true
}
}
]
}
}
}
POST /haoke/user/_search
{
"query": {
"bool": {
"filter": {
"term": {
"age": 20
}
}
}
}
}
4. 中文分词
4.1 什么是分词
4.2 分词api
指定分词器进行分词 POST /_analyze
{
"analyzer": "standard",
"text": "hello world"
}
指定索引分词
POST /yz_test/_analyze
{
"analyzer": "standard",
"field": "hobby",
"text": "听音乐"
}
4.3 中文分词
-
optional 1 - download pre-build package from here: https://github.com/medcl/elasticsearch-analysis-ik/releases
create plugin folder
cd your-es-root/plugins/ && mkdir ik
unzip plugin to folder
your-es-root/plugins/ik
-
optional 2 - use elasticsearch-plugin to install ( supported from version v5.5.1 ):
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.3.0/elasticsearch-analysis-ik-6.3.0.zip
{
"analyzer": "ik_max_word",
"text": "我是中国人"
}
5. 全文搜索
5.1 构造数据,指定使用ik分词器
PUT /yz_test
{
"settings": {
"index": {
"number_of_shards": "3",
"number_of_replicas": "0"
}
},
"mappings": {
"properties": {
"name": {
"type": "text"
},
"age": {
"type": "integer"
},
"mail": {
"type": "keyword"
},
"hobby": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
先删除原来的yz_test索引:
批量插入数据:
POST http://192.168.226.30:9200/yz_test/_bulk
{"index":{"_index":"yz_test"}}
{"name":"张三","age": 20,"mail": "111@qq.com","hobby":"羽毛球、乒乓球、足球"}
{"index":{"_index":"yz_test"}}
{"name":"李四","age": 21,"mail": "222@qq.com","hobby":"羽毛球、乒乓球、足球、篮球"}
{"index":{"_index":"yz_test"}}
{"name":"王五","age": 22,"mail": "333@qq.com","hobby":"羽毛球、篮球、游泳、听音乐"}
{"index":{"_index":"yz_test"}}
{"name":"赵六","age": 23,"mail": "444@qq.com","hobby":"跑步、游泳、篮球"}
{"index":{"_index":"yz_test"}}
{"name":"孙七","age": 24,"mail": "555@qq.com","hobby":"听音乐、看电影、羽毛球"}
5.2 单词搜索
POST /ys_test/_search
{
"query": {
"match": {
"hobby": "音乐"
}
},
"highlight": {
"fields": {
"hobby": {}
}
}
}
5.3 多词搜索
POST /ys_test/_search
{
"query": {
"match": {
"hobby": "音乐 篮球"
}
},
"highlight": {
"fields": {
"hobby": {}
}
}
}
{
"query": {
"match": {
"hobby": {
"query": "音乐 篮球",
"operator": "and"
}
}
},
"highlight": {
"fields": {
"hobby": {}
}
}
}
"query": {
"match": {
"hobby": {
"query": "游泳 羽毛球",
"minimum_should_match": "80%"
}
}
},
"highlight": {
"fields": {
"hobby": {}
}
}
}
5.4 组合搜索
在搜索时,也可以使用过滤器中的bool组合查询,示例:
POST /ys_test/_search
{
"query": {
"bool": {
"must": {
"match": {
"hobby": "篮球"
}
},
"must_not": {
"match": {
"hobby": "音乐"
}
},
"should": [
{
"match": {
"hobby": "游泳"
}
}
]
}
},
"highlight": {
"fields": {
"hobby": {}
}
}
}
POST /ys_test/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"hobby": "游泳"
}
},
{
"match": {
"hobby": "篮球"
}
},
{
"match": {
"hobby": "音乐"
}
}
],
"minimum_should_match": 2
}
},
"highlight": {
"fields": {
"hobby": {}
}
}
}
5.5 权重
有些时候,我们可能需要对某些词增加权重来影响该条数据的得分。如下
搜索关键字为“游泳篮球”,如果结果中包含了“音乐”权重为10,包含了“跑步”权重为2。
POST /ys_test/_search
{
"query": {
"bool": {
"must": {
"match": {
"hobby": {
"query": "游泳篮球",
"operator": "and"
}
}
},
"should": [
{
"match": {
"hobby": {
"query": "音乐",
"boost": 10
}
}
},
{
"match": {
"hobby": {
"query": "跑步",
"boost": 2
}
}
}
]
}
},
"highlight": {
"fields": {
"hobby": {}
}
}
}
6. Elasticsearch集群
6.1 集群节点
master节点
配置文件中node.master属性为true(默认为true),就有资格被选为master节点。
master节点用于控制整个集群的操作。比如创建或删除索引,管理其它非master节点等。
data节点
配置文件中node.data属性为true(默认为true),就有资格被设置成data节点。
data节点主要用于执行数据相关的操作。比如文档的CRUD。
客户端节点
配置文件中node.master属性和node.data属性均为false。
该节点不能作为master节点,也不能作为data节点。
可以作为客户端节点,用于响应用户的请求,把请求转发到其他节点
部落节点
当一个节点配置tribe.*的时候,它是一个特殊的客户端,它可以连接多个集群,在所有连接的集群上执行搜索和其他操作。
6.2 搭建集群
VMware 克隆centos7 修改IP:
1. rm -rf /etc/udev/rules.d/70-persistent-ipoib.rules
2. vim /etc/sysconfig/network-scripts/ifcfg-ens32 编辑IP,删除UUID
3. 重启网络 service network restart
4. 查看网络配置,IP已生效 ifconfig
[root@localhost ~]# systemctl status network.service
● network.service - LSB: Bring up/down networking
Loaded: loaded (/etc/rc.d/init.d/network; bad; vendor preset: disabled)
Active: failed (Result: exit-code) since Sat 2020-11-07 16:31:35 CST; 7s ago
Docs: man:systemd-sysv-generator(8)
如果重启网络报上面的错误,则执行:
systemctl stop NetworkManager
systemctl disable NetworkManager
systemctl start network.service
关闭防火墙:systemctl stop firewalld.service
查看防护墙是否关闭:firewall-cmd --state
执行开机禁用防火墙自启命令 : systemctl disable firewalld.service
修改主机名和映射(使用root用户): vim /etc/sysconfig/network 添加:
NETWORKING=yes #使用网络
HOSTNAME=node-01 #设置主机名
修改IP映射:vim /etc/hosts 添加:
192.168.226.30 node-01
192.168.226.31 node-02
192.168.226.32 node-03
永久修改主机名:hostnamectl set-hostname node-1
同步配置:
scp /etc/sysconfig/network root@192.168.226.31:/etc/sysconfig/
scp /etc/sysconfig/network root@192.168.226.32:/etc/sysconfig/
scp /etc/hosts root@192.168.226.31:/etc/
scp /etc/hosts root@192.168.226.32:/etc/
修改30机器的elasticsearch.yml,修改完毕同步到另外两台,另外两台只修改node.name
cluster.name: my-application # 集群名称,三台机器要保持一致
node.name: node-1 # 当前节点名称,主机名
network.host: 0.0.0.0 # 设置所有IP都可以访问
http.port: 9200 # 默认端口9200
discovery.seed_hosts: ["192.168.226.30:9300","192.168.226.31:9300","192.168.226.32:9300"]
cluster.initial_master_nodes: ["node-1"]
# 将修改好的配置复制到其他机器,其他机器修改节点名称
scp elasticsearch.yml elsearch@192.168.226.31:/opt/elasticsearch-7.9.3/config/
scp elasticsearch.yml elsearch@192.168.226.32:/opt/elasticsearch-7.9.3/config/
注意:要清空path.data、path.logs目录下所有文件,如果不指定位置,默认在elasticsearch-7.9.3目录下,因为是cp过去的,配置是第一台机器的。
分别启动三台机器: bin/elasticsearch ,后台启动: bin/elasticsearch -d
通过客户端查看,像下图那样,表示集群部署成功
在集群中创建一个索引试一试:
也可通过请求查看集群状态:http://192.168.226.30:9200/_cluster/health
设置Elasticsearch集群开机自启:
1. 编写开启自启脚本
cd /etc/init.d/
vim elasticsearch
#!/bin/bash
#chkconfig: 2345 80 05
#description: elasticsearch
#jdk相关路径
JAVA_HOME=/opt/jdk-15.0.1
CLASSPATH=$JAVA_HOME/lib/
PATH=$PATH:$JAVA_HOME/bin
export PATH JAVA_HOME CLASSPATH
#下面的"<<!"是切换用户后,待执行的命令,执行完后使用"!"来进行结束
su elsearch <<!
#es的安装路径
cd /opt/elasticsearch-7.9.3/
./bin/elasticsearch -d
!
echo "elasticsearch start"
2. scp /etc/init.d/elasticsearch root@192.168.226.31:/etc/init.d/
scp /etc/init.d/elasticsearch root@192.168.226.32:/etc/init.d/
3. 修改文件权限 chmod +x /etc/init.d/elasticsearch
4. 添加开机自启动 chkconfig --add /etc/init.d/elasticsearch
5. reboot重启
6.3 分片和副本
为了将数据添加到Elasticsearch,我们需要索引(index)——一个存储关联数据的地方。实际上,索引只是一个用来指向一个或多个分片(shards)的“逻辑命名空间(logical namespace)”。真正数据存储是在分片中做存储,索引只是去做关联。
一个分片(shard)是一个最小级别“工作单元(worker unit)”,它只是保存了索引中所有数据的一部分。
我们需要知道是分片就是一个Lucene实例,并且它本身就是一个完整的搜索引擎。应用程序不会和它直接通信,而是和索引通信。
分片可以是主分片(primary shard)或者是复制分片(replica shard)。
索引中的每个文档属于一个单独的主分片,所以主分片的数量决定了索引最多能存储多少数据。
复制分片只是主分片的一个副本,副本有两个作用:1. 它可以防止硬件故障导致的数据丢失,2. 同时可以提供读请求,比如搜索或者从别的shard取回文档。
当索引创建完成的时候,主分片的数量就固定了,但是复制分片的数量可以随时调整。
6.4 故障转移
6.4.1 将data节点停止
测试,将其他一台data机器停掉,这里停掉node-03,出现下图状况,黄色表示不是所有的副本分片都可用
稍等一下,发现节点列表中看不到node-3,副本节点分配到了node-1和node-2,集群恢复正常状态:
重新启动node-3,可以看到,node-3重新加入了集群,并且重新分配了节点信息。
6.4.2 将master节点停止
从上图可以看到当前主节点是node-2,现在将node-2停掉
从结果中可以看出,集群对master进行了重新选举,选择node-3为master。并且集群状态变成黄色。
等待一段时间后,集群状态从黄色变为了绿色:
重新启动node-2节点 bin/elasticsearch -d , 发现node-2重新加入到集群,集群状态依然为绿色:
6.5 分布式文档
6.5.1 路由
首先,来看个问题,假如三台集群,那么数据应该存储到哪一台机器?当取该数据的时候又该从哪台机器取?
如图所示:当我们想一个集群保存文档时,文档该存储到哪个节点呢? 是随机吗? 是轮询吗? 其实都不是。
实际上,在ELasticsearch中,会采用计算的方式来确定存储到哪个节点,计算公式如下:
shard = hash(routing) % number_of_primary_shards number_of_primary_shards:指主分片数量
routing值是一个任意字符串,它默认是_id但也可以自定义。
这个routing字符串通过哈希函数生成一个数字,然后除以主分片的数量得到一个余数(remainder),余数的范围永远是0到number_of_primary_shards - 1,这个数字就是特定文档所在的分片。
这就是为什么创建了主分片后,不能修改的原因。
6.5.2 文档写操作
新建、索引和删除请求都是写(write)操作,它们必须在主分片上成功完成才能复制到相关的副本分片上。
下面我们罗列在主分片和复制分片上成功新建、索引或删除一个文档必要的顺序步骤:
1. 客户端给 Node 1 发送新建、索引或删除请求。
2. 节点使用文档的 _id 确定文档属于分片 0 。它转发请求到 Node 3 ,分片 0 位于这个节点上。
3. Node 3 在主分片上执行请求,如果成功,它转发请求到相应的位于 Node 1 和 Node 2 的复制节点上。当所有的复制节点报告成功, Node 3 报告成功到请求的节点,请求的节点再报告给客户端。
4. 客户端接收到成功响应的时候,文档的修改已经被应用于主分片和所有的复制分片。你的修改生效了。
6.5.3 搜索文档(单个文档)
文档能够从主分片或任意一个副本分片被检索。
下面我们罗列在主分片或副本分片上检索一个文档必要的顺序步骤:
1. 客户端给 Node 1 发送get请求。
2. 节点使用文档的 _id 确定文档属于分片 0 。分片 0 对应的副本分片在三个节点上都有。此时,它转发请求到Node 2 。
3. Node 2 返回文档(document)给 Node 1 然后返回给客户端。
对于读请求,为了平衡负载,请求节点会为每个请求选择不同的分片——它会循环所有分片副本。
可能的情况是,一个被索引的文档已经存在于主分片上却还没来得及同步到副本分片上。这时副本分片会报告文档未找到,主分片会成功返回文档。
一旦索引请求成功返回给用户,文档则在主分片和副本分片都是可用的。
6.5.4 全文搜索
对于全文搜索而言,文档可能分散在各个节点上,那么在分布式的情况下,如何搜索文档呢?
全文搜索分为2个阶段,搜索(query)+ 取回(fetch)
搜索(query)
查询阶段包含以下三步:
1. 客户端发送一个 search(搜索) 请求给 Node 3 , Node 3 创建了一个长度为 from+size 的空优先级队
2. Node 3 转发这个搜索请求到索引中每个分片的原本或副本。每个分片在本地执行这个查询并且结果将结果到一个大小为 from+size 的有序本地优先队列里去。
3. 每个分片返回document的ID和它优先队列里的所有document的排序值给协调节点 Node 3 。 Node 3 把这些值合并到自己的优先队列里产生全局排序结果。
取回(fetch)
分发阶段由以下步骤构成:
1. 协调节点辨别出哪个document需要取回,并且向相关分片发出 GET 请求。
2. 每个分片加载document并且根据需要丰富(enrich)它们,然后再将document返回协调节点。
3. 一旦所有的document都被取回,协调节点会将结果返回给客户端。
7. Java客户端
在Elasticsearch中,为java提供了2种客户端,一种是REST风格的客户端,另一种是Java API的客户端。
https://www.elastic.co/guide/en/elasticsearch/client/index.html
7.1 REST客户端
Elasticsearch提供了2种REST客户端,一种是低级客户端,一种是高级客户端。
Java Low Level REST Client:官方提供的低级客户端。该客户端通过http来连接Elasticsearch集群。用户在使用该客户端时需要将请求数据手动拼接成Elasticsearch所需JSON格式进行发送,收到响应时同样也需要将返回的JSON数据手动封装成对象。虽然麻烦,不过该客户端兼容所有的Elasticsearch版本。
Java High Level REST Client:官方提供的高级客户端。该客户端基于低级客户端实现,它提供了很多便捷的API来解决低级客户端需要手动转换数据格式的问题。
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-overview.html
7.2 构造数据
先创建haoke索引,主分片:5 ,副本:2
http://192.168.226.30:9200/haoke/house/_bulk
{"index":{"_index":"haoke","_type":"house"}}
{"id":"1001","title":"整租 · 南丹大楼 1居室 7500","price":"7500"}
{"index":{"_index":"haoke","_type":"house"}}
{"id":"1002","title":"陆家嘴板块,精装设计一室一厅,可拎包入住诚意租。","price":"8500"}
{"index":{"_index":"haoke","_type":"house"}}
{"id":"1003","title":"整租 · 健安坊 1居室 4050","price":"7500"}
{"index":{"_index":"haoke","_type":"house"}}
{"id":"1004","title":"整租 · 中凯城市之光+视野开阔+景色秀丽+拎包入住","price":"6500"}
{"index":{"_index":"haoke","_type":"house"}}
{"id":"1005","title":"整租 · 南京西路品质小区 21213三轨交汇 配套齐* 拎包入住","price":"6000"}
{"index":{"_index":"haoke","_type":"house"}}
{"id":"1006","title":"祥康里 简约风格 *南户型 拎包入住 看房随时","price":"7000"}
7.3 REST低级客户端
7.3.1 引入依赖
https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-low-usage-maven.html
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.10.0</version>
</dependency>
7.3.2 编写测试用例 写在test里
package com.example.demo;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpHost;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.client.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Description:
* Author: yz
* Date: Created in 2020/11/19 13:00
*/
public class TestESLowRest {
private static final ObjectMapper MAPPER = new ObjectMapper();
private RestClient restClient;
@Before
public void init() {
RestClientBuilder restClientBuilder = RestClient.builder(
new HttpHost("192.168.226.30", 9200, "http"),
new HttpHost("192.168.226.31", 9200, "http"),
new HttpHost("192.168.226.32", 9200, "http"));
restClientBuilder.setFailureListener(new RestClient.FailureListener() {
@Override
public void onFailure(Node node) {
System.out.println("出错了 -> " + node);
}
});
this.restClient = restClientBuilder.build();
}
@After
public void after() throws IOException {
restClient.close();
}
// 查询集群状态
@Test
public void testGetInfo() throws IOException {
Request request = new Request("GET", "/_cluster/state");
request.addParameter("pretty", "true");
Response response = this.restClient.performRequest(request);
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
}
// 新增数据
@Test
public void testCreateData() throws IOException {
Request request = new Request("POST", "/haoke/house");
Map<String, Object> data = new HashMap<>();
data.put("id", "2001");
data.put("title", "张江高科");
data.put("price", "3500");
request.setJsonEntity(MAPPER.writeValueAsString(data));
Response response = this.restClient.performRequest(request);
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
}
// 根据id查询数据
@Test
public void testQueryData() throws IOException {
Request request = new Request("GET", "/haoke/house/G0pfE2gBCKv8opxuRz1y");
Response response = this.restClient.performRequest(request);
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
}
// 搜索数据
@Test
public void testSearchData() throws IOException {
Request request = new Request("POST", "/haoke/house/_search");
String searchJson = "{"query": {"match": {"title": "拎包入住"}}}";
request.setJsonEntity(searchJson);
request.addParameter("pretty", "true");
Response response = this.restClient.performRequest(request);
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
}
}
根据id查询数据
从使用中,可以看出,基本和我们使用postman请求几乎是一致的。低级客户端和高级客户端的区别是,高级客户端提供对数据的处理,获取想要的数据,而不是全返回。
7.4 REST高级客户端
7.4.1 引入依赖
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.10.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.10.0</version>
</dependency>
7.4.2 编写测试用例
package com.example.demo;
import org.apache.http.HttpHost;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Description:
* Author: yz
* Date: Created in 2020/11/19 13:40
*/
public class TestESHighRest {
private RestHighLevelClient client;
@Before
public void init() {
RestClientBuilder restClientBuilder = RestClient.builder(
new HttpHost("192.168.226.30", 9200, "http"),
new HttpHost("192.168.226.31", 9200, "http"),
new HttpHost("192.168.226.32", 9200, "http"));
this.client = new RestHighLevelClient(restClientBuilder);
}
@After
public void after() throws Exception {
this.client.close();
}
/**
* 新增文档,同步操作
*
* @throws Exception
*/
@Test
public void testCreate() throws Exception {
Map<String, Object> data = new HashMap<>();
data.put("id", "2002");
data.put("title", "南京西路 拎包入住 一室一厅");
data.put("price", "4500");
IndexRequest indexRequest = new IndexRequest("haoke", "house").source(data);
IndexResponse indexResponse = this.client.index(indexRequest, RequestOptions.DEFAULT);
System.out.println("id->" + indexResponse.getId());
System.out.println("index->" + indexResponse.getIndex());
System.out.println("type->" + indexResponse.getType());
System.out.println("version->" + indexResponse.getVersion());
System.out.println("result->" + indexResponse.getResult());
System.out.println("shardInfo->" + indexResponse.getShardInfo());
}
/**
* 新增文档,异步操作
*
* @throws Exception
*/
@Test
public void testCreateAsync() throws Exception {
Map<String, Object> data = new HashMap<>();
data.put("id", "2003");
data.put("title", "南京东路 最新房源 二室一厅");
data.put("price", "5500");
IndexRequest indexRequest = new IndexRequest("haoke", "house").source(data);
this.client.indexAsync(indexRequest, RequestOptions.DEFAULT, new ActionListener<IndexResponse>() {
@Override
public void onResponse(IndexResponse indexResponse) {
System.out.println("id->" + indexResponse.getId());
System.out.println("index->" + indexResponse.getIndex());
System.out.println("type->" + indexResponse.getType());
System.out.println("version->" + indexResponse.getVersion());
System.out.println("result->" + indexResponse.getResult());
System.out.println("shardInfo->" + indexResponse.getShardInfo());
}
@Override
public void onFailure(Exception e) {
System.out.println(e);
}
});
System.out.println("ok");
Thread.sleep(20000);
}
/**
* 指定返回的字段
*
* @throws Exception
*/
@Test
public void testQuery() throws Exception {
GetRequest getRequest = new GetRequest("haoke", "house", "GkpdE2gBCKv8opxuOj12");
String[] includes = new String[]{"title", "id"};
String[] excludes = Strings.EMPTY_ARRAY;
FetchSourceContext fetchSourceContext = new FetchSourceContext(true, includes, excludes);
getRequest.fetchSourceContext(fetchSourceContext);
GetResponse response = this.client.get(getRequest, RequestOptions.DEFAULT);
System.out.println("数据 -> " + response.getSource());
}
/**
* 判断是否存在
*
* @throws Exception
*/
@Test
public void testExists() throws Exception {
GetRequest getRequest = new GetRequest("haoke", "house", "GkpdE2gBCKv8opxuOj12"); // 不返回的字段
getRequest.fetchSourceContext(new FetchSourceContext(false));
boolean exists = this.client.exists(getRequest, RequestOptions.DEFAULT);
System.out.println("exists -> " + exists);
}
/**
* 删除数据
*
* @throws Exception
*/
@Test
public void testDelete() throws Exception {
DeleteRequest deleteRequest = new DeleteRequest("haoke", "house", "GkpdE2gBCKv8opxuOj12");
DeleteResponse response = this.client.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println(response.status());// OK or NOT_FOUND }
}
/*** 更新数据 ** @throws Exception */
@Test
public void testUpdate() throws Exception {
UpdateRequest updateRequest = new UpdateRequest("haoke", "house", "G0pfE2gBCKv8opxuRz1y");
Map<String, Object> data = new HashMap<>();
data.put("title", "张江高科2");
data.put("price", "5000");
updateRequest.doc(data);
UpdateResponse response = this.client.update(updateRequest, RequestOptions.DEFAULT);
System.out.println("version -> " + response.getVersion());
}
/**
* 测试搜索
*
* @throws Exception
*/
@Test
public void testSearch() throws Exception {
SearchRequest searchRequest = new SearchRequest("haoke");
searchRequest.types("house");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchQuery("title", "拎包入住"));
sourceBuilder.from(0);
sourceBuilder.size(5);
sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
searchRequest.source(sourceBuilder);
SearchResponse search = this.client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println("搜索到 " + search.getHits().getTotalHits() + " 条数据.");
SearchHits hits = search.getHits();
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsString());
}
}
}
创建数据:
返回指定的字段:
测试条件搜索:
8. Elasticsearch、Filebeat、Kibana添加安全认证
问题描述:经测试发现,电销管理系统存在Elasticsearch未授权访问漏洞(高危):接口:http://192.168.226.30:9200/ 可直接遍历Elasticsearch敏感接口,去查询其所属的数据库以及相关内容
解决:X-pack安全配置 生成节点证书(从 6.8.0 和 7.1.0 版本开始,x-pack安全功能免费提供)
- 生成证书颁发机构:执行bin/elasticsearch-certutil ca命令,会提示输入文件目录和密码,我这里没有输入直接回车了,然后生成elastic-stack-ca.p12文件。
- 生成证书和私钥:执行bin/elasticsearch-certutil cert --ca elastic-stack-ca.p12(elastic-stack-ca.p12 是前面生成的证书),会提示输入证书颁发机构密码,证书生成目录和证书密码,我这里都没有设置。生成文件elastic-certificates.p12。
加密集群中的节点之间的通信和账户验证 - 在/opt/elasticsearch-7.10.0/config目录下面创建xpack目录,并将前面生成的elastic-certificates.p12证书移进去。并将证书复制给其他节点。
- 配置elasticsearch.yml配置文件(其他节点配置一样),添加:
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.keystore.path: xpack/elastic-certificates.p12
xpack.security.transport.ssl.truststore.path: xpack/elastic-certificates.p12
- 如果生成生成证书和私钥的时候设置了密码,则还需要进行下面操作(每个节点都要)添加密码:
bin/elasticsearch-keystore add xpack.security.transport.ssl.keystore.secure_password
bin/elasticsearch-keystore add xpack.security.transport.ssl.truststore.secure_password
- 运行elasticsearch
- 设置密码(账号默认为elastic)
在elasticsearch-7.10.0/bin/目录下运行:
./elasticsearch-setup-passwords interactive
它会不止是设置elasticsearch,其他的kibana、logstash也会一起设置了,密码最好全设置同一个,也可以用下面命令自动生成密码:
./elasticsearch-setup-passwords auto
- 修改密码方式
curl -H "Content-Type:application/json" -XPOST -u elastic 'http://172.31.10.148:9201/_xpack/security/user/elastic/_password' -d '{ "password" : "123456" }'
- 添加xdt账户并添加至admin角色操作方法
curl -XPOST -H 'Content-type: application/json' -u elastic:elastic 'http://10.59.30.96:9200/_xpack/security/user/xdt?pretty' -d '{
"password" : "123456",
"full_name" : "xdt",
"roles" : ["admin"],
"email" : "xdt@bakclass.com"
}'
加密HTTP客户端通信(选配)
- 配置elasticsearch.yml配置文件(其他节点配置一样),添加:
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.keystore.path: certs/elastic-certificates.p12
xpack.security.http.ssl.truststore.path: certs/elastic-certificates.p12
elasticsearch.yml
filebeat.yml
kibana.yml
参考:https://blog.csdn.net/xiexiong0702/article/details/109855842
elasticsearch7.x x-pack破解:
x-pack是收费项目,默认一个月有效期。官网https://license.elastic.co/registration申请的license一年有效期,我这里改成了50年有效期,随意更改。
x-pack的lisence校验在elasticsearch-7.10.0/modules/x-pack-core的x-pack-core-7.10.0.jar中,破解之前需要用反编译工具(如:JD-GUI,luyten)把jar打开源码出来,修改两个class:
org.elasticsearch.license.LicenseVerifier.class 验证licence是否有效
org.elasticsearch.xpack.core.XPackBuild.class 验证jar包是否被修改
修改后的效果,LicenseVerifier.java 把校验全删掉:
XPackBuild.java 静态代码块中 try的部分全部删除
把java文件编译成class,通过WinRAR打开x-pack-core-7.10.0.jar,将修改好的class放入到对应的目录中:
将修改过的x-pack-core-7.10.0.jar 放入到服务器对应的目录中
禁用elasticsearch安全协议
导出许可证书之前要先关闭xpack安全认证,打开../config/elasticsearch.yml文件在末尾添加:xpack.security.enabled: false
并启动elasticsearch服务:./bin/elasticsearch -d
下载证书:https://license.elastic.co/registration 修改下载的证书有效期,黑体部分:
{"license":{"uid":"de954ef5-cfb0-4b6b-b4aa-7d4104da19bf","type":"platinum","issue_date_in_millis":1607644800000,"expiry_date_in_millis":2524579200999,"max_nodes":100,"issued_to":"yang zeng (google)","issuer":"Web Form","signature":"AAAAAwAAAA0D7IvcscVpQh8kG8GsAAABmC9ZN0hjZDBGYnVyRXpCOW5Bb3FjZDAxOWpSbTVoMVZwUzRxVk1PSmkxaktJRVl5MUYvUWh3bHZVUTllbXNPbzBUemtnbWpBbmlWRmRZb25KNFlBR2x0TXc2K2p1Y1VtMG1UQU9TRGZVSGRwaEJGUjE3bXd3LzRqZ05iLzRteWFNekdxRGpIYlFwYkJiNUs0U1hTVlJKNVlXekMrSlVUdFIvV0FNeWdOYnlESDc3MWhlY3hSQmdKSjJ2ZTcvYlBFOHhPQlV3ZHdDQ0tHcG5uOElCaDJ4K1hob29xSG85N0kvTWV3THhlQk9NL01VMFRjNDZpZEVXeUtUMXIyMlIveFpJUkk2WUdveEZaME9XWitGUi9WNTZVQW1FMG1DenhZU0ZmeXlZakVEMjZFT2NvOWxpZGlqVmlHNC8rWVVUYzMwRGVySHpIdURzKzFiRDl4TmM1TUp2VTBOUlJZUlAyV0ZVL2kvVk10L0NsbXNFYVZwT3NSU082dFNNa2prQ0ZsclZ4NTltbU1CVE5lR09Bck93V2J1Y3c9PQAAAQA1Q6ycx7Of0lc1ShV7S6sX+bbNyMVHx8HUpwUKPoGNveBsn8cyFZnau0bBEcb97pLAl5XDYWYBFsytPP8vzSW3fPQhcgBUUefUNC9a5ow2veq1H3h/L5AJv2P1NPAFvOzmSC1Wxh88xKm4EdMtUgzrXc6isV8bOoFENBN1bZHHMxsi4dKyQjKfgXmkF8fT3IQaUj532h5RMUb3MfmSX/14DM7W5R1I8I0Vji9nEUEqld69QP7zsG6pNuNGbNj7iBYy+Nsnaw0DijQOfPjEJZ8baONTtzn/vN00vkakG3ANmtCP7svfOKvpAVMAd0QncpCzFgHhfuoox/F8QLiUmCdP","start_date_in_millis":1607644800000}}
许可证书分有三类GOLD(黄金),PLATINUM(白金),ENTERPRISE(企业),我上面把type手动改成了白金版,然后再把过期时间改到了2050年
新建一个license.json,把上边修改过的许可json串复制进去
激活 上传激活文件:
我已经上传过,所以会显示到期时间:
开启elasticsearch安全协议
导出许可证书之后要开启xpack安全认证,打开../config/elasticsearch.yml文件在末尾添加:xpack.security.enabled: true
并启动elasticsearch服务:./bin/elasticsearch -d
xpack有效期查看:
最后
以上就是开心未来为你收集整理的Elastic Stack之Elasticsearch教程的全部内容,希望文章能够帮你解决Elastic Stack之Elasticsearch教程所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复