This is a creation in Article, where the information may have evolved or changed.
Why to use NSQ
has recently been looking for a high-performance, highly available message queue to do communication between internal services. At first think of using ZEROMQ, but in the process of finding information, accidentally found NSQ this Golang developed message queue, after all, is Golang original things, full-featured, the key is the performance is good. Which support dynamic expansion, eliminate the single point of failure and other characteristics, can be very good to meet my needs
The previous comparison between NSQ and other MQ looks really powerful. Here is a simple record of how NSQ is used
NSQ Service Side
NSQ Service-Side introduction
Before using the NSQ service, it is still necessary to understand several core components of NSQ
The entire NSQ service consists of three main parts
Nsqlookupd
Let's take a look at what the official words say:
NSQLOOKUPD is the daemon responsible for managing topology information. The client discovers the producer of the specified topic (topic) by querying NSQLOOKUPD, and the NSQD node broadcasts the topic (topic) and channel information
Simply put, NSQLOOKUPD is the central management Service, which uses TCP (default port 4160) to manage the NSQD service, using HTTP (the default port 4161) to manage the Nsqadmin service. Also provides query functionality for clients
In general, NSQLOOKUPD has the following features or features
-
Uniqueness, there is only one NSQLOOKUPD service in a NSQ service. It is also possible to deploy multiple nsqlookupd in the cluster, but there is no association between them
-
De-NSQLOOKUPD, even if it crashes, will not affect the running NSQD service
-
Serves as a middleware for NSQD and naqadmin information interactions
-
Provides an HTTP query service to periodically update the NSQD address directory for clients
Nsqadmin
Official quote: A WEB UI that aggregates real-time statistics for a cluster and performs different management tasks
In general, Nsqadmin has the following features or features
-
Provide a unified management of topic and channel operation interface and a variety of real-time monitoring data display, interface design is very simple, easy to operate
-
Show the number of all the message, uh .... Pack x Sharp
-
Be able to create topic and channel in the background, this should not be used to
-
All features of Nsqadmin must rely on nsqlookupd,nsqadmin to simply pass user actions to NSQLOOKUPD and show data from NSQLOOKUPD
Nsqd
Official quote: NSQD is a daemon that is responsible for receiving, queuing, and delivering messages to clients
Simply put, the real work is the service, it is mainly responsible for the message of the transceiver, queue maintenance. NSQD will listen to a TCP port (4150) and an HTTP port (4151) and an optional HTTPS port by default
In general, NSQD has the following features or features
-
For subscribers to the same topic, the same channel consumer uses a load balancing policy (not polling)
-
As long as the channel exists, even consumers without the channel will cache the producer's message in the queue (note the expiration of the message)
-
The message in the queue is guaranteed to be consumed at least once, even if NSQD exits, the messages in the queue are staged on the disk (except for unexpected cases such as ending the process)
-
Limited memory consumption, ability to configure the number of message caches in memory for each channel queue in NSQD, and once exceeded, message will be cached to disk
-
Topic,channel once established, will always exist, in a timely manner in the management station or in code to remove invalid topic and channel, to avoid waste of resources
This is the official figure, the first channel (meteics) because there are multiple consumers, so triggered the load balancing mechanism. After two channel because there is no consumer, all the message will be cached in the corresponding queue until the consumer appears
One problem here is that if a channel is only being delivered by a producer, will it cause server resources to be exhausted? Perhaps the NSQD internally did the corresponding treatment, but to avoid the occurrence of this situation
NSQ server-to-client relationship
Understand the relationship between NSQLOOKUPD,NSQD and consumers and producers in the client
Consumers
Consumers have two ways of connecting with NSQD
-
Consumers directly connected to the NSQD, this is the simplest way, the disadvantage is that the NSQD service can not achieve dynamic scaling (of course, to achieve one is also possible)
-
The consumer obtains the connection address of all NSQD on the NSQLOOKUPD via HTTP query nsqlookupd and then establishes a connection with the NSQD (the official recommended practice), but the client constantly queries the NSQLOOKUPD for the latest NSQD address directory ( Don't like polling this way with http ...)
Or take a closer look at the chart, the official consumer model:
Producers
Producers must directly connect NSQD to deliver a message (online said, can connect to NSQLOOKUPD, let nsqlookupd automatically choose a nsqd to complete delivery, but I use producer TCP is not even nsqlookupd, I don't know if HTTP can ...),
One problem here is that if the producer is connected to the NSQD, then the message will fail, so the client must implement the corresponding fallback plan.
Installing NSQ
Method One
go get github.com/tools/godep
After the execution, check whether the GODEP is installed in the bin directory, the general will automatically install, if not, with Go install manual installation under
go get github.com/bmizerany/assert
godep get github.com/bitly/nsq/...
If the installation succeeds, a lot of nsq_ will appear in the Bin directory ... The beginning of the executable file
Method Two
Run NSQ
Running a standalone NSQD service
NSQD is an independent service, start a NSQD can complete message transceiver, start a single-machine nsqd, very simple
nsqd
The client can use HTTP, or TCP, where I use the official GO-NSQ package as the client, using TCP to send and receive message
Running the NSQ service cluster
nsqlookupd
nsqd--lookupd-tcp-address=127.0.0.1:4160
nsqd --lookupd-tcp-address=127.0.0.1:4160 -tcp-address=0.0.0.0:4152 -http-address=0.0.0.0:4153
nsqd --lookupd-http-address=127.0.0.1:4161
GO-NSQ-based client implementation
A few notable places
-
Producer will not be re-connected after disconnection, you need to manually re-connect, consumer disconnection will automatically re-connect
-
Consumer's re-connect time configuration item has two functions (this design must be spit out, a bit better separately)
-
Consumer detects that a connection to the NSQD has been broken, and every x seconds to the NSQD request to reconnect
-
Consumer every x seconds, HTTP polling to Nsqlookud to update their NSQD address directory
-
The consumer time of the default is 60s (...). The dishes are cold), I changed to 1s
-
Consumer can receive the same name topic data from different NSQD node at the same time, in order to avoid confusion, it must be processed on the client
-
The interface callbacks set in Addconurrenthandlers and AddHandler are performed in a different goroutine
-
Producer cannot publish (Publish) empty message, otherwise it will cause panic
Go_nsq-send.go
//Nsq send test
Package main
Import (
"bufio"
"fmt"
"github.com/nsqio/go-nsq"
"os"
)
Var producer *nsq.Producer
// main function
Func main() {
strIP1 := "127.0.0.1:4150"
strIP2 := "127.0.0.1:4152"
InitProducer(strIP1)
Running := true
/ / Read the console input
Reader := bufio.NewReader(os.Stdin)
For running {
Data, _, _ := reader.ReadLine()
Command := string(data)
If command == "stop" {
Running = false
}
For err := Publish("test", command); err != nil; err = Publish("test", command) {
/ / Switch IP reconnection
strIP1, strIP2 = strIP2, strIP1
InitProducer(strIP1)
}
}
//shut down
producer.Stop()
}
// Initialize the producer
Func InitProducer(str string) {
Var err error
fmt.Println("address: ", str)
Producer, err = nsq.NewProducer(str, nsq.NewConfig())
If err != nil {
Panic(err)
}
}
//make an announcement
Func Publish(topic string, message string) error {
Var err error
If producer = nil {
If message == "" { //Cannot post an empty string, otherwise it will cause error
Return nil
}
Err = producer.Publish(topic, []byte(message)) // Post a message
Return err
}
Return fmt.Errorf("producer is nil", err)
}
go _nsq-receive.go
//Nsq receiving test
Package main
Import (
"fmt"
"time"
"github.com/nsqio/go-nsq"
)
// consumer
Type ConsumerT struct{}
// main function
Func main() {
InitConsumer("test", "test-channel", "127.0.0.1:4161")
For {
time.Sleep(time.Second * 10)
}
}
/ / Process the message
Func (*ConsumerT) HandleMessage(msg *nsq.Message) error {
fmt.Println("receive", msg.NSQDAddress, "message:", string(msg.Body))
Return nil
}
/ / Initialize the consumer
Func InitConsumer(topic string, channel string, address string) {
Cfg := nsq.NewConfig()
cfg.LookupdPollInterval = time.Second //Set the reconnection time
c, err := nsq.NewConsumer(topic, channel, cfg) // Create a new consumer
If err != nil {
Panic(err)
}
c.SetLogger(nil, 0) //mask system logs
c.AddHandler(&ConsumerT{}) // Add consumer interface
/ / Establish NSQLookupd connection
If err := c.ConnectToNSQLookupd(address); err != nil {
Panic(err)
}
/ / Establish multiple nsqd connections
// if err := c.ConnectToNSQDs([]string{"127.0.0.1:4150", "127.0.0.1:4152"}); err != nil {
// panic(err)
// }
// Create a nsqd connection
// if err := c.ConnectToNSQD("127.0.0.1:4150"); err != nil {
// panic(err)
// }
}