消息队列-MQ

296 阅读10分钟

这是我参与更文挑战的第3天,活动详情查看:更文挑战

一、概念及常见消息队列

  • 消息的传递通过中间件(消息队列),而不是相互调用
  • 排队:应用程序通过队列进行通信
  • 应用场景 (优点):解耦、异步、削峰
  • 遇到问题
    • MQ服务器稳定性;
    • 数据一致性;
    • 消息丢失;
    • 消息积压;
    • 重复消费;
    • 事物问题等

1.多线程与消息队列

  • 多线程: 编程层面、服务器的资源消耗在本机上
  • 消息队列:架构层面、服务器的资源消耗转移到消费者服务器上
  • 多线程和消息队列的使用并不冲突:
    • 使用消息队列消息异步传输,用户不着急知道结果都可以使用;
    • 多线程可以作为队列的生产者和消费者

2.重复消费

  • 原因:一般为网络原因导致(MQ没有收到完整的消息通知)
  • 如何保证?(幂等性)
    • 强校验:事务处理、操作前检查、数据库唯一性约束
    • 弱校验:重复一下也没那么重要的情况

3.消息丢失

  • 原因
    • 使用了自动确认消息模式
    • 没有确认事务
    • 磁盘持久化问题
  • 如何保证?
    • ACK确认机制
    • 消息持久化
    • 消息补偿机制
    • 设置集群镜像模式(?)

二、ActiveMQ

三、RabbitMQ

1.概念

2.重要组件

  • ConnectionFactory 连接管理器
  • Channel 信道 消息推送使用的通道
  • Exchange 交换器 用于接收、分配消息
  • Queue 队列 用于存储生产者的消息
  • RoutingKey 路由键 把生成的数据分配到交换器上
  • BindingKey 绑定键 把交换器的消息绑定到队列上

3.数据模型

  • 2.1 在 RabbitMQ 中,消息并不是直接被投递到 Queue(消息队列) 中的,中间还必须经过Exchange(交换器) 这一层;
    • Exchange(交换器) 会把我们的消息分配到对应的 Queue(消息队列) 中。
  • 2.2 RabbitMQ 中通过 Binding(绑定) 将 Exchange(交换器) 与 Queue(消息队列) 关联起来,
    • 在绑定的时候一般会指定一个 BindingKey(绑定建) ,这样 RabbitMQ 就知道如何正确将消息路由到队列了。
  • 2.3 Exchange 和 Queue 的绑定可以是多对多的关系
    -> Procedure   生产者
    -> Exchange    交换器
           Binding(绑定)
           Bindingkey
    -> Queue       消息队列
    -> Consumer    消费者

4.Exchange Types(交换器类型)

  • 4.1 fanout(默认)
    • 发送到该Exchange的消息路由到所有与它绑定的Queue中,不需要做任何判断操作速度最快的一种、适用于广播。
  • 4.2 direct
    • 把消息路由到那些 Bindingkey 与 RoutingKey 完全匹配的 Queue 中完全匹配 BindingKey 和 RoutingKey
  • 4.3 topic
    • 模糊匹配去路由到具体的Queue中 其中:*多个 #匹配多个单词
  • 4.4 headers (不推荐)
    • headers 类型的交换器不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中的 headers 属性进行匹配
  • RoutingKey:发送消息的时候设置的
  • Bindingkey:指定当前Exchange下,什么样的RoutingKey会被下派到当前绑定的Queue中

四、Kafka

1.概念及优点

  • 优势
    • 极致的性能:设计中使用了大量的批量、异步的处理思想,最高可处理 每秒千万级别的消息
    • 兼容性强

2.分布式流平台

3.消息模型

  • 发布订阅模式:Producer -> Topic -> Consumer
  • Kafka 中的 Partition(分区) 实际上可以对应成为消息队列中的队列。
Producer  生产者(生产消息的一方)
Consumer  消费者(消费消息的一方)
Broker    代理(kafka实力  多个 Kafka Broker 组成一个 Kafka Cluster) 
Topic     主题(消息的载体)
Partition 分区  属于Topic的一部分
  • 四个比较核心的API
    • producer:允许应用程序发布一个消息至一个或多个kafka的topic中
    • consumer:允许应用程序订阅一个或多个主题,并处理所产生的对他们记录的数据流
    • stream-api: 允许应用程序从一个或多个主题上消费数据然后将消费的数据输出到一个或多个其他的主题当中,有效地变换所述输入流,以输出流。类似于数据中转站的作用
    • connector-api:允许构建或运行可重复使用的生产者或消费者,将topic链接到现有的应用程序或数据系统。

4.kafka 配置文件

Kafka 配置文件

  • 配置文件一: kafka-3/conf/server.properties
############################# Server Basics #############################
# The id of the broker. This must be set to a unique integer for each broker.
# 当前机器在集群中的唯一标识,和zookeeper的myid性质一样
broker.id=3

