SpringCloud

一.微服务的调用方式
基于RestTemplate发起的http请求实现远程调用
使用Srping提供的RestTemplate进行远程调用


RestTemplate中使用getForObject或postForObject来发起http的get或post请求
注意:得到的是json格式的数据,使用第二个参数RestTemplate可以自动为我们提供反序列化将json格式转换为java对象
提供者与消费者
- 服务提供者:一次业务中,被其他微服务调用的业务。(提供接口给其他微服务)
- 服务消费者:一次业务中,调用其他微服务的服务。(调用其他微服务提供的接口)
由订单微服务调用查询用户微服务

总结:
1.服务的调用关系
- 服务提供者:暴露接口给其他微服务调用
- 服务消费者:调用其他微服务提供的接口
- 提供者与消费者角色是相对的
- 一个微服务既可以是提供者也可以使消费者
二.Eureka注册中心
服务调用出现的问题

Eureka的作用

总结:
在Eureka架构中,微服务角色有两种:
EurekaServer:服务端,注册中心
- 记录服务信息
- 心跳监控
EurekaClient:客户端
Provider:服务提供者,例如案例中的user-server
- 注册自己的信息到EurekaServer
- 每隔30秒向EurekaServer发送心跳
consumer:服务消费者,例如案例中的order-server
- 根据服务名称从EurekaServer拉取服务列表
- 基于服务列表做负载均衡,选中一个微服务后发起远程调用
搭建EurekaServer
搭建EurekaServer步骤
1.创建项目,引入spring-cloud-starter-netflix-eureka-server的依赖
1 | <dependency> |
2.编写启动类,添加@EnableEurekaServer注解(开启EurekaServer)
3.添加applicat.yml文件,编写下面配置
1 | server: |
Ribbon负载均衡
负载均衡流程

负载均衡策略


通过定义IRule实现可以修改负载均衡规则,有两种方式:
1.代码方式:在配置类中定义一个全新的IRule:
1 |
|
2.配置文件方式:在yml中,添加新的配置也可以修改规则
1 | userservice: |
饥饿加载
Ribbon默认采用的是懒加载,即第一次访问时才会去创建LoadBanlanceClient,请求时间很长,而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载
1 | ribbon: |
对比:
默认懒加载第一次访问所需时间:366ms

第二次访问所需时间:15ms

开启饥饿加载后第一次所需时间:296ms(第一次加载时还要完成dispatcherServlet初始化)

第二次访问所需时间:14ms

总结:
1.Ribbon负载均衡规则
- 规则接口是IRule
- 默认实现的是ZoneAvoidanceRule,根据zone选择服务列表,然后轮询
2.负载均衡自定义方式
- 代码方式:配置灵活、但修改时需要重新打包发布
- 配置方式:直观,方便,无需重新打包发布,但是无法做到全局配置
3.饥饿加载
- 开启饥饿加载
- 指定饥饿加载的服务名称
三.Nacos服务注册中心
Nacos服务搭建
- 下载安装包
- 解压
- 在bin目录下运行指令:startup.cmd -m standalone
- 默认服务地址端口为localhost:8848,可以在conf配置文件中修改端口
Nacos服务注册或发现
- 引入nacos.discovery依赖
- 配置nacos地址spring.cloud.nacos.server-addr
Nacos服务分级存储模型

总结:
1.Nacos服务分级存储模型
- 一级是服务,例如userservice
- 二级是集群,例如杭州或上海
- 三级是实例,例如杭州机房的某台部署了userservice的服务器
2.如何设置实例的集群属性
- 修改application.yml文件,添加spring.cloud.nacos.discovery.cluster-name属性即可
1 | spring: |

根据集群负载均衡

application.yml配置文件设置如下
1 | spring: |
设置NacosRule,默认先在本地同集群中寻找服务(NacosRule会优先选择同集群服务,然后在同集群服务中再使用随机选择方式,如果找不到同集群服务就会去其他集群寻找并且会报警告)
application.yml配置文件设置如下
1 | userservice: |
警告信息如下

总结:
1.Nacos负载均衡策略
- 优先选择同集群服务实例列表
- 本地集群找不到提供者,才会去其他集群寻找,并且会报警告
- 确定了可用实例列表后,再采用随机负载均衡挑选实例
加权负载均衡

给服务设置权重

总结:
1.实例的权重控制
- Nacos控制台可以设置实例的权重值,0-1之间
- 同集群内的多个实例,权重越高被访问的频率越高
- 权重设置为0则完全不会被访问
环境隔离 - namespace

