实战虚拟机安装服务
1.docker安装elasticsearch
docker run -d \
--name elasticsearch \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
-e "discovery.type=single-node" \
-e "xpack.security.enabled=false" \
-v /work_docker/elk/elasticsearch/data:/usr/share/elasticsearch/data \
-v /work_docker/elk/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
--privileged \
-p 9200:9200 \
-p 9300:9300 \
elasticsearch:8.13.0
命令解释:
-e "cluster.name=es-docker-cluster":设置集群名称 -e "http.host=0.0.0.0":监听的地址,可以外网访问 -e "ES_JAVA_OPTS=-Xms512m -Xmx512m":内存大小 -e "ES_JAVA_OPTS="-Xms256m -Xmx256m" :可以设置小内存(太小不行,无法启动,各种功能栈溢出内存不够),但是Xms要小于Xmx -e "discovery.type=single-node":非集群模式 -v es-data:/usr/share/elasticsearch/data:挂载逻辑卷,绑定es的数据目录 -v es-logs:/usr/share/elasticsearch/logs:挂载逻辑卷,绑定es的日志目录 -v es-plugins:/usr/share/elasticsearch/plugins:挂载逻辑卷,绑定es的插件目录 --privileged:授予逻辑卷访问权 --network es-net :加入一个名为es-net
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://192.168.206.128:9200 \
-p 5601:5601 \
kibana:8.13.0
1.在线安装ik插件
docker exec -it es ./bin/elasticsearch-plugin install https://github.com/infinilabs/analysis-ik/releases/download/v8.12.2/elasticsearch-analysis-ik-8.12.2.zip
重启es容器
2.配置扩展词词典
1.打开IK分词器config目录
2.在IKAnalyzer.cfg.xml配置文件内容添加
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict">ext.dic</entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<!-- <entry key="remote_ext_dict">words_location</entry> -->
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
2.docker安装kibana
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://192.168.206.128:9200 \
-p 5601:5601 \
kibana:8.13.0
3.docker 安装 LogStashd
拉取镜像并拷贝配置
docker run -d --name logstash logstash:7.14.1
# 拷贝数据
docker cp logstash:/usr/share/logstash/config ./config
docker cp logstash:/usr/share/logstash/data ./data
docker cp logstash:/usr/share/logstash/pipeline ./pipeline
#文件夹赋权
chmod -R 777 ./config ./data ./pipeline
修改相应配置文件
修改config 下的 logstash.yml 文件,主要修改 es 的地址
http.host: "0.0.0.0"
xpack.monitoring.elasticsearch.hosts: [ "http://elasticsearch:9200" ]
http.host:当设置为 “0.0.0.0” 时,意味着服务将在所有可用网络接口上监听HTTP请求,允许任何IP地址的客户端连接。 xpack.monitoring.elasticsearch.hosts:指向Elasticsearch服务的URL ->http://elasticsearch:9200,但这里没有使用具体的IP地址,而是用了一个名为 elasticsearch 的服务名或容器名。
修改config下的jvm.options
在Elasticsearch
、Logstash
或其他使用Java
虚拟机(JVM
)的应用程序中,jvm.options
文件是用来配置JVM
运行时参数的重要文件。当你需要调整JVM
相关的设置,比如堆内存大小、垃圾回收策略、线程数量等时,就需要修改这个文件。
# 增加JVM堆内存大小
-Xms512m
-Xmx512m
修改pipeline 下的 logstash.conf
input {
tcp {
mode => "server"
host => "0.0.0.0"
port => 5044
codec => json_lines
}
}
output {
elasticsearch {
hosts => ["http://124.221.147.235:9200"]
user => elastic
password => 123456
index => "logs-%{+YYYY.MM}"
codec => "json"
}
stdout {
codec => rubydebug
}
}
input部分:
使用TCP输入插件(tcp)创建一个服务器监听器,等待来自客户端的连接。 mode => "server" 指定为服务器模式,接受来自其他服务或应用的日志数据。 host => "0.0.0.0" 表示在所有网络接口上监听连接请求。 port => 5044 设置了监听端口为5044。 codec => json_lines 指定了编解码器类型,这意味着每个TCP消息应该包含一个或多条JSON格式的数据,每行一条JSON记录。
output部分:
使用Elasticsearch输出插件(elasticsearch)将处理后的日志事件发送到Elasticsearch集群。 hosts => "127.0.0.1:9200" 设置了Elasticsearch集群的地址与端口,这里指本地主机上的默认Elasticsearch实例。 index => "%{[spring.application.name]}-%{+YYYY.MM.dd}" 定义了索引名称模板。该模板会根据事件中的字段动态生成索引名,其中: %{[spring.application.name]} 是从日志事件中提取的Spring Boot应用的名字作为索引前缀。 %{+YYYY.MM.dd} 是基于当前日期时间动态生成的索引后缀,每天都会创建一个新的索引以存储当天的日志数据。 这样配置后,Logstash将作为一个TCP日志收集服务器运行,并且能够接收JSON格式的日志数据,然后将其按照指定的规则写入到Elasticsearch集群中相应的索引里,便于后续进行搜索、分析和可视化展示。
启动容器并挂载
#注意先删除之前的容器
docker rm -f logstash
# 启动容器并挂载
docker run --name logstash \
-p 5044:5044 \
-p 9600:9600 \
--privileged=true \
-e ES_JAVA_OPTS="-Duser.timezone=Asia/Shanghai" \
-v /work_docker/elk/logstash/pipeline/:/usr/share/logstash/pipeline/ \
-v /work_docker/elk/logstash/config/:/usr/share/logstash/config/ \
-v /work_docker/elk/logstash/data/:/usr/share/logstash/data/ \
-d logstash:8.12.0
ELK概念介绍
索引&类型&文档&字段&映射
1.ElasticSearch 接口
ElasticSearch 对外提供的 API 是以 HTTP 协议的方式,通过 JSON 格式以 REST 约定对外提供。
HTTP 配置文件放在 elasticsearch.yml 中。REST 通常是开发的一种约定,当所有开发者都遵循这种约定时,就会简化开发的沟通成本。
REST 约定用 HTTP 的请求头 POST、GET、PUT、DELETE 正好对应 CRUD(Create、Read、Update、Delete)四种数据操作。如果应用程序符合 REST 原则,可称为 RESTful Web Service。
1 集群信息操作命令
1 查询集群状态
http://hlink1:9200/_cat/health?v
返回结果的主要字段意义:
cluster:集群名,是在 ES 的配置文件中配置的 cluster.name 的值。 status:集群状态。集群共有 green、yellow 或 red 中的三种状态。green 代表一切正常(集群功能齐全),yellow 意味着所有的数据都是可用的,但是某些复制没有被分配(集群功能齐全),red 则代表因为某些原因,某些数据不可用。如果是 red 状态,则要引起高度注意,数据很有可能已经丢失。 node.total:集群中的节点数。 node.data:集群中的数据节点数。 shards:集群中总的分片数量。 pri:主分片数量,英文全称为 private。 relo:复制分片总数。 unassign:未指定的分片数量,是应有分片数和现有的分片数的差值(包括主分片和复制分片)。
2 使用 help 参数查询
http://hlink1:9200/_cat/health?help
指定返回的参数
curl -XGET "hlink1:9200/_cat/health?h=cluster,pri,relo&v"
3 查询集群中的节点信息
http://hlink1:9200/_cat/nodes?v
4 查询集群索引信息
http://hlink1:9200/_cat/indices?v
2 index 操作命令
1 创建索引 PUT 请求
http://hlink1:9200/study
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "study"
}
2 查看索引 GET 请求
http://hlink1:9200/study
{
"study": {
"aliases": {},
"mappings": {},
"settings": {
"index": {
"routing": {
"allocation": {
"include": {
"_tier_preference": "data_content"
}
}
},
"number_of_shards": "1",
"provided_name": "study",
"creation_date": "1711964771851",
"number_of_replicas": "1",
"uuid": "rZhAVVYjRHSgSSPGRfLPIA",
"version": {
"created": "8503000"
}
}
}
}
}
3 删除索引 DELETE 请求
http://hlink1:9200/study
{
"acknowledged": true
}
3 document 操作命令
1 创建文档 POST 请求
索引创建完成后,可以创建 document 文档,该文档对应数据库中 表的行数据 添加的数据格式为 JSON 内容
{ "name": "3分钟秒懂大数据", "introduction": "专注于大数据技术研究" }
http://hlink1:9200/study/_doc
{
"_index": "study",
"_id": "0GAamY4BAp9AOplvze8p",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 2,
"_primary_term": 1
}
2 更新文档 POST 请求
上面的数据创建后,没有指定数据唯一性标识(ID),默认情况下,ES 服务器会随机生成一个。更新时可以指定唯一性标识
#请求体内容如下: { "name": "3分钟秒懂大数据", "introduction": "专注于Hadoop、Kafka、Flink等多个组件的技术研究" }
http://hlink1:9200/study/_doc/0GAamY4BAp9AOplvze8p
{
"_index": "study",
"_id": "0GAamY4BAp9AOplvze8p",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 3,
"_primary_term": 1
}
3 查询文档 GET 请求
查看文档时,需要指明文档的唯一性标识
http://hlink1:9200/study/_doc/0GAamY4BAp9AOplvze8p
{
"_index": "study",
"_id": "0GAamY4BAp9AOplvze8p",
"_version": 2,
"_seq_no": 3,
"_primary_term": 1,
"found": true,
"_source": {
"name": "分钟秒懂大数据",
"introduction": "专注于大数据技术研究"
}
}
4 删除文档 DELETE 请求
删除一个文档不会立即从磁盘上移除,它只是被标记成已删除(逻辑删除)
http://hlink1:9200/study/_doc/0GAamY4BAp9AOplvze8p
2.映射mapping
创建数据库表需要设置字段名称,类型,长度,约束等;索引库也一样,需要知道这个类型下有哪些字段,每个字段有哪些约束信息,这就叫做映射(mapping)。
查看映射
语法: GET /{索引名称}/_mapping
动态映射
在关系数据库中,需要事先创建数据库,然后在该数据库下创建数据表,并创建 表字段、类型、长度、主键等,最后才能基于表插入数据。而Elasticsearch中不 需要定义Mapping映射(即关系型数据库的表、字段等),在文档写入 Elasticsearch时,会根据文档字段自动识别类型,这种机制称之为动态映射。
映射规则对应:
数据 | 对应的类型 |
null | 字段不添加 |
true\ | flase |
字符串 | text/keyword |
数值 | long |
小数 | float |
日期 | date |
特殊类型:字符串
text:用于长文本,对本文内容进行分词(产生倒排索引文档列表),支持多关键字全文查询。例如:对电商项目商品名称,或者文章正文设置为text 缺点:不能进行聚合(分组),不支持排序。 keyword:用于词条精确查询,支持等值查询,不需要进行分词字符串(分词后无意义)。例如:用户昵称、用户手机号、身份证号、图片地址。场景:根据品牌名称等值查询。支持聚合(分组)、排序 不支持多关键词查询
静态映射
静态映射是在Elasticsearch中也可以事先定义好映射,即手动映射,包含文档的各字段类型、分词器等,这称为静态映射。
#删除原创建的索引
DELETE /my_index
#创建索引,并同时指定映射关系和分词器等。
GET /study_index/_mapping
PUT /study_index
{
"mappings": {
"properties": {
"title": {
"type": "keyword",
"index": true,
"store": true
},
"category": {
"type": "text",
"index": true,
"store": true,
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
},
"images": {
"type": "text",
"index": false,
"store": true
},
"price": {
"type": "integer",
"index": true,
"store": true
}
}
}
}
结果:
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "study_index"
}
Springboot应用ELK
spring-boot-starter-data-elasticsearch
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
1.ElasticSearchRepository方式
1.了解注解
@Document
: 代表一个文档记录
indexName
: 用来指定索引名称
type
: 用来指定索引类型
@Id
: 用来将对象中id和ES中_id映射
@Field
: 用来指定ES中的字段对应Mapping
type
: 用来指定ES中存储类型
analyzer
: 用来指定使用哪种分词器
2.新建实体类
@Data
@Document(indexName = "study_index")
public class Product {
@Id
private String id;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title;
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String category;
@Field(type = FieldType.Text)
private String images;
@Field(type = FieldType.Long)
private Long price;
}
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
/**
* 描述
*
* @param title String
* @return List<Product>
*/
List<Product> findByTitle(String title);
/**
* 描述
*
* @param title String
* @param price Long
* @return List<Product>
*/
List<Product> findByTitleAndPrice(String title, Long price);
}
3自定义查询
Keyword | Sample | Elasticsearch Query String |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
我们只要按照上面的定义在接口中定义相应的方法,无须写实现就可实现我们想要的功能
Demo
1.保存文档和修改文档
@PostMapping("/save")
public Product findById(Product product) {
return repository.save(product);
}
2.删除文档
@GetMapping("/deleteById")
public String deleteById(@RequestParam("id") String id) {
repository.deleteById(id);
return "ok";
}
3.检索一条记录
@GetMapping("/findById")
public Product findById(@RequestParam("id") String id) {
Optional<Product> result = repository.findById(id);
return result.orElse(null);
}
4.查询所有
@GetMapping("/findAll")
public Page<Product> findAll(@RequestParam("page") int page, @RequestParam("size") int size) {
PageRequest pageRequest = PageRequest.of(page, size);
return repository.findAll(pageRequest);
}
5.排序
@GetMapping("/findAllSort")
public Iterable<Product> findAll(@RequestParam("key") String key) {
Sort sort = Sort.by(Sort.Order.desc(key));
return repository.findAll(sort);
}
2.ElasticsearchClient方式
1.配置类
@Configuration
public class ElasticSearchConfig {
@Bean
public ElasticsearchClient client() {
// ES服务器URL
String serverUrl = "http://192.168.206.128:9200";
// ES用户名和密码
String name = "xxx";
String password = "xxx";
// BasicCredentialsProvider credential = new BasicCredentialsProvider();
// credential.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(name, password));
RestClient restClient = RestClient.builder(HttpHost.create(serverUrl)).build();
ElasticsearchTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
return new ElasticsearchClient(transport);
}
}
2.索引CRUD
1.增加index
@Autowired
ElasticsearchClient client;
@PostMapping("/create")
public String create(@RequestParam("indexName") String indexName) {
CreateIndexResponse indexResponse = null;
try {
indexResponse = client.indices().create(c -> c.index(indexName));
} catch (ElasticsearchException | IOException e) {
log.info(e.getMessage());
return e.getMessage();
}
return String.valueOf(indexResponse != null && indexResponse.acknowledged());
}
2.查询Index
@GetMapping("/query")
public String query(@RequestParam("indexName") String indexName) throws IOException {
GetIndexResponse getIndexResponse = null;
try {
getIndexResponse = client.indices().get(i -> i.index(indexName));
} catch (ElasticsearchException e) {
log.info(e.getMessage());
return e.getMessage();
}
if (getIndexResponse != null) {
Optional<IndexState> first = getIndexResponse.result().values().stream().findFirst();
return first.map(IndexState::toString).orElse(null);
}
return "unknown";
}
3.判断index是否存在
@GetMapping("/exists")
public boolean exists(@RequestParam("indexName") String indexName) throws IOException {
BooleanResponse booleanResponse = client.indices().exists(e -> e.index(indexName));
log.info(String.valueOf(booleanResponse.value()));
return booleanResponse.value();
}
4.删除index
@GetMapping("/delete")
public String delete(@RequestParam("indexName") String indexName) throws IOException {
DeleteIndexResponse deleteIndexResponse = null;
try {
deleteIndexResponse = client.indices().delete(d -> d.index(indexName));
} catch (ElasticsearchException e) {
log.info(e.getMessage());
return e.getMessage();
}
if (deleteIndexResponse != null) {
log.info(String.valueOf(deleteIndexResponse.acknowledged()));
return String.valueOf(deleteIndexResponse.acknowledged());
}
return "unknown";
}
3.Document CRUD
1.插入document
@PostMapping("/addDocument")
public R<?> addDocument(@RequestParam("indexName") String indexName, Product product) {
IndexResponse indexResponse = null;
try {
indexResponse = client.index(i -> i.index(indexName).document(product));
} catch (IOException e) {
return R.fail(e.getMessage());
}
return R.data(indexResponse.result());
}
2.更新Document
@PostMapping("/updateDocument")
public R<?> updateDocument(@RequestParam("indexName") String indexName, Product product) {
UpdateResponse<Product> updateResponse = null;
try {
updateResponse = client.update(u -> u.index(indexName).id(product.getId()).doc(product), Product.class);
} catch (ElasticsearchException | IOException e) {
return R.fail(e.getMessage());
}
return R.success(updateResponse.toString());
}
3.判断Document是否存在
@GetMapping("/existDocument")
public R<?> existDocument(@RequestParam("indexName") String indexName, @RequestParam("id") String id) {
BooleanResponse indexResponse = null;
try {
indexResponse = client.exists(e -> e.index(indexName).id(id));
} catch (ElasticsearchException | IOException e) {
return R.fail(e.getMessage());
}
return R.data(indexResponse.value());
}
4.查询Document
@GetMapping("/getDocument")
public R<?> getDocument(@RequestParam("indexName") String indexName, @RequestParam("id") String id) {
GetResponse<Product> getResponse = null;
try {
getResponse = client.get(g -> g.index(indexName).id(id), Product.class);
} catch (ElasticsearchException | IOException e) {
return R.fail(e.getMessage());
}
return R.data(getResponse.source());
}
5.删除Document
@GetMapping("/deleteDocument")
public R<?> deleteDocument(@RequestParam("indexName") String indexName, @RequestParam("id") String id) {
DeleteResponse deleteResponse = null;
try {
deleteResponse = client.delete(d -> d.index(indexName).id(id));
} catch (ElasticsearchException | IOException e) {
return R.fail(e.getMessage());
}
return R.data(deleteResponse.result());
}
6.查询
@GetMapping("/search")
public R<?> search(@RequestParam("indexName") String indexName, @RequestParam("key") String key, @RequestParam(
"value") String value, @RequestParam("page") int page, @RequestParam("size") int size) {
SearchResponse<Product> search = null;
try {
search = client.search(s -> s.index(indexName)
.query(q -> q.term(t -> t.field(key).value(v -> v.stringValue(value))))
.from(page).size(size)
.sort(f -> f.field(o -> o.field("price").order(SortOrder.Desc))), Product.class);
} catch (ElasticsearchException | IOException e) {
return R.fail(e.getMessage());
}
return R.data(search.hits().hits().stream().map(Hit::source).collect(Collectors.toList()));
}
3.SpringBoot日志整合 ELK
引入Maven依赖
<!-- logstash -->
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
version>7.1.1</version>
</dependency>
修改项目内的 logback.xml 文件 增加 logstash 配置
<springProperty scope="context" name="appName" source="spring.application.name"/>
<!--输出到logstash的appender-->
<appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
<!--可以访问的logstash日志收集端口-->
<destination>127.0.0.1:5044</destination>
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">
<customFields>{"spring.application.name":"${appName}"}</customFields>
</encoder>
</appender>
<root level="info">
<appender-ref ref="logstash"/>
</root>