├── go.mod
├── .idea
├── misc.xml
├── vcs.xml
├── .gitignore
├── modules.xml
└── nsq_go.iml
├── nsq_go_test.go
├── README.md
├── nsq_go.go
├── nsq_untils.go
├── nsq_consumer.go
├── go.sum
└── nsq_producer.go
/go.mod:
--------------------------------------------------------------------------------
1 | module nsq_go
2 |
3 | go 1.16
4 |
5 | require (
6 | github.com/nsqio/go-nsq v1.1.0
7 | go.uber.org/zap v1.19.1
8 | )
9 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | # Datasource local storage ignored files
5 | /dataSources/
6 | /dataSources.local.xml
7 | # Editor-based HTTP Client requests
8 | /httpRequests/
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/nsq_go.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/nsq_go_test.go:
--------------------------------------------------------------------------------
1 | package nsq_go
2 |
3 | import (
4 | "github.com/nsqio/go-nsq"
5 | "go.uber.org/zap"
6 | "testing"
7 | )
8 |
9 | func TestNsqGoStart(t *testing.T) {
10 | //1.nsqGo 启动
11 | logger, _ := zap.NewProduction()
12 | nsqlookupHttpAddress:= []string{"127.0.0.1:4161"} //nsqlookup 的地址数组
13 | NsqGoStart(nsqlookupHttpAddress, logger, 30, nsq.LogLevelWarning)
14 |
15 | //2.nsqGo 初始化生产者
16 | InitNsqProducer(nsq.NewConfig(), 3)
17 |
18 | //3.nsqGo 初始化消费者
19 | NewNsqConsumer("channelTest", "topicTest", OnNsqMsg, true, nsq.NewConfig())
20 |
21 | //4.nsqGo 生产者发消息
22 | NsqPush("topicTest", []byte("helloWorld"))
23 |
24 | }
25 |
26 | //消息Nsq消息处理
27 | func OnNsqMsg(msg *nsq.Message) {
28 | nsqGoLogInfo("ok", zap.Reflect("msg", msg))
29 | }
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # nsq_go
2 |
3 | `nsq_go`适用于集群版nsq的场景 ,内部封装了`nsq`的生产者和消费者,生产者根据`nsqlookupd`发现连接`nsqd`。
4 | View the go-nsq [documentation](http://godoc.org/github.com/bitly/go-nsq#Config) for configuration options.
5 |
6 | ## Example
7 |
8 | ```go
9 | func TestNsqGoStart(t *testing.T) {
10 |
11 | //1.nsqGo 启动
12 | logger, _ := zap.NewProduction()
13 | nsqlookupHttpAddress:= []string{"127.0.0.1:4161"} //nsqlookup 的地址数组
14 | NsqGoStart(nsqlookupHttpAddress, logger, 30, nsq.LogLevelWarning)
15 |
16 | //2.nsqGo 初始化生产者
17 | InitNsqProducer(nsq.NewConfig(), 3)
18 |
19 | //3.nsqGo 初始化消费者
20 | NewNsqConsumer("channelTest", "topicTest", OnNsqMsg, true, nsq.NewConfig())
21 |
22 | //4.nsqGo 生产者发消息
23 | NsqPush("topicTest", []byte("helloWorld"))
24 |
25 | }
26 |
27 | //消息Nsq消息处理
28 | func OnNsqMsg(msg *nsq.Message) {
29 | nsqGoLogInfo("ok",zap.Reflect("msg",msg))
30 | }
31 |
32 | ```
33 |
34 | # License
35 |
36 | MIT
--------------------------------------------------------------------------------
/nsq_go.go:
--------------------------------------------------------------------------------
1 | package nsq_go
2 |
3 | import (
4 | "github.com/nsqio/go-nsq"
5 | "go.uber.org/zap"
6 | )
7 |
8 | var myNsqGo *nsqGo
9 | var myLogger *zap.Logger
10 |
11 | type nsqGo struct {
12 | nsqdTcpAddress []string //Nsqd的Tcp地址
13 | nsqdHttpAddress []string //Nsqd的http地址
14 | nsqlookupHttpAddress []string //Nsqlookup的HTTP地址
15 | lookupdPollInterval int64 //消费者通过lookUp轮询查找新Nsqd的时间间隔// 也是生产者需要等多久才可以往新的Nsqd发消息的时间 (秒)
16 | logLevel nsq.LogLevel //nsq日志级别
17 |
18 | }
19 |
20 | //1.nsqlookupHttpAddress地址 2.日志 3.消费者通过lookUp轮询查找新Nsqd的时间间隔,也是生产者需要等多久才可以往新的Nsqd发消息的时间 (秒)
21 | func NsqGoStart(nsqlookupHttpAddress []string, logger *zap.Logger, lookupdPollInterval int64, lvl nsq.LogLevel) {
22 | //日志赋值
23 | if myLogger == nil {
24 | myLogger = logger
25 | }
26 | if len(nsqlookupHttpAddress) <= 0 {
27 | nsqGoLogInfo("nsq_go nsqlookupHttpAddress 0")
28 | return
29 | }
30 | if myNsqGo == nil {
31 | myNsqGo = &nsqGo{
32 | nsqlookupHttpAddress: nsqlookupHttpAddress,
33 | lookupdPollInterval: lookupdPollInterval,
34 | logLevel: lvl,
35 | }
36 | }
37 | nsqGoLogInfo("nsq_go start successful !!!!")
38 | }
39 |
--------------------------------------------------------------------------------
/nsq_untils.go:
--------------------------------------------------------------------------------
1 | package nsq_go
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "go.uber.org/zap"
7 | "io/ioutil"
8 | "log"
9 | "net/http"
10 | "time"
11 | )
12 |
13 | //http post请求
14 | func nsqGoHttpPost(url string, data interface{}, contentType string) string {
15 | // 超时时间:5秒
16 | client := &http.Client{Timeout: 5 * time.Second}
17 | jsonStr, _ := json.Marshal(data)
18 | resp, err := client.Post(url, contentType, bytes.NewBuffer(jsonStr))
19 | if err != nil {
20 | nsqGoLogError("post err", zap.Error(err))
21 | return ""
22 | }
23 | defer resp.Body.Close()
24 |
25 | result, _ := ioutil.ReadAll(resp.Body)
26 | return string(result)
27 | }
28 |
29 | //http get请求
30 | func nsqGoHttpGet(url string) []byte {
31 | resp, err := http.Get(url)
32 | if err != nil {
33 | nsqGoLogError("nsqLookup get err", zap.Error(err))
34 | return nil
35 | }
36 | defer resp.Body.Close()
37 | body, err := ioutil.ReadAll(resp.Body)
38 | if err != nil {
39 | nsqGoLogError("get err", zap.Error(err))
40 | return nil
41 | }
42 | return body
43 | }
44 |
45 | //调试日志接口
46 | func nsqGoLogDebug(msg string, fields ...zap.Field) {
47 | if myLogger == nil {
48 | log.Println("nsqGo myLogger nil")
49 | return
50 | }
51 | myLogger.Debug(msg, fields...)
52 | }
53 |
54 | //关键信息日志接口
55 | func nsqGoLogInfo(msg string, fields ...zap.Field) {
56 | if myLogger == nil {
57 | log.Println("nsqGo myLogger nil")
58 | return
59 | }
60 | myLogger.Info(msg, fields...)
61 | }
62 |
63 | //警告信息日志接口
64 | func nsqGoLogWarn(msg string, fields ...zap.Field) {
65 | if myLogger == nil {
66 | log.Println("nsqGo myLogger nil")
67 | return
68 | }
69 | myLogger.Warn(msg, fields...)
70 | }
71 |
72 | //错误信息日志接口
73 | func nsqGoLogError(msg string, fields ...zap.Field) {
74 | if myLogger == nil {
75 | log.Println("nsqGo myLogger nil")
76 | return
77 | }
78 | myLogger.Error(msg, fields...)
79 | }
80 |
81 | func nsqGoSetTimeout(interval int, fn func(...interface{}) int, args ...interface{}) {
82 | if interval < 0 {
83 | nsqGoLogError("new timeout interval", zap.Int("interval", interval))
84 | return
85 | }
86 | nsqGoLogInfo("new timeout interval", zap.Int("interval", interval))
87 |
88 | go func() {
89 | var tick *time.Timer
90 | for interval > 0 {
91 | tick = time.NewTimer(time.Second * time.Duration(interval))
92 | select {
93 | case <-tick.C:
94 | tick.Stop()
95 | interval = fn(args...)
96 | }
97 | }
98 | }()
99 | }
100 |
--------------------------------------------------------------------------------
/nsq_consumer.go:
--------------------------------------------------------------------------------
1 | package nsq_go
2 |
3 | import (
4 | "fmt"
5 | "github.com/nsqio/go-nsq"
6 | "go.uber.org/zap"
7 | "sync"
8 | "time"
9 | )
10 |
11 | //消费者处理函数啊
12 | type nsqConsumerFunc func(msg *nsq.Message)
13 |
14 | var myNsqConsumerMgr *nsqConsumerMgr
15 | //nsq的消息处理管理者
16 | type nsqConsumerMgr struct {
17 | nsqHandlerMap map[string]nsqConsumerFunc //key 是话题 value是一个处理函数
18 | nsqLock sync.RWMutex
19 | }
20 |
21 | //nsq消费者
22 | type nsqConsumerHandler struct {
23 | topic string //主题
24 | channel string //通道
25 | }
26 |
27 | //一个话题创建一个消费者 //创建时才建立channel通道
28 | // 创建话题消费者 //1.服务器name 2.主题 3.话题处理handler 地址 4.是否临时一旦断开 就不发消息了 5.日志级别
29 | func NewNsqConsumer(channel, topic string, fn nsqConsumerFunc, isEphemeral bool, conf *nsq.Config) {
30 | if myNsqGo == nil {
31 | nsqGoLogError("nsqGo not start")
32 | return
33 | }
34 | if conf == nil {
35 | nsqGoLogError("conf nil")
36 | return
37 | }
38 | //向每个nsq注册topic (不注册也不影响使用,topic会在使用的时候创建,只是会些警告)
39 | for _, v := range myNsqGo.nsqdHttpAddress {
40 | address := fmt.Sprintf("http://%s/topic/create?topic=%s", v, topic)
41 | nsqGoHttpPost(address, nil, "application/json")
42 | }
43 | if myNsqConsumerMgr == nil{
44 | myNsqConsumerMgr = &nsqConsumerMgr{
45 | nsqHandlerMap: make(map[string]nsqConsumerFunc),
46 | }
47 | }
48 | //注册handler
49 | myNsqConsumerMgr.nsqLock.Lock()
50 | myNsqConsumerMgr.nsqHandlerMap[topic] = fn
51 | myNsqConsumerMgr.nsqLock.Unlock()
52 | if isEphemeral {
53 | channel = fmt.Sprintf("%s#ephemeral", channel)
54 | }
55 | //添加消费者
56 | conf.LookupdPollInterval = time.Duration(myNsqGo.lookupdPollInterval) * time.Second //设置服务发现的轮询时间,例如新的nsq出现
57 | //channel 是服务器名字
58 | c, err := nsq.NewConsumer(topic, channel, conf)
59 | if err != nil {
60 | nsqGoLogError("[nsq] newConsumer err", zap.Error(err), zap.String("serverName", channel))
61 | return
62 | }
63 | consumer := &nsqConsumerHandler{
64 | channel: channel,
65 | topic: topic,
66 | }
67 | c.AddHandler(consumer)
68 | //设置日志
69 | c.SetLoggerLevel(myNsqGo.logLevel)
70 | //lookup地址
71 | addressList := myNsqGo.nsqlookupHttpAddress
72 | if err := c.ConnectToNSQLookupds(addressList); err != nil { // 通过nsqlookupd查询
73 | nsqGoLogError("[nsq] newConsumer err", zap.Error(err))
74 | return
75 | }
76 | nsqGoLogInfo("nsq Consumer init successfull !!! ", zap.String("serverName", channel), zap.String("topic", topic), zap.Bool("isEphemeral", isEphemeral))
77 | return
78 | }
79 |
80 | // HandleMessage 是需要实现的处理消息的方法
81 | func (n *nsqConsumerHandler) HandleMessage(msg *nsq.Message) (err error) {
82 | myNsqConsumerMgr.nsqLock.RLock()
83 | defer myNsqConsumerMgr.nsqLock.RUnlock()
84 | nsqGoLogInfo("【nsq】消费者处理", zap.String("Topic", n.topic), zap.Int64("time", time.Now().Unix()))
85 | if fn, ok := myNsqConsumerMgr.nsqHandlerMap[n.topic]; ok {
86 | fn(msg)
87 | } else {
88 | nsqGoLogError("nsq topic Handler nil", zap.String("topic", n.topic))
89 | }
90 | return
91 | }
92 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
2 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
6 | github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
7 | github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
8 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
9 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
10 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
11 | github.com/nsqio/go-nsq v1.1.0 h1:PQg+xxiUjA7V+TLdXw7nVrJ5Jbl3sN86EhGCQj4+FYE=
12 | github.com/nsqio/go-nsq v1.1.0/go.mod h1:vKq36oyeVXgsS5Q8YEO7WghqidAVXQlcFxzQbQTuDEY=
13 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
14 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
15 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
16 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
17 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
18 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
19 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
20 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
21 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
22 | go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
23 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
24 | go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4=
25 | go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
26 | go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
27 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
28 | go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
29 | go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
30 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
31 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
32 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
33 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
34 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
35 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
36 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
37 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
38 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
39 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
40 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
41 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
42 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
43 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
44 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
45 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
46 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
47 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
48 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
49 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
50 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
51 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
52 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
53 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
54 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
55 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
56 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
57 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
58 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
59 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
60 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
61 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
62 |
--------------------------------------------------------------------------------
/nsq_producer.go:
--------------------------------------------------------------------------------
1 | package nsq_go
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "github.com/nsqio/go-nsq"
7 | "go.uber.org/zap"
8 | "strings"
9 | "sync/atomic"
10 | "time"
11 | )
12 |
13 | var nsqdManager *nsqdMgr
14 |
15 | // nsqdMgr nsqd管理
16 | type nsqdMgr struct {
17 | // 生产者nsq列表
18 | producerList []*nsqdProducer
19 |
20 | // 当前发消息的生产者下标
21 | currIndex int32
22 |
23 | // 记录当前所有nsq的 信息 (主要用于判断更新的nsq是否新增)
24 | //k:tcp地址
25 | nodesTcpMap map[string]struct{}
26 | //k:http地址
27 | nodesHttpMap map[string]struct{}
28 | }
29 |
30 | // nsqdProducer nsqd生成者
31 | type nsqdProducer struct {
32 | // 启动时间
33 | startTime int64
34 | producer *nsq.Producer
35 | }
36 |
37 | // nsqNodeList nsq节点列表,用于从 nsqlookup 获取消息 解析使用
38 | type nsqNodeList struct {
39 | Producers []*nsqdNode `json:"producers"`
40 | }
41 |
42 | // nsqdNode nsqd节点信息
43 | type nsqdNode struct {
44 | RemoteAddress string `json:"remote_address"` // nsqd的远端地址(ip+端口)
45 | HostName string `json:"host_name"`
46 | BroadcastAddress string `json:"broadcast_address"`
47 | TcpPort int `json:"tcp_port"`
48 | HttpPort int `json:"http_port"`
49 | Version string `json:"version"`
50 | Topics []string `json:"topics"`
51 | }
52 |
53 | // InitNsqProducer 初始化nsq生产者
54 | func InitNsqProducer(nsqConfig *nsq.Config, updateNsqInterval int) {
55 | if myNsqGo == nil {
56 | nsqGoLogError("[nsq] not start")
57 | return
58 | }
59 | if nsqConfig == nil {
60 | nsqGoLogError("[nsq] config nil")
61 | return
62 | }
63 | if nsqdManager == nil {
64 | nsqdManager = &nsqdMgr{
65 | currIndex: 0,
66 | nodesTcpMap: make(map[string]struct{}),
67 | nodesHttpMap: make(map[string]struct{}),
68 | }
69 | }
70 | // 更新节点信息
71 | updateNsqdNodes(nsqConfig)
72 | // 定时更新
73 | nsqGoSetTimeout(updateNsqInterval, func(args ...interface{}) int {
74 | updateNsqdNodes(nsqConfig)
75 | return updateNsqInterval
76 | })
77 | nsqGoLogInfo("[nsq] init producer successful")
78 | }
79 |
80 | // NsqPush 发送消息,采用轮训发送
81 | // isSync是否同步参数,isSync填写该值就是同步,不填默认异步
82 | func NsqPush(topic string, data []byte, isSync ...interface{}) bool {
83 | if nsqdManager == nil {
84 | nsqGoLogError("[nsq] manager nil")
85 | return false
86 | }
87 | lenList := len(nsqdManager.producerList)
88 | // 给一个nsqd发送即可,若发送失败就发送下一个
89 | for i := 0; i < lenList; i++ {
90 | nsqGoLogDebug("[nsq] send message", zap.String("topic", topic))
91 | // TODO: 这次感觉使不使用原子操作都影响不大?
92 | nsqdManager.currIndex++
93 | index := atomic.AddInt32(&nsqdManager.currIndex, 1) % int32(lenList)
94 | if index == 0 {
95 | atomic.StoreInt32(&nsqdManager.currIndex, 0)
96 | }
97 | nsqGoProducer := nsqdManager.producerList[index]
98 | if nsqGoProducer == nil {
99 | nsqGoLogError("[nsq] producer nil")
100 | continue
101 | }
102 | if nsqGoProducer.producer == nil {
103 | nsqGoLogError("[nsq] producer.producer nil")
104 | continue
105 | }
106 | // 防止消费者还未发现新添加的nsqd
107 | if nsqGoProducer.startTime+myNsqGo.nsqlookupPollInterval >= time.Now().Unix() {
108 | continue
109 | }
110 | if len(isSync) > 0 {
111 | err := nsqGoProducer.producer.Publish(topic, data)
112 | if err != nil {
113 | nsqGoLogError("[nsq] publishAsync err", zap.Error(err))
114 | continue
115 | }
116 | } else {
117 | err := nsqGoProducer.producer.PublishAsync(topic, data, nil)
118 | if err != nil {
119 | nsqGoLogError("[nsq] publishAsync err", zap.Error(err))
120 | continue
121 | }
122 | }
123 | return true
124 | }
125 | nsqGoLogError("[nsq] all producer publish err", zap.Int("len", lenList))
126 | return false
127 | }
128 |
129 | // updateNsqdNodes 更新nsqd的节点信息
130 | // 会填充到 Config.Server.NsqConfig.nsqlookupHttpAddress,Config.Server.NsqConfig.NsqdTcpAddress 中
131 | func updateNsqdNodes(nsqConfig *nsq.Config) {
132 | if nsqConfig == nil {
133 | nsqGoLogError("[nsq] config nil")
134 | return
135 | }
136 | if myNsqGo == nil {
137 | nsqGoLogError("[nsq] not start")
138 | return
139 | }
140 | if nsqdManager == nil {
141 | nsqGoLogError("[nsq] manager is nil")
142 | return
143 | }
144 | // 增加的nsqd节点
145 | addNodesTcpMap := make(map[string]struct{})
146 | addNodesHttpMap := make(map[string]struct{})
147 | // 当前的nsqd连接 (会删除一些没有使用的)
148 | currNodesTcpMap := make(map[string]struct{})
149 | for _, address := range myNsqGo.nsqlookupHttpAddress {
150 | url := fmt.Sprintf("http://%v/nodes", address)
151 | body := nsqGoHttpGet(url)
152 | if len(body) == 0 {
153 | continue
154 | }
155 | nl := &nsqNodeList{}
156 | err := json.Unmarshal(body, nl)
157 | if err != nil {
158 | nsqGoLogError("[nsq] json unmarshal err", zap.Error(err))
159 | return
160 | }
161 | for _, v := range nl.Producers {
162 | if v == nil {
163 | continue
164 | }
165 | strList := strings.Split(v.RemoteAddress, ":")
166 | if len(strList) != 2 {
167 | nsqGoLogError("[nsq] remoteAddress err", zap.String("remoteAddress", v.RemoteAddress))
168 | continue
169 | }
170 | ip := strList[0]
171 | if ip == "127.0.0.1" {
172 | ip = strings.Split(address, ":")[0]
173 | }
174 | tcpAddress := fmt.Sprintf("%v:%v", ip, v.TcpPort)
175 | // 填充tcpMap
176 | if _, ok := nsqdManager.nodesTcpMap[tcpAddress]; !ok {
177 | nsqdManager.nodesTcpMap[tcpAddress] = struct{}{}
178 | // 增加Tcp
179 | addNodesTcpMap[tcpAddress] = struct{}{}
180 | }
181 | // 记录当前的
182 | currNodesTcpMap[tcpAddress] = struct{}{}
183 |
184 | httpAddress := fmt.Sprintf("%v:%v", ip, v.HttpPort)
185 | // 填充HttpMap
186 | if _, ok := nsqdManager.nodesHttpMap[httpAddress]; !ok {
187 | nsqdManager.nodesHttpMap[httpAddress] = struct{}{}
188 | // 增加http
189 | addNodesHttpMap[httpAddress] = struct{}{}
190 | }
191 | }
192 | }
193 | isFirst := len(myNsqGo.nsqdTcpAddress) == 0
194 | // 填充 增加的
195 | for address := range addNodesTcpMap {
196 | myNsqGo.nsqdTcpAddress = append(myNsqGo.nsqdTcpAddress, address)
197 | // 增加nsqd的生产者
198 | producer, err := nsq.NewProducer(address, nsqConfig)
199 | if err != nil {
200 | nsqGoLogError("[nsq] create producer failed", zap.Error(err))
201 | }
202 | startTime := time.Now().Unix()
203 | if isFirst {
204 | // 第一次可以立即使用
205 | startTime = 0
206 | }
207 | nsqdManager.producerList = append(nsqdManager.producerList, &nsqdProducer{
208 | startTime: startTime,
209 | producer: producer,
210 | })
211 | }
212 | for address := range addNodesHttpMap {
213 | myNsqGo.nsqdHttpAddress = append(myNsqGo.nsqdHttpAddress, address)
214 | }
215 |
216 | // 删除没有注册的
217 | for i := 0; i < len(nsqdManager.producerList); i++ {
218 | p := nsqdManager.producerList[i]
219 | if p == nil || p.producer == nil {
220 | nsqGoLogError("[nsq] producer nil", zap.Int("index", i))
221 | continue
222 | }
223 | isDel := true
224 | for add := range currNodesTcpMap {
225 | if p.producer.String() == add {
226 | isDel = false
227 | break
228 | }
229 | }
230 | if !isDel {
231 | continue
232 | }
233 | // 判断下 但是基本不可能
234 | // slice中 如: arr:=[]int{1} arr[len(arr):]这样是合法的√
235 | // arr[len(arr)]这样才是下标越界×
236 | if i+1 > len(nsqdManager.producerList) {
237 | nsqGoLogError("arr index out", zap.Int("i+1", i+1), zap.Int("len", len(nsqdManager.producerList)))
238 | continue
239 | }
240 | // 停止
241 | p.producer.Stop()
242 | // producerList 删除
243 | nsqdManager.producerList = append(nsqdManager.producerList[:i], nsqdManager.producerList[i+1:]...)
244 | i--
245 | // NodesTcpMap 删除
246 | delete(nsqdManager.nodesTcpMap, p.producer.String())
247 | // 重置currIndex
248 | nsqdManager.currIndex = 0
249 | }
250 | }
251 |
252 | // GetNsqdNum 获得nsqd数量
253 | func GetNsqdNum() int {
254 | if nsqdManager == nil {
255 | nsqGoLogError("[nsq] manager nil")
256 | return 0
257 | }
258 | return len(nsqdManager.producerList)
259 |
260 | }
261 |
--------------------------------------------------------------------------------