创建namespace步骤



将order-service放入dev空间后,order-service就访问不到userservice,请求时会报错

总结:
1.Nacos环境隔离
- namespace用来做环境隔离
- 每个namespace都有唯一id
- 不同namespace下的服务不可见
Nacos注册中心原理

临时实例和非临时实例
服务注册到Nacos时,可以选择注册为临时或非临时实例,通过下面配置来设置
1 | spring: |
临时实例宕机时,会从nacos的服务列表中剔除,而非临时实例则不会
总结:
1.Nacos与Eureka的共同点
- 都支持服务注册和服务拉取
- 都支持服务提供者心跳方式做健康监测
2.Nacos与Eureka的区别
- Nacos支持服务端主动监测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
- 临时实例心跳不正常会被剔除,而非临时实例则不会被剔除
- Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
- Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP模式
Nacos配置管理
微服务配置拉取
原本的配置文件读取过程

使用Nacos配置管理后配置文件读取过程

统一配置管理
1.引入Nacos的配置管理客户端依赖
1 | <!--nacos配置管理客户端依赖--> |
2.在userservice中的source目录添加一个bootstrap.yml文件,这个文件是引导文件,优先级高于application.yml:
1 | spring: |
总结:
将配置交给Nacos管理的步骤
①在Nacos中添加配置文件
②在微服务中引入nacos的config依赖
③在微服务中添加bootstrap.yml,配置nacos地址、当前环境、服务名称、文件后缀名。这些决定了程序启动时去nacos读取哪个文件
配置热更新
有两种配置热更新方式:
1.使用@Value方式注入

2.使用@ConfigurationProperties注入

再结合@Autowired使用

总结:
Nacos配置更改后,微服务可以实现热更新,方式:
①通过@Value注解注入,结合@RefreshScope来刷新
②通过@ConfigurationProperties注入,自动更新
注意事项:
- 不是所有配置都适合放到配置中心,维护起来比较麻烦
- 建议将一些关键参数,需要运行时调整的参数放到Nacos配置中心,一般都是自定义配置
多环境配置共享
多服务共享配置
多种配置的优先级:
- 服务名-profile.yaml(Nacos中配置) > 服务名称.yaml(Nacos中配置) > 本地配置
总结:
微服务会从Nacos读取的配置文件:
①[服务名]-[spring.profile.active].yaml,环境配置
②[服务名].yaml,默认配置,多环境共享
优先级:
[服务名]-[环境].yaml > [服务名].yaml > 本地配置
Nacos集群搭建
1.集群结构图
官方给出的Nacos集群图:

其中包含3个nacos节点,然后一个负载均衡器代理3个Nacos。这里负载均衡器可以使用nginx。
我们计划的集群结构:

