SpringCloud实战 在分布式博客项目中整合ElasticSearch完成文章检索

1,795 阅读5分钟

前言

最近在开发一个分布式的博客项目,目前正在开发检索功能,因为做这个项目本意也是自己从0的开始学习微服务开发,想尽量多的应用一些技术栈,所以,系统的检索部分,就整合了ELasticSearch全文检索来做,本文分享一下,ELasticSearch从安装、到SpringBoot整合的全过程。

安装

我选择了在linux系统安装Es,系统版本是Centos7,使用Vagrant快速创建,感兴趣的朋友可参考 Vagrant + VirtualBox 快速搭建本地centos7环境

手动安装

下载安装包

我们先要下载安装包,主要有下面这些:

  • Jdk:Es的运行环境
  • ElasticSearch安装包:全文检索服务
  • Kibana:Es的可视化工具
  • ik分词器:第三方分词器

经过简单调研,Es版本我选择了7.17.18,因为7.x已经挺久了,资料肯定更全面,再加上我是自学的目的,所以选择了这个版本。还有一个原因,ik分词器,在7.x最后支持的版本好像就是7.17.18(我在github没看到有7.17.22的版本)。

Kibana是Es的可视化工具,主要用于我们开发过程中,查看数据使用。

jdk我还是用的1.8,这个版本在运行es7.x时可能会提示过时,不过还能用。

下载地址:

安装Es

将elasticsearch-7.17.18-linux-x86_64.tar.gz拷贝到/opt目录下解压

tar -zxvf elasticsearch-7.17.18-linux-x86_64.tar.gz -C /opt

然后需要进行一些配置

配置1

执行下面命令

sudo vi /etc/security/limits.conf 

添加如下配置:

{启动es的用户名} hard nproc 4096
{启动es的用户名} soft nproc 4096
{启动es的用户名} hard nofile 65535
{启动es的用户名} soft nofile 65535

再执行下面命令

sudo systemctl daemon-reload

配置2

执行下面命令:

sudo vi /etc/sysctl.conf

添加如下配置:

vm.max_map_count=655360

执行命令:

sudo sysctl -p

配置3

执行命令

chmod -R 777 /opt

配置4:es配置文件

修改es的配置文件:主要是设置了一些节点名称、开放任意ip访问、关闭geoip库等。

sudo vi /opt/elasticsearch-7.17.18/config/elasticsearch.yml

添加如下配置:

cluster.name: my-cluster
node.name: single-node
cluster.initial_master_nodes: ["single-node"]

ingest.geoip.downloader.enabled: false
network.host: 0.0.0.0

接下来就可以启动Es了(不要用root用户,会启动失败。用上面配置的启动es的用户名)

nohup /opt/elasticsearch-7.17.18/bin/elasticsearch &

启动后可以通过9200端口访问

安装Kibana

先解压

tar -zxvf kibana-7.17.18-linux-x86_64.tar.gz -C /opt

修改配置文件

sudo vi /opt/kibana-7.17.18/config/kibana.yml

将server.host这个配置项的值改为0.0.0.0

启动

nohup /opt/kibana-7.17.18/bin/kibana &

启动后可以通过5601端口访问

安装ik分词器

将下载好的elasticsearch-analysis-ik-7.17.18.zip拷贝到/opt/elasticsearch-7.17.18/plugins下解压即可,然后重启es

可以在/opt/elasticsearch-7.17.18/bin目录下,执行下面命令 查看已安装的插件

elasricsearch-plugin list

docker安装

配置 yum 源

备份原文件

 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup

下载新文件

wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

没有 wget 的 可以去网址下载之后 拷贝到服务器上 修改名称
运行命令

yum clean all
yum makecache

安装必须依赖

sudo yum install -y yum-utils  device-mapper-persistent-data  lvm2

设置 docker 源

sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

安装 docker 和 docker compose

sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

设置开启自启

sudo systemctl enable docker

配置镜像

在 /etc/docker/daemon.json 中配置镜像站

{
    "registry-mirrors": [
        "https://dockerpull.pw"
    ]
}

刷新

sudo systemctl daemon-reload && sudo systemctl restart docker

