Spring Boot 2.6.5集成elasticsearch8.1.2

3,771 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情

前言

上篇文章说要集成elasticsearch8.x版本,今天把遇到的坑和集成步骤发一下供大家参考。本文主要参考的文章见:点击查看整合的参考地址,点击查看参考的官方文档

step1:安装elasticsearch8.1.2

这里说一下,我安装的是最新版本的es(截止到2022年4月2日),并且在maven仓库里可以找到对应版本的连接器,大家可以在整合的时候按照最新的版本进行整合。

  • 下载es8.1.2 可以访问这个地址,下载的更快一些加速地址,点击download之后我下载的版本是8.1.2
  • 解压
  • 修改配置文件 es8.1.2默认使用了xpack进行加密,所以我们先改一下配置文件,先用不加密的方式进行本地demo开发。先找到解压出来文件目录下的config目录,找到elasticsearch.yml文件,我们直接覆盖为以下内容
cluster.name: my-es
xpack.security.enabled: false
xpack.security.enrollment.enabled: false
xpack.security.http.ssl:
  enabled: false
  keystore.path: certs/http.p12
xpack.security.transport.ssl:
  enabled: false
  verification_mode: certificate
  keystore.path: certs/transport.p12
  truststore.path: certs/transport.p12
cluster.initial_master_nodes: ["WIN-O8STQHDPFBA"]
http.host: [_local_, _site_]

这里我们主要是配置了节点名称和禁用xpack加密

  • 跳转到bin目录下,双击elasticsearch.bat就可以在Windows上启动es了,启动完成后我们访问127.0.0.1:9200,出现下面的信息代表启动成功了
{
  "name" : "WIN-O8STQHDPFBA",
  "cluster_name" : "my-es",
  "cluster_uuid" : "ph9V_VxtR4u0Qk92UlxCAw",
  "version" : {
    "number" : "8.1.2",
    "build_flavor" : "default",
    "build_type" : "zip",
    "build_hash" : "31df9689e80bad366ac20176aa7f2371ea5eb4c1",
    "build_date" : "2022-03-29T21:18:59.991429448Z",
    "build_snapshot" : false,
    "lucene_version" : "9.0.0",
    "minimum_wire_compatibility_version" : "7.17.0",
    "minimum_index_compatibility_version" : "7.0.0"
  },
  "tagline" : "You Know, for Search"
}

step2:整合es8.1.2至spring boot

  • 从spring.start.io初始化一个spring boot的项目。我初始化的版本为 2.6.5,这里只需要spring boot start启动即可,下一步我们引入必须的依赖
  • 在pom.xml中添加完所需的依赖后完整的依赖如下
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.2</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish</groupId>
        <artifactId>jakarta.json</artifactId>
        <version>2.0.1</version>
    </dependency>
    <dependency>
        <groupId>co.elastic.clients</groupId>
        <artifactId>elasticsearch-java</artifactId>
        <version>8.1.2</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.12.0</version>
    </dependency>
</dependencies>
  • 在application.yml中添加配置,内容如下
spring:
  elasticsearch:
    uris: 127.0.0.1:9200
server:
  port: 4001
logging:
  level:
    root: debug

这里我们指定es的uri服务的端口和打印日志为debug

  • 统一的clientBean 我们定义一个统一的bean用来处理统一的client。在config文件夹下创建文件内容如下:
package code.liang.top.config;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Arrays;

/**
 * es config
 *
 * @author lmh
 */
@Configuration
public class ElasticSearchConfig {
    @Value("${spring.elasticsearch.uris}")
    private String elasticsearchUris;

    @Bean
    public ElasticsearchClient elasticsearchClient() {
        if (StringUtils.isNoneBlank(elasticsearchUris)) {
            RestClient client = RestClient.builder(Arrays.stream(elasticsearchUris.split(",")).map(HttpHost::create).toArray(HttpHost[]::new)).build();
            ElasticsearchTransport transport = new RestClientTransport(client, new JacksonJsonpMapper());
            return new ElasticsearchClient(transport);
        } else {
            throw new RuntimeException("未读取到es的uri配置信息");
        }
    }
}

这里我们同时支持了多个esclient集群连接

step3:常用api和原理

我们在第二步已经定义好了一个client bean,下面我简单介绍一下实现的原理

index索引相关原理

话不多说,先上代码

package code.liang.top.modules.controller;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.indices.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**
 * @author lmh
 */
