ElasticSearchProperties 配置类,可以配置在中心中


public class ElasticSearchProperties {

    /**
     * 连接超时时间(毫秒)
     */
    private Integer connectTimeout = 3000;

    /**
     * socket 超时时间
     */
    private Integer socketTimeout = 30000;

    /**
     * 连接请求超时时间
     */
    private Integer connectionRequestTimeout = 500;

    /**
     * keepAlive策略时间
     */
    private Integer keepAliveStrategy = 60 * 1000;
}


EsClientFactory es客户端工厂


/**
 * @author yangwenxin
 * @Date 2022/5/13
 * @Description
 */
@Slf4j
public class EsClientFactory {

    /**
     * 创建es客户端
     *
     * @param properties 配置信息
     * @param address    地址, 示例:127.0.0.1:9201,127.0.0.1:9202,127.0.0.1:9203
     * @param userName   用户名
     * @param password   密码
     * @return
     */
    public static RestHighLevelClient createRestHighLevelClient(ElasticSearchProperties properties, String address,
                                                                String userName, String password) {
        //ElasticSearch 连接地址地址
        HttpHost[] httpHosts = getElasticSearchHttpHosts(address);

        RestClientBuilder restClientBuilder = RestClient.builder(httpHosts).setRequestConfigCallback(requestConfigBuilder -> {
            //设置连接超时时间
            requestConfigBuilder.setConnectTimeout(properties.getConnectTimeout());
            requestConfigBuilder.setSocketTimeout(properties.getSocketTimeout());
            requestConfigBuilder.setConnectionRequestTimeout(properties.getConnectionRequestTimeout());
            return requestConfigBuilder;
        }).setHttpClientConfigCallback(httpClientBuilder -> {
            httpClientBuilder.disableAuthCaching();
            httpClientBuilder.setKeepAliveStrategy((response, context) -> properties.getKeepAliveStrategy());
            //设置账密
            return getHttpAsyncClientBuilder(httpClientBuilder, userName, password);
        });
        log.info("init esClient success, address : {}", address);
        return new RestHighLevelClient(restClientBuilder);
    }


    /**
     * ElasticSearch 连接地址
     * 多个逗号分隔
     * 示例:127.0.0.1:9201,127.0.0.1:9202,127.0.0.1:9203
     */
    private static HttpHost[] getElasticSearchHttpHosts(String address) {
        String[] hosts = address.split(",");
        HttpHost[] httpHosts = new HttpHost[hosts.length];
        for (int i = 0; i < httpHosts.length; i++) {
            String host = hosts[i];
            host = host.replaceAll("http://", "").replaceAll("https://", "");
            httpHosts[i] = new HttpHost(host.split(":")[0], Integer.parseInt(host.split(":")[1]), "http");
        }
        return httpHosts;
    }

    private static HttpAsyncClientBuilder getHttpAsyncClientBuilder(HttpAsyncClientBuilder httpClientBuilder, String username, String password) {
        if (StringUtils.isBlank(username) || StringUtils.isEmpty(password)) {
            return httpClientBuilder;
        }
        //账密设置
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        //es账号密码(一般使用,用户elastic)
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
        httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
        return httpClientBuilder;
    }
}


ElasticSearchClientService es客户端初始化,从数据库中读取信息初始化客户端。其中DataSourceConfig就是配置了一些es链接的信息,账号密码地址等。


/**
 * @author yangwenxin
 * @Date 2022/5/13
 * @Description
 */
@Service
@Slf4j
public class ElasticSearchClientService {

    @Autowired
    private ElasticSearchProperties elasticSearchProperties;

    private Map<String, EsConfig> esClientMap = new HashMap<>();

    @PostConstruct
    public void init() {
        initEsClientMap();
    }

    /**
     * 初始化es数据源客户端到缓存
     */
    public void initEsClientMap() {
        // TODO 数据库查询数据源信息 自己项目中查询数据库方法,或者将ES信息配置在配置中心
        List<DataSourceConfig> dataSourceConfigs = Lists.newArrayList();
        if (CollectionUtils.isEmpty(dataSourceConfigs)) {
            return;
        }
        // address相同认为是一组  es数据源初始化address包含账号密码
        Map<String, List<DataSourceConfig>> dataSourceConfigMaps = dataSourceConfigs.stream().collect(
                Collectors.groupingBy(
                        DataSourceConfig::getAddress
                ));

        dataSourceConfigMaps.forEach((key, value) -> {
            DataSourceConfig dataSourceConfig = value.get(0);
            RestHighLevelClient restHighLevelClient = EsClientFactory.createRestHighLevelClient(
                    elasticSearchProperties,
                    dataSourceConfig.getAddress(),
                    dataSourceConfig.getUsername(),
                    dataSourceConfig.getPassword()
            );
            value.forEach(v -> {
                EsConfig esConfig = new EsConfig(restHighLevelClient, v.getInstance());
                esClientMap.put(v.getDataSourceNo(), esConfig);
            });
        });
    }

