- 同步通信相当于打电话,打电话者与接电话者只能建立一条通信连接,并且消息发送过去必须要得到对方的回应才能知道消息已经被成功接收
- 异步通信相当于发送微信,发送者与多个接收者建立多条通信连接,发送者只需要把消息发送过去即可,=并不需要得到对方的回应
耦合度高:每次加入新的需求,都要修改原来的代码
性能下降:调用者需要等待服务提供者的响应,如果调用链过长则响应时间等于每次调用的时间之和
资源浪费:调用链中的每个服务在等待响应过程中,不能释放请求占用的资源,高并发场景下会极度浪费系统资源
级联失败:如果服务提供者出现问题,所哟调用方都会跟着出问题,如同多米诺骨牌一样,迅速导致整个微服务群故障。
优势一:服务解耦
服务消费者(支付服务)只需要将请求(订单完成事件)发送给Broker即可,而Broker需要广播请求到来了(订单完成事件来了),此时服务消费者的工作就完成了,而其他服务提供者只要向Broker订阅事件就行了(获取请求),==这样做到了服务消费者与提供者解耦的操作,因此当我们想要添加或者删除一个服务时只要简单地让服务取消或加入订阅Broker即可,并不需要改代码
优势二:性能提升,吞吐量提高
服务提消费者不需要再等待服务提供者了,大大提高了性能
优势三:服务没有强依赖,不担心级联失败问题
服务消费者与提供者没有关系了,就算服务提供者出现问题了也不会影响到服务消费者
优势四:流量削峰
当请求量过大,Broker起到了缓冲地作用,将请求缓存起来
[异步通信的缺点]:
依赖于Broker的可靠性、安全性、吞吐能力
架构复杂了,业务没有明显的流程线了,不好追踪管理
MQ(MessageQueue),中文是消息队列,字面来看就是存放消息的队列.也就是事件驱动架构中的Broker
RabbitMQ是基于Erlang语言开发的开源消息通信中间件,官网地址:https://www.rabbitmq.com/
代码说明:
PublicerTest代码:可以看到Consumer发送完消息后就把连接和通道关闭了,这充分说明解耦合
PublicerTest代码执行后RabbitMQ界面会发生变化:
可见上面消息的发送和接收有点麻烦,而SpringAMQP大大简化了上面的开发.
[AMQP与Spring AMQP]:
Spring AMQP的官方网站:https://spring.io/projects/spring-amqp
[Spring AMQP的特征]:
[案例]:利用SpringAMQP实现HelloWorld中的基础消息队列功能
[消息发送步骤实现]:
[消息接收步骤实现]:
[案例]:模拟WorkQueue,实现一个队列绑定多个消费者
[实现步骤]:
Publicer实现一秒循环发送五十条消息的代码:
Consunmer中实现两个消费者共同接收消息(消费者1每秒接收50条,消费者2每秒处理10条)
发现两个消费者处理的消息条数是一样的,只是处理的总时间不同,并没有达到能者多劳的目的,这是因为RabbitMQP有个预取机制,消费者会提前把消息平均(你一个我一个)拿过来存着,不管我是否消费完了, 我们可以通过改配置取消这个机制,==修改Consumer的application.yml文件,设置preFetch这个值,可以通过控制预取消息的上限:
之前的简单那模型与WorkQueue模型的特点是一个消息只能给一个消费者,消费完后这个消息就会消失;而发布订阅模式与之前的案例的区别就是允许将同一个消息发送给多个消费(比如订单消息需要发送给订单服务、仓储服务等等一起处理).实现方式就是加入了exchange(交换机,常见的类型有 广播[FanoutExchange]、路由[Direct Exchange] 和 话题[TopicExchange])
注意:exchange负责消息路由,而不是存储,路由失败则消息丢失
[工作模型]:FanoutExchange会将接收到的消息路由到每一个跟其绑定的queue
[案例]:利用SpringAMQP演示FanoutExchange的使用
[实现步骤]:
步骤一:
在consumer服务声明Exchange(交换机API)、Queue(消息队列API)、Binding(绑定关系API)
SpringAMQP提供了声明交换机、队列、绑定关系的API,例如:
在consumer服务创建一个类,添加@Configuration注解,并声明FanoutExchange、Queue和绑定关系对象Binding,目录和代码如下:
[工作模型]:DirectExchange会将接收到的消息根据规则路由到指定的queue,因此称为路由模式(routes)
[案例]:利用SpringAMQP演示DirectExchange的使用
[实现步骤]:
步骤一:在consumer服务声明Exchange、Queue和RoutingKey
步骤二:在publicer服务中发送消息,并指定routingKey
[工作模型]:TopicExchange与Direct Exchange类似,区别在于routingKEY必须是多个单词的列表,并且以==.==分割
[案例]:利用SpringAMQP演示TopicExchange的使用
[现象]:在SpringAMQP的发送方法中,发送消息的类型是Object,也就是说我们可以发送任意对象类型的消息,SpringAMQP会帮我们序列化为字节后发送.
[测试]:我们在publicer服务中发送一个Map
我们在RabbitMQP中发现队列中的Map类型的消息被序列化成了一串长字符,这样会影响消息传递的时间与效率
elasticsearch是一款非常强大的开源搜索引擎,可以帮助我们从海量数据中快速找到所需的内容.
elasticsearch结合kibana、Logstash、Beats,也就是elastic stack(ELK).被广泛应用在日志数据分析、实时监控等领域.====
elasticsearch是elastic stack的核心,负责存储、搜索、分析数据,其官网地址为https://www.elastic.co/cn/.其底层实现是基于Lucene,而Lucene的核心技术是倒排索引
[正向索引]:
传统数据库(如MySQL)采用正向索引,例如给下表(tb_goods)中的id创建索引(提高查询速度而对表字段附加的一种标识),我们可以知道通过id查询数据,在id上创建的索引能帮助我们很快定位到数据,但是我们要想通过title字段中的某一部分查询(比如小米手机中的手机字段),此时索引不仅无法建立而且还得一条条数据查询,导致查询效率十分低下:
[倒排索引]:
elasticsearch采用倒排索引:
[es]:
[kibana为es提供的DSL编写工具Dev Tools]:
es在创建倒排索引时需要对文档分词;在搜索的时候,需要对用户输入内容分词.但是默认的分词规则对中文处理并不友好.我们可以在kibana的DevTools中测试:
所以我们要安装中文分词器IK分词器,官网https://github.com/medcl/elasticsearch-analysis-ik
分词器的底层肯定有一个词典供分词的时候查询,由于我们日常使用的词语会更新或者有些词没有必要分出来,这是我们就需要为词典提供个性化设置
ES官方提供了各种不同语言的客户端,用来操作ES.这些客户端的本质就是组装DSL语句,通过http请求发送给ES.官方文档地址为:http://www.elastic.co/guide/en/elasticsearch/client/index.html
[案例]:利用JavaRestClient实现创建、删除索引库,判断索引库是否存在.
[步骤]:
步骤一:导入数据库tb_hotel.sql
[步骤]:
步骤一:在项目hotel-demo的pom.xml引入es的RestHighLevelClient依赖:
步骤二:因为SpringBoot默认的ES版本是7.6.2,所以我们需要覆盖默认的ES版本,在hotel-demo的pom.xml文件中加入:
步骤三:初始化RestHighLevelClient
可以在测试类中写入如下代码创建索引库,为了方便理解,这里和DSL语句进行对比,其中里面的静态常量MAPPING_TEMPLATE指的就是DSL语句,为了避免代码冗余,这里可以在另一个类中定义静态常量,并把DSL语句赋值给它,后面就可以调用了
[案例]:从数据库中读取一条hotel数据,并将它添加到索引库,模板代码如下
但是根据具体情况还需要更改:
由于数据库读出来的Hotel实体类的经纬度包括longitude(维度)和latitude(经度)两个属性 [见图一],但是索引库创建映射的时候只有location一个属性 [见图二],这样将Hotel实体类放入索引库显然不合适==,所以我们要创建另一个实体类HotelDoc来将Hotel来转换 [见图三]
修改文档数据有两种方式:
方式一:全量更新.再次写入id一样的文档,就会删除旧文档,添加新文档
方式二:局部更新.之更新部分字段,我们只演示方式二
SpringCloud微服务技术栈(下)-分布式搜索