续 迁移用户信息面板功能
完成Sys模块调用
上次课我们完成了Faq模块的Rest接口的开发
下面要在sys模块中调用这个接口
转到knows-sys模块
UserServiceImpl业务逻辑层实现类
getUserVO方法中,利用Ribbon调用Faq模块的根据用户id查询问题数(收藏数)的方法
// 添加Ribbon支持
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
@Resource
private RestTemplate restTemplate;
@Override
public UserVO getUserVO(String username) {
// 根据用户名查询用户
User user=userMapper.findUserByUsername(username);
// 根据用户id查询问题数 和收藏数(作业)
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
String url=
"http://faq-service/v2/questions/count?userId={1}";
Integer count=restTemplate.getForObject(
url,Integer.class,user.getId());
// (作业)调用方法获得当前用户的收藏数......
// 实例化UserVO对象赋值并返回
UserVO userVO=new UserVO()
.setId(user.getId())
.setUsername(user.getUsername())
.setNickname(user.getNickname())
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
.setQuestions(count);
//(作业) 赋值收藏数到userVO对象
// 别忘了返回userVO
return userVO;
}
添加拦截器路径
控制器不需要修改
只需要将拦截器配置到路径,让拦截器在控制器运行前解析用户信息即可
sys模块的WebConfig类中添加拦截器路径
// 配置解析Jwt的拦截器生效
registry.addInterceptor(authInterceptor)
.addPathPatterns(
"/v1/home", // 判断身份跳转首页
"/v1/users/me" // 根据用户返回面板信息
);
前端axios调用修改
knows-client项目
user_info.js
axios({
url:"http://localhost:9000/v1/users/me",
method:"get",
params:{
accessToken:token
}
})
启动Nacos\gateway\auth
启动faq\sys\client
登录学生或讲师,观察首页用户信息面板内容
迁移文件上传功能
微服务环境下,我们的上传功能由knows-resource模块来负责
转到knows-resource模块
创建controller包
包中创建ImageController类,来编写上传文件的代码
将portal项目中SystemController中上传代码复制到这个类中即可
复制后代码如下
@RestController
// 因为项目设置了全局路径/image
// 所以这个控制方法实际访问路径为 /image/file
@RequestMapping("/file")
@Slf4j
public class ImageController {
// 从application.properties文件中获得配置信息的代码
@Value("${knows.resource.path}")
private File resourcePath;
@Value("${knows.resource.host}")
private String resourceHost;
// ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓
@PostMapping
public String upload(MultipartFile imageFile) throws IOException {
// 代码没有修改
// 略....
}
}
代码中使用@Value注解从application.properties配置文件中获得需要的信息,现在knows-resource项目中并没有配置
所以到配置文件中添加:
# 上传文件需要的路径和服务器端口
knows.resource.path=file:F:/upload
knows.resource.host=http://localhost:9000/image
ImageController类上添加跨域注解实现跨域
// 跨域注解
@CrossOrigin
public class ImageController {
//...
}
这个跨域注解适合当前项目只有少量控制器类时
转到knows-client项目
修改question/create.html页面中底部的js代码,上传的路径修改为
axios({
url:"http://localhost:9000/image/file",
method:"post",
data:form
})
启动knows-resource项目
重启knows-client
之前已经启动的服务不要停止
Nacos\gateway\auth\sys\faq
测试在create.html页面上的上传功能
迁移问题详情页
我们已经迁移了我们单体项目中的大部分功能
最后剩下的就是问题详情页的内容了
转到knows-faq模块完成最后的迁移工作
迁移数据访问层
本次迁移目标围绕answer回答和comment评论
先复制两个Mapper接口,导包就能解决错误
再复制xml文件到faq模块
代码如下
<mapper namespace="cn.tedu.knows.faq.mapper.AnswerMapper">
<!-- ↑↑↑↑↑ -->
<!-- 回答包含评论集合的映射结果 -->
<resultMap id="answerCommentMap"
type="cn.tedu.knows.commons.model.Answer">
<!-- ↑↑↑↑↑ -->
<id column="id" property="id" />
<result column="content" property="content" />
<result column="like_count" property="likeCount" />
<result column="user_id" property="userId" />
<result column="user_nick_name" property="userNickName" />
<result column="quest_id" property="questId" />
<result column="createtime" property="createtime" />
<result column="accept_status" property="acceptStatus" />
<collection property="comments"
ofType="cn.tedu.knows.commons.model.Comment">
<!-- ↑↑↑↑↑ -->
<id column="comment_id" property="id" />
<result column="comment_user_id" property="userId" />
<result column="comment_user_nick_name" property="userNickName" />
<result column="comment_answer_id" property="answerId" />
<result column="comment_content" property="content" />
<result column="comment_createtime" property="createtime" />
</collection>
</resultMap>
<select id="findAnswersByQuestionId" resultMap="answerCommentMap">
略...
</select>
</mapper>
迁移业务逻辑层
复制VO类和业务逻辑层接口直接导包即可
业务逻辑层实现类复制过来后发现不但需要导包,而且还需要Ribbon
两个类中需要多次相同的Ribbon请求的结果
为了减少代码冗余,我们创建RibbonClient类来提取出当前项目业务逻辑层中所有Ribbon请求尤其是多次调用的Ribbon请求的方法
在业务逻辑层实现类中需要时调用即可
RibbonClient类代码如下
// 将这个类型对象保存到Spring容器
@Component
public class RibbonClient {
@Resource
private RestTemplate restTemplate;
// 根据用户名获得用户对象
public User getUser(String username){
String url="http://sys-service/v1/auth/user?username={1}";
User user=restTemplate.getForObject(
url,User.class,username);
return user;
}
}
RibbonClient类中定义了根据用户名获得用户对象的方法
Answer和Comment的业务逻辑层实现类中,哪里需要哪里调用
只是别忘了实现在类中使用@Resource注解来获得RibbonClient对象
哪个类需要哪个类添加依赖注入
@Resource
private RibbonClient ribbonClient;
在需要根据用户名获得用户对象的行编写:
User user=ribbonClient.getUser(username);
迁移控制层
也是先导包
然后别忘了将两个控制器的路径都修改为v2开头
@RequestMapping("/v2/answers")
@RequestMapping("/v2/comments")
控制层代码处理完毕
添加拦截器生效路径
我们新迁移的多个方法都需要用户的信息
那么访问这些控制器方法的路径就要设置拦截器生效
knows-faq模块的拦截器配置要新增,以应对新迁移来的方法功能的需要
WebConfig类中再次添加拦截器配置路径如下
registry.addInterceptor(authInterceptor)
.addPathPatterns(
"/v2/questions", //发布问题
"/v2/questions/my", //学生首页
"/v2/questions/teacher", //讲师首页
"/v2/answers", //新增回答
"/v2/answers/*/solved", //采纳回答
"/v2/comments", //新增评论
"/v2/comments/*/delete", //删除评论
"/v2/comments/*/update" //修改评论
);
修改前端axios
knows-client项目
问题详情页所有axios请求都下question_detail.js文件中
修改这个js文件中所有axios请求,注意思考是否需要添加jwt信息
loadQuestion方法
axios({
url:"http://localhost:9000/v2/questions/"+qid,
method:"get"
})
postAnswer方法
let form=new FormData();
form.append("questionId",qid)
form.append("content",content);
form.append("accessToken",token);
axios({
url:"http://localhost:9000/v2/answers",
method:"post",
data:form
})
loadAnswers方法
axios({
url:"http://localhost:9000/v2/answers/question/"+qid,
method:"get"
})
postComment方法
let form=new FormData();
form.append("answerId",answerId);
form.append("content",content);
form.append("accessToken",token);
axios({
url:"http://localhost:9000/v2/comments",
method:"post",
data:form
})
removeComment方法
axios({
url:"http://localhost:9000/v2/comments/"+commentId+"/delete",
method:"get",
params:{
accessToken:token
}
})
updateComment方法
let form=new FormData();
form.append("answerId",answerId);
form.append("content",content);
form.append("accessToken",token);
axios({
url:"http://localhost:9000/v2/comments/"+commentId+"/update",
method:"post",
data:form
})
answerSolved方法
axios({
url:"http://localhost:9000/v2/answers/"+answerId+"/solved",
method:"get",
params:{
accessToken:token
}
})
重启faq模块和client项目,测试上述问题详情的功能
到此为止
达内知道单体项目功能就完全迁移到微服务了!!!
Elasticsearch全文搜索引擎
软件下载
什么是Elasticsearch
Elastic:富有弹性的
search:搜索
简称ES
它是一个由java开发的软件
也是一个中间件,不是SpringCloud的内容,单体项目也能使用
它能实现高效的从大量数据中进行模糊查询
启动这个软件,就相当于启动了一个java项目
控制这个软件的方法就是向这个项目发送各种请求掉用它的Rest接口
这些Rest接口的功能就能对ES中的数据进行增删改查操作
ES也是将数据保存在硬盘中的
Elasticsearch是一个基于Lucene开发的搜索服务器(搜索引擎),Lucene是一套提供了搜索引擎核心功能的Api,Lucene相当于计算的Cpu,Elasticsearch相当于一台安装好的电脑
Lucene也是java语言的产物,专门针对搜索功能,但是它不能直接使用
需要进行代码实现才能完成搜索功能
Elasticsearch还有类似的竞品软件:
solr \ MongoDB
为什么需要Elasticsearch
所有关系型数据库(mysql\oracle\DB2\sqlserver)都会有一个比较严重的缺陷
执行模糊查询(条件开头是模糊查询)的查询效率非常低
一张千万级别的数据库表,进行一次模糊查询需要20秒以上
而这个模糊查询如果在ES中进行可以将效率提高100倍以上
- Elasticsearch是java开发的,需要java环境变量
- Elasticsearch虽然是java开发的,但是任何语言都可以使用它
- Elasticsearch也是支持分布式部署的,满足"高并发,高可用,高性能"
Elasticsearch查询原理
ES查询原理的核心是分词索引
它会将一段文字中包含的所有词汇创建在一个索引库中
查询时,搜索所有包含这个词汇的文字,以提高查询效率
凡是能够按照分词索引进行查询的软件都可以称之为"全文搜索引擎"
我们常用的网站和App中的搜索功能,几乎都是由全文搜索引擎提供的
ES会将要优化查询的表的数据复制到ES的数据库中,也是复制到硬盘上
这样从ES中进行查询即使是大量数据的模糊查询,查询速度也能保持在毫秒级别
Elasticsearch的启动
官方下载链接
https://www.elastic.co/cn/downloads/past-releases#elasticsearch
将下载的280兆的压缩包解压
进入压缩包后得到如下bin目录中的内容
双击运行elasticsearch.bat文件,可以启动ES
dos窗口不能关,一关ES就停止工作了
ES没有支持开机自动启动的功能,所以每次开机需要ES时只能手动启动
怎么证明我们的ES正常工作呢
可以打开浏览器输入地址
localhost:9200
mac系统启动
tar -xvf elasticsearch-7.6.2-darwin-x86_64.tar.gz
cd elasticsearch-7.6.2/bin
./elasticsearch
linux:
tar -xvf elasticsearch-7.6.2-linux-x86_64.tar.gz
cd elasticsearch-7.6.2/bin
./elasticsearch
ES基本使用
我们安装启动好了ES软件
下面就是要调用ES提供的Rest接口,实现ES的各种功能
我们创建一个项目knows-search模块
这个项目用作实现搜索功能
父子相认
<module>knows-search</module>
子项目pom.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.tedu</groupId>
<artifactId>knows</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>cn.tedu</groupId>
<artifactId>knows-search</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>knows-search</name>
<description>Demo project for Spring Boot</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
</dependencies>
</project>
这个项目创建出来,我们先用于操作ES
在项目中创建一个http request文件(http client\http客户端)
这个文件可以编写http请求,发送给指定的服务器
现在我们的请求目标就是启动的ES:localhost:9200
文件名可以自定义
在文件中编写代码
### 这里先做最基本的ES测试
GET http://localhost:9200
### ES分词测试
POST http://localhost:9200/_analyze
Content-Type: application/json
{
"text": "运筹帷幄之间决胜千里之外",
"analyzer": "standard"
}
“analyzer”: "standard"是分词器的设定
默认其实就是standard是可以省略的
这个默认的分词器只能识别英文进行分词,原因是英文分词靠空格
而不能实现中文分词
我们需要安装一个插件,添加能够识别中文的分词器
这个插件的名字叫ik
因为安装插件相当于修改了java程序的配置
所以要想新配置生效,一定会重启java程序
关闭正在运行的ES窗口
重新打开运行
再次运行分词代码
{
"text": "罗技激光无线游戏鼠标",
"analyzer": "ik_smart"
}
再次运行分词,就能看到中文分词效果了
IK分词插件
上面已经完成了中文分词插件的安装
但是只使用了一个ik_smart的分词器
实际上插件还包含其他的分词器,他们有不同的特征
### 分词功能测试
POST http://localhost:9200/_analyze
Content-Type: application/json
{
"text": "北京举行了冬季奥林匹克运动会",
"analyzer": "ik_max_word"
}
### 分词功能测试
POST http://localhost:9200/_analyze
Content-Type: application/json
{
"text": "北京举行了冬季奥林匹克运动会",
"analyzer": "ik_smart"
}
上面同样的中文文字片段不同的中文分词器分词结果不同
经过分析我们可以知
ik_max_word:会详细的将文字片段分词,已经分词过的内容可能继续分词
分词详细,查全率高,但是占用空间大,查询速度慢
ik_smart:会粗略的将文字片段分词,已经分词过的内容不会再次分词
分词粗略,查全率低,但是占用空间小,查询速度快
下面我们要对ES进行基本的增删改查操作
操作ES数据
要想操作ES,先了解ES保存数据的结构
-
ES软件可以创建多个index(索引),我们可以将它理解为数据库中表的概念
-
一个索引中可以保存多个document(文档),每个文档相当与数据库表中的一行
-
一个文档是表中的一行数据,可以理解为一个对象,一般可以表示为json格式,json格式中的每一个属性对应表中的一个列
同学们从发给大家的es文档中
逐行运行每个命令,体会es的功能和数据的变化
详见文档内容
随笔
有关关系型数据库的索引
mysql这样的关系型数据库在按id查询时速度非常快
如果不查询id而查询像姓名这样的列,在默认情况下,会引发全表搜索,会逐行比对和查询,查询效果非常差,效率低
针对这种情况,我们可以在Mysql数据库中创建"索引"
来提高查询速度
类似字典中的偏旁部首查询
再次按照姓名查询时,可以大幅提高查询速度
但是如果查询条件是前模糊的
就无法使用创建的索引,一定是按全表,逐行搜索,效率非常低
所以才会出现ES
炉爆炎炸
燚
暴