    @PreDestroy
    public void closeEsClient() {
        if (CollectionUtils.isEmpty(esClientMap)) {
            return;
        }
        esClientMap.forEach((key, value) -> {
            try {
                RestHighLevelClient client = value.getRestHighLevelClient();
                if (client != null) {
                    client.close();
                }
            } catch (Exception e) {
                log.error("close restHighLevelClient error! key: {},errMsg : {}", key, e.getMessage());
            }
        });
    }

    public EsConfig getEsConfigByNo(String dateSourceNo) {
        return esClientMap.get(dateSourceNo);
    }

    public synchronized void refreshEsDataSource(){
        initEsClientMap();
        log.info("refresh esDataSource success!");
    }


    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class EsConfig {
        /**
         * es客户端
         */
        private RestHighLevelClient restHighLevelClient;
        /**
         * 索引名称
         */
        private String dataSourceName;
    }
}


EsClientUtil 查询工具类


@Slf4j
public class EsClientUtil {

    private static final String DEFAULT_TYPE = "_doc";

    /**
     * query string 查询, queryString语法。语法自行百度
     * @param request
     * @return
     * @throws IOException
     */
    public static LogDataResp queryStringQuery(RestHighLevelClient client, QueryLogDataReq request) {
        String indexName = request.getQueryIndex();
        try {
            SearchRequest searchRequest = new SearchRequest(indexName);
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

            // 查询语句
            BoolQueryBuilder must =
                    QueryBuilders.boolQuery().must(
                            QueryBuilders.queryStringQuery(request.getQueryScript())
                    );

            // 时间范围  TODO 时区
            if(StringUtils.isNotBlank(request.getTimeFieldName()) && request.getTimeFrameMinutes() != null){
                RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery(request.getTimeFieldName())
                        .gte("now-" + request.getTimeFrameMinutes() + "m")
                        .lte("now")
                        // es时区默认是UTC
                        .timeZone("UTC");
                must.must(rangeQueryBuilder);
                // 根据时间降序排列
                searchSourceBuilder.sort(request.getTimeFieldName(), SortOrder.DESC);
            }
            searchSourceBuilder.query(must).from(0).size(request.getQuerySize());
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            // 返回值解析
            return buildLogDataResponse(searchResponse,null);

        } catch (Exception e) {
            String errMsg = MessageFormat.format("EsClientUtil#queryStringQuery failed, params={0}", request);
            log.error(errMsg, e);
            return LogDataResp.buildFailed(e.getMessage());
        }
    }

    /**
     * 查询所有的索引
     * @param client
     * @return
     * @throws IOException
     */
    public static List<String> queryAllIndexAlias(RestHighLevelClient client) {
        try {
            GetAliasesRequest getAliasesRequest = new GetAliasesRequest();
            GetAliasesResponse getAliasesResponse = client.indices().getAlias(getAliasesRequest, RequestOptions.DEFAULT);
            Map<String, Set<AliasMetaData>> aliases = getAliasesResponse.getAliases();
            if(!aliases.isEmpty()){
                return new ArrayList<>(aliases.keySet());
            }
            return Arrays.asList();
        } catch (Exception e) {
            String errMsg = "EsClientUtil#queryAllIndexAlias error";
            log.error("errMsg" , e);
            throw new RuntimeException(errMsg, e);
        }
    }

    /**
     * 向索引插入单条记录
     * @param client
     * @param indexName    索引
     * @param docId        文档ID
     * @param docSource    文档 JSON格式
     * @return
     * @throws IOException
     */
    public static boolean insertDoc(RestHighLevelClient client, String indexName, String docId, Object docSource) {
        try {
            IndexRequest request = new IndexRequest(indexName,DEFAULT_TYPE);
            if (null != docId) {
                request.id(docId);
            }
            request.source(JSON.toJSONString(docSource), XContentType.JSON);
            IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
            if ((indexResponse.status() == RestStatus.CREATED) || (indexResponse.status() == RestStatus.OK)) {
                return true;
            }
            log.error("insertDoc fail status:{} => {}", indexResponse.status(), indexResponse);
            return false;
        } catch (Exception e) {
            String errMsg =
                    MessageFormat.format("EsClientUtil#insertDoc failed, indexName={0},docId={1}, docSource={2}",
                            indexName,docId,docSource);
            log.error(errMsg, e);
            throw new RuntimeException(errMsg, e);
        }
    }

