学成在线(第11天)

Posted anan-java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了学成在线(第11天)相关的知识,希望对你有一定的参考价值。

 课程搜索需求分析

 需求分析

技术图片

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的能力。

 

 

 

 

以上是关于学成在线(第11天)的主要内容,如果未能解决你的问题,请参考以下文章

学成在线(第3天)

学成在线(第15天)

学成在线(第1天)

学成在线(第17天)

学成在线(第6天)

学成在线(第7天)