黑马2021最新版 SpringCloud基础篇全技术栈导学(RabbitMQ+Docker+Redis+搜索+分布式)

wuchangjian2021-10-28 14:57:26编程学习

前言

基础篇全部代码和资料已上传到gitee,大家需要可自取: https://gitee.com/da-ji/spring-cloud-learning-notes
点个Star,后续更新高级篇和面试篇不迷路 ⚆_⚆

本笔记基于:
1、 尚硅谷 2020.3 SpringCloud(H版&alibaba)框架开发教程
2、 黑马 2021.8 SpringCloud*+RabbitMQ+Docker+Redis+搜索+分布式

代码和资料基于:
黑马 2021.8 SpringCloud*+RabbitMQ+Docker+Redis+搜索+分布式

第一套教程是经典的尚硅谷阳哥的教程,好处是经过时间的沉淀,已经非常成熟,网上大神的笔记也多,只要是人类出现的问题网上一搜都有答案;非常适合自学。

第二套教程是黑马程序员的2021年8月份最新版教程,截止到发稿时应该是全网最最新的教程,在计算机技术日新月异的今天,尽可能往前学最新的技术至少没错。而且该套教程有一个特点在于,将课程分为实用篇和高级篇:

  • 实用篇基本上以微课堂的形式出现,平均视频时长也就10分钟左右,易于接受,涵盖了80%开箱上手就能用的知识;
  • 而高级篇就比较深入和复杂了,为应对企业的复杂工作设计,每个视频长度都为一小时左右,同时也是面试常问的地方。

由于本人已经工作了,为了在工作中快速拿起来就能用,我选择的学习路线是:先刷黑马程序员的实用篇,以最少的时间快速掌握SpringCloud的相关知识,然后视情况而定深入学尚硅谷的教程或是黑马程序员的高级篇。

最后,这两篇教程虽然都非常好,但是都没有推出面试篇(源码深入讲解),如果大家经济上允许,可以支持一波培训机构内部课程;经济不允许也可以自学,当然我也会在博客和Gitee中陆续更新一些更高深的技术。

为方便大家速查,后文中这种颜色的字体,代表知识点在对应模块代码中的位置
为方便大家速查,后文中这种颜色的字体,代表知识点在对应模块代码中的位置
为方便大家速查,后文中这种颜色的字体,代表知识点在对应模块代码中的位置

课程资料链接:https://pan.baidu.com/s/169SFtYEvel44hRJhmFTRTQ 提取码:1234
课程资料链接:https://pan.baidu.com/s/169SFtYEvel44hRJhmFTRTQ 提取码:1234
课程资料链接:https://pan.baidu.com/s/169SFtYEvel44hRJhmFTRTQ 提取码:1234

目录

  • 前言
  • 一、微服务技术栈导学
  • 二、Dubbo&Zookeeper
  • 三、微服务远程调用Demo——RestTemplate基本使用
  • 四、Eureka注册中心
  • 五、Ribbon负载均衡
  • 六、Nacos注册中心
    • 6.1 安装启动
    • 6.2 Nacos自定义负载均衡策略
    • 6.3 Nacos实现配置热更新
    • 6.4 Nacos集群
  • 七、Feign远程调用
    • 7.1 还原事故现场
    • 7.2 Feign自定义配置
    • 7.3 Feign性能优化
    • 7.4 Feign最佳实践
  • 八、统一Gateway网关
    • 8.1 概述
    • 8.2 搭建网关服务
    • 8.3 路由过滤
      • 8.3.1 断言工厂:对请求进行过滤
      • 8.3.2 过滤器GatewayFilter:对请求和响应进行过滤
      • 8.3.3 全局过滤器GlobalFilter:可以自定义过滤逻辑代码实现
      • 8.3.4 过滤器链执行顺序
    • 8.4 网关跨域问题处理
  • 九、Docker
    • 9.1 Docker概念
    • 9.2 Docker常用命令
  • 十、MQ(Message Queue)消息队列
    • 10.1 概述
    • 10.2 RabbitMQ安装
    • 10.3 常见消息模型
      • 10.3.1 简单队列模型
    • 10.4 Spring AMQP
      • 10.3.2 WorkQueue模型
      • 10.3.3 发布-订阅模型
        • A. Fanout Exchange
        • B. Direct Exchange
        • C. Topic Exchange
      • 10.3.4 消息转换器
  • 十一、ElasticSearch分布式搜索
    • 11.1 ES基础概念
    • 11.2 安装部署ES
    • [Debug] 停止ES容器(或是重启Linux)后,如何恢复Docker网络:
    • 11.3 索引库操作
    • 11.4 文档操作
    • 11.5 RestClient操作索引库和文档
    • 11.6 DSL查询语法
    • 11.7 Java RestClient查询语法
    • 11.8 ES综合案例:黑马旅游
    • 11.9 ES数据聚合
    • 11.10 ES数据补全
    • 11.11 ES与MySQL之间数据同步(面试常问)
    • 11.12 搭建高可用ES集群
  • 后记

一、微服务技术栈导学

从单体架构过度到微服务架构,需要一系列中间技术支撑,其中重要的部分包括:

  • 注册中心:Eureka 、Zookeeper、Nacos
  • 服务网关:Zuul 、Gateway
  • 微服务远程调用:RestTemplate、Feign
  • 容器化技术 Docker
  • 消息队列 MQ(多种实现方式)
  • 负载均衡 Ribbon 、 Nginx
  • 分布式搜索技术:ElasticSearch

尚硅谷阳哥的SpringCloud版本选型:
在这里插入图片描述

黑马程序员的SpringCloud版本选型:

在这里插入图片描述
可以看到,黑马的版本明显较新,本文采用黑马程序员的版本(Hoxton.SR10 + SpringBoot 2.3.x)

二、Dubbo&Zookeeper

核心代码位置:在模块 dubbo+zookeeper 下
这部分是跟狂神说Java学习的(黑马版直接跳过了这两个技术),Zookeeper与Eureka 、Nacos一样也是一种注册中心。

三、微服务远程调用Demo——RestTemplate基本使用

核心代码位置:在模块 01-cloud-demo 下的order-service 和 user-service

