文章目录
从功能上来看,一个最基础的消息队列应该具备生产、存储、消费的能力,也就是能完成“生产者把数据发送到 Broker,Broker 收到数据后,持久化存储数据,最后消费者从 Broker 消费数据”的整个流程。
- 最基础的消息队列应该具备五个模块:
通信协议:用来完成客户端(生产者和消费者)和 Broker 之间的通信,比如生产或消费。
网络模块:客户端用来发送数据,服务端用来接收数据。
存储模块:服务端用来完成持久化数据存储。
生产者:完成生产相关的功能。
消费者:完成消费相关的功能。
问题来了,那么客户端和服务端之间的通信流程是怎么实现的呢?
1. Apache Kafka
协议特点
- 基于TCP的二进制协议:Kafka使用自定义的二进制协议,直接基于TCP传输
- 请求-响应模式:客户端发送请求,服务器返回响应
- 批量传输:支持消息批量发送和接收,提高吞吐量
核心协议组件
Producer API协议:
ProduceRequest {
acks: int16, // 确认级别
timeout: int32, // 超时时间
topic_data: array // 主题数据
}
TopicData {
topic: string, // 主题名
partition_data: array // 分区数据
}
PartitionData {
partition: int32, // 分区ID
record_set: bytes // 消息记录集
}
Consumer API协议:
FetchRequest {
replica_id: int32, // 副本ID
max_wait_time: int32, // 最大等待时间
min_bytes: int32, // 最小字节数
topic_data: array // 主题数据
}
协议优势
- 高性能:二进制协议减少序列化开销
- 简单可靠:协议设计简洁,易于实现
- 向后兼容:版本升级保持协议兼容性
2. Apache RocketMQ
协议特点
- 基于TCP的自定义协议:使用Remoting协议框架
- 请求-响应 + 异步推送:支持同步请求响应和异步消息推送
- 多协议支持:支持HTTP、gRPC等多种接入方式
核心协议结构
Remoting协议头:
RemotingCommand {
code: int32, // 命令码
language: byte, // 语言类型
version: int16, // 协议版本
opaque: int32, // 请求ID
flag: int32, // 标志位
remark: string, // 备注
ext_fields: map, // 扩展字段
body: bytes // 消息体
}
消息发送协议:
SendMessageRequest {
producer_group: string, // 生产者组
topic: string, // 主题
default_topic: string, // 默认主题
default_topic_queue_nums: int32, // 默认队列数
queue_id: int32, // 队列ID
sys_flag: int32, // 系统标志
born_timestamp: int64, // 创建时间戳
flag: int32, // 标志
properties: string, // 属性
reconsume_times: int32, // 重试次数
unit_mode: boolean, // 单元模式
batch: boolean, // 批量标志
max_reconsume_times: int32, // 最大重试次数
body: bytes // 消息体
}
协议优势
- 简洁高效:二进制协议,减少序列化开销
- 功能完整:支持同步、异步、单向等多种通信模式
- 扩展性强:支持自定义扩展字段和版本兼容
- 性能优化:连接复用、批量处理、压缩支持
- 错误处理:完善的错误码和错误处理机制
3. Apache Pulsar
协议特点
- 基于TCP的二进制协议:使用Pulsar Wire Protocol
- 分层架构:协议层、传输层、应用层分离
- 多协议支持:支持Kafka、AMQP等协议适配
核心协议结构
Pulsar Wire Protocol:
BaseCommand {
type: CommandType, // 命令类型
connect: Connect, // 连接命令
connected: Connected, // 连接响应
subscribe: Subscribe, // 订阅命令
producer: Producer, // 生产者命令
send: Send, // 发送命令
send_receipt: SendReceipt, // 发送回执
message: Message, // 消息
ack: Ack, // 确认
flow: Flow, // 流控制
unsubscribe: Unsubscribe, // 取消订阅
success: Success, // 成功响应
error: Error, // 错误响应
close_producer: CloseProducer, // 关闭生产者
close_consumer: CloseConsumer, // 关闭消费者
producer_success: ProducerSuccess, // 生产者成功
ping: Ping, // 心跳
pong: Pong, // 心跳响应
redeliver_unacknowledged_messages: RedeliverUnacknowledgedMessages, // 重发未确认消息
partition_metadata: PartitionMetadata, // 分区元数据
partition_metadata_response: PartitionMetadataResponse, // 分区元数据响应
lookup_topic: LookupTopic, // 查找主题
lookup_topic_response: LookupTopicResponse, // 查找主题响应
consumer_stats: ConsumerStats, // 消费者统计
consumer_stats_response: ConsumerStatsResponse, // 消费者统计响应
reached_end_of_topic: ReachedEndOfTopic, // 到达主题末尾
seek: Seek, // 定位
get_last_message_id: GetLastMessageId, // 获取最后消息ID
get_last_message_id_response: GetLastMessageIdResponse, // 获取最后消息ID响应
active_consumer_change: ActiveConsumerChange, // 活跃消费者变更
get_topics_of_namespace: GetTopicsOfNamespace, // 获取命名空间主题
get_topics_of_namespace_response: GetTopicsOfNamespaceResponse, // 获取命名空间主题响应
get_schema: GetSchema, // 获取模式
get_schema_response: GetSchemaResponse, // 获取模式响应
auth_challenge: AuthChallenge, // 认证挑战
auth_response: AuthResponse, // 认证响应
ack_response: AckResponse, // 确认响应
get_or_create_schema: GetOrCreateSchema, // 获取或创建模式
get_or_create_schema_response: GetOrCreateSchemaResponse, // 获取或创建模式响应
new_txn: NewTxn, // 新事务
new_txn_response: NewTxnResponse, // 新事务响应
add_partition_to_txn: AddPartitionToTxn, // 添加分区到事务
add_partition_to_txn_response: AddPartitionToTxnResponse, // 添加分区到事务响应
add_subscription_to_txn: AddSubscriptionToTxn, // 添加订阅到事务
add_subscription_to_txn_response: AddSubscriptionToTxnResponse, // 添加订阅到事务响应
end_txn: EndTxn, // 结束事务
end_txn_response: EndTxnResponse, // 结束事务响应
end_txn_on_partition: EndTxnOnPartition, // 在分区上结束事务
end_txn_on_partition_response: EndTxnOnPartitionResponse, // 在分区上结束事务响应
end_txn_on_subscription: EndTxnOnSubscription, // 在订阅上结束事务
end_txn_on_subscription_response: EndTxnOnSubscriptionResponse, // 在订阅上结束事务响应
tc_client_connect_request: TcClientConnectRequest, // TC客户端连接请求
tc_client_connect_response: TcClientConnectResponse, // TC客户端连接响应
watch_topic_list: WatchTopicList, // 监听主题列表
watch_topic_list_success: WatchTopicListSuccess, // 监听主题列表成功
watch_topic_update: WatchTopicUpdate, // 监听主题更新
watch_topic_list_close: WatchTopicListClose, // 监听主题列表关闭
}
协议优势
- 统一协议:一套协议支持多种消息模式
- 高性能:二进制协议,支持批量操作
- 功能完整:内置事务、模式管理等高级功能
4. RabbitMQ
协议特点
- AMQP协议:Advanced Message Queuing Protocol
- 多协议支持:AMQP 0-9-1、AMQP 1.0、MQTT、STOMP等
- 面向连接:基于TCP长连接,支持心跳保活
AMQP 0-9-1协议结构
协议帧格式:
Frame {
type: byte, // 帧类型
channel: int16, // 通道号
size: int32, // 负载大小
payload: bytes, // 负载数据
end: byte // 结束标记
}
帧类型:
- METHOD (1): 方法帧,包含AMQP方法
- HEADER (2): 头部帧,包含消息属性
- BODY (3): 体帧,包含消息内容
- HEARTBEAT (8): 心跳帧
连接建立流程:
Client -> Server: Connection.Start
Server -> Client: Connection.Start-Ok
Client -> Server: Connection.Tune
Server -> Client: Connection.Tune-Ok
Client -> Server: Connection.Open
Server -> Client: Connection.Open-Ok
消息发布协议:
Basic.Publish {
reserved1: int16, // 保留字段
exchange: string, // 交换机名
routing_key: string, // 路由键
mandatory: boolean, // 强制标志
immediate: boolean // 立即标志
}
协议优势
- 标准化:AMQP是开放标准协议
- 互操作性强:不同厂商实现可互操作
- 功能丰富:支持复杂的路由和交换模式
协议对比总结
特性 | Kafka | RocketMQ | Pulsar | RabbitMQ |
---|---|---|---|---|
协议类型 | 自定义二进制 | 自定义二进制 | 自定义二进制 | AMQP标准 |
传输层 | TCP | TCP | TCP | TCP |
连接模式 | 短连接 | 长连接 | 长连接 | 长连接 |
消息模式 | 发布-订阅 | 发布-订阅 | 发布-订阅 | 发布-订阅 |
事务支持 | 有限 | 完整 | 完整 | 有限 |
顺序保证 | 分区内有序 | 队列内有序 | 分区内有序 | 队列内有序 |
协议复杂度 | 简单 | 中等 | 复杂 | 中等 |
扩展性 | 高 | 高 | 很高 | 中等 |
每种协议都有其适用场景:
- Kafka:适合高吞吐量的日志收集和流处理,消息简短、支持批量传输
- RocketMQ:适合金融级可靠性和事务消息
- Pulsar:适合云原生和混合部署场景,计算和存储分离
- RabbitMQ:适合传统企业应用和复杂路由需求
题外话:Pulsar和RocketMQ之间的对比
从消息协议来看,RocketMQ、Pulsar都具有可靠性,但它们的设计理念、适用场景和优势各有不同。
1. 架构设计差异
RocketMQ架构
Producer -> Broker -> Consumer
| | |
简单架构 单机存储 直接消费
Pulsar架构
Producer -> Broker -> BookKeeper -> Consumer
| | | |
复杂架构 计算存储分离 分布式存储 多协议消费
关键差异:
- RocketMQ:计算和存储耦合,架构简单
- Pulsar:计算和存储分离,架构复杂但灵活
2. 适用场景对比
RocketMQ更适合的场景
- 金融级事务消息:
// RocketMQ的事务消息
TransactionProducer {
// 1. 发送半事务消息
sendMessageInTransaction(topic, message, transactionListener);
// 2. 执行本地事务
executeLocalTransaction(message);
// 3. 提交或回滚
commitTransaction();
}
优势:
-
事务完整性:保证本地事务和消息发送的一致性
-
回滚机制:支持事务回滚和消息回滚
-
金融级可靠性:满足金融业务的高可靠性要求
-
顺序消息处理:
// RocketMQ的顺序消息
// 全局顺序
Producer.send(message, MessageQueueSelector.selectByHash(key));
// 分区顺序
Producer.send(message, MessageQueueSelector.selectByHash(key));
优势:
-
严格顺序:保证消息的严格顺序
-
性能优化:顺序写入,性能更好
-
简单易用:API简单,易于实现
-
传统企业应用:
// 企业级特性
- 消息轨迹追踪
- 消息重试机制
- 死信队列处理
- 消息过滤
Pulsar更适合的场景
- 云原生部署:
// Pulsar的云原生特性
- 多租户支持
- 命名空间隔离
- 动态扩缩容
- 容器化部署
- 多协议支持:
// Pulsar支持多种协议
- Kafka协议适配
- AMQP协议适配
- MQTT协议适配
- 自定义协议
- 流处理集成:
// Pulsar Functions
PulsarFunction {
process(inputTopic, outputTopic, function);
}
3. 性能对比
延迟对比
场景 RocketMQ Pulsar
简单消息发送 1-5ms 1-5ms
事务消息 10-50ms 50-100ms
顺序消息 1-3ms 5-10ms
批量消息 5-10ms 10-20ms
吞吐量对比
场景 RocketMQ Pulsar
单机吞吐量 5M msg/s 8M msg/s
集群吞吐量 50M msg/s 100M msg/s
事务吞吐量 1M msg/s 500K msg/s
4. 运维复杂度
RocketMQ运维
# 部署简单
./mqnamesrv & # 启动NameServer
./mqbroker & # 启动Broker
# 监控简单
./mqadmin clusterList # 查看集群
./mqadmin topicList # 查看主题
Pulsar运维
# 部署复杂
./pulsar-daemon start bookie # 启动BookKeeper
./pulsar-daemon start broker # 启动Broker
./pulsar-daemon start proxy # 启动Proxy
# 监控复杂
./pulsar-admin clusters list # 查看集群
./pulsar-admin tenants list # 查看租户
./pulsar-admin namespaces list # 查看命名空间
5. 成本考虑
资源消耗对比
组件 RocketMQ Pulsar
CPU使用率 中等 较高
内存使用率 中等 较高
磁盘I/O 中等 较高
网络带宽 中等 较高
运维成本
方面 RocketMQ Pulsar
学习成本 低 高
运维复杂度 低 高
故障排查 简单 复杂
扩展成本 中等 较高