Elasticsearch High Level Rest Client

2022-12-01 21:33:06 浏览数 (1)

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 依赖引入

强烈建议 客户端组件与Elasticsearch版本保持一致

代码语言:javascript复制
<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)的。

代码语言:javascript复制
@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是需要关闭以释放资源的;此外,RestHighLevelClientclose()方法的内部逻辑其实就是执行RestClientclose()方法。既然我们全局维护了一个RestHighLevelClient实例,那也就是说在Spring Boot程序关停时,需要执行RestHighLevelClientclose()方法。细心的朋友可能会发现:RestHighLevelClient实现了AutoCloseable接口,即实现了close()方法,那这意味着什么呢?Bean在初始化完成后,Spring会将DisposableBeanAutoCloseableBean保存在Map中,当Spring Boot程序在优雅关停时会自动执行DisposableBeandestroy()方法和AutoCloseableclose()方法,如下图所示:

3 RestHighLevelClient Bean

在上面的整合示例中,我们直接注入了RestHighLevelClient实例。可我们并没有显式地将其声明一个Bean,仅仅引入了elasticsearch-rest-client依赖而已,那这究竟是如何实现的呢?首先,在我们服务启动类中声明了@SpringBootApplication注解,我们看一下SpringBootApplication注解接口的源码:

代码语言:javascript复制
@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

代码语言:javascript复制
@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文件中加载所有自动配置类。

代码语言:javascript复制
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

代码语言:javascript复制
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestClient.class)
@EnableConfigurationProperties(ElasticsearchRestClientProperties.class)
@Import({
        ElasticsearchRestClientConfigurations.RestClientBuilderConfiguration.class,
        ElasticsearchRestClientConfigurations.RestHighLevelClientConfiguration.class,
        ElasticsearchRestClientConfigurations.RestClientFallbackConfiguration.class
})
public class ElasticsearchRestClientAutoConfiguration {
}

最后,看一下ElasticsearchRestClientConfigurations及其内部静态类RestHighLevelClientConfiguration的源码也就豁然开朗了。

代码语言:javascript复制
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);
        }
    }
}

0 人点赞