简单的通过brew 安装:
通过brew install zeromq 直接安装
使用之前先get : go get github.com/alecthomas/gozmq
安装或者启动的时候遇到的一个问题:
TPS:
ZeroMQ 性能最好,RabbitMQ次之,而ActiveMQ最差
持久化
ZeroMQ不支持持久化,RabbitMQ,ActiveMQ都支持
技术性
RabbitMQ功能更全,ActiveMQ次之,ZeroMQ最差RabbitMQ都功能完善,只需会调用即可,ActiveMQ Performance 差,而ZeroMQ可以实现RabbitMQ,但是都得需要我们自己编写,代码量大
并发性
毫无疑问,作为一个用了算长的一段时间的RabbitMQ,非它莫属,天生为高并发而生的ErLang语言
ZeroMQ基于socket的字节流,消息完整性不需要我们保证,RabbitMQ则需要我们自己控制
使用时间:RabbitMQ>ActiveMQ 而ZeroMQ未学,因而从头学:
照着offical doc 来吧
server 端: 服务器既可以接收消息,也可以回复消息
func main() {
context, _ := zmq2.NewContext(5)
soc, _ := context.NewSocket(zmq2.REP)
defer soc.Close()
soc.Bind("tcp://*:5555")
//waitting for msg
for{
msg, _ := soc.Recv(0)
fmt.Println("receive msg:",msg)
// logic code
time.Sleep(time.Second)
//send new msg back to client
sprintf := fmt.Sprintf("world")
soc.Send(sprintf,0)
}
}
client端:
func main() {
context, _ := zmq4.NewContext()
socket, _ := context.NewSocket(zmq4.REQ)
defer socket.Close()
fmt.Println("connecting to hello world server ...")
socket.Connect("tcp://localhost:5555")
for i := 0; i < 10; i++ {
//send hello
msg := fmt.Sprintf("hello")
socket.Send(msg, 0)
fmt.Println("send msg :", msg)
//waiting for reply
reply, _ := socket.Recv(0)
fmt.Println("[client] receive msg:", reply)
}
}
结果:
整个过程是很顺序化的,发送一次会接收一次,很固定的模式
当我们试图发送多次的时候:
i2, _ := socket.Send(msg, 0)
fmt.Println(i2)
send, e := socket.Send(msg+"-test", 0)
if nil!=e{
logrus.Errorf("error:%v",e)
fmt.Println(send)
}
结果:
报错了
部分总结:这是请求-响应模式,一次请求对应一次响应,一个循环期间,这对不可以再发送消息,并且因为ZMQ默认不支持持久化,所以当服务器宕机之后,如果想持久化需要手动编写code,依据:
If you kill the server (Ctrl-C) and restart it,
the client won't recover properly.
Recovering from crashing processes isn't quite that easy.
Making a reliable request-reply flow is complex enough,既不支持持久化,若想需要build manually```
The REQ-REP socket pair is in lockstep. T
he client issues zmq_send() and then zmq_recv(),
in a loop (or once if that's all it needs).
Doing any other sequence (e.g., sending two messages in a row) will result in a return code of -1 from the send or recv call. Similarly,
the service issues zmq_recv() and then zmq_send() in that order, as often as it needs to.
当试图发送2条信息的时候回返回-1错误码,这点类似于Java中的SynchronousQueue ,take()和put()是绑定一起的,但是tpc这个性能是最高的,how?
Now this looks too simple to be realistic,
but ZeroMQ sockets have, as we already learned, superpowers.
You could throw thousands of clients at this server, all at once,
and it would continue to work happily and quickly.
ZeroMQ允许你开放成千上万个server-client pair对
Send函数总是能见到一个flag的参数名称,关于这个参数的定义:
1.ZMQ_DONTWAIT(0) 既以非阻塞的形式发送消息 2.ZMQ_SNDMORE(1)既当一个数据过大的时候,允许数据分成多个message来发送,但是逻辑上应该知道这些message应该合为一个来处理
ZMQ没有约定数据格式,所以数据格式都需要自个儿控制,与protobuf类似
官方提示关于String 数据,不同语言String 可能结构不同,c 以null字节结尾,而当Python 发送数据的时候zmq可能不会接收到null ----既Py发送string 给C客户端(string不是以null结尾),C客户端不支持这种格式的string,则会发生错误(因此数据通信格式的定义很关键)
func main() {
context, _ := zmq4.NewContext() //创建包含io的上下文
socket, _ := context.NewSocket(zmq4.PUB) //建立一个socket连接,并且指定为publishe
defer socket.Close()
socket.Bind("tcp://*:5556")//绑定端口和ip地址
socket.Bind("ipc://weather.ipc")
rand.Seed(time.Now().UnixNano()) //时间种子,与Java中的Random randm=new Random(47)相同
for{
// make values that will fool the boss
zipCode := rand.Intn(100000) //可以理解为RabbitMQ中的channel,通过这个channel发送到queue中,,而client只需要订阅这个channel即可
temperature := rand.Intn(215) - 80
relhumidity := rand.Intn(50) + 10
msg := fmt.Sprintf("%d %d %d", zipCode, temperature, relhumidity)
socket.Send(msg, 0) //发送消息,官网定义这个消息会入队的形式发送,内部真正调用的是C中的func
//C.zmq_send(soc.soc, unsafe.Pointer(&d[0]), C.size_t(len(data)), C.int(flags))
}
}
Client端:
func main() {
context, _ := zmq4.NewContext()
socket, _ := context.NewSocket(zmq4.SUB)
defer socket.Close()
var temps []string
var err error
var temp int64
total_temp := 0
filter := "59937"
// find zipcode
if len(os.Args) > 1 { // ./wuclient 85678
filter = string(os.Args[1])
}
//// Subscribe to just one zipcode (whitefish MT 59937) //
fmt.Printf("Collecting updates from weather server for %s…\n", filter)
socket.SetSubscribe(filter) //可以与RabbitMQ这么理解:通过订阅某个channel,从而与其上的queue绑定了
socket.Connect("tcp://localhost:5556")
for i := 0; i < 101; i++ {
datapt, _ := socket.Recv(0)
temps = strings.Split(string(datapt), " ")
temp, err = strconv.ParseInt(temps[1], 10, 64)
if err == nil {
total_temp += int(temp)
}
}
fmt.Printf("Average temperature for zipcode %s was %dF \n\n", filter, total_temp/100)
}
1. 部分总结:**发布订阅模式,订阅的key,我们发现是消息的首个主体,如 a asdddd,通过空格分隔,则订阅的key就是a,具体源码无法看到,因为是native方法,只能猜测并大致验证是这样的**
2. 提示:**client必须订阅某个主体,这个就跟RabbitMQ中的consumer必须绑定某个queue和routingKey一样,client才能收到消息,并且这种模式下,server只能发不可收,client只能收不可发,并且client可以sub多个server,并且client端启动的时候总是会忽略收到的第一个消息,同时client与server不具有先后顺序,既都能单独存在**
3. 一个subscriber可以订阅多个publisher,每次调用只会占用1个连接,若收到消息,会以公平的方式交错的接收数据从而自个儿处理
4. 当一个Publisher无任何Subscriber的时候,消息则会直接丢弃
5. 从3.x开始,filter分发过滤逻辑交由server端(若使用的是tcp或icp协议)