核心代码如下图:实现了跨服务远程调用
在这里插入图片描述
总结:RestTemplate微服务调用方式

基于RestTemplate发起的http请求实现远程调用

http请求做远程调用是与语言无关的调用,只要知道对方
的ip、端口、接口路径、请求参数即可。

四、Eureka注册中心

核心代码位置:在模块 01-cloud-demo 下的eureka-server(注册的是order-service 和 user-service)

Eureka的作用:

  • 消费者该如何获取服务提供者具体信息?
    • 服务提供者启动时向eureka注册自己的信息
    • eureka保存这些信息
    • 消费者根据服务名称向eureka拉取提供者信息
  • 如果有多个服务提供者,消费者该如何选择?
    • 服务消费者利用负载均衡算法,从服务列表中挑选一个
  • 消费者如何感知服务提供者健康状态?
    • 服务提供者会每隔30秒向EurekaServer发送心跳请求,报告健康状态
    • eureka会更新记录服务列表信息,心跳不正常会被剔除
    • 消费者就可以拉取到最新的信息

注意点:

Eureka自己也是一个微服务,Eureka启动时,要把自己也注册进去。这是因为如果后续搭建Eureka集群时做数据交流:

server:
  port: 10086 # 服务端口
spring:
  application:
    name: eurekaserver # eureka的服务名称
eureka:
  client:
    service-url:  # eureka的地址信息
      defaultZone: http://127.0.0.1:10086/eureka

上段代码块中,defaultZone,将自己也注册进去了。效果如下图:

在这里插入图片描述

五、Ribbon负载均衡

两个疑问:

  • 如果有多个服务提供者,服务调用者如何知道究竟调用哪个服务呢?
  • 而且服务调用者为何不用写死服务提供者的链接(ip和端口),只需要写服务名称即可?为什么我们只输入了服务名称就可以访问了呢?
    String url = "http://userservice/user/" + order.getUserId(); //由于已经在Eureka里面配置了服务,这里只需要写配置的服务名即可

这都是Ribbon的负载均衡做到的,针对问题一,通过跟断点得知,Ribbon是通过几种不同的负载均衡算法实现的这一个机制(比如轮询算法);针对问题二,Ribbon会根据服务名称去Eureka注册中心拉取服务,如下两个图所示:

在这里插入图片描述
在这里插入图片描述
Ribbon 负载均衡策略

在这里插入图片描述
RoundRobin —— 意为轮询,操作系统也有类似的概念(CPU时间片轮转)

在这里插入图片描述
可以使用如下代码配置对某个服务的负载均衡策略(在 application.yml里配置)

userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务为例
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则

Ribbon开启饥饿加载
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。

而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:

ribbon:
  eager-load:
    enabled: true # 开启饥饿加载
    clients:
      - userservice # 指定饥饿加载的服务名称
      - xxxxservice # 如果需要指定多个,需要这么写

六、Nacos注册中心

和前面的Eureka、Zookeeper是同类型技术

6.1 安装启动

下载地址:https://github.com/alibaba/nacos/releases
本文选用1.4.1版本

解压完成后,cd到nacos的bin目录下,然后输入命令:
startup.cmd -m standalone

关闭的话,如果是linux系统,就运行shutdown.sh即可

在这里插入图片描述
出现如上图所示界面,说明启动成功。通过上图也可知它的默认端口是8848(国人做的注册中心果然不一样 8848氪金手机~)

输入地址http://127.0.0.1:8848/nacos 即可访问主页,用户名和密码都是nacos
在这里插入图片描述核心代码位置:在模块 01-cloud-demo 下注册了order-service 和 user-service,同时注释掉了两个模块的Eureka代码(包括pom.xml也注释了,毕竟是同类技术)

注意,必须将之前的Eureka代码和pom都注释掉,而且把SpringCloud也注释掉(因为已经用了SpringCloudAlibaba),否则有可能报:APPLICATION FAILED TO START这个错误

对比之前的Eureka,我们是在idea里面专门启动了一个Eureka的工程,所以 Eureka不需要下载,就可以通过端口号访问Eureka的注册中心。而Nacos是 下载并运行的,所以不需要在idea启动某个模块,直接通过运行Nacos的startup.cmd即可通过端口号访问Nacos的注册中心。

6.2 Nacos自定义负载均衡策略

也是使用的Ribbon,下面一个例子将Nacos配置成同集群优先的负载均衡策略:

默认的ZoneAvoidanceRule并不能实现根据同集群优先来实现负载均衡。

Nacos中提供了一个NacosRule的实现,可以优先从同集群中挑选实例。

1)给order-service配置集群信息

修改order-service的application.yml文件,添加集群配置:

spring:
  cloud:
    nacos:
      server-addr: localhost:8848
      discovery:
        cluster-name: HZ # 集群名称

2)修改负载均衡规则

修改order-service的application.yml文件,修改负载均衡规则:

userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则 

配置完成之后,就可以实现同集群优先的 负载均衡了

6.3 Nacos实现配置热更新

有两种方式,都在代码中配置了,具体位置在:
核心代码位置:在模块 01-cloud-demo 下 user-service,第一种方式是通过配置文件方式(PatternProperties.java);第二种方式是通过注解@Value("${yaml里定义的键值对}")的方式

  • 热更新注意点:
    你在Nacos中配置的:
    在这里插入图片描述你在bootstrap.yaml里配置的:
    在这里插入图片描述这两张图应该是一致的,注意 -.的区别!!!

  • 热更新优先级
    Nacos带环境的配置 > Nacos不带环境的配置 > 本地yaml文件配置

    很好理解,Nacos带环境可以理解为专属化配置(开发环境和生产环境)、肯定优先于Nacos不带环境的全局配置;本地yaml文件配置则肯定低于Nacos的配置。

6.4 Nacos集群

位置:在模块 01-cloud-demo 下根目录,有一个叫Nacos集群搭建.md的文件

注意点:修改两个配置文件:

  • 修改cluster.conf
    在这里插入图片描述
  • 修改Nacos的application.properties(不是你的application.properties)

在这里插入图片描述在这里插入图片描述

