Elasticsearch
Introduction
Elasticsearch 是一个基于分布式架构的搜索和分析引擎,主要用于处理大规模的文本数据。它建立在 Apache Lucene 之上,并提供了一个基于 RESTful API 的接口,使得数据的索引和搜索变得非常简便。以下是 Elasticsearch 的简介和一些基本概念:
简介
Elasticsearch 是一个开源的搜索引擎,最初由 Elasticsearch N.V.(现在是 Elastic)开发。它具有高可用性、可扩展性和实时性的特点,适用于各种搜索、日志和分析应用场景。Elasticsearch 通常与 Logstash、Kibana 和 Beats 共同使用,形成 Elastic Stack(之前称为 ELK Stack)。
基本概念
索引 (Index): 索引类似于数据库中的表。在 Elasticsearch 中,索引是一个包含文档的集合,并且这些文档共享相同的结构。每个索引都有一个唯一的名字,通过这个名字来引用索引中的数据。
文档 (Document): 文档是 Elasticsearch 中最基本的数据单元,类似于关系数据库中的行。每个文档表示一个 JSON 格式的对象,并存储在索引中。
类型 (Type): 在 Elasticsearch 7.0 之前,类型是索引内用于定义文档结构的逻辑分组。在 7.0 及之后的版本中,类型被废弃,每个索引只能有一种类型。
字段 (Field): 字段是文档的组成部分,类似于关系数据库中的列。每个字段都有一个名字和一个值,值可以是字符串、数字、布尔值、日期等。
映射 (Mapping): 映射定义了文档和字段的结构,包括字段的数据类型、字段是否可被索引、字段的分词器等。类似于关系数据库中的模式定义。
节点 (Node): 节点是运行 Elasticsearch 实例的基本单元,每个节点存储数据并参与集群的索引和搜索功能。一个集群可以包含一个或多个节点。
集群 (Cluster): 集群是由一个或多个节点组成的集合,具有相同的集群名字。集群中的节点共同协作,分布式地存储数据并提供搜索功能。
分片 (Shard): 分片是索引的子集,Elasticsearch 自动将数据分割成多个分片,以便实现分布式存储和处理。每个索引在创建时会分配一个或多个主分片和副本分片。
副本 (Replica): 副本是主分片的副本,用于提高数据的可用性和容错性。当主分片发生故障时,副本可以替代主分片提供服务。
查询 DSL (Query DSL): 查询 DSL 是 Elasticsearch 提供的一种用于构建复杂查询请求的 JSON 格式的查询语言。通过查询 DSL,用户可以组合多个查询条件来检索数据。
工作原理
索引数据: 数据通过 RESTful API 或客户端库发送到 Elasticsearch,Elasticsearch 将数据转换为 JSON 格式,并存储在相应的索引中。
搜索数据: 用户可以通过 RESTful API 或客户端库发送查询请求,Elasticsearch 解析查询条件,搜索相关的索引和分片,并返回匹配的结果。
分布式处理: Elasticsearch 将数据分割成多个分片,并在集群中的多个节点上分布式存储和处理。这种分布式架构使得 Elasticsearch 具有高可用性和可扩展性。
Elasticsearch 的这些特性使其成为处理海量数据和复杂搜索需求的理想选择。无论是实时日志分析、全文搜索,还是复杂的数据分析,Elasticsearch 都能提供强大的支持。
安装
安装 Elasticsearch 的方法有很多种,简单的安装的方法如下
mkdir -p /data/docker/elastic-search/
cd /data/docker/elastic-search/
docker run -d \
--name=elastic-search \
--restart=always \
-e "discovery.type=single-node" \
-e "xpack.security.enabled=true" \
-e "ELASTIC_PASSWORD=YourElasticPassword" \
-p 9200:9200 \
-p 9300:9300 \
-v $(pwd)/elastic-search/data:/usr/share/elasticsearch/data \
litongjava/elasticsearch:7.10.0
Java 工程整合 Elasticsearch
在一个普通的 Java 工程中整合 Elasticsearch,步骤如下:
- 添加依赖: 在你的项目中添加 Elasticsearch 的依赖。使用 Maven 或 Gradle 来管理依赖项。以下是 Maven 依赖: 注意版本 需要和 服务端的版本相匹配
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.10.0</version>
</dependency>
- 创建 Elasticsearch 客户端: 使用
RestHighLevelClient
创建 Elasticsearch 客户端。
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
import org.elasticsearch.client.RestHighLevelClient;
public class ElasticsearchConfig {
private static final String HOST = "192.168.1.2";
private static final int PORT = 9200;
private static final String USERNAME = "elastic";
private static final String PASSWORD = "YourElasticPassword";
public static RestHighLevelClient createClient() {
// credentialsProvider
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(USERNAME, PASSWORD));
// httpHost
HttpHost httpHost = new HttpHost(HOST, PORT, "http");
// httpClientConfigCallback
HttpClientConfigCallback httpClientConfigCallback = httpClientBuilder -> httpClientBuilder
.setDefaultCredentialsProvider(credentialsProvider);
// builder
RestClientBuilder builder = RestClient.builder(httpHost).setHttpClientConfigCallback(httpClientConfigCallback);
RestHighLevelClient client = new RestHighLevelClient(builder);
return client;
}
}
- 使用 Elasticsearch 客户端进行操作: 你可以使用
RestHighLevelClient
进行各种操作,如索引文档、搜索文档等。下面是一个示例,展示如何索引和搜索文档:
package com.litongjava.tio.web.hello.example;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
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.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import com.litongjava.tio.web.hello.config.ElasticsearchConfig;
public class ElasticsearchExample {
public static void main(String[] args) throws IOException {
RestHighLevelClient client = ElasticsearchConfig.createClient();
// 索引文档
IndexRequest indexRequest = new IndexRequest("posts");
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("user", "kimchy");
jsonMap.put("postDate", "2020-01-01");
jsonMap.put("message", "trying out Elasticsearch");
indexRequest.source(jsonMap);
IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
System.out.println("Index Response: " + indexResponse.getResult());
// 搜索文档
SearchRequest searchRequest = new SearchRequest("posts");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
System.out.println("Search Response: " + searchResponse.toString());
client.close();
}
}
- 关闭客户端: 在应用程序结束时,确保关闭 Elasticsearch 客户端以释放资源。
client.close();
output
Index Response: CREATED
ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...
Search Response: {"took":22,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":0,"relation":"eq"},"max_score":null,"hits":[]}}
通过以上步骤,你就可以在一个普通的 Java 工程中成功整合 Elasticsearch 并进行基本的操作了。
使用 Tio-Boot 集成 Elasticsearch
在本指南中,我们将介绍如何将 Elasticsearch 集成到 Tio-Boot 项目中。完成本教程后,您将了解如何配置 Elasticsearch,创建客户端配置类,并实现一个控制器来与 Elasticsearch 进行交互。
步骤 1:配置
首先,我们需要配置 Elasticsearch 的连接详细信息。这可以通过属性文件 app.properties 来完成:
elasticsearch.rest.urls=http://192.168.1.2:9200
elasticsearch.rest.username=elastic
elasticsearch.rest.password=YourElasticPassword
这些属性包括 Elasticsearch 实例的 URL 以及用于身份验证的用户名和密码。
步骤 2:配置类
接下来,我们创建一个配置类来设置 Elasticsearch 的 RestHighLevelClient
。这个类从配置文件中读取属性,并初始化客户端。
import java.io.IOException;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.elasticsearch.client.RestHighLevelClient;
import com.litongjava.jfinal.aop.annotation.ABean;
import com.litongjava.jfinal.aop.annotation.AConfiguration;
import com.litongjava.tio.boot.server.TioBootServer;
import com.litongjava.tio.utils.environment.EnvUtils;
import com.litongjava.tio.utils.hutool.StrUtil;
import lombok.extern.slf4j.Slf4j;
@AConfiguration
@Slf4j
public class ElasticsearchConfig {
@ABean
public static RestHighLevelClient createClient() {
// 读取配置属性
String urls = EnvUtils.get("elasticsearch.rest.urls");
String username = EnvUtils.get("elasticsearch.rest.username");
String password = EnvUtils.get("elasticsearch.rest.password");
if (StrUtil.isEmpty(urls)) {
return null;
}
// 创建 HttpHost 实例
HttpHost httpHost = HttpHost.create(urls);
// 配置 RestClientBuilder
RestClientBuilder builder = RestClient.builder(httpHost);
if (StrUtil.isNotEmpty(username)) {
// 设置凭证提供者
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
// 配置 HTTP 客户端回调
builder.setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder
.setDefaultCredentialsProvider(credentialsProvider));
}
// 创建 RestHighLevelClient
RestHighLevelClient client = new RestHighLevelClient(builder);
// 确保在关闭时关闭客户端
TioBootServer.me().addDestroyMethod(() -> {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
});
// 记录连接状态
try {
log.info("client:{}", client.ping(RequestOptions.DEFAULT));
} catch (IOException e) {
e.printStackTrace();
}
return client;
}
}
解释
在配置类 ElasticsearchConfig
中,createClient
方法执行以下操作:
- 读取 Elasticsearch 连接的配置属性。
- 从 URL 创建
HttpHost
实例。 - 使用凭证配置
RestClientBuilder
。 - 初始化
RestHighLevelClient
并确保在服务器关闭时正确关闭。 - 记录连接状态,以验证客户端是否可以 ping 通 Elasticsearch 服务器。
RestHighLevelClient
被注解为 @ABean
,这使得它成为 Tio-Boot 应用上下文中的一个管理 bean。这允许我们在应用的其他部分取出和使用该客户端。
步骤 3:控制器
现在,我们创建一个控制器来处理请求并与 Elasticsearch 进行交互。这个控制器将提供用于创建和搜索文档的端点。
package com.litongjava.tio.web.hello.controller;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.elasticsearch.action.DocWriteResponse.Result;
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.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.annotation.RequestPath;
import com.litongjava.tio.utils.resp.RespBodyVo;
@RequestPath("/elastic-search")
public class ElasticSearchController {
public RespBodyVo create() {
RestHighLevelClient client = Aop.get(RestHighLevelClient.class);
// 索引文档
IndexRequest indexRequest = new IndexRequest("posts");
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("user", "kimchy");
jsonMap.put("postDate", "2020-01-01");
jsonMap.put("message", "trying out Elasticsearch");
indexRequest.source(jsonMap);
IndexResponse indexResponse;
try {
indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
return RespBodyVo.fail(e.getMessage());
}
Result result = indexResponse.getResult();
return RespBodyVo.ok(result);
}
public RespBodyVo search() {
RestHighLevelClient client = Aop.get(RestHighLevelClient.class);
// 搜索文档
SearchRequest searchRequest = new SearchRequest("posts");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse;
try {
searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
return RespBodyVo.fail(e.getMessage());
}
return RespBodyVo.ok(searchResponse);
}
}
解释
ElasticSearchController
类定义了两个端点:
- 创建端点 (
/elastic-search/create
):该端点将一个新文档索引到posts
索引中,并包含示例数据。 - 搜索端点 (
/elastic-search/search
):该端点在posts
索引中搜索所有文档。
这两个端点都使用 Aop.get
方法注入的 RestHighLevelClient
bean,演示了如何从应用上下文中检索管理 bean。
启动项目
整合完成后,启动速度由 300ms 增加到了 1452ms. 启动项目后。您应该看到一个日志条目,表明客户端已成功连接到 Elasticsearch:
2024-08-06 14:45:35.892 [main] INFO c.l.t.w.h.c.ElasticsearchConfig.createClient:68 - client:true
您可以通过以下 URL 在浏览器中测试端点:
create
端点应返回一个表示文档已创建的响应:
{ "data": "CREATED", "ok": true, "msg": null, "code": 1 }
search
端点应返回一个包含搜索结果的响应:
{
"data": {
"hits": {
"total": { "value": 1, "relation": "eq" },
"hits": [
{
"_index": "posts",
"_id": "1",
"_source": {
"user": "kimchy",
"postDate": "2020-01-01",
"message": "trying out Elasticsearch"
}
}
]
},
"took": 599
},
"ok": true,
"msg": null,
"code": 1
}
结论
在本指南中,我们演示了如何将 Elasticsearch 集成到 Tio-Boot 项目中。通过将 RestHighLevelClient
配置为一个管理的 bean,我们可以在控制器中轻松注入和使用它,实现与 Elasticsearch 的无缝交互。