直接根据项目中的docker-compose文件安装

    docker compose -f docker-compose.env.yml up -d

SpringBoot整合

引入依赖

<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>  
</dependency>

导入这个starter,基本不用进行什么配置,只需要在yml配置es地址即可

spring:
  elasticsearch:
    uris: 192.168.56.20:9200

在需要操作Es的地方,注入RestHighLevelClient即可,通过这个对象来操作es。

@Autowired
private RestHighLevelClient client;

业务应用

简单介绍一下 我的项目中目前对es的应用:

项目中创建一个blog-search模块,来处理全文检索相关的请求。用户在发布文章时,在文章保存接口中,通过openFeign远程调用blog-search,将组装好的文章信息,上传到es:

检索页面如下:

搜索.png

代码如下:

//调用检索服务,将博客检索信息上传到es  
BlogDocument doc = baseMapper.getBlogDocumentByBlogId(blog.getUid());  
searchFeignService.publish(doc);

blog-search中,上传部分:

@RestController
@RequestMapping("/publish")
public class PublishController {

    @Autowired
    private RestHighLevelClient client;

    /**
     * 根据传入的doc对象,上传文章数据到es
     *
     * @param doc
     * @return
     */
    @PostMapping
    public Result publish(@RequestBody BlogDocument doc) {
        IndexRequest indexRequest = new IndexRequest("blog_list");

        indexRequest.id(doc.getUid());
        indexRequest.source(JSONUtil.toJsonStr(doc), XContentType.JSON);

        try {
            IndexResponse indexResponse = client.index(indexRequest, RequestOptions.DEFAULT);
            return Result.success("文章上传到es成功!");
        } catch (Exception e) {
            return Result.error("文章上传到es失败!" + e.getMessage());
        }
    }
}

用户在系统中检索时,就从es查询数据:

@Service("es")
public class EsSearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient client;

    /**
     * 根据传入的关键字以及分页参数,检索数据 封装为SearchVo并返回
     *
     * @param keyword
     * @param page
     * @return
     */
    @Override
    public SearchVo search(String keyword, Long page) {
        if (page == null) page = 1L;
        page = (page - 1L) * 10L;

        SearchRequest searchRequest = new SearchRequest("blog_list");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //设置检索参数
        if (StrUtil.isNotBlank(keyword)) {
            searchSourceBuilder.query(QueryBuilders.matchQuery("title", keyword));
            //设置检索关键字高亮
            HighlightBuilder builder = new HighlightBuilder();
            builder.field("title");
            builder.preTags("<span style='color:red'>");
            builder.postTags("</span>");
            searchSourceBuilder.highlighter(builder);
        }

        //设置分页参数
        searchSourceBuilder.from(page.intValue());
        searchSourceBuilder.size(10);

        searchRequest.source(searchSourceBuilder);

        try {
            SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = searchResponse.getHits();
            SearchVo vo = new SearchVo();
            if (hits.getHits() != null) {
                vo.setTotal(hits.getTotalHits().value);
                List<BlogDocument> list = Arrays.stream(hits.getHits())
                        .map(hit -> {
                            BlogDocument blogDocument = JSONUtil.toBean(hit.getSourceAsString(), BlogDocument.class);
                            //处理关键字高亮
                            HighlightField title = hit.getHighlightFields().get("title");
                            blogDocument.setTitle(title.getFragments()[0].toString());
                            return blogDocument;
                        }).collect(Collectors.toList());
                vo.setList(list);
            }
            return vo;
        } catch (Exception e) {
            return null;
        }
    }
}

有一个注意事项:我们博客文章,可能用自定义的ik分词器更好,用es默认分词器可能一些常见的词识别不出来。所以要在创建es中的索引时,指定分词器类型,一般ik_smart就够用了。

在kibana的Dev Tools执行下面指令

PUT /blog_list
{
  "settings": {
    "analysis": {
      "analyzer": {
        "ik_smart": {
          "type": "ik_smart"
        }
      }
    }
  }
}

总结

以上,就是我在自己的分布式项目中,对于Es的简单应用了。希望可以帮到你,谢谢