修改完成后保存即可。

  • 使用Nginx对Nacos做反向代理
    这里需要Nginx前置知识,可以看我以下这一篇文章:Nginx入门:通俗理解反向代理和负载均衡,简单配置Nginx

如果你的Nacos配置集群死活报下图的错误:
在这里插入图片描述
请检查你的MySQL版本,需要在5.7及以上,而且在8.0以下(比较苛刻)

七、Feign远程调用

核心代码位置:如下图所示:

在这里插入图片描述
order-service会引入上图的feign-api,实现远程调用

7.1 还原事故现场

由于上一章(第六章)做了Nacos集群,但是整个第七章是基于单体的注册中心。所以要把集群恢复成单体。

  • nacos不使用集群启动,恢复你standalone环境,主要是修改配置文件的nacos端口
  • 这样做的目的是让微服务注册进注册中心。你用nacos还原事故现场也行,用eureka还原事故现场也行。反正能还原即可。
  • 打开你的数据库服务

引入feign版本报错bug问题解决:
我手工指定了一个版本,版本号是:

		<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>2.2.9.RELEASE</version>
        </dependency>

7.2 Feign自定义配置

分为配置文件方式和代码方式。

  • 配置文件方式:
    在这里插入图片描述
  • 代码方式(新建一个配置类):
    在这里插入图片描述

我们采用的是代码方式,并全局生效(新建一个配置类)

7.3 Feign性能优化

底层的客户端实现是:

  • URLConnection:默认实现,不支持连接池
  • Apache HttpClient: 支持连接池(常用)
  • OKHttp:支持连接池

第一种方式是默认的,不支持连接池。所以这里的性能优化指的是:换成第二种方式或者第三种方式。

其中第二种方式 Apache HttpClient 常用于模拟postman的样式,发送一个form-data样式的post请求,也可在这个post请求里上传文件。我们也采用的是这种方式

性能优化步骤:
1、引入jar包:

<!--HttpClient依赖-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-httpclient</artifactId>
            <version>10.1.0</version>
        </dependency>

2、在配置文件yml中配置:

feign:
  httpclient:
    enabled: true # 支持HttpClient的开关
    max-connections: 200 # 最大连接数
    max-connections-per-route: 50 # 单个路径的最大连接数

这里的改动都是在order-service模块下

7.4 Feign最佳实践

打成jar包方式:

java中的JAR包

1、在项目中添加单独的jar包步骤:

写好自己的maven项目后,执行clean package,即可得到一个jar包

2、在项目中引入单独的jar包图解:

在这里插入图片描述
上图其实是在项目的根目录创建了一个叫lib的文件夹,里面存着自定义jar包。然后即可引入。
3、针对1和2的补充,有的时候没必要非得打jar包,可以写一个子模块引入呀,如下图所示:

在这里插入图片描述
这块看不懂,可以自行搜索maven的jar包引入方式和顺序

八、统一Gateway网关

8.1 概述

三大功能:

  • 身份认证和权限校验
  • 服务路由、负载均衡
  • 请求限流

在SpringCloud中网关技术包括两种:gateway和zuul
其中Zuul是基于Servlet的实现,属于阻塞式编程,而Gateway则是基于SPring5中提供的WebFlux,属于响应式编程的实现,具备更好的性能。

8.2 搭建网关服务

核心代码位置:如下图

在这里插入图片描述

步骤:

  • 新建模块
  • 编写配置文件yml:
    • 注册进nacos的配置
    • 网关自身的端口号
    • 网关路由配置
server:
  port: 10010
