Add pub/sub to client/server and make broker more low level

This commit is contained in:
Asim
2015-06-12 19:52:27 +01:00
parent cdf2f2cbcd
commit b91af916f9
24 changed files with 542 additions and 193 deletions

View File

@@ -1,24 +1,19 @@
package broker
import (
"code.google.com/p/go-uuid/uuid"
"golang.org/x/net/context"
)
type Broker interface {
Address() string
Connect() error
Disconnect() error
Init() error
Publish(context.Context, string, []byte) error
Subscribe(string, func(context.Context, *Message)) (Subscriber, error)
Publish(string, *Message) error
Subscribe(string, Handler) (Subscriber, error)
}
type Handler func(*Message)
type Message struct {
Id string
Timestamp int64
Topic string
Body []byte
Header map[string]string
Body []byte
}
type Subscriber interface {
@@ -31,24 +26,14 @@ type options struct{}
type Option func(*options)
var (
Address string
Id string
DefaultBroker Broker
DefaultBroker Broker = newHttpBroker([]string{})
)
func NewBroker(addrs []string, opt ...Option) Broker {
return newHttpBroker([]string{Address}, opt...)
return newHttpBroker(addrs, opt...)
}
func Init() error {
if len(Id) == 0 {
Id = "broker-" + uuid.NewUUID().String()
}
if DefaultBroker == nil {
DefaultBroker = newHttpBroker([]string{Address})
}
return DefaultBroker.Init()
}
@@ -60,10 +45,10 @@ func Disconnect() error {
return DefaultBroker.Disconnect()
}
func Publish(ctx context.Context, topic string, body []byte) error {
return DefaultBroker.Publish(ctx, topic, body)
func Publish(topic string, msg *Message) error {
return DefaultBroker.Publish(topic, msg)
}
func Subscribe(topic string, function func(context.Context, *Message)) (Subscriber, error) {
return DefaultBroker.Subscribe(topic, function)
func Subscribe(topic string, handler Handler) (Subscriber, error) {
return DefaultBroker.Subscribe(topic, handler)
}

View File

@@ -7,21 +7,14 @@ import (
"io/ioutil"
"net"
"net/http"
"os"
"os/signal"
"strconv"
"strings"
"sync"
"syscall"
"time"
"code.google.com/p/go-uuid/uuid"
log "github.com/golang/glog"
c "github.com/myodc/go-micro/context"
"github.com/myodc/go-micro/errors"
"github.com/myodc/go-micro/registry"
"golang.org/x/net/context"
)
type httpBroker struct {
@@ -39,28 +32,22 @@ type httpSubscriber struct {
id string
topic string
ch chan *httpSubscriber
fn func(context.Context, *Message)
fn Handler
svc *registry.Service
}
// used in brokers where there is no support for headers
type envelope struct {
Header map[string]string
Message *Message
}
var (
DefaultSubPath = "/_sub"
)
func newHttpBroker(addrs []string, opt ...Option) Broker {
addr := ":0"
if len(addrs) > 0 {
if len(addrs) > 0 && len(addrs[0]) > 0 {
addr = addrs[0]
}
return &httpBroker{
id: Id,
id: "broker-" + uuid.NewUUID().String(),
address: addr,
subscribers: make(map[string][]*httpSubscriber),
unsubscribe: make(chan *httpSubscriber),
@@ -96,9 +83,6 @@ func (h *httpBroker) start() error {
go http.Serve(l, h)
go func() {
ce := make(chan os.Signal, 1)
signal.Notify(ce, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
for {
select {
case ch := <-h.exit:
@@ -107,8 +91,6 @@ func (h *httpBroker) start() error {
h.running = false
h.Unlock()
return
case <-ce:
h.stop()
case subscriber := <-h.unsubscribe:
h.Lock()
var subscribers []*httpSubscriber
@@ -150,26 +132,27 @@ func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
return
}
var e *envelope
if err = json.Unmarshal(b, &e); err != nil {
var m *Message
if err = json.Unmarshal(b, &m); err != nil {
errr := errors.InternalServerError("go.micro.broker", fmt.Sprintf("Error parsing request body: %v", err))
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
if len(e.Message.Topic) == 0 {
topic := m.Header[":topic"]
delete(m.Header, ":topic")
if len(topic) == 0 {
errr := errors.InternalServerError("go.micro.broker", "Topic not found")
w.WriteHeader(500)
w.Write([]byte(errr.Error()))
return
}
ctx := c.WithMetadata(context.Background(), e.Header)
h.RLock()
for _, subscriber := range h.subscribers[e.Message.Topic] {
subscriber.fn(ctx, e.Message)
for _, subscriber := range h.subscribers[topic] {
subscriber.fn(m)
}
h.RUnlock()
}
@@ -195,26 +178,14 @@ func (h *httpBroker) Init() error {
return nil
}
func (h *httpBroker) Publish(ctx context.Context, topic string, body []byte) error {
func (h *httpBroker) Publish(topic string, msg *Message) error {
s, err := registry.GetService("topic:" + topic)
if err != nil {
return err
}
message := &Message{
Id: uuid.NewUUID().String(),
Timestamp: time.Now().Unix(),
Topic: topic,
Body: body,
}
header, _ := c.GetMetadata(ctx)
b, err := json.Marshal(&envelope{
header,
message,
})
msg.Header[":topic"] = topic
b, err := json.Marshal(msg)
if err != nil {
return err
}
@@ -229,7 +200,7 @@ func (h *httpBroker) Publish(ctx context.Context, topic string, body []byte) err
return nil
}
func (h *httpBroker) Subscribe(topic string, function func(context.Context, *Message)) (Subscriber, error) {
func (h *httpBroker) Subscribe(topic string, handler Handler) (Subscriber, error) {
// parse address for host, port
parts := strings.Split(h.Address(), ":")
host := strings.Join(parts[:len(parts)-1], ":")
@@ -251,11 +222,10 @@ func (h *httpBroker) Subscribe(topic string, function func(context.Context, *Mes
id: uuid.NewUUID().String(),
topic: topic,
ch: h.unsubscribe,
fn: function,
fn: handler,
svc: service,
}
log.Infof("Registering subscriber %s", node.Id)
if err := registry.Register(service); err != nil {
return nil, err
}

View File

@@ -3,14 +3,9 @@ package nats
import (
"encoding/json"
"strings"
"time"
"code.google.com/p/go-uuid/uuid"
"github.com/apcera/nats"
"github.com/myodc/go-micro/broker"
c "github.com/myodc/go-micro/context"
"golang.org/x/net/context"
)
type nbroker struct {
@@ -22,12 +17,6 @@ type subscriber struct {
s *nats.Subscription
}
// used in brokers where there is no support for headers
type envelope struct {
Header map[string]string
Message *broker.Message
}
func (n *subscriber) Topic() string {
return n.s.Subject
}
@@ -67,34 +56,21 @@ func (n *nbroker) Init() error {
return nil
}
func (n *nbroker) Publish(ctx context.Context, topic string, body []byte) error {
header, _ := c.GetMetadata(ctx)
message := &broker.Message{
Id: uuid.NewUUID().String(),
Timestamp: time.Now().Unix(),
Topic: topic,
Body: body,
}
b, err := json.Marshal(&envelope{
header,
message,
})
func (n *nbroker) Publish(topic string, msg *broker.Message) error {
b, err := json.Marshal(msg)
if err != nil {
return err
}
return n.conn.Publish(topic, b)
}
func (n *nbroker) Subscribe(topic string, function func(context.Context, *broker.Message)) (broker.Subscriber, error) {
func (n *nbroker) Subscribe(topic string, handler broker.Handler) (broker.Subscriber, error) {
sub, err := n.conn.Subscribe(topic, func(msg *nats.Msg) {
var e *envelope
if err := json.Unmarshal(msg.Data, &e); err != nil {
var m *broker.Message
if err := json.Unmarshal(msg.Data, &m); err != nil {
return
}
ctx := c.WithMetadata(context.Background(), e.Header)
function(ctx, e.Message)
handler(m)
})
if err != nil {
return nil, err

View File

@@ -1,13 +1,8 @@
package rabbitmq
import (
"time"
"code.google.com/p/go-uuid/uuid"
"github.com/myodc/go-micro/broker"
c "github.com/myodc/go-micro/context"
"github.com/streadway/amqp"
"golang.org/x/net/context"
)
type rbroker struct {
@@ -28,24 +23,20 @@ func (s *subscriber) Unsubscribe() error {
return s.ch.Close()
}
func (r *rbroker) Publish(ctx context.Context, topic string, body []byte) error {
header, _ := c.GetMetadata(ctx)
msg := amqp.Publishing{
MessageId: uuid.NewUUID().String(),
Timestamp: time.Now().UTC(),
Body: body,
Headers: amqp.Table{},
func (r *rbroker) Publish(topic string, msg *broker.Message) error {
m := amqp.Publishing{
Body: msg.Body,
Headers: amqp.Table{},
}
for k, v := range header {
msg.Headers[k] = v
for k, v := range msg.Header {
m.Headers[k] = v
}
return r.conn.Publish("", topic, msg)
return r.conn.Publish("", topic, m)
}
func (r *rbroker) Subscribe(topic string, function func(context.Context, *broker.Message)) (broker.Subscriber, error) {
func (r *rbroker) Subscribe(topic string, handler broker.Handler) (broker.Subscriber, error) {
ch, sub, err := r.conn.Consume(topic)
if err != nil {
return nil, err
@@ -56,12 +47,9 @@ func (r *rbroker) Subscribe(topic string, function func(context.Context, *broker
for k, v := range msg.Headers {
header[k], _ = v.(string)
}
ctx := c.WithMetadata(context.Background(), header)
function(ctx, &broker.Message{
Id: msg.MessageId,
Timestamp: msg.Timestamp.Unix(),
Topic: topic,
Body: msg.Body,
handler(&broker.Message{
Header: header,
Body: msg.Body,
})
}