Elasticsearch High Level Rest Client
Elasticsearch自身暴露了一套REST API
,可以直接调用它们来配置和访问Elasticsearch。为了方便各类语言与REST API
交互,官方提供若干客户端组件,这类似于一套SDK
;有了这套SDK
,首先,不再需要显式地编写REST API
的URL信息,只需要传入参数和解析响应即可;其次,SDK
层封装了异步API,可以更方便地进行异步编程。
Elasticsearch有三个版本的Java Client,分别是:
- TransportClient
- High Level Rest Client
- index(IndexRequest)
- delete(DeleteRequest)
- update(UpdateRequest)
- get(GetRequest)
- search(SearchRequest)
- ···
- Low Level Rest Client
- performRequest(Request)
TransportClient已经废弃,官方会在Elasticsearch 8.0中彻底移除它。High Level Rest Client在Low Level Rest Client基础上构建,而Low Level Rest Client底层依赖于Apache HttpClient
组件。High Level Rest Client对REST API
中的接口单独封装,可以构建多种Request对象,较为灵活;而Low Level Rest Client仅仅暴露performRequest()
,也就是说该方法承载了REST API
中所有的逻辑。
1 依赖引入
代码语言:javascript复制强烈建议 客户端组件与Elasticsearch版本保持一致
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.9.1</version>
<exclusions>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
<exclusion>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.9.1</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.9.1</version>
</dependency>
2 使用方法
2.1 配置
代码语言:javascript复制spring.elasticsearch.rest.uris=A.B.C.D:9200,A.B.C.E:9200,A.B.C.F:9200
spring.elasticsearch.rest.read-timeout=15S
spring.elasticsearch.rest.connection-timeout=15S
spring.elasticsearch.rest.username=elastic
spring.elasticsearch.rest.password=ENC(P9fUyEbC VN6OJtexPVLEd5 QF0h5c2A)
对应配置类模型如下:
代码语言:javascript复制@ConfigurationProperties(prefix = "spring.elasticsearch.rest")
public class ElasticsearchRestClientProperties {
private List<String> uris = new ArrayList<>(Collections.singletonList("http://localhost:9200"));
private String username;
private String password;
private Duration connectionTimeout = Duration.ofSeconds(1);
private Duration readTimeout = Duration.ofSeconds(30);
}
2.2 注入RestHighLevelClient实例
你只需要像下面这样在业务逻辑层注入RestHighLevelClient
实例,然后就具备针对文档的增删改查(CRUD
)能力了。RestHighLevelClient
虽然是单例Bean
,但不必担心线程安全问题,因为RestHighLevelClient
及其内部RestClient
实例均是线程安全(thread-safe)的。
@Resource
private RestHighLevelClient restHighLevelClient;
官方文档关于RestHighLevelClient中close()方法的描述 The high-level client will internally create the low-level client used to perform requests based on the provided builder. That low-level client maintains a pool of connections and starts some threads so you should close the high-level client when you are well and truly done with it and it will in turn close the internal low-level client to free those resources.
从官方描述中,我们知道RestHighLevelClient
是需要关闭以释放资源的;此外,RestHighLevelClient
中close()
方法的内部逻辑其实就是执行RestClient
中close()
方法。既然我们全局维护了一个RestHighLevelClient
实例,那也就是说在Spring Boot程序关停时,需要执行RestHighLevelClient
中close()
方法。细心的朋友可能会发现:RestHighLevelClient
实现了AutoCloseable
接口,即实现了close()
方法,那这意味着什么呢?Bean
在初始化完成后,Spring会将DisposableBean
和AutoCloseableBean
保存在Map
中,当Spring Boot程序在优雅关停时会自动执行DisposableBean
中destroy()
方法和AutoCloseable
中close()
方法,如下图所示:
3 RestHighLevelClient Bean
代码语言:javascript复制在上面的整合示例中,我们直接注入了
RestHighLevelClient
实例。可我们并没有显式地将其声明一个Bean
,仅仅引入了elasticsearch-rest-client
依赖而已,那这究竟是如何实现的呢?首先,在我们服务启动类中声明了@SpringBootApplication
注解,我们看一下SpringBootApplication
注解接口的源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
}
)
public @interface SpringBootApplication {
}
然后,在SpringBootApplication
注解接口的源码中发现了@EnableAutoConfiguration
注解,打开EnableAutoConfiguration
源码后发现其通过@Import
注解引入了AutoConfigurationImportSelector
。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
继续,我们来看一下AutoConfigurationImportSelector
类的源码,selectImports()
无疑是最核心的逻辑,它决定了加载哪些自动配置类。具体地,首先判断是否启用@EnableAutoConfiguration
注解,如果已经启用,那么getAutoConfigurationEntry()
将会从spring-boot-autoconfigure
模块中META-INF
目录下spring.factories
文件中加载所有自动配置类。
public class AutoConfigurationImportSelector implements
DeferredImportSelector,
BeanClassLoaderAware,
ResourceLoaderAware,
BeanFactoryAware,
EnvironmentAware,
Ordered {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
spring-factories(部分摘录)
代码语言:javascript复制org.springframework.boot.autoconfigure.EnableAutoConfiguration=
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,
org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,
org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,
org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchRestClientAutoConfiguration
在spring-factories
文件中,我们看到了Elasticsearch的自动配置类入口:ElasticsearchRestClientAutoConfiguration
。既然知道了自动配置的入口,距离解答我们的疑问也就很近了。从ElasticsearchRestClientAutoConfiguration
源码中可以看到通过@Import
注解引入了ElasticsearchRestClientConfigurations.RestHighLevelClientConfiguration
。
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestClient.class)
@EnableConfigurationProperties(ElasticsearchRestClientProperties.class)
@Import({
ElasticsearchRestClientConfigurations.RestClientBuilderConfiguration.class,
ElasticsearchRestClientConfigurations.RestHighLevelClientConfiguration.class,
ElasticsearchRestClientConfigurations.RestClientFallbackConfiguration.class
})
public class ElasticsearchRestClientAutoConfiguration {
}
最后,看一下ElasticsearchRestClientConfigurations
及其内部静态类RestHighLevelClientConfiguration
的源码也就豁然开朗了。
class ElasticsearchRestClientConfigurations {
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(RestClientBuilder.class)
static class RestClientBuilderConfiguration {
@Bean
RestClientBuilder elasticsearchRestClientBuilder(
ElasticsearchRestClientProperties properties,
ObjectProvider<RestClientBuilderCustomizer> builderCustomizers) {
// ······
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestHighLevelClient.class)
static class RestHighLevelClientConfiguration {
@Bean
@ConditionalOnMissingBean
RestHighLevelClient elasticsearchRestHighLevelClient(RestClientBuilder restClientBuilder) {
return new RestHighLevelClient(restClientBuilder);
}
}
}