三个nacos节点的地址:
| 节点 | ip | port |
|---|---|---|
| nacos1 | 192.168.150.1 | 8845 |
| nacos2 | 192.168.150.1 | 8846 |
| nacos3 | 192.168.150.1 | 8847 |
2.搭建集群
搭建集群的基本步骤:
- 搭建数据库,初始化数据库表结构
- 下载nacos安装包
- 配置nacos
- 启动nacos集群
- nginx反向代理
2.1.初始化数据库
Nacos默认数据存储在内嵌数据库Derby中,不属于生产可用的数据库。
官方推荐的最佳实践是使用带有主从的高可用数据库集群,主从模式的高可用数据库可以参考传智教育的后续高手课程。
这里我们以单点的数据库为例来讲解。
首先新建一个数据库,命名为nacos,而后导入下面的SQL:
1 | CREATE TABLE `config_info` ( |
2.2.下载nacos
nacos在GitHub上有下载地址:https://github.com/alibaba/nacos/tags,可以选择任意版本下载。
本例中才用1.4.1版本:

2.3.配置Nacos
将这个包解压到任意非中文目录下,如图:

目录说明:
- bin:启动脚本
- conf:配置文件
进入nacos的conf目录,修改配置文件cluster.conf.example,重命名为cluster.conf:

然后添加内容:
1 | 127.0.0.1:8845 |
然后修改application.properties文件,添加数据库配置
1 | spring.datasource.platform=mysql |
2.4.启动
将nacos文件夹复制三份,分别命名为:nacos1、nacos2、nacos3
然后分别修改三个文件夹中的application.properties,
nacos1:
1 | server.port=8845 |
nacos2:
1 | server.port=8846 |
nacos3:
1 | server.port=8847 |
然后分别启动三个nacos节点:
1 | startup.cmd |
2.5.nginx反向代理
找到课前资料提供的nginx安装包:
解压到任意非中文目录下:
修改conf/nginx.conf文件,配置如下:
1 | upstream nacos-cluster { |
而后在浏览器访问:http://localhost/nacos即可。
代码中application.yml文件配置如下:
1 | spring: |
2.6.优化
实际部署时,需要给做反向代理的nginx服务器设置一个域名,这样后续如果有服务器迁移nacos的客户端也无需更改配置.
Nacos的各个节点应该部署到多个不同服务器,做好容灾和隔离
总结:
①搭建MySQL集群并初始化数据表
②下载解压nacos
③修改集群配置(节点信息)、数据库配置
④分别启动多个nacos节点
⑤nginx反向代理
http客户端Fegin-快速入门
feign替代RestTemplate

使用RestTemplate存在下面问题:
代码可读性差,编程体验不统一
参数复杂URL难以维护
使用Feign的步骤如下:
1.引入依赖
1 | <!--fegin客户端依赖--> |
2.在启动类添加注解开启Feign的功能:
1 |
|
3.编写Feign客户端:
1 |
|
主要是基于springMVC的注解来声明远程调用的信息,比如:
- 服务名称:userservice
- 请求方式:GET
- 请求路径:/user/{id}
- 请求参数:Long id
- 返回值类型:User
总结:
①引入依赖
②添加EnableFeignClients注解
③编写FeignClient接口
④使用FeignClient中定义的方法代替RestTemplate
自定义Feign的配置
配置Feign的两种方式:
方式一:配置文件方式
①全局生效:

②局部生效:

方式二:java代码方式,需要先声明一个Bean:
1 | public class DefaultConfig { |
①而后如果是全局配置,则放到@EnableFeignClients这个注解中:
1 |
②如果是局部配置,则放到@FeignClient这个注解中:
1 |
Feign性能优化
Feign需要优化的原因:避免多次创建和消耗连接所带来的性能消耗,http连接需要三次握手而断开连接又需要四次挥手,降低了性能。
Feign底层的客户端实现:
- URLConnection:默认实现,不支持连接池
- Apache HttpClient:支持连接池
- OKHttp:支持连接池
因此优化Feign的性能优化主要包括:
①使用连接池代替默认的URLConnection
②日志级别:最好使用basic或none
Feign的性能优化-连接池配置
Feign添加HttpClient的支持:
引入依赖:
1 | <!--引入HttpClient依赖--> |
配置连接池:
1 | feign: |
Feign的最佳实践
方式一:继承

缺点:①耦合度太高,后期如果UserAPI需要更改,其子类和实现类都要进行修改
②这种继承对springMVC是不起作用的,即方法参数是继承不下来的,UserController需要自己去实现逻辑,而且注解需要自己再写一遍。
方式二:抽取

缺点:①如果orderservice只需要一个其中的某一两个方法,但是用依赖会将整个feign-api引入,就显得比较多余。
抽取FeignClient
实现最佳实践方式二的步骤如下:

①创建module并引入feign依赖

②在orderservice引入feign-api的依赖
1 | <!--引入feign的统一api--> |
启动时发现报找不到UserClient,编译没报错说明有这个类确实有,无法注入成功说明这个类没有创建对象,在spring容器中找不到

原因:原先能创建对象的原因是@FeignClient(value = “userservice”)这个注解,spring扫描到这个注解就会给这个注解创建对象,而现在UserCilent在feign-api的包中,spring扫描不到所以无法创建对象。
解决方案:

方式一:会将Client包中的所有的客户端都加载进来
方式二:会精准加载对应的客户端(推荐)
解决后启动成功!

统一网关Gateway


总结:
- 对用户请求做身份认证、权限校验
- 将用户请求路由到微服务,并实现负载均衡
- 对用户请求做限流(防止超额请求访问微服务)
搭建网关服务
搭建网关的步骤:
1.创建新的module,引入springCloudGateway的依赖和nacos的服务发现依赖(因为网关本身也是一个微服务所以也需要注册到nacos中)
1 | <!-- nacos服务发现依赖--> |
2.编写路由配置及nacos地址
1 | server: |
重启测试发现即使我们在gateway中没有做业务逻辑也是可以查询到数据的,因为网关将请求发送到对应的微服务中。

整个流程图解:

总结:
网关搭建步骤:
1.创建项目,引入nacos服务发现和gateway依赖
2.配置application.yml,包括服务基本信息,nacos地址,路由
路由配置包括:
1.路由id:路由的唯一标示
2.路由目标(url):路由的目标地址,http代表固定地址,lb代表根据服务名负载均衡
3.路由断言(predicates):判断路由规则
路由断言工厂Route Predicate Factory



需要时可以根据需求去spring官方文档查阅直接用

路由过滤器GatewayFilter

Spring官方提供了30多种过滤器,可按需查询配置

实现方式(只对user/**下的请求有效)

默认过滤器

总结:
- 过滤器的作用是什么?
①对路由的请求或响应做加工处理,比如添加请求头
②配置在路由下的过滤器只对当前路由的请求生效
- defaultFilters的作用是什么?
①对所有路由都生效的过滤器
全局过滤器GlobalFilter

案例:

步骤:自定义过滤器
自定义类,实现GlobaFilter接口,添加Order注解
1 |
|
使用@Order注解或者实现Ordered中的getOrder方法来设置过滤的执行优先级(数值越低优先级越高)
运行效果:
失败:

成功:

总结:
- 全局过滤器的作用是什么?
对所有路由都生效的过滤器,并且可以自定义处理逻辑
- 实现全局过滤器的步骤?
①实现GlobaFilter接口
②添加@Order注解或实现Ordered接口
③编写处理逻辑
过滤器执行顺序

执行顺序规则:

跨域问题处理

网关处理采用的同样是CORS方案,并且只需要简单配置即可实现:
1 | spring: |
总结:
CORS跨域需要配置的参数包括哪几个:
- 允许哪些域名跨域?
- 允许哪些请求头?
- 允许哪些请求方式?
- 是否允许使用cookie?
- 有效期是多久?
什么是Docker





总结:
Docker是一个快速交付应用、运行应用的技术:
1.可以将程序及其依赖、运行环境一起打包为一个镜像,可以迁移到任意Linux操作系统
2.运行时利用沙箱机制形成隔离容器,各个应用互不干扰
3.启动、移除都可以通过一行命令完成,方便快捷
Docker架构



总结:
镜像:
- 将应用程序及其依赖、环境、配置打包在一起
容器:
- 镜像运行起来就是容器,一个镜像可以运行多个容器
Docker结构:
- 服务端:接收命令或远程请求,操作镜像或容器
- 客户端:发送命令或者请求到Docker服务端
DockerHub:
- 一个镜像托管的服务器,类似的还有阿里云镜像服务,统称为DockerRegistry
0.安装Docker
Docker 分为 CE 和 EE 两大版本。CE 即社区版(免费,支持周期 7 个月),EE 即企业版,强调安全,付费使用,支持周期 24 个月。
Docker CE 分为 stable test 和 nightly 三个更新频道。
官方网站上有各种环境下的 安装指南,这里主要介绍 Docker CE 在 CentOS上的安装。
1.CentOS安装Docker
Docker CE 支持 64 位版本 CentOS 7,并且要求内核版本不低于 3.10, CentOS 7 满足最低内核的要求,所以我们在CentOS 7安装Docker。
1.1.卸载(可选)
如果之前安装过旧版本的Docker,可以使用下面命令卸载:
1 | yum remove docker \ |
1.2.安装docker
首先需要大家虚拟机联网,安装yum工具
1 | yum install -y yum-utils \ |
然后更新本地镜像源:
1 | 设置docker镜像源 |
然后输入命令:
1 | yum install -y docker-ce |
docker-ce为社区免费版本。稍等片刻,docker即可安装成功。
1.3.启动docker
Docker应用需要用到各种端口,逐一去修改防火墙设置。非常麻烦,因此建议大家直接关闭防火墙!
启动docker前,一定要关闭防火墙后!!
启动docker前,一定要关闭防火墙后!!
启动docker前,一定要关闭防火墙后!!
1 | # 关闭 |
通过命令启动docker:
1 | systemctl start docker # 启动docker服务 |
然后输入命令,可以查看docker版本:
1 | docker -v |
如图:
1.4.配置镜像加速
docker官方镜像仓库网速较差,我们需要设置国内镜像服务:
参考阿里云的镜像加速文档:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
2.CentOS7安装DockerCompose
2.1.下载
Linux下需要通过命令下载:
1 | # 安装 |
如果下载速度较慢,或者下载失败,可以使用课前资料提供的docker-compose文件:

上传到/usr/local/bin/目录也可以。
2.2.修改文件权限
修改文件权限:
1 | # 修改权限 |
2.3.Base自动补全命令:
1 | # 补全命令 |
如果这里出现错误,需要修改自己的hosts文件:
1 | echo "199.232.68.133 raw.githubusercontent.com" >> /etc/hosts |
3.Docker镜像仓库
搭建镜像仓库可以基于Docker官方提供的DockerRegistry来实现。
官网地址:https://hub.docker.com/_/registry
3.1.简化版镜像仓库
Docker官方的Docker Registry是一个基础版本的Docker镜像仓库,具备仓库管理的完整功能,但是没有图形化界面。
搭建方式比较简单,命令如下:
1 | docker run -d \ |
命令中挂载了一个数据卷registry-data到容器内的/var/lib/registry 目录,这是私有镜像库存放数据的目录。
访问http://YourIp:5000/v2/_catalog 可以查看当前私有镜像服务中包含的镜像
3.2.带有图形化界面版本
使用DockerCompose部署带有图象界面的DockerRegistry,命令如下:
1 | version: '3.0' |
3.3.配置Docker信任地址
我们的私服采用的是http协议,默认不被Docker信任,所以需要做一个配置:
1 | # 打开要修改的文件 |
Docker镜像命令

命令:
docker –help 查看所有命令
如:docker ./springcloud/ –help 查看该命令及其使用方法
步骤:
拉取镜像:
docker pull redis
查看镜像:
docker ./springcloud/
保存本地镜像为一个压缩包:
docker save -o redis.tar (如果不存在该文件会自动创建)

删除本地redis镜像:
docker rmi redis:latest
加载压缩包为本地镜像:
docker load -i redis.tar
Docker容器相关命令


案例一

操作:
创建容器:返回的一长串是容器的唯一标识id

查看容器状态:可以获取容器的创建时间,状态,宿主机和容器端口映射关系以及容器名字

通过浏览器访问宿主机ip端口出现如下页面说明nginx容器已经启动成功了(阿里云服务器需要配置安全组端口才可以访问到80端口)

- 查看日志:docker logs ikun(后面需要跟上容器确切的容器名称)

- 查看跟踪日志:docker logs -f ikun

案例二
1.进入容器中:docker exec -it ikun bash


2.去docker hub官方查找静态文件夹目录

3.查看html文件内容:cat index.html

4.替换文件内容:
sed -i ‘s#Welcome to nginx#大头川欢迎您#g’ index.html
sed -i ‘s###g’ index.html
5.浏览器访问成功替换:

6.删除容器:docker rm ikun(此命令不能删除运行中的容器) 、docker rm -f ikun(强制删除,可以删除运行中的容器)

数据卷命令
为什么要使用数据卷?


操作数据卷基本命令

总结:
数据卷的作用:
- 将容器与数据分离,解耦合,方便操作容器内数据,保证数据安全
数据卷操作:
- docker volume create
- docker volume ls
- docker volume inspect
- docker volume rm
- docker volume prune
挂载数据卷

案例一
基于数据卷挂载

创建数据卷加载到容器内的html目录:(容器内的目录可以去docker hub官方查阅)
docker volume create html (创建数据卷)
docker run –name kun -p 80:80 -v html:/usr/share/nginx/html -d nginx (运行容器并加载数据卷)
docker volume inspect html (查看数据卷挂载点;查看html数据卷位置)
cd /var/lib/docker/volumes/html/_data (进入目录)
使用高级编辑工具直接编辑html文件 或 使用 vi index.html 修改文件

注意:运行容器并加载数据卷时,如果数据卷不存在docker会为我们自动创建数据卷
案例二
基于目录挂载

1.创建文件夹

2.运行mysql容器(其中容器中的conf文件夹和data文件夹可以去docker hub官方查询)

3.运行成功后,data文件夹就会映射到容器中data文件夹的数据

4.测试数据库远程连接

两种数据卷挂载方式对比

基于数据卷挂载:
优点:docker会全自动帮我们创建数据卷挂载目录,我们挂载时只需要挂载文件不需要考虑目录问题
缺点:目录位置不清楚,目录结构较深
基于目录挂载:
优点:目录是自己创建,虽然创建目录较为麻烦,但是后期可以自己快速定位到挂载文件目录
缺点:需要自己管理目录
Dockerfile自定义镜像
镜像结构
- 镜像是将应用程序及其需要的系统函数库、环境、配置、依赖打包而成。

总结:
镜像是分层的结构,每一层称为一个Layer
- BaseImage层:包含基本的系统函数库、环境变量、文件系统
- Entrypoint:入口,是镜像中应用启动的命令
- 其他:在Baseimage基础上添加依赖、安装程序、完成整个应用的安装和配置
自定义镜像

案例一

dockerfile内容

1.在tmp目录下创建docker-demo目录
cd tmp
mkdir docker-demo
2.将资料放到指定文件夹下后(-t 后面添加要构建的名称和版本,空格后需要加上dockerfile的路径,由于已经在docker-demo路径下所以只需添加.即可)
执行 docker build -t javaweb:1.0 .

3.构建完成后查看镜像是否加载完成

4.运行容器
docker run –name web -p 8090:8090 -d javaweb:1.0
5.使用浏览器访问端口(阿里云需要配置安全组允许访问8090端口)

案例二
由于构建镜像中这么多步骤而真正构建java项目的只有一行,其余步骤都是安装jdk,为了避免在很多微服务构建时中重复做这些操作,我们可以进行分层,将构建好的分层做一个镜像,以后就可以在此镜像基础上做构建,而java:8-alpine是一个体积非常小的jdk镜像,相当于已经帮我们把其余步骤做完了,我们可以在它的基础上进行构建


基于java:8-alpine构建时只需要简单的步骤就能完成构建

1.修改dockerfile文件并重新构建项目

同样也能完成构建

RabbitMQ部署指南
1.单机部署
我们在Centos7虚拟机中使用Docker来安装。
1.1.下载镜像
方式一:在线拉取
1 | docker pull rabbitmq:3-management |
方式二:从本地加载
在课前资料已经提供了镜像包:
上传到虚拟机中后,使用命令加载镜像即可:
1 | docker load -i mq.tar |
1.2.安装MQ
执行下面的命令来运行MQ容器:
1 | docker run \ |
2.集群部署
接下来,我们看看如何安装RabbitMQ的集群。
2.1.集群分类
在RabbitMQ的官方文档中,讲述了两种集群的配置方式:
- 普通模式:普通模式集群不进行数据同步,每个MQ都有自己的队列、数据信息(其它元数据信息如交换机等会同步)。例如我们有2个MQ:mq1,和mq2,如果你的消息在mq1,而你连接到了mq2,那么mq2会去mq1拉取消息,然后返回给你。如果mq1宕机,消息就会丢失。
- 镜像模式:与普通模式不同,队列会在各个mq的镜像节点之间同步,因此你连接到任何一个镜像节点,均可获取到消息。而且如果一个节点宕机,并不会导致数据丢失。不过,这种方式增加了数据同步的带宽消耗。
我们先来看普通模式集群。
2.2.设置网络
首先,我们需要让3台MQ互相知道对方的存在。
分别在3台机器中,设置 /etc/hosts文件,添加如下内容:
1 | 192.168.150.101 mq1 |
并在每台机器上测试,是否可以ping通对方:
springAMQP
入门案例的消息发送
流程如下:
步骤一:
1.在父工程中引入spring-amqp的依赖
2.在publisher(消息发送者)服务中利用RabbitTemplate发送消息到simple.queue这个队列
因为publisher和consumer中都需要引入amqp依赖,因此直接把依赖放到父依赖中
1 | <!--AMQP依赖,包含RabbitMQ--> |
步骤二:
1.在publisher服务中编写application.yml,添加mq连接信息:
1 | spring: |
2.在publisher服务中新建一个测试类,编写测试方法:
1 |
|
总结:
什么是AMQP?
- 应用间消息通信的一种协议,与语言和平台无关。
SpringAMQP如何发送消息?
- 引入amqp的starter依赖
- 配置RabbitMQ地址
- 利用RabbitTemplate的convertAndSend方法
入门案例的消息接收
步骤3:
流程如下:
1.在consumer服务中编写application.yml,添加mq连接信息:
1 | spring: |
2.在consumer服务中新建一个类,编写消费逻辑:
1 |
|
总结:
SpringAMQP如何接收消息?
- 引入amqp的starter依赖
- 配置RabbitMQ地址
- 定义类,添加@Component注解
- 类中声明方法,添加@RabbitListener注解,方法参数就是消息
注意:消息一旦消费就会从队列删除,RabbitMQ没有回溯消息功能
消费预取限制
原因:默认情况是rabbitmq会把消息平均分配给多个消费者,但是有些消费者的处理速度很慢,导致其他消费者都处理完成以后还在处理。所以设置消费预取限制,*规定每个消费者每次预取多少个消息处理,*避免消息的堆积***。
修改application.yml文件,设置preFetch这个值,可以控制预取消息的上限:
1 | spring: |
总结:
Work模型的使用:
- 多个消费者绑定到一个队列,同一条消息只会被一个消费者处理
- 通过设置prefetch来控制消费者预取的消息数量
交换机类型
因为消息发布者发布的消息每次只能被一个消费者消费,如果我们在处理支付功能时需要把支付成功消息同时发送给订单管理,库存管理和短信业务模块,普通的模式不能满足我们的需求,所以我们需要使用交换机来完成。
注意:交换机只进行消息的转发,并不会存储消息,如果转发失败消息则会丢失。
Fanoutchange
步骤1:在consumer服务声明Exchange、Queue、Binding
在consumer服务创建一个类,添加@Configuration注解,并声明FanoutExchange、Queue和绑定关系对象Binding,代码如下:
1 |
|
步骤2:在consumer服务声明两个消费者
在consumer服务的SpringRabbitListener类中,添加两个方法,分别监听fanout.queue和fanout.queue2:
1 |
|
步骤3:在publisher服务发送消息到FanoutExchange
在publisher服务的SpringAmqpTest类中添加测试方法:
1 |
|
总结:
交换机的作用是什么?
- 接收publisher发送的消息
- 将消息按照规则路由到与之绑定的队列
- 不能缓存消息,路由失败,消息丢失
- FanoutExchange的会将消息路由到每个绑定的队列
声明队列、交换机、绑定关系的Bean是什么?
- Queue
- FanoutExchange
- Binding
DirectExchange

步骤1:
1.在consumer服务中,编写两个消费者方法,分别监听direct.queue1和direct.queue2
2.并利用@RabbitListener声明Exchange、Queue、RoutingKey
1 |
|
步骤2
在publisher服务的SpringAmqpTest类中添加测试方法
1 |
|
运行结果:

总结:
Direct交换机与Fanout交换机的差异?
- Fanout交换机将消息路由给每一个与之绑定的队列
- Direct交换机根据RoutingKey判断路由给哪个队列
- 如果多个队列具有相同的RoutingKey,则与Fanout功能类似
基于@RabbitListener注解声明队列和交换机有哪些常见注解?
- @Queue
- @Exchange
TopicExchange




总结:
Direct交换机与Topic交换机的差异?
两者非常相似,主要差异为Topic交换机BindingKey支持通配符,RoutingKey多个单词以.分隔
消息转换器
如果我们传的发送的消息是object类型,spring会对类型进行序列化处理。

基于默认的序列化得到的数据有以下缺点:
1.性能差
2.安全性:容易出现注入的问题
3.数据长度过长

因此我们可以修改序列化方式来改良这种情况

1 |
|
再运行查看消息队列:

消费者接收消息也需要导入依赖

总结:
SpringAMQP中消息的序列化和反序列化是怎么实现的?
- 利用MessageConverter实现的,默认是JDK的序列化
- 注意发送方与接收方必须使用相同的MessageConverter
Elasticsearch




正向索引和倒排索引
正向索引

如果有上千万条数据则需要扫描上千万次,效率低
倒排索引

正向索引:文本找词
倒排索引:词找文本

安装elasticsearch
1.部署单点es
1.1.创建网络
因为我们还需要部署kibana容器,因此需要让es和kibana容器互联。这里先创建一个网络:
1 | docker network create es-net |
1.2.加载镜像
这里我们采用elasticsearch的7.12.1版本的镜像,这个镜像体积非常大,接近1G。不建议大家自己pull。
课前资料提供了镜像的tar包:

大家将其上传到虚拟机中,然后运行命令加载即可:
1 | # 导入数据 |
同理还有kibana的tar包也需要这样做。
1.3.运行
运行docker命令,部署单点es:
1 | docker run -d \ |
命令解释:
-e "cluster.name=es-docker-cluster":设置集群名称-e "http.host=0.0.0.0":监听的地址,可以外网访问-e "ES_JAVA_OPTS=-Xms512m -Xmx512m":内存大小-e "discovery.type=single-node":非集群模式-v es-data:/usr/share/elasticsearch/data:挂载逻辑卷,绑定es的数据目录-v es-logs:/usr/share/elasticsearch/logs:挂载逻辑卷,绑定es的日志目录-v es-plugins:/usr/share/elasticsearch/plugins:挂载逻辑卷,绑定es的插件目录--privileged:授予逻辑卷访问权--network es-net:加入一个名为es-net的网络中-p 9200:9200:端口映射配置
在浏览器中输入:http://192.168.150.101:9200 即可看到elasticsearch的响应结果:

2.部署kibana
kibana可以给我们提供一个elasticsearch的可视化界面,便于我们学习。
2.1.部署
运行docker命令,部署kibana
1 | docker run -d \ |
--network es-net:加入一个名为es-net的网络中,与elasticsearch在同一个网络中-e ELASTICSEARCH_HOSTS=http://es:9200":设置elasticsearch的地址,因为kibana已经与elasticsearch在一个网络,因此可以用容器名直接访问elasticsearch-p 5601:5601:端口映射配置
kibana启动一般比较慢,需要多等待一会,可以通过命令:
1 | docker logs -f kibana |
查看运行日志,当查看到下面的日志,说明成功:

此时,在浏览器输入地址访问:http://192.168.150.101:5601,即可看到结果
2.2.DevTools
kibana中提供了一个DevTools界面:

这个界面中可以编写DSL来操作elasticsearch。并且对DSL语句有自动补全功能。
3.安装IK分词器
3.1.在线安装ik插件(较慢)
1 | 进入容器内部 |
3.2.离线安装ik插件(推荐)
1)查看数据卷目录
安装插件需要知道elasticsearch的plugins目录位置,而我们用了数据卷挂载,因此需要查看elasticsearch的数据卷目录,通过下面命令查看:
1 | docker volume inspect es-plugins |
显示结果:
1 | [ |
说明plugins目录被挂载到了:/var/lib/docker/volumes/es-plugins/_data 这个目录中。
2)解压缩分词器安装包
下面我们需要把课前资料中的ik分词器解压缩,重命名为ik

3)上传到es容器的插件数据卷中
也就是/var/lib/docker/volumes/es-plugins/_data :

4)重启容器
1 | 4、重启容器 |
1 | # 查看es日志 |
5)测试:
IK分词器包含两种模式:
ik_smart:最少切分
ik_max_word:最细切分
1 | GET /_analyze |
结果:
1 | { |
3.3 扩展词词典
随着互联网的发展,“造词运动”也越发的频繁。出现了很多新的词语,在原有的词汇列表中并不存在。比如:“奥力给”,“传智播客” 等。
所以我们的词汇也需要不断的更新,IK分词器提供了扩展词汇的功能。
1)打开IK分词器config目录:

2)在IKAnalyzer.cfg.xml配置文件内容添加:
1 |
|
3)新建一个 ext.dic,可以参考config目录下复制一个配置文件进行修改
1 | 传智播客 |
4)重启elasticsearch
1 | docker restart es |

日志中已经成功加载ext.dic配置文件
5)测试效果:
1 | GET /_analyze |
注意当前文件的编码必须是 UTF-8 格式,严禁使用Windows记事本编辑
3.4 停用词词典
在互联网项目中,在网络间传输的速度很快,所以很多语言是不允许在网络上传递的,如:关于宗教、政治等敏感词语,那么我们在搜索时也应该忽略当前词汇。
IK分词器也提供了强大的停用词功能,让我们在索引时就直接忽略当前的停用词汇表中的内容。
1)IKAnalyzer.cfg.xml配置文件内容添加:
1 |
|
3)在 stopword.dic 添加停用词
1 | 习大大 |
4)重启elasticsearch
1 | # 重启服务 |
日志中已经成功加载stopword.dic配置文件
5)测试效果:
1 | GET /_analyze |
注意当前文件的编码必须是 UTF-8 格式,严禁使用Windows记事本编辑
4.部署es集群
部署es集群可以直接使用docker-compose来完成,不过要求你的Linux虚拟机至少有4G的内存空间
首先编写一个docker-compose文件,内容如下:
1 | version: '2.2' |
Run docker-compose to bring up the cluster:
1 | docker-compose up |
操作索引库
1 | #模拟请求 |
操作文档
1 | #插入文档 |