⼿把⼿教写消息队列(golang)-使⽤channel实现消息队列
前⾔
这周⼊职了新公司,⽼板想探探他的底,看了⼀眼他的简历,呦呵,精通kafka,这⼩姑娘有两下⼦,既然这样,那你写⼀个消息队列吧。因为要⽤go语⾔写,这可给愁坏了。赶紧来求助我,我这么坚贞不屈⼀⼈,在的软磨硬泡下还是答应他了,所以接下来我就⼿把⼿教怎么写⼀个消息队列。下⾯我们就来看⼀看我是怎么写的吧~~~。
本代码已上传到我的github:
有需要的⼩伙伴,可⾃⾏下载,顺便给个⼩星星吧~~~
什么是消息队列
真是把我愁坏了,⾃⼰写的精通kafka,竟然不知道什么是消息队列,于是,⼀向好脾⽓的我开始给讲⼀讲什么是消息队列。
消息队列,我们⼀般称它为MQ(Message Queue),两个单词的结合,这两个英⽂单词想必⼤家都应该知道吧,其实最熟悉的还
是Queue吧,即队列。队列是⼀种先进先出的数据结构,队列的使⽤还是⽐较普遍的,但是已经有队列了,怎么还需要MQ呢?
我:问你呢,,知道吗?为什么还需要MQ?
:快点讲,想挨打呀?
我:噗。。。 算我多嘴,哼~~~
⽋⽋的我开始了接下来的耐⼼讲解…
举⼀个简单的例⼦,假设现在我们要做⼀个系统,该登陆系统需要在⽤户登陆成功后,发送封邮件到⽤户邮箱进⾏提醒,需求还是很简单的,我们先开看⼀看没有MQ,我们该怎么实现呢?画⼀个时序图来看⼀看:
看这个图,邮件发送在请求登陆时进⾏,当密码验证成功后,就发送邮件,然后返回登陆成功。这样是可以的,但是他是有缺陷的。这让我们的登陆操作变得复杂了,每次请求登陆都需要进⾏邮件发送,如果这⾥出现错误,整个登陆请求也出现了错误,导致登陆不成功;还有⼀个问题,本来我们登陆请求调⽤接⼝仅仅需要100ms,因为中间要做⼀次发送邮件的等待,那么调⽤⼀次登陆接⼝的时间就要增长,这就是问题所在,⼀封邮件他的优先级 不是很⾼的,⽤户也不需要实时收到这封邮件,所以这时,就体现了消息队列的重要性了,我们⽤消息队列进⾏改进⼀下。
这⾥我们将发送邮件请求放到Mq中,这样我们就能提⾼⽤户体验的吞吐量,这个很重要,顾客就是上帝嘛,毕竟也没有⼈喜欢⽤⼀个很慢很慢的app。
这⾥只是举了MQ众多应⽤中的其中⼀个,即异步应⽤,MQ还在系统解藕、削峰/限流中有着重要应⽤,这两个我就不具体讲解了,原理都⼀样,好好思考⼀下,你们都能懂得。
channel
好啦,终于知道什么是消息队列了,但是现在还是没法进⾏消息队列开发的,因为还差⼀个知识点,即go语⾔中的channel。这个很重要,我们还需要靠这个来开发我们的消息队列呢。
因篇幅有限,这⾥不详细介绍channel,只介绍基本使⽤⽅法。
什么是channel
Goroutine 和 Channel 是 Go 语⾔并发编程的两⼤基⽯。Goroutine ⽤于执⾏并发任务,Channel ⽤于 goroutine 之间的同步、通信。Go提倡使⽤通信的⽅法代替共享内存,当⼀个Goroutine需要和其他Goroutine资源共享时,Channel就会在他们之间架起⼀座桥梁,并提供确保安全同步的机制。channel本质上其实还是⼀个队列,遵循FIFO原则。具体规则如下:
先从 Channel 读取数据的 Goroutine 会先接收到数据;
先向 Channel 发送数据的 Goroutine 会得到先发送数据的权利;
创建通道
创建通道需要⽤到关键字 make ,格式如下:
通道实例:=make(chan数据类型)
数据类型:通道内传输的元素类型。
通道实例:通过make创建的通道句柄。
⽆缓冲通道的使⽤
Go语⾔中⽆缓冲的通道(unbuffered channel)是指在接收前没有能⼒保存任何值的通道。这种类型的通道要求发送 goroutine 和接收goroutine 同时准备好,才能完成发送和接收操作。
⽆缓冲通道的定义⽅式如下:
通道实例 := make(chan 通道类型)
通道类型:和⽆缓冲通道⽤法⼀致,影响通道发送和接收的数据类型。
缓冲⼤⼩:0
通道实例:被创建出的通道实例。
写个例⼦来帮助⼤家理解⼀下吧:
package main
import(
"sync"
"time"
)
go语言能做什么func main(){
c :=make(chan string)
var wg sync.WaitGroup
wg.Add(2)
go func(){
defer wg.Done()
c <-`Golang梦⼯⼚`
}()
go func(){
defer wg.Done()
time.Sleep(time.Second *1)
println(`Message: `+<-c)
}()
wg.Wait()
}
带缓冲的通道的使⽤
Go语⾔中有缓冲的通道(buffered channel)是⼀种在被接收前能存储⼀个或者多个值的通道。这种类型的通道并不强制要求 goroutine 之间必须同时完成发送和接收。通道会阻塞发送和接收动作的条件也会不同。只有在通道中没有要接收的值时,接收动作才会阻塞。只有在通道没有可⽤缓冲区容纳被发送的值时,发送动作才会阻塞。
有缓冲通道的定义⽅式如下:
通道实例 := make(chan 通道类型, 缓冲⼤⼩)
通道类型:和⽆缓冲通道⽤法⼀致,影响通道发送和接收的数据类型。
缓冲⼤⼩:决定通道最多可以保存的元素数量。
通道实例:被创建出的通道实例。
来写⼀个例⼦讲解⼀下:
package main
import(
"sync"
"time"
)
func main(){
c :=make(chan string,2)
var wg sync.WaitGroup
wg.Add(2)
go func(){
defer wg.Done()
c <-`Golang梦⼯⼚`
c <-`asong`
}()
go func(){
defer wg.Done()
time.Sleep(time.Second *1)
println(`: `+<-c)
println(`作者: `+<-c)
}()
wg.Wait()
}
好啦,通道的概念就介绍到这⾥了,如果需要,下⼀篇我出⼀个channel详细讲解的⽂章。
消息队列编码实现
准备篇
终于开始进⼊主题了,都听的快要睡着了,我轰隆⼀嗓⼦,⽴马精神,但是呢,asong也是挨了⼀顿⼩电炮,代价惨痛呀,呜呜呜…
在开始编写代码编写直接,我需要构思我们的整个代码架构,这才是正确的编码⽅式。我们先来定义⼀个接⼝,把我们需要实现的⽅法先列出来,后期对每⼀个代码进⾏实现就可以了。因此可以列出如下⽅法:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。