- kafka:
一、基本架构基本组成:生产者、Cluster集群下的Topic、消费者组Topic组成:用Offset表示消息在partition中的位置ISR与Replica:用于容灾(高可用)总体架构:Broker集群Kafka架构:需要注册中心Zookeeper二、一条消息的发送从Producer到Broker:Batch发送、压缩Broker将消息存到本地磁盘:顺序写Broker读出消息给Consumer:优化传统Broker查询中的数据拷贝:零拷贝Consumer组接收消息:分配策略、reBalance三、使Kafka性能提高的功能
一、基本架构
基本组成:生产者、Cluster集群下的Topic、消费者组
Topic组成:用Offset表示消息在partition中的位置
ISR与Replica:用于容灾(高可用)
总体架构:Broker集群
Kafka架构:需要注册中心Zookeeper
二、一条消息的发送
从Producer到Broker:Batch发送、压缩
Broker将消息存到本地磁盘:顺序写
- 消息在Broker本地磁盘的存储架构:
- 注意:这些LogSegment的日志文件以第一个消息的offset命名
- 了解磁盘底层读写方式:寻道的成本高,应该尽量避免
- Broker采用顺序写的方式对数据进行存储,能提高磁盘的写入效率
Broker读出消息给Consumer:
- Broker用二分查找offset所在文件
- 比如:找offset == 28,就要找到图中6.log文件
- 通过不同索引文件找到消息:
- 时间戳索引查找:进一步优化查找
优化传统Broker查询中的数据拷贝:零拷贝
- 零拷贝优化:
- 注:Broker数据写入的时候也是用到相关技术,直接将数据从应用空间写入磁盘(不需要拷贝到内核态)
Consumer组接收消息:分配策略、reBalance
- 涉及消费者组中consumer与Partition的分配问题:
- 用Coordinator进行分配:实现重平衡
三、使Kafka性能提高的功能
rocketmq:
- RocketMQ:
一、介绍MQ1)MQ = Message + Queue:2)RocketMQ重要概念:3)rockermq的安装:二、API1)快速入门:2)细说消费者:3)消费模式:push、pull4)MQ发送各种消息:同步、异步、单项、延迟、批量* 一、各种消息的使用情况:“ * ”越多越常用* 二、同步消息:安全但是性能不高* 三、异步消息:在发送端不可等待的时候,可以减少业务耗时* 四、单向消息:不用返回结果,吞吐量大、消息可能丢失(比如发送日志)* 五、延迟消息:* 六、批量消息:5)顺序消息:计算投递队列的算法 + consumer顺序模式6)tag:区分过滤同类型的消息7)key:消息去重的唯一标识8)重复消费问题的解决方案之一:MySQL去重表 - 唯一索引9)重试发送 / 接收消息:retryTimes10)springBoot整合mq:11)消费的两种模式:负载均衡、广播12)解决消息堆积问题:理论13)消息丢失问题:14)安全:<过>三、原理1)事务原理:2)延迟消息原理:3)消费重试:四、秒杀项目1)秒杀前置知识:<过>2)秒杀架构:<过>3)环境准备:<过>* 表:* 模块:* 配置:4)业务:关注mq部分五、RocketMQ与Kafka的联系结构命名的联系:架构:存储结构:
一、介绍MQ
1)MQ = Message + Queue:
- 一、MQ的两个概念:
- Message:消息,是数据
- Queue:队列,是一种数据结构
- 二、队列:
- 作用一:削峰限流 -> MQ缓存消息,服务端就可以返回结果了,减少业务耗时
- 作用二:解耦合 -> 消费者服务启不启动不影响生产者的服务,健壮性、高可用性
- 三、了解几种MQ:rocketmq、kafka
- 用表格对比:
- 四、中间件:mq属于消息中间件
- 什么是跨平台:
2)RocketMQ重要概念:
- 一、一些成分的含义:
- 二、broker内部:Topic + Queue
- Topic:更多指一种标签,给同一类信息打上标签
- Queue:队列,是真实存在的一种数据结构,一个Topic对应多个queue,存在在broker中
- 三、broker集群:讲讲name server作用
- 四、详解RocketMQ架构:
3)rockermq的安装:
- 在Linux中安装RocketMQ,配置和启动,记录ip、port消息,在windows开发也可以直接连(springboot yml)
二、API
1)快速入门:
- 一、大概流程:
- ①引入依赖:这里先用原生api熟悉
- ②实现:/ 注意是原生api的顺序 /
- producer:创建生产者组 -> 连接nameserver -> 启动生产者组 -> 创建消息并发送 -> 关闭生产者(shutdown)
- consumer:创建消费者组 -> 连接nameserver -> 订阅 -> 设置监听器(设置顺序/并发模式等) -> 启动消费者组 -> 挂起当前线程
- “ system.in.read() ”:将该线程一直挂起就可以一直监听消息
- ③以上consumer打印内容:
- 三、补充 - 生产者的一些细节:
2)细说消费者:
- 一、聊消费者组:
- 消费者组内的消息订阅关系必须一致
- mq内消息的投递有两种策略:负载均衡模式和广播模式
- 二、消费者和队列的数量关系:需要保持队列的数量 >= 消费者的数量,以免消费者的浪费
- 三、代理者点位、消费者点位、差值的概念:
- 差值:就是这两个点位的差,就是还没有消费的消息数
3)消费模式:push、pull
- 一、pull、push模式:了解
4)MQ发送各种消息:同步、异步、单项、延迟、批量
* 一、各种消息的使用情况:“ * ”越多越常用
* 二、同步消息:安全但是性能不高
- 之前的“ 快速入门 ”就是发送同步消息(发送消息 -> 返回结果“SEND_OK”)
* 三、异步消息:在发送端不可等待的时候,可以减少业务耗时
- 结果:本线程先结束,再发送消息
* 四、单向消息:不用返回结果,吞吐量大、消息可能丢失(比如发送日志)
- 补充:一般发送日志,可以发送到数据库,然后进行进一步排查
- 代码示例:
* 五、延迟消息:
- 场景:买火车票时,producer先选座位,MQ等待15分钟通知,consumer根据付没付钱进行相关业务处理
- producer发送消息:一共有18个投递等级
- consumer接收消息:
- 细节:如果之前在配置一些东西的时候,默认时8g然后改为512m的话,阉割之后,第一次发送和接收消息的时间不准,第二次就准了
* 六、批量消息:
- producer:集合可以作为send()参数
- 注意:批量消息全部会进入一个队列
5)顺序消息:计算投递队列的算法 + consumer顺序模式
- 一、场景:比如“ 下订单、发短信、发货”一个topic的三条消息,需要的是顺序发送消息
- 但是默认的消息发送是并发模式的,即不能保证producer先发送和consumer先接收的消息
- 比如producer发送三个消息,是在这几个队列中轮询发送的,而我们要求的是三个消息有顺序的在一个队列中
- 细说“有序”:只需要局部有序,这三条消息在一条队列中有序,就能保证顺序了
- 二、实现
- 环境准备:创建一个messageModel类:
- 假设一些数据
- producer发送顺序消息:
- 核心是send()方法的第二个参数:*new MessageQueueSelector() {}*里面的决定消息发送队列的逻辑,这样处理可以使消息可以轮询发送给不同的队列(因为按订单号的累加,取膜后的i也会累加),避免直接返回mqs.get(0)这样堆在一个队列中
- consumer接收顺序消息:监听器指定为顺序模式(MessageListenerOrderly)
- 效果:多线程但是顺序消费
- 三、补充 - 顺序模式和并发模式的重试的区别:
- 顺序模式下如果消费不成功,会无限重试
- 并发模式下如果消费失败超过16次,会将消息放进死信队列
6)tag:区分过滤同类型的消息
- 场景:同样是订单消息,但是“衣服”对应的逻辑是发快递,而“生鲜”对应的逻辑是附近门店配送
- 如何区分这两类消息:
- ①可以定义为两个不同的topic
- ②通过tag进行区分
- 一、实现 - producer:发送两个tag不同,同一topic的消息:
- 二、consumer:
- 同一个消费者组的订阅关系必须一致:订阅不同tag必须不同消费者组
- 三、效果:两个组都订阅了vip1,各收到一份vip1的消息
- 注意:这里是两个组,都订阅肯定都要受到;如果是一个组的几个消费者,则是可以选择负载均衡或者广播
- 什么时候用两个topic,什么时候用tag:看看官方文档
7)key:消息去重的唯一标识
- key、msgId:自带的和自定义的
- 一、示例:
- producer:发送带key消息(在定义消息的时候指定)
- consumer:拿到key -> .getKeys()
- 二、消息的去重:
- 为什么需要消息的去重:因为消息可能会有重复
- ①生产者多次投递:几乎不可能因为遵循的是tcp协议(三次握手,如下),但是有一点点可能
- ②消费者方因为扩容时会重试:重平衡机制
- 三、如何解决:在comsumer方解决,通过唯一标识key
- 怎么判断key存不存在(有没有被消费过):将消费的key存放进一个容器中(redis等数据库),拿到消息时先setnx插入redis,如果插入失败则已存在
8)重复消费问题的解决方案之一:MySQL去重表 - 唯一索引
- 一、解决思路:
- 补充 - 了解幂等性:多次操作的影响相同
- 二、解决方法:MySQL建一个去重表(key字段建立唯一索引),接收消息的时候,先把key插入数据库,如果插入成功才执行业务
- producer:
- consumer:核心就是业务前插入数据库 + 数据库表字段设置唯一索引
- ①用jdbcTemplate:
- ②用原生的jdbc:
9)重试发送 / 接收消息:retryTimes
- 一、producer:重试发送消息
- 二、consumer:重试接收消息
- 注意:重试间隔时间逐次递增
- 三、死信队列:并发消费模式下重试次数用完,将消息发配死信队列!
- 四、处理死信队列:
- ①将这些消息存储起来,让人工介入
- ②更常用:将正常处理业务和处理死信消息结合起来
10)springBoot整合mq:
- 首先要自己添加依赖,因为官方没有收录版本
- 一、producer项目:
- yml配置:name server、group
- 发送同步消息:
- 异步消息:
- 单向消息、延迟消息:
- 顺序消息之producer:前置环境准备于之前相同(MsgModel自定义类)
- 带tag消息:用冒号分割
- 带key消息:放在消息头,setHeader()
- 二、consumer项目:
- yml:
- 监听器:一直监听,不用system.in.read()。直接在类上定义消费者组
- 根据需要,在定义泛型的时候可以指定MessageExt或者String(只拿消息体)
- 没有返回值:报错了就是拒收,没有报错就是SUCCESS
- 顺序消息之consumer:指定顺序模式(默认并发模式)
- 消费带tag的消息:
11)消费的两种模式:负载均衡、广播
- 消费的负载均衡 / 广播模式差别:
- 集群模式(负载均衡模式)消费者点位随消费而后移
- 但是广播模式,mq不会记录消费者点位(消没消费都不管)
12)解决消息堆积问题:理论
- 怎么看:一般一个队列超过10w消息,就算消息堆积了
- 为什么会有消息堆积 -> 解决:三个方法
- 详细代码操作:
13)消息丢失问题:
- 一、为什么会丢失消息:原理
- ①同步刷盘:在producer发送消息到broker时,broker同步将消息写入磁盘(物理上,断电不丢失),如果写入时宕机,可能存在丢失问题(少)
- ②异步刷盘:在producer发送消息到broker时,broker将消息存在buffer(缓冲区,在内存,断电消息丢失)中,再批量存入磁盘
- 二、怎么避免消息补发:
- ①在producer发送消息同时,将消息记录进mysql中,如果消息被mq接收成功,则status = 2,否则status = 1。设置定时任务,每天检查出status = 1的信息进行补发
- ②开启broker集群模式,搞主备模式,将消息持久化在不同的硬件上
14)安全:<过>
三、原理
1)事务原理:
- Ps:具体流程体现的思想:两阶段提交 - 搜索
2)延迟消息原理:
3)消费重试:
四、秒杀项目
1)秒杀前置知识:<过>
- QPS:每秒处理请求的数量
2)秒杀架构:<过>
- 尽量减少接口的处理时间:
- 项目架构:
3)环境准备:<过>
* 表:
- goods:
- order:
* 模块:
- web服务:
- service服务:
- 再加入fastjson、mq等依赖(官方没有配置)
* 配置:
- web服务:
- service服务:
4)业务:关注mq部分
- web服务的用户抢购处理:
- web服务:定时任务,将mysql库存同步到redis中
- ps:后面还有秒杀项目的测试、redis分布式锁等