先学习一下go-micro源码
先贴出Broker接口定义:
// Broker is an interface used for asynchronous messaging.
type Broker interface {
Options() Options
Address() string
Connect() error
Disconnect() error
Init(...Option) error
Publish(string, *Message, ...PublishOption) error
Subscribe(string, Handler, ...SubscribeOption) (Subscriber, error)
String() string
}
首先是基于内存的实现
源码位于: https://github.com/micro/go-micro/tree/master/broker/memory
type memoryBroker struct {
opts broker.Options
sync.RWMutex
connected bool
Subscribers map[string][]*memorySubscriber
}
基于内存的实现很简单
- NewBroker,只是创建一个memoryBroker实例然后初始化opts和Subscribers。
- Connect和Disconnect都只是简单的用一下读写锁,然后判断memoryBroker的connected,如果可以connect/disconnect,则对应修改connected。
- Publish,根据topic从Subscribers中取出对应的subs,然后遍历subs调用每个订阅者的handler去处理memoryPublication
......
subs, ok := m.Subscribers[topic]
if !ok {
return nil
}
p := &memoryPublication{
topic: topic,
message: message,
}
for _, sub := range subs {
if err := sub.handler(p); err != nil {
return err
}
}
......
- Subscribe,根据传入的topic和handler创建memorySubscriber并添加到memoryBroker.Subscribers中,最后创建一个go routine去等待sub.exit的信号,当sub.exit可读时做移除subscriber的清理工作
......
sub := &memorySubscriber{
exit: make(chan bool, 1),
id: uuid.New().String(),
topic: topic,
handler: handler,
opts: options,
}
m.Subscribers[topic] = append(m.Subscribers[topic], sub)
// 开一个routine去等待退出信号,即创建后就阻塞在<-sub.exit这里了
// 为啥不用同步调用呢?
go func() {
<-sub.exit
m.Lock()
var newSubscribers []*memorySubscriber
for _, sb := range m.Subscribers[topic] {
if sb.id == sub.id {
continue
// 跳过这个subscriber,把其他subscriber添加到一个新的切片中去
// 这样就实现了从切片中移除subscriber
// 个人认为如果用链表存储subscriber更好
}
newSubscribers = append(newSubscribers, sb)
}
m.Subscribers[topic] = newSubscribers
m.Unlock()
}()
......
另外贴上使sub.exit可读的代码:
func (m *memorySubscriber) Unsubscribe() error {
m.exit <- true
return nil
}
也就是说(只有)取消订阅的时候就会将这个subscriber从memoryBroker的订阅者中移除,个人觉得这里用channel通信是完全没有必要的
接下来是基于http的实现
源码位于:https://github.com/micro/go-micro/blob/master/broker/http_broker.go
httpSubscriber
type httpSubscriber struct {
opts SubscribeOptions
id string
topic string
fn Handler
svc *registry.Service
hb *httpBroker
}
- Unsubscribe,和memorySubscriber不一样,httpSubscriber取消订阅时直接通过自己保存的httpBroker的指针去调用httpBroker的unsubscribe方法。不过httpBroker.unsubscribe的实现倒是和memorySubscriber取消订阅的操作差不多
httpPublication
type httpPublication struct {
m *Message
t string
}
httpBroker
代码量比较大,这里就不贴出源码分析了,直接上流程图:
httpBroker十分依赖于registry服务发现功能,go-micro的registry实现,据官方文档说时可以和zookeeper等整合,That sounds good!后续接着研究registry实现。