微服务解决方案 -- 高效搜索 Elastic Search 7.6.2 (下)

微服务解决方案 – 高效搜索Elastic Search 7.6.2 (下)

前言

上一篇ES 是我在4周前写的,由于工作原因,一直没有发布,现在我们公司已经开始使用了ES,刚好就把这个一起写了。
微服务解决方案 – 高效搜索 Elastic Search 7.6.2 (上)

先说一下为什么我们公司要使用ES,因为我们公司有一个表大概在20W+,我们希望能够快速的查找相似数据,进行对比。MySQL虽然能使用like进行查找,但是分词等技术还得想办法解决。于是我们本来是打算使用MySQL+Drools,进行查询使用规则引擎给每一个查询结果进行打分去实现(不过我一直觉得加上Drools好像没什么意义)。

然后经过一段时间的讨论,最终决定还是使用ES去 实现这种查找,20W 虽然不多,但相对于MySQL的模糊查找来说,ES显得更加合适。

SpringBoot

首先创建一个springboot项目,查看一下我们的版本,因为我们公司的小伙伴使用的是2.1.8.RELEASE,所以我得和他们保持统一。
点开parent 项目
在这里插入图片描述
然后点开spring-boot-start-parentparent项目(葫芦娃找爷爷)
在这里插入图片描述
最后我们查找一下elasticsearch的依赖
在这里插入图片描述
到时候得手动修改他的版本号,因为我们装的ES7.6.2的版本。

我们只需要在自己的项目里的<properties></properties>加上和他一样的标签就行。

<properties>
    <!-- springboot 2.1.8.RELEASE 默认是6.4.3 手动修改版本-->
	<elasticsearch.version>7.6.2</elasticsearch.version>
</properties>
<dependencies>
	<!-- springboot start  -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
    </dependency>

    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>elasticsearch-rest-high-level-client</artifactId>
    </dependency>
</dependencies>

这样我们的版本就被替换成正确的了
在这里插入图片描述
接下来就是配置springboot,这里只贴关键代码
首先得配置es search的配置类,

package com.laoshiren.hello.elasticsearch.provider.configure;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * ProjectName:     hello-elasticsearch
 * Package:         com.laoshiren.hello.elasticsearch.provider.configure
 * ClassName:       ElasticSearchClientConfiguration
 * Author:          laoshiren
 * Date:            2020/7/9 16:21
 * Version:         1.0.0
 */
@Configuration
public class ElasticSearchClientConfiguration {

    @Value("${laoshiren.elastic.hostname}")
    private String hostname;
    @Value("${laoshiren.elastic.port}")
    private int port;
    @Value("${laoshiren.elastic.scheme}")
    private String scheme;

    @Bean
    public RestHighLevelClient restHighLevelClient(){
        return new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost(hostname, port, scheme)));
    }

}

包名叫configure吧,类名应该叫XXXConfiguration吧,配置就配置了一个RestHighLeveClient,就相当于有了xxxTemplate的感觉,我们拿这个东西去访问我们的ES,这里需要3个参数scheme,hostname,port,分别是协议,地址,端口。写在配置文件里。

laoshiren:
  elastic:
    hostname: 120.26.114.23
    port: 9200
    scheme: http

然后去测试类获取Client去写一个空方法调用吧(TDD编程嘛)

@SpringBootTest
@RunWith(SpringRunner.class)
public class ESClient {

    @Test
    public void runEmpty(){

    }

    @Resource
    private RestHighLevelClient client;
    @Resource
    private ObjectMapper objectMapper;

    @Test
    public void initClient(){
        System.out.println(client);
    }
}

等这2个方法都不报错,我们就可以继续学习了。

API

希望大家可以使用debug的方式查看每次请求完成后的response
创建索引

@Test
public void createIndex() throws IOException {
    // 索引请求
    CreateIndexRequest request = new CreateIndexRequest("organization");
    // 执行
    CreateIndexResponse response = client.indices()
            .create(request, RequestOptions.DEFAULT);
    System.out.println(response.index());
}

判断索引存不存在

@Test
public void existsIndex() throws IOException {
    GetIndexRequest request = new GetIndexRequest("organization");
    boolean exists = client.indices()
            .exists(request, RequestOptions.DEFAULT);
    System.out.println(exists);
}

删除索引

@Test
public void deleteIndex() throws IOException{
    DeleteIndexRequest request = new DeleteIndexRequest("tb_user");
    AcknowledgedResponse delete = client.indices()
            .delete(request, RequestOptions.DEFAULT);
    System.out.println(delete.isAcknowledged());
}

创建文档

@Test
public void createDoc() throws Exception {
    TbUser tbUser = new TbUser();
    tbUser.setCustomerNo("0001")
            .setGrpContNo("2020")
            .setFirstName("laoshiren1207")
            .setTransAmt(900)
            .setCreateDate(new Date());
    // 指定索引
    IndexRequest request = new IndexRequest("tb_user");
    // 设置规则
    request.id("1")
            .timeout(TimeValue.timeValueSeconds(5));
    // 对象转换json
    request.source(objectMapper.writeValueAsString(tbUser), XContentType.JSON);
    // 发送请求
    IndexResponse index = client.index(request, RequestOptions.DEFAULT);
    System.out.println(index.toString());
    // 命令返回的状态
    System.out.println(index.status());
}

文档存不存在

@Test
public void existsDoc()throws Exception{
    GetRequest request = new GetRequest("tb_user","1");
    boolean exists = client.exists(request, RequestOptions.DEFAULT);
    System.out.println(exists);
}

获取文档