############################# Socket Server Settings #############################

# listeners=PLAINTEXT://:9092
# 当前kafka对外提供服务的端口默认是9092
port=9093

# The number of threads that the server uses for receiving requests from the network and sending responses to the network
# 这个是borker进行网络处理的线程数(接收网络请求)
num.network.threads=3

# The number of threads that the server uses for processing requests, which may include disk I/O
# 这个是borker进行I/O处理的线程数
num.io.threads=8

# The send buffer (SO_SNDBUF) used by the socket server
# 发送缓冲区buffer大小,数据不是一下子就发送的,先回存储到缓冲区了到达一定的大小后在发送,能提高性能
socket.send.buffer.bytes=102400

# The receive buffer (SO_RCVBUF) used by the socket server
# kafka接收缓冲区大小,当数据到达一定大小后在序列化到磁盘
socket.receive.buffer.bytes=102400

# The maximum size of a request that the socket server will accept (protection against OOM)
# 这个参数是向kafka请求消息或者向kafka发送消息的请请求的最大数,这个值不能超过java的堆栈大小
socket.request.max.bytes=104857600


############################# Log Basics #############################
# A comma separated list of directories under which to store log files
log.dirs=/usr/mfj/testKafka/datas/data3

# The default number of log partitions per topic. More partitions allow greater
# parallelism for consumption, but this will also result in more files across
# the brokers.
# 默认的分区数,一个topic默认1个分区数
num.partitions=1

# The number of threads per data directory to be used for log recovery at startup and flushing at shutdown.
# This value is recommended to be increased for installations with data dirs located in RAID array.
num.recovery.threads.per.data.dir=1

############################# Internal Topic Settings  #############################
# The replication factor for the group metadata internal topics "__consumer_offsets" and "__transaction_state"
# For anything other than development testing, a value greater than 1 is recommended to ensure availability such as 3.
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1

############################# Log Flush Policy #############################

# The number of messages to accept before forcing a flush of data to disk
#log.flush.interval.messages=10000

# The maximum amount of time a message can sit in a log before we force a flush
#log.flush.interval.ms=1000

############################# Log Retention Policy #############################

# The minimum age of a log file to be eligible for deletion due to age
#默认消息的最大持久化时间,168小时,7天
log.retention.hours=168

# A size-based retention policy for logs. Segments are pruned from the log unless the remaining
# segments drop below log.retention.bytes. Functions independently of log.retention.hours.
#log.retention.bytes=1073741824

# The maximum size of a log segment file. When this size is reached a new log segment will be created.
# 这个参数是:因为kafka的消息是以追加的形式落地到文件,当超过这个值的时候,kafka会新起一个文件
log.segment.bytes=1073741824

# The interval at which log segments are checked to see if they can be deleted according
# to the retention policies
# 每隔300000毫秒去检查上面配置的log失效时间(log.retention.hours=168 ),到目录查看是否有过期的消息如果有,删除
log.retention.check.interval.ms=300000

############################# Zookeeper #############################

# Zookeeper connection string (see zookeeper docs for details).
# This is a comma separated host:port pairs, each corresponding to a zk
# server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002".
# You can also append an optional chroot string to the urls to specify the
# root directory for all kafka znodes.
#设置zookeeper的连接端口
zookeeper.connect=127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183

# Timeout in ms for connecting to zookeeper
zookeeper.connection.timeout.ms=18000


############################# Group Coordinator Settings #############################

# The following configuration specifies the time, in milliseconds, that the GroupCoordinator will delay the initial consumer rebalance.
# The rebalance will be further delayed by the value of group.initial.rebalance.delay.ms as new members join the group, up to a maximum of max.poll.interval.ms.
# The default value for this is 3 seconds.
# We override this to 0 here as it makes for a better out-of-the-box experience for development and testing.
# However, in production environments the default value of 3 seconds is more suitable as this will help to avoid unnecessary, and potentially expensive, rebalances during application startup.
group.initial.rebalance.delay.ms=0
  • 配置文件二:kafka-3/config/zookeeper.properties
# the directory where the snapshot is stored.
dataDir=/tmp/zookeeper
# the port at which the clients will connect
clientPort=2181
# disable the per-ip limit on the number of connections since this is a non-production config
maxClientCnxns=0
# Disable the adminserver by default to avoid port conflicts.
# Set the port to something non-conflicting if choosing to enable this
admin.enableServer=false
# admin.serverPort=8080

Zookeeper 配置文件

  • 配置文件:zookeeper-3/conf/zoo.cfg
# The number of milliseconds of each tick
# 这个时间是作为 Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,也就是每个 tickTime 时间就会发送一个心跳。
tickTime=2000