    /**
     * 向索引插入批量记录  不指定id
     * @param client
     * @param indexName   索引名称
     * @param docSources  数据 JSON格式
     * @return
     */
    public static boolean insertDocBulk(RestHighLevelClient client,String indexName, List<Object> docSources) {
        try {
            if (CollectionUtils.isEmpty(docSources)) {
                return false;
            }
            BulkRequest bulkRequest = new BulkRequest();
            docSources.forEach(docSource -> {
                IndexRequest request = new IndexRequest(indexName,DEFAULT_TYPE);
                request.source(JSON.toJSONString(docSource), XContentType.JSON);
                bulkRequest.add(request);
            });
            BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
            if (!bulkResponse.hasFailures()) {
                return true;
            }
            log.error("insertDocBulk fail status:{}, errorMsg:{}", bulkResponse.status(),
                    bulkResponse.buildFailureMessage());
            return false;
        } catch (Exception e) {
            String errMsg =
                    MessageFormat.format("EsClientUtil#insertDocBulk failed, indexName={0}",
                            indexName);
            log.error(errMsg, e);
            throw new RuntimeException(errMsg, e);
        }
    }

    /**
     * 向索引插入批量记录  指定id
     * @param client
     * @param indexName      索引名称
     * @param docSourceMap   key->Id value->docSource JSON格式
     * @return
     */
    public static boolean insertDocBulk(RestHighLevelClient client,String indexName, Map<String,Object> docSourceMap) {
        try{
            if (CollectionUtils.isEmpty(docSourceMap)) {
                return false;
            }
            BulkRequest bulkRequest = new BulkRequest();
            docSourceMap.forEach((key,value) -> {
                IndexRequest request = new IndexRequest(indexName,DEFAULT_TYPE);
                request.id(key);
                request.source(JSON.toJSONString(value), XContentType.JSON);
                bulkRequest.add(request);
            });
            BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
            if (!bulkResponse.hasFailures()) {
                return true;
            }
            log.error("insertDocBulk fail status:{}, errorMsg:{}", bulkResponse.status(),
                    bulkResponse.buildFailureMessage());
            return false;
        } catch (Exception e) {
            String errMsg =
                    MessageFormat.format("EsClientUtil#insertDocBulk failed, indexName={0}",
                            indexName);
            log.error(errMsg, e);
            throw new RuntimeException(errMsg, e);
        }
    }

    /**
     * 根据ID 删除文档
     * @param client
     * @param indexName  索引名称
     * @param docId
     * @return
     */
    public static boolean deleteDocById(RestHighLevelClient client, String indexName, String docId) {
        try{
            DeleteRequest deleteRequest = new DeleteRequest(indexName,DEFAULT_TYPE,docId);
            DeleteResponse delete = client.delete(deleteRequest, RequestOptions.DEFAULT);
            int total = delete.getShardInfo().getTotal();
            return total > 0;
        } catch (Exception e) {
            String errMsg =
                    MessageFormat.format("EsClientUtil#deleteDocById failed, indexName={0},docId={1} ",
                            indexName, docId);
            log.error(errMsg, e);
            throw new RuntimeException(errMsg, e);
        }
    }

    /**
     * 根据ID更新文档
     * @param client
     * @param indexName   索引名称
     * @param docId       文档ID
     * @param docSource   文档数据 JSON格式
     * @return
     */
    public static boolean updateDocById(RestHighLevelClient client, String indexName, String docId, Object docSource) {
        try{
            UpdateRequest updateRequest = new UpdateRequest(indexName,DEFAULT_TYPE,docId);
            updateRequest.doc(JSON.toJSONString(docSource),XContentType.JSON);
            // 执行更新
            UpdateResponse update = client.update(updateRequest, RequestOptions.DEFAULT);
            int total = update.getShardInfo().getTotal();
            return total > 0;
        } catch (Exception e) {
            String errMsg =
                    MessageFormat.format("EsClientUtil#updateDoc failed, indexName={0},docSource={1} ",
                            indexName, docSource);
            log.error(errMsg, e);
            throw new RuntimeException(errMsg, e);
        }
    }