@Test
public void getDoc() throws Exception{
    GetRequest request = new GetRequest("tb_user","1");
    GetResponse response = client.get(request, RequestOptions.DEFAULT);
    // 所有信息
    System.out.println(response.toString());
    // 获取doc
    System.out.println(response.getSourceAsString());
}

更新文档

@Test
public void postDocForUpdate() throws Exception {
    UpdateRequest request = new UpdateRequest("tb_user","1");
    request.timeout(TimeValue.timeValueSeconds(5));
    // 新对象
    TbUser tbUser = new TbUser();
    // 202007111030
    tbUser.setCreateDate(new Date());
    // 文档类型 XContentType
    request.doc(objectMapper.writeValueAsString(tbUser),XContentType.JSON);
    UpdateResponse update = client.update(request, RequestOptions.DEFAULT);
    System.out.println(update.status());
}

删除文档

@Test
public void deleteDoc() throws Exception{
    DeleteRequest request = new DeleteRequest("tb_user","1");
    request.timeout(TimeValue.timeValueSeconds(5));
    DeleteResponse delete = client.delete(request,RequestOptions.DEFAULT);
    System.out.println(delete.status());
}

批量insert操作

@Test
public void bulkInsert()throws Exception{
    BulkRequest bulkRequest = new BulkRequest();
    // 批量操作
    bulkRequest.timeout(TimeValue.timeValueSeconds(30));
    List<TbUser> list = new ArrayList<>();
    TbUser tbUser =new TbUser();
    tbUser.setCreateDate(new Date())
            .setTransAmt(23)
            .setFirstName("laoshiren")
            .setGrpContNo("00001")
            .setCustomerNo("00003");
    list.add(tbUser);
    TbUser tbUser2 =new TbUser();
    tbUser2.setCreateDate(new Date())
            .setTransAmt(23)
            .setFirstName("周杰伦")
            .setGrpContNo("00001")
            .setCustomerNo("00003");
    list.add(tbUser2);
    // 获取索引
    for (int i = 0; i< list.size(); i++) {
        //批处理请求
        IndexRequest index = new IndexRequest("tb_user")
                .id("" + (i + 2))
                // 转换json string
                .source(objectMapper.writeValueAsString(list.get(i)), XContentType.JSON);
        bulkRequest.add(index);
    }
    BulkResponse bulk = client.bulk(bulkRequest, RequestOptions.DEFAULT);
    System.out.println(bulk.status());
    System.out.println(bulk.hasFailures());
}

搜索

@Test
public void search() throws Exception{
    SearchRequest request = new SearchRequest("tb_user");
    // 构造
    SearchSourceBuilder builder = new SearchSourceBuilder();
    // 中文或者自定义字符串就要加上 field.keyword
    MatchQueryBuilder query= QueryBuilders.MatchQuery("firstName", "a");
    // 构建所需查询
    builder.query(query);
    builder.from(0);
    builder.size(5);
    builder.timeout(new TimeValue(60,TimeUnit.SECONDS));
    request.source(builder);
    SearchResponse search = client.search(request, RequestOptions.DEFAULT);
    System.out.println(search.toString());
    // 所需数据
    System.out.println(search.getHits());
    for (SearchHit hit : search.getHits().getHits()) {
        String s = objectMapper.writeValueAsString(hit);
        System.out.println(s);
    }
}

搜索不仅仅只有这一个他的QueryBuilders提供了大量的条件查询比如boolQuerytermQuery等。像我在工作中我就会用MatchQuery

BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
map.keySet().forEach(it -> {
    if (!StringUtils.isBlank(map.get(it).toString())) {
        boolQuery.must(
                QueryBuilders.matchQuery(it, map.get(it).toString())
                        // 指定分词
                        .analyzer(analyzer));
    }
});

更多的API可以参考其他的博主或者B站的一些up主,比如三太子敖丙,狂神说,lusifer(撸帝)还有什么很多的培训机构的一些文章视频都可以看看,一些新技术他们肯定会知道了解。
熟读唐诗三百首,不会吟诗也会吟嘛

Bug

java.io.IOException: 远程主机强迫关闭了一个现有的连接。
	at org.elasticsearch.client.RestClient.extractAndWrapCause(RestClient.java:828) ~[elasticsearch-rest-client-7.6.2.jar:7.6.2]

最近两天在准备测试,发现这个client长时间连接不使用会报异常,第一次请求报错,再一次请求就恢复正常了。所以修改了一下代码

 @Bean(name = "restSearchClient")
public RestHighLevelClient restHighLevelClient(){
    return new RestHighLevelClient(
            RestClient.builder(new HttpHost(host,port,scheme))
                    .setRequestConfigCallback(requestConfigBuilder -> {
                        requestConfigBuilder.setConnectTimeout(-1);
                        requestConfigBuilder.setSocketTimeout(30000);
                        requestConfigBuilder.setConnectionRequestTimeout(30000);
                        return requestConfigBuilder;
                    })
    );
}

隔了一晚,发现加上了好像也没什么用处,所以我就打算既然连接会死,那就每个一段时间请求一下ES服务器的的信息,即使死了,那再下次一请求这个客户端一定是可以。

@Scheduled(cron = "0 0 * * * ? ")
public void restClientKeepAlive() {
    try {
        log.info("schedule 保持ES客户端存活 start");
        MainResponse response = restClient.info(RequestOptions.DEFAULT);
        log.info("schedule 保持ES客户端存活 end");
    } catch (IOException ignore) {}
}

不知道有没有大佬还有其他的解决方法没有,可以指点一下,

联系方式联系方式
QQ1027575422
Email15207034473@163.com
©️2020 CSDN 皮肤主题: 数字50 设计师:CSDN官方博客 返回首页