此Starter使用的版本

  • elasticsearch-rest-high-level-client 7.2.0
  • elasticsearch-rest-client 7.2.0
  • elasticsearch
  • elastic 7.*

为什么写这个Starter

公司的Elastic版本还是6.x,而且使用的transportClient,作为客户端。一则再升级版本会面临客户端不支持,另外一个官方也已经不再推荐使用transportClient,写这个Starter练练手,另外有备无患。

如何写一个SpringBoot的Starter

写一个Starter相对来说其实很简单,按约定格式引入依赖,加入一个spring.factories文件即可。

  • 创建一个Maven项目,名称使用xxx-spring-boot-starter命名,以便和官方的Starter区分开,比如au92-elastic-spring-boot-starter

  • POM引用,实际需要用到的依赖包只有两个

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    
  • resources目录创建文件夹META-INF,新建一个spring.factories文件

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.au92.common.elastic.ElasticSearchAutoConfiguration
    
  • 创建一个ElasticSearchProperties类加载配置信息

    package com.au92.common.elastic;
    
    import lombok.Data;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    /**
    * @author: p_x_c
    */
    @Data
    @ConfigurationProperties(prefix = "elasticsearch")
    public class ElasticSearchProperties {
        /**
        * es集群地址,多个用英文逗号分开
        */
        private String clusterNodes = "http://127.0.0.1:9200";
        /**
        * es 用户名
        */
        private String userName = StringUtils.EMPTY;
    
        /**
        * es 用户密码
        */
        private String password = StringUtils.EMPTY;
    
        /**
        * 默认es索引名称
        */
        private String index = "default";
    }
    
  • 新建 ElasticSearchAutoConfiguration 用来将配置自动加载进来

    package com.au92.common.elastic;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.http.HttpHost;
    import org.elasticsearch.client.RestClient;
    import org.elasticsearch.client.RestClientBuilder;
    import org.elasticsearch.client.RestHighLevelClient;
    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Scope;
    
    import javax.annotation.Resource;
    import java.io.IOException;
    import java.util.Arrays;
    import java.util.Objects;
    import java.util.stream.Collectors;
    
    import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE;
    
    /**
    * @author: p_x_c 
    */
    @Slf4j
    @Configuration
    @EnableConfigurationProperties(ElasticSearchProperties.class)
    public class ElasticSearchAutoConfiguration implements DisposableBean {
        private RestHighLevelClient client;
    
        @Resource
        private ElasticSearchProperties properties;
    
        @Override
        public void destroy() throws Exception {
            log.info("销毁ES连接");
            if (client != null) {
                client.close();
            }
        }
    
        /**
        * 创建client
        *
        * @return
        * @throws IOException
        */
        @Bean
        @ConditionalOnMissingBean(RestHighLevelClient.class)
        public RestHighLevelClient client() throws IOException {
            if (!Objects.equals(null, client)) {
                log.warn("client对象没有正确关闭");
                client.close();
            }
            RestClientBuilder builder = RestClient.builder(setting());
            setConnectTimeOutConfig(builder);
            setMutiConnectConfig(builder);
            client = new RestHighLevelClient(builder);
            return client;
        }
    
        /**
        * Elastic Service
        *
        * @return
        */
        @Bean
        @Scope(value = SCOPE_PROTOTYPE)
        @ConditionalOnMissingBean(IElasticSearchService.class)
        public IElasticSearchService service() {
            return new ElasticSearchServiceImpl();
        }
    
        /**
        * 解析配置文件
        *
        * @return
        */
        private HttpHost[] setting() {
            return Arrays.stream(properties.getClusterNodes().split(ElasticConstant.CLUSTER_SPLIT))
                    .map(x -> {
                        String[] addressPortPairs = x.split(ElasticConstant.SCHEME_SPLIT);
                        String schemeName = addressPortPairs[0].toLowerCase();
                        String hostname = addressPortPairs[1].substring(2);
                        Integer port = Integer.valueOf(addressPortPairs[2]);
                        return new HttpHost(hostname, port, schemeName);
                    })
                    .collect(Collectors.toList())
                    .toArray(new HttpHost[0]);
    
        }
    
        /**
        * 超时设置
        *
        * @param builder
        */
        private void setConnectTimeOutConfig(RestClientBuilder builder) {
            builder.setRequestConfigCallback(requestConfigBuilder -> {
                requestConfigBuilder.setConnectTimeout(ElasticConstant.CONNECT_TIME_OUT)
                        .setSocketTimeout(ElasticConstant.SOCKET_TIME_OUT)
                        .setConnectionRequestTimeout(ElasticConstant.CONNECTION_REQUEST_TIME_OUT);
                return requestConfigBuilder;
            });
        }
    
        /**
        * 并发连接数设置
        *
        * @param builder
        */
        private void setMutiConnectConfig(RestClientBuilder builder) {
            builder.setHttpClientConfigCallback(httpClientBuilder -> {
                httpClientBuilder.setMaxConnTotal(ElasticConstant.MAX_CONNECT_NUM)
                        .setMaxConnPerRoute(ElasticConstant.MAX_CONNECT_PER_ROUTE);
                return httpClientBuilder;
            });
        }
    }
    

抛开其他业务相关的逻辑,一个Spring Boot的Starter已经可以算完成了。

用到的其他工具类和一些操作类

https://gist.github.com/Chairo/1b0d06d45e5155b134b7b43abb3d84f6