    /**
     * 根据ID查询
     * @param client
     * @param indexName
     * @param docId
     * @return
     */
    public static <T> T queryDocById(RestHighLevelClient client, String indexName, String docId, Class<T> clazz) {
        try {
            GetRequest request = new GetRequest(indexName,DEFAULT_TYPE,docId);
            GetResponse response = client.get(request, RequestOptions.DEFAULT);
            if(response.isSourceEmpty()){
                return null;
            }
            return JSON.parseObject(response.getSourceAsString(), clazz);
        } catch (Exception e) {
            String errMsg =
                    MessageFormat.format("EsClientUtil#queryDocById failed, indexName={0},docId={1} ",
                            indexName, docId);
            log.error(errMsg, e);
            throw new RuntimeException(errMsg, e);
        }
    }

    /**
     * 根据条件查询分页
     * 注:from-size分页 可以随机跳转页面,在深度分页的情况下,效率是非常低的。深度分页参考search_after 不支持随机跳转,只支持
     * @param index    索引
     * @param pageNo   页码(第几页)
     * @param pageSize 页容量- Elasticsearch默认配置单次最大限制10000
     */
    public static <T> List<T> searchPageByIndex(RestHighLevelClient client, String index, SearchSourceBuilder searchSourceBuilder, Integer pageNo, Integer pageSize, Class<T> clazz) {
        if(searchSourceBuilder == null){
            searchSourceBuilder = new SearchSourceBuilder();
        }
        searchSourceBuilder.from((pageNo-1)*pageSize);
        searchSourceBuilder.size(pageSize);
        return searchByQuery(client, index, searchSourceBuilder, clazz);
    }

    /**
     * 条件查询
     *
     * @param indexName         索引
     * @param sourceBuilder 条件查询构建起
     * @param <T>           数据类型
     * @return T 类型的集合
     */
    public static <T> List<T> searchByQuery(RestHighLevelClient client, String indexName, SearchSourceBuilder sourceBuilder, Class<T> clazz) {
        try {
            // 构建查询请求
            SearchRequest searchRequest = new SearchRequest(indexName).source(sourceBuilder);
            // 获取返回值
            SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHit[] hits = response.getHits().getHits();
            if (null == hits || hits.length == 0) {
                return new ArrayList<>(0);
            }
            List<T> resultList = new ArrayList<>(hits.length);
            for (SearchHit hit : hits) {
                resultList.add(JSON.parseObject(hit.getSourceAsString(), clazz));
            }
            return resultList;
        } catch (Exception e) {
            String errMsg =
                    MessageFormat.format("EsClientUtil#searchByQuery failed, indexName={0}, sourceBuilder={1} ",
                            sourceBuilder);
            log.error(errMsg, e);
            throw new RuntimeException(errMsg, e);
        }
    }


    /**
     * 结果解析构建返回结果
     * @param searchResponse
     * @param aggName
     * @return
     */
    private static LogDataResp buildLogDataResponse(SearchResponse searchResponse, String aggName){
        LogDataResp logDataResponse = new LogDataResp();
        if(searchResponse.status().getStatus() != RestStatus.OK.getStatus()){
            logDataResponse.setSucceeded(Boolean.FALSE);
            return logDataResponse;
        }

        long totalHits = searchResponse.getHits().getTotalHits();
        logDataResponse.setTotalHit(totalHits);
        SearchHit[] hits = searchResponse.getHits().getHits();
        if(hits.length > 0){
            List<Map> hitSourceList = new ArrayList<>();
            for(SearchHit hit : hits){
                hitSourceList.add(hit.getSourceAsMap());
            }
            logDataResponse.setHitSourceList(hitSourceList);
        }

        if(aggName != null){
            Aggregations aggregations = searchResponse.getAggregations();
            Terms aggregation = (Terms)aggregations.get(aggName);
            List<? extends Terms.Bucket> buckets = aggregation.getBuckets();
            Map map = Maps.newHashMap();
            List<Map> aggregationBucketList = new ArrayList<>();
            for (Terms.Bucket bucket : buckets) {
                map.put(bucket.getKeyAsString(),bucket.getDocCount());
            }
            aggregationBucketList.add(map);
            logDataResponse.setAggregationBucketList(aggregationBucketList);
        }
        return logDataResponse;
    }


}

另外,如果说把es当成一个数据库使用,可以看下开源项目easy-es,操作更方便。文档地址:快速开始 | Easy-Es