分布式消息system、messagebroker(了解kafka、rabbitmq之前的基础)
大家好,这里是codeshow。
在本课程中,我们将学习分布式消息系统。
在消息系统中,消息是可以在两个或多个关系之间交换的数据。
这里的数据通常是二进制数据。使用json或 Protocol Buffers 等序列化程序发送和接收数据。
中间有消息系统,一侧是publisher,另一侧是订阅者关系。
publisher是产生消息并将它们提交到消息队列的客户端。将消息发送到队列称为publish。
订阅者是从消息队列接收消息的客户端。从队列中获取消息称为consume。
消息由publisherpublish,消息通过消息系统传递给订阅者。
消息总是单向流动的:publisher到消息系统再到订阅者。
作为参考,我们使用术语producer而不是publisher和consumer而不是订阅者。
此外,pub 和 sub 用于缩短publisher和订阅者。
为了理解publisher和订阅者之间的关系,我将以代码为例进行解释。
package main
import "time"
func main() {
c := make(chan string)
go pub(c)
go sub(c)
select {}
}
func sub(c <-chan string) {
for {
message := <-c
println(message)
}
}
func pub(c chan<- string) {
c <- "Hello World 1"
time.Sleep(1 * time.Second)
c <- "Hello World 2"
time.Sleep(1 * time.Second)
c <- "Hello World 3"
time.Sleep(1 * time.Second)
c <- "Hello World 4"
time.Sleep(24 * time.Hour)
}
这段代码中,在main函数中创建了一个string类型的channel。
pub 函数publish一个“Hello World”角色。
当消息到达 for 无限循环中的通道时,sub 输出“Hello World”。
在 pub 函数中,我们向频道publish一个字符串,
sub 函数通过通道consume字符串。
consume和处理消息的模块也称为worker。
前面讨论了publisher、消息系统和订阅者之间的关系。
在上面的旧语言代码中,pub 函数充当publisher,sub 函数充当订阅者。
而你可以把这个pub和sub之间的string通道类型的c变量想象成一个消息系统。
如果一种语言不提供消息传递,例如 GoLanguage,则框架会提供。
以Java的Spring Framework为例,它会发出ApplicationEvent类型的消息。
并且可以通过 EventListener 注解订阅publish的消息。
这时,消息系统由Spring框架负责。
请注意,消息和事件是相似的。
两者的区别在于,消息传递通常将消息存储在一个名为 Queue 的存储中,因此订阅者可以稍后而不是立即处理它们。
事件在它们发生的那一刻被发送给订阅者,并且事件被销毁。
如上所述,消息传递有一个称为队列的存储。
这就是它被称为消息队列的原因。
通常,消息传递系统是为分布式环境中的消息传递和处理而设计的。
所以 Go 语言的通道和 Spring Framework 的 EventListeners 都不是有限上下文中的消息系统。
但是,无论是前文还是后文,都遵循事件驱动的架构,可以看作是广义的消息系统。
下面我们来看下使用最多的分布式消息系统kafka和rabbitmq。
首先, kafka和rabbitmq都是运行在分布式环境下的消息系统。
也称为messagebroker。
分布式消息传递系统通常提供可伸缩性、高可用性和可靠性。
1.为了扩展性, kafka和rabbitmq可以配置cluster。
这允许通过增加节点数量来进行水平扩展。
吞吐量可以通过水平缩放来增加。
2. 高可用性意味着消息系统即使在某些节点出现故障时也能保持运行。
3.可靠性是消息传递的可靠行为,例如不丢失已发布的消息,不向订阅者发送重复的消息。
kafka和 rabbitmq 作为分布式消息系统满足上述条件。
并且publisher一次publish一条消息,但是通过消息系统,这条消息可以被复制并投递到多个queue中。
例如,
kafka将消息划分为topic。
而这个topic又分为几个partition。
在订阅者端,多个worker可以通过consumer组使用一条消息。
rabbitmq 由 exchange 和queue组成。
交换器确定将消息传递到哪些queue的路由。
Rabbitmq 有 4 种交换类型:direct、fanout、 topic和 headers。
不仅可以将消息转发到各个交换机,还可以在交换机之间进行路由。
相对于kafka,rabbitmq有着非常灵活方便的消息路由设计。
相反,因为 rabbitmq 是queue,如果consumer从queue中取出一条消息,则该消息将从queue中删除。
另一方面,在kafka中,存储单元是topic和partition,而不是queue。
topic是区分消息的逻辑单元,实际存储的单元是partition。
此外,当consumer从partition消费消息时,它不会像queue删除消息。
记住一个特定的offset并订阅offset之后的数据。它就像一个数组。
在这个区别上,rabbitmq可以将多个订阅者附加到一个queue,而kafka只能将一个consumer组附加到一个partition。
两者的区别还在于存储上的区别。
rabbitmq 主要在内存中,kafka在磁盘上。
这就是我们使用kafka作为持久数据存储的原因,就像数据库一样。
这种行为差异反映在消息系统的设计中,具体取决于消息系统的用途。
rabbit mq是针对message队列的。
所以相对于kafka,消息队列在路由和queue的创建和删除方面非常灵活。
相对于rabbitmq, kafka针对的是大消息处理。
因此,与 rabbitmq 相比,它在批量处理方面具有更优越的性能。
上面,我们查看了分布式消息系统,并简要查看了 rabbitmq 和kafka。
我们将通过代码来深入了解kafka和rabbitmq。
最后,我们总结了使用消息系统的 5 个原因。
1.通信:数据可以在publisher和订阅者之间传递。这使您摆脱了不同语言、平台和异构性之间的约束。
2.异步:相对于同步通信,异步通信相对于过程通信可以提高性能。
3.高可用性:在分布式消息系统中,即使某些消息系统宕机,也能保证正常运行,尽管性能可能会下降。
4. 重试:即使worker宕机,消息仍保留在queue中,因此如果worker重新启动,它可以在上一次失败的操作之后继续。即使实时性降低,数据的一致性也变得可靠。
5. 松耦合:通过消息系统间接调用,而不是直接调用,publisher方不需要知道消息发布后的所有业务逻辑。publisher的角色仅在发布之前。然后通过消息系统将消息传递给订户。现在是订阅者负责处理消息。通过间接而非直接的publisher调用实现的松散耦合使我们的软件更加灵活和可扩展。
订阅和点赞通知设置对内容创作者非常有帮助。
谢谢