Go-NSQ消息队列

简介

  • 用Go编写,开源的内存分布式消息队列中间件。

  • 开源大规模地处理每天数以十亿计级别的消息。

  • 分布式和去中心化拓扑结构、无单点故障,这样集群中任意一台机器挂了不会影响集群稳定性。

https://github.com/nsqio/nsq

应用场景

  • 异步处理,把非关键流程异步化,提高系统的响应时间和健壮性。
    • 用户注册 <--------> 注册信息写入数据库(50ms) ----> 写入队列5ms <--异步读取---  发送注册邮件短信(50ms),如果不使用异步处理,整个请求的时间为100ms,使用异步则为55ms。
  • 应用解耦,通过消息队列。
    • 订单系统 ------>  消息队列  -------> 库存系统
  • 流量削峰
    • 抢购系统:用户请求  --------- >  消息队列  <--------秒杀业务处理 

NSQ基本架构

Nsq组件

  • NSQD

    • 负责消息接收、保存以及发送消息给消费者的进程,可以同时部署多个nsqd进程,这样消息就能分布在多台服务器上。
  • NSQLookupd

    • 负责维护所有NSQD状态,提供服务发现的进程。也就是说每起一个NSQD服务则会把ip跟端口注册到nsqlookupd中。
  • NSQAdmin

    • web管理平台,实时监控集群以及执行各种管理任务。

NSQ架构图解

Topic:对应一个具体的队列。

Channel:每个消费者对应一个Channel,实现消息可重复消费。

NSQ接收和发送消息流程

  • 消息默认不支持持久化,可配置成持久化。

  • 每条消息至少传递一次

  • 消息不保证有序。

由于NSQ消息都是存储在内存中,当内存不足时,会由一个Goroutine存入硬盘,当NSQ内存配置为0时,会全部存储到硬盘中,同时也实现了数据持久化。

 

 

部署实例

生产者将消息放入所有的Channel中,也就是每个Channel都能收到这条消息(这样能保证每个系统都能收到消息),然后metrics这个管道对应的有ABC三个消费者,这时候假设A服务器性能比较好,那么会不会一直是A服务器在小菲数据,而BC一直空闲着,NSQD消费规则轮流消费。

 

 

 

 

NSQ搭建

具体安装启动步骤可以参考官网文档

Windows下解压后可以直接点击开启NsqLookUpd服务

nsqadmin.exe --lookupd-http-address  127.0.0.1:4161   // 启动nsqandmin,绑定lookupd
nsqd.exe --lookupd-tcp-address 127.0.0.1:4160    // 启动nsqd 绑定lookupd

接下来就能通过nsqadmin可视化管理nsq了,amdin默认端口:4171

 

 

 NSQ使用

 go get github.com/nsqio/go-nsq

通过NSQ实现生产者消费者模型

package main

import (
    "bufio"
    "fmt"
    "os"
    "strings"

    "github.com/nsqio/go-nsq"
)

// 生产者
var producer *nsq.Producer

// 初始化生产者
func initProducer(str string) error {
    var err error
    config := nsq.NewConfig()
    producer, err = nsq.NewProducer(str, config)
    if err != nil {
        return err
    }
    return nil

}

func main() {
    // nsq 的地址
    nsqAddress := "127.0.0.1:4150"
    err := initProducer(nsqAddress)
    if err != nil {
        fmt.Printf("initProducer failed err:%v\n", err)
    }
    // 读取控制台输入,放入队列
    reader := bufio.NewReader(os.Stdin)
    for {
        data, err := reader.ReadString('\n')
        if err != nil {
            fmt.Printf("read string failed err:%v\n", err)
            continue
        }
        data = strings.TrimSpace(data) // 去掉空格
        if data == "stop" {
            break
        }
        // 放入消息队列中
        err = producer.Publish("login_queue", []byte(data))
        if err != nil {
            fmt.Printf("publish  messge failed err:%v\n", err)
        }
        fmt.Printf("publish data:%s ok\n", data)
    }
}
生产者
package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"

    "github.com/nsqio/go-nsq"
)

type Consumer struct {
}

func (*Consumer) HandleMessage(msg *nsq.Message) error {
    fmt.Println("receive: ", msg.NSQDAddress, "message:", string(msg.Body))
    return nil

}

func initConsumer(topic string, channel string, address string) error {
    config := nsq.NewConfig()
    // 设置服务发现的轮询时间
    config.LookupdPollInterval = 15 * time.Second
    // 新建一个消费者
    conn, err := nsq.NewConsumer(topic, channel, config)
    if err != nil {
        return err
    }
    consumer := &Consumer{}
    // 添加消费者接口
    conn.AddHandler(consumer)
    // 建立nsqlookupd连接
    if err := conn.ConnectToNSQLookupd(address); err != nil {
        return err
    }
    return nil
}

func main() {
    // 监听nsqlookupd服务
    err := initConsumer("login_queue", "first", "127.0.0.1:4161")
    if err != nil {
        fmt.Printf("init consumer failed err:%v\n", err)
        return
    }
    // 声明信道
    conn := make(chan os.Signal)
    signal.Notify(conn, syscall.SIGINT)
    <-conn
}
消费者

 

posted @ 2020-05-29 17:11  TrumpC  阅读(549)  评论(0)    收藏  举报