spring:
  application:
    name: gateway
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848 # nacos地址
    gateway:
      routes:
        - id: user-service # 路由标识,必须唯一
          uri: lb://userservice # 路由的目标地址 lb就是负载均衡,后面跟着是服务名称
          predicates: # 路由断言,判断请求是否符合规则
            - Path=/user/** # 路径断言,判断路径是否是以/user开头,如果是则符合规则
        - id: order-service
          uri: lb://orderservice
          predicates:
            - Path=/order/**

除了上面这些,还可以配置路由过滤器。后面会讲到。

配置完毕后,启动你的网关服务和你的user-service和order-service服务,即可通过网关访问到user-service和order-service

工作原理总结
在这里插入图片描述

8.3 路由过滤

8.3.1 断言工厂:对请求进行过滤

在这里插入图片描述
如果你不会写匹配表达式,可以去spring官网查:

在这里插入图片描述
如果你的请求不符合路由断言,那你的请求就会被拒绝,返回一个404. 我们可以通过配置路由断言工厂的方式来过滤某些请求。

8.3.2 过滤器GatewayFilter:对请求和响应进行过滤

它和8.3.1讲述的断言工厂一样,都配置在yaml里

  • GatewayFilter 和 8.3.1讲述的断言工厂的区别:
    在这里插入图片描述
  • 与断言工厂类似,spring也为我们提供了过滤器工厂:
    在这里插入图片描述

在这里插入图片描述
GatewayFilter可以针对某一类路由标识单独配置,也可以配置成全局配置(所有路由id都生效),具体可自行百度,但是过滤器链执行顺序有变化,可以看8.8.4详解

8.3.3 全局过滤器GlobalFilter:可以自定义过滤逻辑代码实现

在这里插入图片描述

在这里插入图片描述

案例正确执行的效果图:
不加参数被过滤器拦截:
在这里插入图片描述
加了参数,不被拦截,正确获得响应!

在这里插入图片描述

8.3.4 过滤器链执行顺序

原理:关键词 适配器模式

顺序:
在这里插入图片描述

8.4 网关跨域问题处理

域名不一致就是跨域:

  • 域名不同 比如www.baidu.com 和 www.bilibili.com
  • 域名相同,端口不同

跨域是一个前端的概念,浏览器禁止请求的发起者和服务端发生跨域ajax请求,该请求会被浏览器拦截。
解决方案:CORS

之所以之前的user-service调用order-service不存在跨域,是因为不是ajax请求。因为这是一个浏览器行为,只有ajax请求会被拦截

处理方法:
简单配置即可:

spring:
  cloud:
    gateway:
      globalcors: # 全局的跨域处理
        add-to-simple-url-handler-mapping: true # 解决options请求被拦截问题
        corsConfigurations:
          '[/**]':
            allowedOrigins: # 允许哪些网站的跨域请求
              - "http://localhost:8090"
              - "http://www.leyou.com"
            allowedMethods: # 允许的跨域ajax的请求方式
              - "GET"
              - "POST"
              - "DELETE"
              - "PUT"
              - "OPTIONS"
            allowedHeaders: "*" # 允许在请求中携带的头信息
            allowCredentials: true # 是否允许携带cookie
            maxAge: 360000 # 这次跨域检测的有效期

如果想要演示,需要启动一个前端工程模拟一个ajax请求。

九、Docker

Docker命令居多,可以看我下面两张思维导图,包含了概念理解和常用命令。

9.1 Docker概念

在这里插入图片描述

9.2 Docker常用命令

在这里插入图片描述

十、MQ(Message Queue)消息队列

10.1 概述

  • 事件驱动架构的概念
    MQ是事件驱动架构的实现形式,MQ其实就是事件驱动架构的Broker。

  • 异步应用场景:
    如果是传统软件行业:虽然不需要太高并发,但是涉及到和其它系统做对接,我方系统处理速度(50ms)远快于对方系统处理速度(1-3s),为了兼顾用户的体验,加快单据处理速度,故引入MQ。
    用户只用点击我方系统的按钮,我方按钮发送到MQ即可给用户返回处理成功信息。背后交由对方系统做处理即可。至于处理失败,补偿机制就不是用户体验要考虑的事情了,这样可以大大提升用户体验。

  • 异步通讯优缺点:

    • 优点:
      • 耦合度低
      • 吞吐量提升
      • 故障隔离
      • 流量削峰
    • 缺点:
      • 依赖于MQ的可靠性,安全性,吞吐能力(因为加了一层MQ,当然高度依赖它)
      • 业务复杂了,业务没有明显的流程线,不好追踪管理
  • MQ常见技术介绍:
    在这里插入图片描述

10.2 RabbitMQ安装

如何安装,见下图文件:RabbitMQ部署指南.md
在这里插入图片描述

执行MQ容器的命令和简单说明:

docker run \
 -e RABBITMQ_DEFAULT_USER=root \  #用户名
 -e RABBITMQ_DEFAULT_PASS=root \  # 密码
 --name mq \	
 --hostname mq1 \	# 主机名,将来做集群部署要用
 -p 15672:15672 \	# 端口映射,映射RabbitMQ管理平台端口
 -p 5672:5672 \		# 端口映射,消息通信端口
 -d \	# 后台运行
 rabbitmq:3-management	# 镜像名称

#号不被识别,下面提供一个没有#的版本

docker run \
 -e RABBITMQ_DEFAULT_USER=root \
 -e RABBITMQ_DEFAULT_PASS=root \
 --name mq \
 --hostname mq1 \
 -p 15672:15672 \
 -p 5672:5672 \
 -d \
 rabbitmq:3-management

最后在浏览器地址栏输入:你的端口号:15672
在这里插入图片描述
如果看到上图页面,就说明成功了!

虚拟主机,租户隔离的概念,重要!!!
vitural host:虚拟主机,是对queue、exchange等资源的逻辑分组

10.3 常见消息模型

10.3.1 简单队列模型

核心代码位置:下图所示
在这里插入图片描述

10.4 Spring AMQP

概述

AMQP(Advanced Message Queuing Protocol),是用于在应用程序之间传递业务信息的开放标准,该协议与语言和平台无关,更符合微服务中独立性的要求

SpringAMQP就是Spring基于AMQP定义的一套API规范。

使用Spring AMQP实现简单队列模型步骤:

以生产者为例:

由于这玩意已被spring托管了,所以对比之前rabbitmq demo的方式,不需要在代码里写配置了,直接在spring的application.yml里写配置文件即可.

配置如下:

# 1.1.设置连接参数,分别是:主机名、端口号、用户名、密码、vhost
spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: root
    password: root
    virtual-host: /

然后编写测试类,以及测试代码,位置如下图所示:

在这里插入图片描述
消费者一侧,和生产者类似。不再赘述,如下图进行配置即可:
在这里插入图片描述
至于如何启动消费者 一侧?如下图所示:
在这里插入图片描述

10.3.2 WorkQueue模型

之所以 10.3.2 放在 10.4章,因为demo模型的演示,今后就是以 Spring AMQP为例了

概述
其实就是一个队列,绑定了多个消费者,一条消息只能由一个消费者进行消费,默认情况下,每个消费者是轮询消费的。区别于下文的发布-订阅模型(该模型允许将同一消息发给多消费者)

案例:
在这里插入图片描述

10.3.3 发布-订阅模型

概念
允许将同一个消息发给多个消费者。
其实就是加了一层交换机而已,如下图所示:
在这里插入图片描述
交换机类型有很多,下文逐一介绍。下图表示了各交换机类型的继承关系
在这里插入图片描述

最后,交换机只能做消息的转发而不是存储,如果将来路由(交换机和消息队列queue的连接称作路由)没有成功,消息会丢失

A. Fanout Exchange

位置如下图,注意一定要放在consumer包下,因为是消费者消费行为:
在这里插入图片描述
生产者添加代码位置如下图:

在这里插入图片描述

队列绑定成功后,打开mq可视化页面,会看到如下图所示:

在这里插入图片描述
写好代码后,分别启动生产方,消费方,即可看到调试成功信息输出:
在这里插入图片描述

概念
这种模型中生产者发送的消息所有消费者都可以消费。

案例:
在这里插入图片描述

总结:workQueue模式和FanoutQueue模式区别:
P代表生产者,C代表消费者 X代表交换机,红色部分代表消息队列
workQueue:
在这里插入图片描述
FanoutQUeue:
在这里插入图片描述
可以发现,FanoutQueue增加了一层交换机,可以多个队列对应多个消费者。而且比起WorkQueue,FanoutQueue生产者是先发送到交换机; 而WorkQueue是直接发送到队列

B. Direct Exchange

概念:DirectExchange 会将接收到的消息根据规则路由到指定的queue,因此称为路由模式,如下图所示:

P代表生产者,C代表消费者 X代表交换机,红色部分代表消息队列
在这里插入图片描述

  • 每一个queue都会与Exchange设置一个BindingKey
  • 将来发布者发布消息时,会指定消息的RoutingKey
  • Exchange将消息路由到BingingKey与RoutingKey一致的队列
  • 实际应用时,可以绑定多个key。
  • 如果所有queue和所有Exchange绑定了一样的key,那生产者所有符合key的消息消费者都会消费。如果这样做,那DirectExchange就相当于FanoutExchange了(Direct可以模拟Fanout的全部功能)

案例如图:
在这里插入图片描述

消费者添加代码位置如下图:
在这里插入图片描述
发送队列添加代码位置如下图:

在这里插入图片描述

这次的案例,我们用注解的方式声明队列和绑定交换机,之前Fanout的Demo是手写了个配置类。 直接在监听队列里面声明如下图注解即可:
在这里插入图片描述
上图的@QueueBinding点进去:
在这里插入图片描述
上面的key是个数组,可以写多个key。

写完代码后启动消费者的SpiringBoot主启动类(报错信息不用管),然后进入rabbitMQ可视化控制台,出现下图则说明配置成功:
在这里插入图片描述
随后运行发送队列的Test代码,打开消费者的控制台,出现如下图输出,则说明案例测试通过:
在这里插入图片描述

C. Topic Exchange

概念: 和上面的Direct Exchange及其相似:

(下图来源于Java旅途 ,作者大尧)
在这里插入图片描述
案例:
在这里插入图片描述
发送队列、消费者的添加代码位置和上面的DirectExchange位置一致,就在DirectExchange代码下面。

写完代码后启动消费者的SpiringBoot主启动类(报错信息不用管),然后进入rabbitMQ可视化控制台,出现下图则说明配置成功:

在这里插入图片描述

10.3.4 消息转换器

引入:

在之前的案例中,我们发送到队列的都是String类型,但是实际上,我们可以往消息队列中扔进去任何类型。我们看下图,convertAndSend这个方法,第三个参数也是Object。这说明可以发送任何类型给消息队列:
在这里插入图片描述

案例:
创建一个队列,向该队列扔一个任意对象(Object类型)

创建队列位置、发送队列的添加代码位置如下图
创建队列位置:
在这里插入图片描述
发送:
在这里插入图片描述

写完代码后启动发送的Test,去看RabbitMQ控制台,发现我们发过来的对象在内部被序列化(ObjectOutPutStream)了,如下图所示:

在这里插入图片描述
如果不知道什么是ObjectOutPutStream可自行百度:
在这里插入图片描述

上面说的ObjectOutPutStream这个序列化方式,缺点很多(性能差、长度太长、安全性有问题)。我们可以在这里调优一下,推荐JSON的序列化方式。于是引出了这一节的正文:自定义消息转换器(覆盖了原有的Bean配置):

在这里插入图片描述
声明配置位置如下图

在这里插入图片描述
配置了消息转换器转换成json,然后重复之前的步骤,使用发送者发送一条消息到队列,发送完成后打开RabbitMQ控制台,出现如下图所示:
在这里插入图片描述
该对象被成功序列为json格式了!!!!!

  • 对刚才发送过来的json格式消息进行接收,需要修改消费者一侧的代码。并不复杂,如下图所示:
    在这里插入图片描述
    消费者配置、监听消息位置如下2图:
    在这里插入图片描述
    在这里插入图片描述

总结

  • 消息序列化和反序列化使用MessageConverter实现
  • SpringAMQP的消息序列化默认底层是使用JDK的序列化
  • 我们可以手动配置成其它的序列化方式(覆盖MessageConverter配置Bean),推荐json
  • 发送方和接收方必须使用相同的MessageConverter

十一、ElasticSearch分布式搜索

11.1 ES基础概念

ES概述:

ELK(Elastic Stack)是以Elastic为核心的技术栈,如下图所示:

在这里插入图片描述

ElasticSearch底层是Lucene(侧面说明了ES和Hadoop千丝万缕的关系)

推荐下面一篇文章:深入浅出大数据(From Zhihu)https://zhuanlan.zhihu.com/p/54994736
在这里插入图片描述
这个Lucene使用java写成的,其实就是个jar包,我们引入之后就可以使用这个Lucene的API。而ES就是基于Lucene的二次开发,对其API进行进一步封装:
在这里插入图片描述

倒排索引基础概念:

先了解传统MySQL的正向索引:
在这里插入图片描述
倒排索引基本概念:
在这里插入图片描述

这个倒排索引其实和生活中字典相当像,你拿到一本字典的目录,肯定不会傻到先找页码,你肯定是先大略看一眼目录的关键字,然后找到关键字之后,去看关键字旁边的页码,最后再根据页码翻到书对应的那一页。

倒排索引其实就是上面的例子。

然而MySQL这种正向索引,就是基于文档id创建索引,查询词条的时候必须先找到文档,然后根据文档内容判断是否包含词条。

倒排索引正式一点的说法就是:对文档内容分词,对词条创建索引,并记录词条所在文档的信息,查询时先根据词条查询文档id,然后根据id找到该文档。

文档和词条的概念:

每一条数据就是一个文档,对文档的内容分词,得到的词语就是词条。

ES 和 MySQL 概念对比
在这里插入图片描述
在这里插入图片描述

11.2 安装部署ES

见课前资料的:安装elasticsearch.md

使用docker容器化部署,这里针对启动容器命令解析一下:

-e “ES_JAVA_OPTS=-Xms512m -Xmx512m” 配置堆内存(JVM)。因为ES底层是java实现的,所以要配置jvm内存大小。默认值是1T,对于轻量级服务器太大了,所以适当减少为512M(但是不能再弄少了,再少的话可能跟着视频走,会出现内存不足的问题)

-e “discovery.type=single-node” 单点模式运行(区别于集群模式运行)

两个-v参数:数据卷挂载,分别是数据保存目录(data),和插件目录(plugins)

–network es-net 将ES容器加入到刚刚创建的docker网络中

-p 9200:9200 和 -p 9300:9300 是暴露的端口,9200是用户访问的http协议端口,9300是ES容器节点互联的端口

elasticsearch:7.12.1 镜像名称

docker run -d \
	--name es \
    -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
    -e "discovery.type=single-node" \
    -v es-data:/usr/share/elasticsearch/data \
    -v es-plugins:/usr/share/elasticsearch/plugins \
    --privileged \
    --network es-net \
    -p 9200:9200 \
    -p 9300:9300 \
elasticsearch:7.12.1
  • 安装部署kibana(数据可视化界面)

    黑马官方的kibana的tar包有问题,建议自己从docker hub拉下来镜像。但是拉下来之前要注意 ES 和 kibana的版本对应关系:
    找到对应版本后(我已经找好了),执行命令:

    docker pull kibana:7.12.1

    从官网拉下来,这个过程比较慢,慢慢等

  • 什么是分词器?为什么要安装分词器?
    在这里插入图片描述
    在这里插入图片描述
    分词器我们选择IK分词器(来源于github,专门适配了中文)
    在这里插入图片描述
    该分词器的具体安装也在文档里有写。

  • 分词器总结
    在这里插入图片描述

[Debug] 停止ES容器(或是重启Linux)后,如何恢复Docker网络:

在这里插入图片描述

11.3 索引库操作

先给出ES官方帮助文档地址:
https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html

索引库相当于MySQL中的Table。具体操作有两个:

  • Mapping映射属性
  • 索引库的CRUD

先介绍Mapping映射属性:
在这里插入图片描述

  • 创建索引库
    在这里插入图片描述
    一个简单的创建索引库的语句:
# 创建索引库
PUT /heima
{
  "mappings": {
    "properties": {
      "info": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "email": {
        "type": "keyword",
        "index": false
      },
      "name": {
        "type": "object",
        "properties": {
          "firstName": {
            "type": "keyword"
          },
          "lastName": {
            "type": "keyword"
          }
        }
      }
    }
  }
}
  • 查看、修改、删除索引库

查看索引库:GET /索引库名
删除索引库:DELETE /索引库名

修改索引库从设计上被禁止了,索引库和mapping一旦创建无法修改,但是可以添加新的字段 (该字段必须是全新的字段)

它们的语法如下:

# 查询
GET /heima

# 修改(必须添加一个全新的字段)
PUT /heima/_mapping
{
  "properties":{
    "age":{
      "type": "integer"
    }
  }
}

# 删除
DELETE /heima

11.4 文档操作

索引库相当于数据库的table,文档就相当于数据库的行。

  • 添加文档
    在这里插入图片描述
# 插入一个文档
POST /heima/_doc/1
{
  "info": "黑马程序员java讲师",
  "email": "112837@qq.com",
  "name":{
    "firstName":"云",
    "lastName":"赵"
  }
}
  • 查看、删除文档
    在这里插入图片描述
# 查询
GET /heima/_doc/1

# 删除
DELETE /heima/_doc/1

每次写操作的时候,都会使得文档的"_version"字段+1

  • 修改文档方式1 全量修改
    它会删除旧文档,新增新文档

语法:和新增的语法完全一致,只不过新增是POST,全量修改是PUT
示例:

# 插入一个文档
PUT /heima/_doc/1
{
  "info": "黑马程序员java讲师",
  "email": "112837@qq.com",
  "name":{
    "firstName":"云",
    "lastName":"赵"
  }
}

如果id在索引库里面不存在,并不会报错,而是直接新增,如果索引库存在该记录,就会先删掉该记录,然后增加一个全新的。

  • 修改文档方式2 增量修改
    只修改某记录的指定字段值
    语法:
# 局部修改文档字段
# 第三行,必须跟一个doc
POST /heima/_update/1
{
  "doc": {  
    "email":"lbwnb@qq.com"
  }
}

文档操作总结
在这里插入图片描述

11.5 RestClient操作索引库和文档

  • 概念
    ES官方为各种语言操作ES提供了客户端API,用来操作ES。其实本质都是组装ES语句,通过http请求发送给ES。 官方文档地址:https://www.elastic.co/guide/en/elasticsearch/client/index.html
    在这里插入图片描述
    可以看到有很多语言的版本。

  • 案例和代码位置
    在这里插入图片描述

代码位置(大量代码写在测试类中),该案例需要导入数据库,数据库执行脚本位置同代码目录:
在这里插入图片描述

  • 编写DSL语句,创建索引库(相当与MySQL中建表)

语句如下:

# 酒店的mapping
PUT /hotel
{
  "mappings": {
    "properties": {
      "id":{
        "type": "keyword"         
      },
      "name":{
        "type": "text"
        , "analyzer": "ik_max_word",
        "copy_to": "all"
      },
      "address":{
        "type": "keyword"
        , "index": false
      },
      "price":{
        "type": "integer"
      },
      "score":{
        "type": "integer"
      },
      "brand":{
        "type": "keyword",
        "copy_to": "all"
      },
      "city":{
        "type": "keyword"
      },
      "starName":{
        "type": "keyword"
      },
      "business":{
        "type": "keyword",
        "copy_to": "all"
      },
      "location":{
        "type": "geo_point"
      },
       "pic":{
        "type": "keyword"
        , "index": false
      },
      "all":{
        "type": "text",
        "analyzer": "ik_max_word"
      }
    }
  }
}

有时候可能会疑惑,同样的一个文本型字段,有的用text,有的用keyword。到底怎么选择呢?首先要了解索引和分词的概念:

  • 索引(参与搜索,排序筛选等操作)
  • 分词(把词看作一个整体还是把词用某种规则分开)
    • 比如 : 上海,北京这种字段,不需要分词(这种字段在一个整体才有意义,分词就乱套了)
    • “震惊!卢bw将于2022年复出” 这种就需要分词搜索,既然要分词了,肯定要选择分词器。
      了解了上面的概念,再看一下下图(图来源于博客园——瘦风的南墙):
      在这里插入图片描述
      备注1:index如果设置成false,则既不参与索引也不参与分词。
      备注2:索引库的id总是被要求成keyword(也就是String)类型,即使数据库的主键id可能是int

字段参数(用于聚合):copy to ;
地理位置特殊数据类型:geo_point

使用RestClient操作文档(索引库相当于数据库的table,文档就相当于数据库的行。),全都写在demo代码中,还是那句话:Java的API本质都是组装ES语句,通过http请求发送给ES。

11.6 DSL查询语法

先给出帮助文档,帮助文档永远是学东西最准确的方式:
https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html

  • 快速入门—简单查询:
    在这里插入图片描述
    在这里插入图片描述

在这里插入图片描述

全文检索查询例:

# match 和 multi_match
GET /hotel/_search
{
  "query": {
    "match": {
      "address": "如家外滩"
    }
  }
}
GET /hotel/_search
{
  "query": {
    "multi_match": {
      "query": "外滩如家",
      "fields": ["brand","name","business"]
    }
  }
}

在这里插入图片描述

精确查询例:

# 精确查询(term查询)
GET /hotel/_search
{
  "query": {
    "term": {
      "city": {
        "value": "上海"
      }
    }
  }
}

# 精确查询(范围range)
GET /hotel/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 100,
        "lte": 300
      }
    }
  }
}

在这里插入图片描述
在这里插入图片描述

地理查询例:

# distance查询
GET /hotel/_search
{
  "query": {
    "geo_distance":{
      "distance": "5km",
      "location": "31.21, 121.5"
    }
  }
}
  • 快速入门—打分算法:

打分算法(重点):
在这里插入图片描述
对默认算分方式进行修改:
在这里插入图片描述
组合查询-function score 对应的Java RestClient代码:

在这里插入图片描述

在这里插入图片描述
上面例子的查询语句:

GET /hotel/_search
{
  "query": {
    "function_score": {
      "query": {
        "match": {
          "address": "外滩"
        }
      },
      "functions": [
        {
          "filter": {
            "term": {
              "brand": "如家"
            }
          },"weight": 10
        }
      ]
    }
  }
}


  • 快速入门—复合查询:

复合查询可以将其它简单查询组合起来,实现更复杂的搜索逻辑。

Boolean Query

在这里插入图片描述
注意,算分条件越多,性能就会越差。所以能使用filter的就别使用must,能不算分就不算分

案例:搜索名字包含“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店
参考答案:

GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {"match": {
          "name": "如家"
        }}
      ],
      "must_not": [
        {"range": {
          "price": {
            "gt": 400
          }
        }}
      ],
      "filter": [
        {
          "geo_distance": {
            "distance": "100km",
            "location": {
              "lat": 31.21,
              "lon": 121.5
            }
          }
        }
      ]
    }
  }
}
  • 快速入门—搜索结果处理:

搜索结果的处理主要包括排序、分页、高亮。默认ES是根据得分排序的,但是你如果指定了按某种字段排序,就会按你指定的方法排序。

A.排序
在这里插入图片描述
案例:
在这里插入图片描述
查询语句实现:

# sort排序
GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "score": "desc"
    },
    {
      "price": "asc"
    }
  ]组合查询-function score 对应的Java RestClient代码:

}

案例2:

在这里插入图片描述

查询语句实现2:


GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "_geo_distance": {
        "location": {
          "lat": 31.03,
          "lon": 121.61
        }, 
        "order": "asc"
        , "unit": "km"
      }
    }
  ]
}

地理位置排序对应的java restclient代码:
在这里插入图片描述

注意:一旦指定了某种排序之后,ES就会放弃打分。因为打分没意义了:
在这里插入图片描述
B.分页

ES默认情况只返回10条数据,如果想返回更多条数据,则需修改分页参数。

分页语法(有点像MySQL的limit):
在这里插入图片描述

示例:


GET /hotel/_search
{
  "query": {
    "match_all": {}
  },
  "sort": [
    {
      "price": "asc"
    }
  ],
  "from": 20
  , "size": 5
}

分页出现的问题:ES底层是倒排索引,不利于分页,所以分页查询是一种逻辑上的分页。比如现在要查从990开始,截取10条数据(990~1000这10条),对ES来讲,是先查出来0~1000条数据,查出来之后逻辑分页截取10条给你。这么做如果是单体,最多只是效率问题,但是如果是集群,就会坏事。如下图所示:

在这里插入图片描述
针对只能查询10000条结果的解决方案:
在这里插入图片描述
C.高亮

在这里插入图片描述
示例:

# 高亮查询,默认情况下ES搜索字段必须与高亮字段一致
GET /hotel/_search
{
  "query": {
    "match": {
      "name": "如家"
    }
  },"highlight": {
    "fields": {
      "name": {
        
      }
    }
  }
}

在这里插入图片描述
总结:
在这里插入图片描述

11.7 Java RestClient查询语法

要构建查询条件,只要记住一个类:QueryBuilders。
要构建搜索DSL,只需记住一个API:SearchRequest的source()方法(支持链式编程)

核心代码位置:
在这里插入图片描述

这里只有一个注意点:高亮结果的解析,比较麻烦。代码要配合下图理解:
在这里插入图片描述

11.8 ES综合案例:黑马旅游

代码位置:就是11.7那个类,直接启动SpringBoot主启动类,然后访问localhost:8089即可访问到前端页面

要实现的功能:

  • 酒店搜索和分页
  • 酒店结果过滤
  • 我周边的酒店
  • 酒店竞价排名

视频可能出现的bug:

bug1 : 如果前端显示异常(搜索不生效),根据前端debug信息,修改index.html的第417行代码修改成如下图所示:
在这里插入图片描述
bug2: 黑马旅游网的酒店竞价排名实现不了

由于在视频里创建索引库里并没有创建isAD这个字段,我们需要手动追加该字段。在kibana控制台执行如下代码即可修复:

# 给索引库新增一个叫isAD的字段,类型是布尔类型
PUT /hotel/_mapping
{
  "properties":{
    "isAD":{
      "type": "boolean"
    }
  }
}

# 给索引库id为45845的记录赋值,让其isAD字段为true(用于测试广告竞价排名,该记录会靠前)
POST /hotel/_update/45845
{
  "doc": {  
    "isAD":true
  }
}


GET hotel/_doc/45845

11.9 ES数据聚合

聚合,类似于MySQL的group by(对数据的统计分析和计算)。聚合不能是text类型,不能分词

聚合一共有几十种,在官方文档可以查到,但是主要分为三大类:
在这里插入图片描述
管道聚合 可以理解为linux的 |

1、Bucket聚合

在这里插入图片描述
查询实例:
在这里插入图片描述
上图图例的结果是由count进行降序排列的,如果想让其升序排列,只需如下代码:

# 聚合功能
GET hotel/_search
{
  "size": 0,
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brand",
        "size": 10,
        "order": {
          "_count": "asc"  #结果按照count升序排列
        }
      }
    }
  }
}

限定聚合范围:
在这里插入图片描述

2、Metrics聚合

在这里插入图片描述
示例:

# 嵌套聚合metric
GET hotel/_search
{
  "size": 0,
  "aggs": {
    "brandAgg": {
      "terms": {
        "field": "brand",
        "size": 10,
        "order": {
          "scoreAgg.avg": "asc"   # 根据下面的子聚合结果的avg进行升序排序
        }
      },
      "aggs": {
        "scoreAgg": {
          "stats": {
            "field": "score"
          }
        }
      }
    }
  }
}

使用Java Restclient实现上面几种聚合方式,位置如下:
在这里插入图片描述

Java Restclient对应Json的图例:
在这里插入图片描述
Java代码对应结果解析的图例:
在这里插入图片描述
3、聚合案例:
在这里插入图片描述
案例位置同上面的 ES综合案例:黑马旅游

11.10 ES数据补全

比如你在京东输入 sj 这两个字母,搜索框就会猜测出你想输入手机。这个就是数据补全

安装数据补全分词器:

分词器在课前资料里有
2
测试你的分词器是否生效:

POST _analyze
{
  "text": ["卢本伟"],
  "analyzer": "pinyin"
}

自定义配置分词器:
概念:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

将下图位置的自定义配置分词器的第一段粘贴至kibana控制台,即可完成自定义配置:
在这里插入图片描述

Completion Suggester查询实现自动补全:
在这里插入图片描述

Completion Suggester语法:


// 自动补全查询
GET /test/_search
{
  "suggest": {
    "title_suggest": {
      "text": "s", // 关键字
      "completion": {
        "field": "title", // 补全字段
        "skip_duplicates": true, // 跳过重复的
        "size": 10 // 获取前10条结果
      }
    }
  }
}

总结:
自动补全对字段的要求:
类型是completion类型;字段值是多词条的数组。

在这里插入图片描述
案例:实现hotel索引库的自动补全、拼音搜索功能:
在这里插入图片描述
找到下图位置,复制粘贴进kibana控制台并且执行(这一步是重建酒店数据索引库,在此之前要删掉原有的酒店数据索引库):
在这里插入图片描述
注意事项:
在这里插入图片描述
在Java代码中重新定义转换实体的操作,定义一个新的字段suggestion,并且在kibana控制台进行测试:
在这里插入图片描述

经过上面一番操作后,类型为completion类型的suggestion字段就有了我们想要自动补全的例子,然后执行下面的查询语句:
在这里插入图片描述
至此,自动补全、拼音搜索的demo已成功展示!

对上图的DSL语句在Java RestAPI里面进行发送:
在这里插入图片描述
在这里插入图片描述

使用Java Restclient实现上面自动补全方式,位置如下:

在这里插入图片描述
案例效果:
在这里插入图片描述

11.11 ES与MySQL之间数据同步(面试常问)

概念

ES中的酒店数据来自于MySQL索引库,因此mysql数据发生改变时,ES的值也会跟着改变,这个就是ES和MySQL的数据同步。

思考:在微服务中,操作MySQL的业务和操作ES的业务可能在不同的微服务上,这种情况应该怎么实现数据同步呢?

解决方案:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
案例:利用MQ实现mysql与es的数据同步
在这里插入图片描述
思路:
在这里插入图片描述

数据同步案例后台管理页面代码位置如下图(数据库就用之前的ES综合案例:黑马旅游):

在这里插入图片描述
数据同步案例前端显示代码就是之前的ES综合案例:黑马旅游。前后端的微服务是分离的,端口号也不同。
实际上,这个项目hotel-admin项目相当于生产者,负责发送数据库增删改消息;hotel-demo(之前的黑马旅游前端项目)相当于消费者,负责监听消息并更新ES中的数据。

这样就实现了在微服务中,操作MySQL的业务和操作ES的业务在不同的微服务上的跨服务数据同步

用心跟着代码走,这个案例是完全可以做完并实现视频全部功能的,没有一句废话多余。

11.12 搭建高可用ES集群

概念
在这里插入图片描述

搭建ES集群

位置同之前的elasticsearch.md,找到该文档第四节:部署ES集群

集群脑裂问题
在这里插入图片描述
在这里插入图片描述
脑裂问题:一个集群出现了2个主节点:
在这里插入图片描述
在这里插入图片描述

集群分布式存储和分布式查询
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

集群故障转移

在这里插入图片描述

集群故障转移总结:

  • Master挂掉后,EligibleMaster选举为新的主节点
  • master节点监控分片,节点状态,将故障节点的分片转移到正常节点,确保数据安全。

后记

黑马 SpringCloud 2021 基础篇笔记和代码已更新完毕,不得不说黑马的这套课程的确是良心之作,而且官方居然还开源出来让大家都可以学习,实在是难能可贵。

如果大家在学习基础篇的同时有疑问,欢迎在评论区讨论和留言,也可以关注我,日后我还会陆续更新完高级篇和面试篇。

相关文章

[Life Sciences.AI]专栏介绍及内容分类(持续更新......)

主要记录多肽和蛋白等生物大分子相关的操作和与人工智能的结合。 蛋白相关 蛋白...

Javascript学习笔记:数组的连接、反转与排序

1.concat()将多个数字或元素连接起来 var 水浒...

Leetcode-go 插件

1、setting里面设置搜索 leetcode  ...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。