消息队列的定位本质上还是一个队列, 用于存放进程间通信的一些消息(可以想象成一个保管室,甲有东西要给乙,但是乙此时不一定有时间马上去拿, 便跟甲约定, 你把东西放到 xxx 保管室, 我有时间的时候直接去xxx保管室拿)。
消息队列就是这个“保管室”, 最简单的需求:生产者产生消息之后存储在消息队列里面, 消费者去消息队列获取消息进行处理。伴随着这个简单的需求使用频率和场景越来越多, 衍生出来的消息队列种类和所侧重的其他功能也越来越多(比如消息怎么存储,产生和消费消息时使用什么协议,怎么解决消息消费失败问题,怎么提高生产、消费消息时的并发量,怎么提高消费的可靠性,有多个生产者、消费者存在时怎么确定哪个消息是给谁的等)。
所有消息队列的核心功能其实就三个 : 生产消息、存储消息、消费消息。再抽象一点其实就是生产者存储数据, 消费者读取数据(可能需要删除)。
而我们使用消息队列的目的大多是为了解决“乙没时间或者不能立马(生产速度大于消费速度)处理甲产生的任务”这个问题。
常见的开源消息队列及其特点 :
除了上面的几种, 我们平时在做一些需求的时候,如果对数据的可靠性要求不是特别高或者不是特别重要的地方,在满足 生产者存储数据, 消费者读取数据 的前提下, 也可以使用 mysql 、 redis 、自己写的队列等作为消息队列。
如下, 用 go 的 channel 创建一个消息队列的demo(用来处理并发量不大的异步请求)
package main
import (
"fmt"
"time"
)
var chMq = make(chan string, 10)
func SendMsg(msg string) {
chMq <- msg
}
func receiveMsg() string {
defer func() {
if r := recover(); r != nil {
if e, ok := r.(error); ok {
fmt.Println(e, "receiveMsg panic")
}
fmt.Println("receiveMsg panic: ", r)
}
}()
for {
select {
case msg := <-chMq:
return msg
default:
}
}
}
func work(msg string) {
fmt.Println(msg)
time.Sleep(1 * time.Second)
}
func Consumer() {
for {
msg := receiveMsg()
work(msg)
}
}
func main() {
go Consumer()
for i := 0; i < 11; i++ {
go SendMsg(fmt.Sprintf("hello : %d", i))
}
select {}
}