Both Redis and NSQ have well-developed subscription and release implementations, but refer to their source code implementations to make a practical example of the similarities and differences between the two sides and the mechanism will be more impressive.
Practice implementing a simple subscribe/unsubscribe/release information feature is sufficient.
Server.go
The dict in the server structure uses a map to store information about the channel, while the channel structure uses a map to hold the client that subscribes to the channel.
This is not the same as in Redis, where the dict with the channel name as Map Key,value is its corresponding client list. While the client is insured
With all of its subscribed channel information, there is no such thing as good or bad, and Redis is relatively more convenient for client statistics, which is more concise in practice.
Package Pubsubimport ("Errors" "Sync") type Client struct {Id intip string}type Server struct {Dict Map[string]*channel//ma P[channel.name]*channelsync.rwmutex}func NewServer () *server {s: = &server{}s.dict = Make (Map[string]*Channel)// All Channelreturn s}//subscription func (SRV *server) Subscribe (client *client, channelname string) {//Customer is srv.rlock in the customer list of channel ( ) ch, found: = srv. Dict[channelname]srv. Runlock () if!found {ch = newchannel (channelname) ch. Addclient (client) srv. Lock () srv. Dict[channelname] = chsrv. Unlock ()} else {ch. Addclient (client)}}//unsubscribe from the func (SRV *server) unsubscribe (client *client, channelname string) {srv. Rlock () ch, found: = srv. Dict[channelname]srv. Runlock () if found {if ch. Deleteclient (client) = = 0 {ch. Exit () srv. Lock () Delete (srv. Dict, ChannelName) srv. Unlock ()}}}//publishes the message func (SRV *server) publishmessage (channelname, message string) (bool, error) {srv. Rlock () ch, found: = srv. Dict[channelname]if!found {srv. Runlock () return false, errors. New ("ChannelName does not exist!")} Srv. Runlock () Ch. Notify(message) Ch. Wait () return True, nil}
Channel.go
Each channel is responsible for putting information into the waitgroup, sending it to the client or queue, in which case a message is printed. When clients is empty, exit ().
Import ("FMT" "Sync" "sync/atomic") type Channel struct {Name stringclients map[int]*client//Exitchan Chan intsync.rwm Utexwaitgroup waitgroupwrappermessagecount uint64exitflag int32}func newchannel (channelName string) *Channel {retur n &channel{name:channelname,//exitchan:make (chan int), Clients:make (map[int]*client),}}func (ch *Channel) Ad Dclient (client *client) bool {ch. Rlock () _, Found: = Ch.clients[client. Id]ch. Runlock () Ch. Lock () if!found {ch.clients[client. ID] = client}ch. Unlock () return Found}func (ch *channel) deleteclient (client *client) int {var ret intch. Replymsg (FMT. Sprintf ("Remove client:%d from channel:%s", ch. Name, client. Id)) Ch. Lock () Delete (ch.clients, client. ID) Ch. Unlock () Ch. Rlock () ret = len (ch.clients) ch. Runlock () return Ret}func (ch *channel) Notify (message string) bool {ch. Rlock () defer ch. Runlock () for CID, _: = Range ch.clients {ch. Replymsg (FMT. Sprintf ("channel:%s client:%d message:%s", ch. Name, CID, message))}return true}func (ch *channel) replymsg (mesSage string) {Ch.waitGroup.Wrap (func () {fmt. PRINTLN (Message)})}func (ch *channel) Wait () {ch.waitGroup.Wait ()}func (ch *channel) Exiting () bool {return atomic. LoadInt32 (&ch.exitflag) = = 1}func (ch *channel) Exit () {if!atomic. CompareAndSwapInt32 (&ch.exitflag, 0, 1) {return}//close (Ch.exitchan) ch. Wait ()}func (ch *channel) putmessage (clientID int, message string) {ch. Rlock () defer ch. Runlock () if Ch. Exiting () {return}//select {//Case <-t.exitchan://return//}fmt. PRINTLN (Ch. Name, ":", Message) atomic. AddUint64 (&ch.messagecount, 1) return}
Main program:
Subscription/release exercise//author:xiong Chuan Liang //date:2015-3-17package mainimport ( . "PubSub") func main () { C1: = &client{id:100,ip: "172.18.1.1"} c3:= &client{id:300,ip: " 172.18.1.3 "} SRV: = NewServer () srv. Subscribe (C1, "Topic") srv. Subscribe (C3, "Topic") srv. PublishMessage ("Topic", "Test information 1") srv. Unsubscribe (C3, "Topic") srv. PublishMessage ("Topic", "Test information 2222") srv. Subscribe (C1, "Topic2") srv. Subscribe (C3, "Topic2") srv. PublishMessage ("Topic2", "Topic2 Test Information") }/* Run Result: channel:topic client:100 message: Test information 1channel:topic Client : Message: Test information 1 Delete client:300channel:topic client:100 from Channel:topic message: Test information 2222channel:topic2 client:100 Test information for Message:topic2 channel:topic2 client:300 message:topic2 Test Information */
Not too complicated to test, a cursory look like no problem.
MAIL: [Email protected]
blog:http://blog.csdn.net/xcl168
Simple subscription publishing mechanism implementation (Golang)