用Golang写一个简单的eventBus(Pub/Sub)

编写一个简单的EventBus

先放github地址
用go写一个Pub/Sub比Java简单多了,因为go有chan这机制。

总线(Bus)

管理所有专题(topic)和订阅该专题的用户。以map形式存储。

这里加一把表级锁。

type Bus struct {
	subNode map[string]*node
	rw      sync.RWMutex
}

节点(node)

node内管理着订阅同一专题的用户序列。
这里加了一把序列锁,在Bus的表级锁被举起时,node的锁不会使用,这样能减小锁粒度,提高并发度。

// node contains a slice of subs that subscribe same topic
type node struct {
	subs []Sub
	// Note node's rw will not be used when bus's rw is helded.
	rw sync.RWMutex
}

用户(Sub)

Sub内有一个chan成员变量,每次有消息被发送到chan内时,由于chan的机制,Sub就能及时收到被推送的消息。

type Sub struct {
	out chan interface{}
}

订阅

把sub添加到bus中map便利的topic对应的序列中。
注意这里用到了两种锁的上锁解锁时机。

func (b *Bus) Subscribe(topic string, sub Sub) {
	b.rw.Lock()
	if n, ok := b.subNode[topic]; ok {
		// found the node
		b.rw.Unlock()
		n.rw.Lock()
		defer n.rw.Unlock()
		n.subs = append(n.subs, sub)
	} else {
		defer b.rw.Unlock()
		n := NewNode()
		b.subNode[topic] = &n
	}
}

发布

逻辑就是遍历用户序列,把消息发送到sub的chan中

func (b *Bus) Publish(topic string, msg interface{}) error {
	b.rw.Lock()
	if n, ok := b.subNode[topic]; ok {
		// found the node
		b.rw.Unlock()
		n.rw.RLock()
		defer n.rw.RUnlock()
		// got the subs list and publish msg
		go func(subs []Sub, msg interface{}) {
			for _, sub := range subs {
				sub.receive(msg)
			}
		}(n.subs, msg)
		// successed return null
		return nil
	} else {
		// topic not exist
		return errors.New("topic not exist")
	}

}

使用

见仓库内的test文件

猜你喜欢

转载自www.cnblogs.com/Jun10ng/p/13173368.html