# The number of ticks that the initial 
# synchronization phase can take
# 这个配置项是用来配置 Zookeeper 接受客户端(这里所说的客户端不是用户连接 Zookeeper 服务器的客户端,而是 Zookeeper 服务器集群中连接到 Leader 的 Follower 服务器)初始化连接时最长能忍受多少个心跳时间间隔数。
# 当已经超过 5个心跳的时间(也就是 tickTime)长度后 Zookeeper 服务器还没有收到客户端的返回信息,那么表明这个客户端连接失败。总的时间长度就是 5*2000=10 秒
initLimit=10

# The number of ticks that can pass between 
# sending a request and getting an acknowledgement
# 这个配置项标识 Leader 与Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,总的时间长度就是5*2000=10秒
syncLimit=5

# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just 
# example sakes.
# 快照日志的存储路径
# 事物日志的存储路径,如果不配置这个那么事物日志会默认存储到dataDir制定的目录,这样会严重影响zk的性能,当zk吞吐量较大的时候,产生的事物日志、快照日志太多
dataDir=/usr/mfj/testZkeeper/datas/data3
dataLogsDir=/usr/mfj/testZkeeper/logs/logs3


# the port at which the clients will connect
# 这个端口就是客户端连接 Zookeeper 服务器的端口,Zookeeper 会监听这个端口,接受客户端的访问请求。修改他的端口改大点
clientPort=2183


server.1=localhost:2881:3881
server.2=localhost:2882:3882
server.3=localhost:2883:3883

#server.1=192.168.7.100:12888:13888
#server.2=192.168.7.101:12888:13888
#server.3=192.168.7.107:12888:13888
#server.1 这个1是服务器的标识也可以是其他的数字, 表示这个是第几号服务器,用来标识服务器,这个标识要写到快照目录下面myid文件里
#192.168.7.107为集群里的IP地址,第一个端口是master和slave之间的通信端口,默认是2888,第二个端口是leader选举的端口,集群刚启动的时候选举或者leader挂掉之后进行新的选举的端口默认是3888

5.kafKa-命令

  • 1.使用安装包中的脚本启动单节点 Zookeeper 实例
 bin/zookeeper-server-start.sh -daemon config/zookeeper.properties
  • 2.启动 kafka 服务(kafka-server-start.sh):
 bin/kafka-server-start.sh config/server.properties
 bin/kafka-server-start.sh -daemon config/server.properties     # 后台启动
  • 3.创建topic:
 bin/kafka-topics.sh --zookeeper localhost:2181 --create --topic sunhb-topic-1 --replication-factor 1 --partitions 1 
  • 4.查看topic列表:
 bin/kafka-topics.sh --zookeeper localhost:2181 --list
  • 5.修改主题分区
 # 使用alter指令修改主题
 bin/kafka-topics.sh --zookeeper localhost:2181 --alter --topic sunhb-topic-1 --partitions 10
  • 6.删除主题命令
 /bin/kafka-topics --delete --topic sunhb-topic-1 --zookeeper localhost:2181
  • 7.生产者生产消息(kafka-console-producer.sh):
  bin/kafka-console-producer.sh --broker-list localhost:9092 --topic  sunhb-topic-1
  • 8.消费者消费消息(kafka-console-consumer.sh):
  bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic  sunhb-topic-1  --from-beginning 
  • 9.查看描述 topics 信息(kafka-topics.sh):
  bin/kafka-topics.sh --zookeeper localhost:2181 --describe
  • 10.查看消费者消费信息日志
  bin/kafka-consumer-groups.sh --bootstrap-server localhost:9091 --describe --group test-group

0d17c387-98ee-48ed-a1da-b98952e54e41.png

6.kafKa-生产者-分区器

  • 详见 参考链接

7.kafka-消费者-分配分区

  • 详见 参考链接

8.kafka 如何保证消息的顺序消费?

  • Kafka 只能为我们 保证 Partition(分区) 中的消息有序 ---(通过offset);不能保证 Topic(主题) 中的 Partition(分区) 的顺序
  • 如何保证?
    • 方式一: 1 个 Topic 只对应一个 Partition
    • 方式二:发送消息的时候指定 key/Partition (推荐)

9.kafka 如何保证消息不丢失?

  • 异步: producer.send(msg);
  • 同步:producer.send(msg).get(); - <数据不会丢失>
  • 生产者丢失消息:
    • Producer 的 retries (重试次数)默认是3, 可以设置大一点
    • 设置重试间隔 间隔太小的话 重试效果不明显
  • 消费者丢失消息:
    • 设置 acks = all (所有副本都要接收到该消息之后该消息才算真正成功被发送)
    • 设置 replication.factor >= 3 (多个副本)
    • 设置 min.insync.replicas > 1 (至少要被写入到 2 个副本才算是被成功发送)
    • 设置 unclean.leader.election.enable = false
      • 当 leader 副本发生故障时就不会从 follower 副本中和 leader 同步程度达不到要求的副本中选择出 leader ,这样降低了消息丢失的可能性
      • Kafka 0.11.0.0版本开始 unclean.leader.election.enable 参数的默认值由原来的true 改为false

五、RocketMQ

参考链接: