zoukankan      html  css  js  c++  java
  • 学成在线(第11天)11-搜索服务 Logstash检索

     课程搜索需求分析

     需求分析

    1、根据分类搜索课程信息。
    2、根据关键字搜索课程信息,搜索方式为全文检索,关键字需要匹配课程的名称、 课程内容。
    3、根据难度等级搜索课程。
    4、搜索结点分页显示。

     搜索流程

    1、课程管理服务将数据写到MySQL数据库
    2、使用Logstash将MySQL数据库中的数据写到ES的索引库。
    3、用户在前端搜索课程信息,请求到搜索服务。
    4、搜索服务请求ES搜索课程信息。

    课程索引

    如何维护课程索引信息?
    1、当课程向MySQL添加后同时将课程信息添加到索引库。
    采用Logstach实现,Logstach会从MySQL中将数据采集到ES索引库。
    2、当课程在MySQL更新信息后同时更新该课程在索引库的信息。
    采用Logstach实现。
    3、当课程在MySQL删除后同时将该课程从索引库删除。
    手工写程序实现,在删除课程后将索引库中该课程信息删除。

    准备课程索引信息

    课程发布成功在MySQL数据库存储课程发布信息,此信息作为课程索引信息。

    创建课程发布表

    课程信息分布在course_base、course_pic等不同的表中。
    课程发布成功为了方便进行索引将这几张表的数据合并在一张表中,作为课程发布信息。
    创建course_pub表

    创建课程发布表模型

    在课程管理服务创建模型:

    @Data
    @ToString
    @Entity
    @Table(name="course_pub")
    @GenericGenerator(name = "jpa‐assigned", strategy = "assigned")
    public class CoursePub implements Serializable {
        private static final long serialVersionUID = ‐916357110051689487L;
        @Id
        @GeneratedValue(generator = "jpa‐assigned")
        @Column(length = 32)
        private String id;
    private String name;
        private String users;
        private String mt;
        private String st;
        private String grade;
        private String studymodel;
        private String teachmode;
        private String description;
        private String pic;//图片
        private Date timestamp;//时间戳
        private String charge;
        private String valid;
        private String qq;
        private Float price;
        private Float price_old;
        private String expires;
        private String teachplan;//课程计划
        @Column(name="pub_time")
        private String pubTime;//课程发布时间
    }
    View Code

     修改课程发布

    在课程管理服务定义dao:
    1)创建course_pub表的dao

    public interface CoursePubRepository extends JpaRepository<CoursePub, String> {
    }

    2) 修改课程发布service

     //保存CoursePub
        public CoursePub saveCoursePub(String id, CoursePub coursePub){
            if(StringUtils.isNotEmpty(id)){
                ExceptionCast.cast(CourseCode.COURSE_PUBLISH_COURSEIDISNULL);
            }
            CoursePub coursePubNew null;
            Optional<CoursePub> coursePubOptional = coursePubRepository.findById(id);
            if(coursePubOptional.isPresent()){
                coursePubNew = coursePubOptional.get();
            }
            if(coursePubNew == null){
                coursePubNew new CoursePub();
            }
            BeanUtils.copyProperties(coursePub,coursePubNew);
            //设置主键
            coursePubNew.setId(id);
            //更新时间戳为最新时间
            coursePub.setTimestamp(new Date());
            //发布时间
    SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY‐MM‐dd HH:mm:ss");
            String date = simpleDateFormat.format(new Date());
            coursePub.setPubTime(date);
            coursePubRepository.save(coursePub);
            return coursePub;
        }
        //创建coursePub对象
        private CoursePub createCoursePub(String id){
            CoursePub coursePub new CoursePub();
            coursePub.setId(id);
            //基础信息
            Optional<CourseBase> courseBaseOptional = courseBaseRepository.findById(id);
            if(courseBaseOptional == null){
                CourseBase courseBase = courseBaseOptional.get();
                BeanUtils.copyProperties(courseBase, coursePub);
            }
            //查询课程图片
            Optional<CoursePic> picOptional = coursePicRepository.findById(id);
            if(picOptional.isPresent()){
                CoursePic coursePic = picOptional.get();
                BeanUtils.copyProperties(coursePic, coursePub);
            }
            //课程营销信息
            Optional<CourseMarket> marketOptional = courseMarketRepository.findById(id);
            if(marketOptional.isPresent()){
                CourseMarket courseMarket = marketOptional.get();
                BeanUtils.copyProperties(courseMarket, coursePub);
            }
            //课程计划
            TeachplanNode teachplanNode = teachplanMapper.selectList(id);
            //将课程计划转成json
            String teachplanString = JSON.toJSONString(teachplanNode);
            coursePub.setTeachplan(teachplanString);
            return coursePub;
        }
    View Code

    修改课程发布方法,添加调用saveCoursePub方法的代码,添加部分的代码如下:

    //课程发布
        @Transactional
        public CoursePublishResult publish(String courseId){
            ....
            //创建课程索引
            //创建课程索引信息
            CoursePub coursePub = createCoursePub(courseId);
            //向数据库保存课程索引信息
            CoursePub newCoursePub = saveCoursePub(courseId, coursePub);
     if(newCoursePub==null){
                //创建课程索引信息失败
                ExceptionCast.cast(CourseCode.COURSE_PUBLISH_CREATE_INDEX_ERROR);
            }
           ....
        }

     搭建ES环境

    开发环境使用ES单机环境,启动ES服务端。
    注意:旧的ES环境,可以删除elasticsearch-6.2.1data odes目录以完全清除ES环境。
    安装elasticsearch-head并启动。

    创建索引库

    创建索引库
    创建xc_course索引库,一个分片,0个副本。

    创建映射

    在postman里添加映射

    Post http://localhost:9200/xc_course/doc/_mapping

    {
    "properties" : {
    
                "description" : {
                    "analyzer" : "ik_max_word", 
                "search_analyzer": "ik_smart",
                    "type" : "text"
                },
                "grade" : {
                    "type" : "keyword"
                },
                "id" : {
                    "type" : "keyword"
                },
                "mt" : {
                    "type" : "keyword"
                },
                "name" : {
                    "analyzer" : "ik_max_word", 
                "search_analyzer": "ik_smart",
                    "type" : "text"
                },
                "users" : {
                    "index" : false, 
                    "type" : "text"
                },
                "charge" : {
                    "type" : "keyword"
                },
                "valid" : {
                    "type" : "keyword"
                },
                "pic" : {
                    "index" : false, 
                    "type" : "keyword"
                },
                "qq" : {
                    "index" : false, 
                    "type" : "keyword"
                },
                "price" : {
                    "type" : "float"
                },
                "price_old" : {
                    "type" : "float"
                },
                "st" : {
                    "type" : "keyword"
                },
                "status" : {
                    "type" : "keyword"
                },
                "studymodel" : { 
                    "type" : "keyword"
                },
                "teachmode" : {
                    "type" : "keyword"
                },
                "teachplan" : {
                    "analyzer" : "ik_max_word", 
                "search_analyzer": "ik_smart",
                    "type" : "text"
                },
    
                "expires" : {
                    "type" : "date", 
                "format": "yyyy-MM-dd HH:mm:ss"
                },
                "pub_time" : {
                    "type" : "date", 
                "format": "yyyy-MM-dd HH:mm:ss"
                },
                "start_time" : {
                    "type" : "date", 
                "format": "yyyy-MM-dd HH:mm:ss"
                },
                "end_time" : {
                    "type" : "date", 
                "format": "yyyy-MM-dd HH:mm:ss"
                }
            }
        }

     Logstash 创建索引

    Logstash是ES下的一款开源软件,它能够同时 从多个来源采集数据、转换数据,然后将数据发送到Eleasticsearch
    中创建索引。
    本项目使用Logstash将MySQL中的数据采用到ES索引中。

    这里资料给的配套文件有很多坑,我把坑都踩了一遍,下面分享解决方法。

    下载Logstash

    下载Logstash6.2.1版本,和本项目使用的Elasticsearch6.2.1版本一致,版本要一致

    安装logstash-input-jdbc

    这里解压老师提供的logstash-6.2.1.zip即可,此logstash中已集成了logstash-input-jdbc插件

    创建模板文件

    Logstash的工作是从MySQL中读取数据,向ES中创建索引,这里需要提前创建mapping的模板文件以便logstash
    使用。
    在logstach的config目录创建xc_course_template.json,内容如下:

    {
       "mappings" : {
          "doc" : {
             "properties" : {
                "charge" : {
                   "type" : "keyword"
                },
                "description" : {
                   "analyzer" : "ik_max_word",
                   "search_analyzer" : "ik_smart",
                   "type" : "text"
                },
                "end_time" : {
                   "format" : "yyyy-MM-dd HH:mm:ss",
                   "type" : "date"
                },
                "expires" : {
                   "format" : "yyyy-MM-dd HH:mm:ss",
                   "type" : "date"
                },
                "grade" : {
                   "type" : "keyword"
                },
                "id" : {
                   "type" : "keyword"
                },
                "mt" : {
                   "type" : "keyword"
                },
                "name" : {
                   "analyzer" : "ik_max_word",
                   "search_analyzer" : "ik_smart",
                   "type" : "text"
                },
                "pic" : {
                   "index" : false,
                   "type" : "keyword"
                },
                "price" : {
                   "type" : "float"
                },
                "price_old" : {
                   "type" : "float"
                },
                "pub_time" : {
                   "format" : "yyyy-MM-dd HH:mm:ss",
                   "type" : "date"
                },
                "qq" : {
                   "index" : false,
                   "type" : "keyword"
                },
                "st" : {
                   "type" : "keyword"
                },
                "start_time" : {
                   "format" : "yyyy-MM-dd HH:mm:ss",
                   "type" : "date"
                },
                "status" : {
                   "type" : "keyword"
                },
                "studymodel" : {
                   "type" : "keyword"
                },
                "teachmode" : {
                   "type" : "keyword"
                },
                "teachplan" : {
                   "analyzer" : "ik_max_word",
                   "search_analyzer" : "ik_smart",
                   "type" : "text"
                },
                "users" : {
                   "index" : false,
                   "type" : "text"
                },
                "valid" : {
                   "type" : "keyword"
                }
             }
          }
       },
       "template" : "xc_course"
    }

    配置mysql.conf

    在logstash的config目录下配置mysql.conf文件供logstash使用,logstash会根据mysql.conf文件的配置的地址从
    MySQL中读取数据向ES中写入索引。

    配置输入数据源和输出数据源。

    input {
      stdin {
      }
      jdbc {
      jdbc_connection_string => "jdbc:mysql://localhost:3306/xc_course?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC"
      # the user we wish to excute our statement as
      jdbc_user => "root"
      #密码记得加双引号
      jdbc_password => ""
      # the path to our downloaded jdbc driver
      #配置本地仓库的mysql数据源jar的路径  
      jdbc_driver_library => "D:/Maven/m2/repository/mysql/mysql-connector-java/5.1.40/mysql-connector-java-5.1.40.jar"
      # the name of the driver class for mysql
      jdbc_driver_class => "com.mysql.jdbc.Driver"
      jdbc_paging_enabled => "true"
      jdbc_page_size => "50000"
      #要执行的sql文件
      #statement_filepath => "/conf/course.sql"
      statement => "select * from course_pub where timestamp > date_add(:sql_last_value,INTERVAL 8 HOUR)"
      #定时配置
      schedule => "* * * * *"
      record_last_run => true
      #配置本地logstash_metadata存放路径,上一次检索数据的时间点
      last_run_metadata_path => "D:/ElasticSearch01/logstash-6.2.1/config/logstash_metadata"
      }
    }
    
    
    output {
      elasticsearch {
      #ES的ip地址和端口
      hosts => "localhost:9200"
      #hosts => ["localhost:9200","localhost:9202","localhost:9203"]
      #ES索引库名称
      index => "xc_course"
      document_id => "%{id}"
      document_type => "doc"
      #覆盖本地的conf的json数据,同上
      template =>"D:/ElasticSearch01/logstash-6.2.1/config/xc_course_template.json"
      template_name =>"xc_course"
      template_overwrite =>"true"
      }
      stdout {
     #日志输出
      codec => json_lines
      }
    }

    说明:
    1、ES采用UTC时区问题
    ES采用UTC 时区,比北京时间早8小时,所以ES读取数据时让最后更新时间加8小时
    where timestamp > date_add(:sql_last_value,INTERVAL 8 HOUR)
    2、logstash每个执行完成会在D:/ElasticSearch01/logstash-6.2.1/config/logstash_metadata记录执行时间下次以此
    时间为基准进行增量同步数据到索引库。

    测试

    logstash.bat ‐f ..configmysql.conf

    启动测试前,要把D:ElasticSearch01logstash-6.2.1bin下的logstash.bat的最后一行的classpath加双冒号,不然启动不了,报找不到启动类。

     

     改好后重新启动

     只查到一个数据,这里也有坑。要把logstash_metadata里的上一次记录的时间改比数据库所有的时间都要小!!

    statement => "select * from course_pub where timestamp > date_add(:sql_last_value,INTERVAL 8 HOUR)"

     改了后,可以看到可以查到所有数据了!

     再去查看Elasticsearch,但是数据获取不到,显示不出来??数据已经在更新了啊!

     有延时?无论我怎么重启,重装都是这样。。。

     最后各种翻博客,卸载重装,查看Elasticsearch的日志文件,不断摸索才发现问题所在。

     这里说的是xc_course_template.json的pub_time中的日期格式和postman里添加映射的日期格式两次不一致导致的!

    这是PDF复制过去的锅,原先日期格式的"-"为中文格式,要改成英文格式!!

    照着上面步骤再重新弄一遍,把坑避免,可以看到终于成功了!!!

     最后总结一下出现的bug:

    1.mysql.conf配置文件中的数据库密码加双引号

    2.bin下的logstash.bat的最后一行的classpath加双冒号

    3.要把logstash_metadata里的上一次记录的时间改比数据库所有的时间都要小

    4.xc_course_template.json的pub_time中的日期格式和postman里添加映射的日期格式中的"-"为中文格式,要改成英文格式

    遇到bug的时候不要急躁,要有耐心,一次不行就卸载重装再试。可以先做其他事分散注意力,一定找问题出在哪里,通过他人的博客寻求思路。

    不断摸索,一定可以迎刃而解,可以提升自己解决bug的能力。

  • 相关阅读:
    ASP.NET API盘点
    C# POST与Get数据
    洛谷 P3373 【模板】线段树 2
    洛谷 P1972 [SDOI2009]HH的项链
    洛谷 P1113 杂务(vector)
    POJ 3249 Test for Job
    POJ 1734 Sightseeing trip(Floyd)
    洛谷 P1202 [USACO1.1]黑色星期五Friday the Thirteenth
    洛谷 P1484 种树
    洛谷 P1801 黑匣子_NOI导刊2010提高(06)
  • 原文地址:https://www.cnblogs.com/anan-java/p/12268307.html
Copyright © 2011-2022 走看看