@Slf4j
@RestController
@RequestMapping("es/index")
@RequiredArgsConstructor
public class EsIndexController {
    private final ElasticsearchClient elasticsearchClient;

    @GetMapping("createIndex")
    public String createIndex() throws IOException {
        CreateIndexResponse createResponse = elasticsearchClient.indices().create(demo -> demo.index("demo1"));
        return Boolean.TRUE.equals(createResponse.acknowledged()) ? "创建成功" : "创建失败";
    }

    @GetMapping("searchIndexList")
    public String searchIndexList() throws IOException {
        GetIndexResponse getIndexResponse = elasticsearchClient.indices().get(demo -> demo.index("*"));
        return String.join(",", getIndexResponse.result().keySet());
    }

    @GetMapping("deleteIndex")
    public String deleteIndex() throws IOException {
        DeleteIndexResponse deleteIndexResponse = elasticsearchClient.indices().delete(demo -> demo.index("demo1"));
        return Boolean.TRUE.equals(deleteIndexResponse.acknowledged()) ? "删除成功" : "删除失败";
    }
}
  1. createIndex 示例相当于使用put请求调用es的这个接口 ip:9200/first_demo_index,请求体为:
{
	"settings": {
		"number_of_shards": 1,
		"number_of_replicas": 0
	}
}
  1. createIndex 示例相当于使用get请求调用es的这个接口 ip:9200/*
  2. deleteIndex 示例相当于使用delete请求调用es的这个接口 ip:9200/first_demo_index

documen相关示例

package code.liang.top.modules.controller;

import cn.hutool.core.util.RandomUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch.core.GetResponse;
import co.elastic.clients.elasticsearch.core.IndexResponse;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import code.liang.top.modules.entity.SysUserEs;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author lmh
 */
@Slf4j
@RestController
@RequestMapping("es/demo")
@RequiredArgsConstructor
public class EsDemoController {
    private final ElasticsearchClient elasticsearchClient;

    @GetMapping("initIndexAndInsert")
    public String initIndexAndInsert() throws IOException {
        BooleanResponse booleanResponse = elasticsearchClient.indices().exists(demo1 -> demo1.index("demo1"));
        if (booleanResponse.value()) {
            elasticsearchClient.indices().delete(demo1 -> demo1.index("demo1"));
        }
        IndexResponse indexResponse = elasticsearchClient.index(demo1 -> demo1.index("demo1").id("123456").document(new SysUserEs().setAge(18).setBalance(100.00).setEnabled(true).setName("hello world")));
        return indexResponse.result().jsonValue();
    }

    @GetMapping("insert")
    public String insert() throws IOException {
        IndexResponse indexResponse = elasticsearchClient.index(demo1 -> demo1.index("demo1").id(System.currentTimeMillis() + "" + RandomUtil.randomString(6)).document(new SysUserEs().setAge(18).setBalance(100.00).setEnabled(true).setName("hello world")));
        return indexResponse.result().jsonValue();
    }

    @GetMapping("search")
    public String search() throws IOException {
        GetResponse<SysUserEs> getResponse = elasticsearchClient.get(demo1 -> demo1.index("demo1").id("123456"), SysUserEs.class);
        return getResponse.source().toString();
    }

    @GetMapping("list")
    public String list() throws IOException {
        SearchRequest searchRequest = new SearchRequest.Builder().build();
        SearchResponse<SysUserEs> getResponse = elasticsearchClient.search(searchRequest, SysUserEs.class);
        List<String> strList = new ArrayList<>();
        getResponse.hits().hits().stream().forEach(item -> {
            strList.add(item.source().toString());
        });
        return String.join(",", strList);
    }
}

底层原理暂不赘述了,大家可以下载代码本地运行然后看控制台的请求体就可以了

结语

今天整合了spring boot 2.6.5+es8.1.2,以后有时间看一下ik分词器,然后自己写一些注解在程序启动的时候基于aop创建索引。所有的代码在gitee上都可以看到,地址四月更文后端地址,大家可以自己下载试运行

  • 明天预告:
    1. nodejs+react+taro开发多端可运行的代码(uni-app其实会vue就不是很难,大家有兴趣我也写一版vue的)
    2. es8.1.2 ik分词关键词匹配查询,这个计划使用docker安装es 上面两个方向我今晚想一下要开发那个吧。还请大家期待一下明天的文章!