Move plugins to go-plugins
This commit is contained in:
		| @@ -1,11 +0,0 @@ | |||||||
| package http |  | ||||||
|  |  | ||||||
| // This is a hack |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/micro/go-micro/broker" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func NewBroker(addrs []string, opt ...broker.Option) broker.Broker { |  | ||||||
| 	return broker.NewBroker(addrs, opt...) |  | ||||||
| } |  | ||||||
| @@ -1,98 +0,0 @@ | |||||||
| package nats |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"strings" |  | ||||||
|  |  | ||||||
| 	"github.com/apcera/nats" |  | ||||||
| 	"github.com/micro/go-micro/broker" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type nbroker struct { |  | ||||||
| 	addrs []string |  | ||||||
| 	conn  *nats.Conn |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type subscriber struct { |  | ||||||
| 	s *nats.Subscription |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *subscriber) Topic() string { |  | ||||||
| 	return n.s.Subject |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *subscriber) Unsubscribe() error { |  | ||||||
| 	return n.s.Unsubscribe() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *nbroker) Address() string { |  | ||||||
| 	if len(n.addrs) > 0 { |  | ||||||
| 		return n.addrs[0] |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *nbroker) Connect() error { |  | ||||||
| 	if n.conn != nil { |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	opts := nats.DefaultOptions |  | ||||||
| 	opts.Servers = n.addrs |  | ||||||
| 	c, err := opts.Connect() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	n.conn = c |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *nbroker) Disconnect() error { |  | ||||||
| 	n.conn.Close() |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *nbroker) Init() error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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, handler broker.Handler) (broker.Subscriber, error) { |  | ||||||
| 	sub, err := n.conn.Subscribe(topic, func(msg *nats.Msg) { |  | ||||||
| 		var m *broker.Message |  | ||||||
| 		if err := json.Unmarshal(msg.Data, &m); err != nil { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		handler(m) |  | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return &subscriber{s: sub}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewBroker(addrs []string, opt ...broker.Option) broker.Broker { |  | ||||||
| 	var cAddrs []string |  | ||||||
| 	for _, addr := range addrs { |  | ||||||
| 		if len(addr) == 0 { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		if !strings.HasPrefix(addr, "nats://") { |  | ||||||
| 			addr = "nats://" + addr |  | ||||||
| 		} |  | ||||||
| 		cAddrs = append(cAddrs, addr) |  | ||||||
| 	} |  | ||||||
| 	if len(cAddrs) == 0 { |  | ||||||
| 		cAddrs = []string{nats.DefaultURL} |  | ||||||
| 	} |  | ||||||
| 	return &nbroker{ |  | ||||||
| 		addrs: cAddrs, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,127 +0,0 @@ | |||||||
| package rabbitmq |  | ||||||
|  |  | ||||||
| // |  | ||||||
| // All credit to Mondo |  | ||||||
| // |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
|  |  | ||||||
| 	"github.com/nu7hatch/gouuid" |  | ||||||
| 	"github.com/streadway/amqp" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type rabbitMQChannel struct { |  | ||||||
| 	uuid       string |  | ||||||
| 	connection *amqp.Connection |  | ||||||
| 	channel    *amqp.Channel |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newRabbitChannel(conn *amqp.Connection) (*rabbitMQChannel, error) { |  | ||||||
| 	id, err := uuid.NewV4() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	rabbitCh := &rabbitMQChannel{ |  | ||||||
| 		uuid:       id.String(), |  | ||||||
| 		connection: conn, |  | ||||||
| 	} |  | ||||||
| 	if err := rabbitCh.Connect(); err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return rabbitCh, nil |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) Connect() error { |  | ||||||
| 	var err error |  | ||||||
| 	r.channel, err = r.connection.Channel() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) Close() error { |  | ||||||
| 	if r.channel == nil { |  | ||||||
| 		return errors.New("Channel is nil") |  | ||||||
| 	} |  | ||||||
| 	return r.channel.Close() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) Publish(exchange, key string, message amqp.Publishing) error { |  | ||||||
| 	if r.channel == nil { |  | ||||||
| 		return errors.New("Channel is nil") |  | ||||||
| 	} |  | ||||||
| 	return r.channel.Publish(exchange, key, false, false, message) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) DeclareExchange(exchange string) error { |  | ||||||
| 	return r.channel.ExchangeDeclare( |  | ||||||
| 		exchange, // name |  | ||||||
| 		"topic",  // kind |  | ||||||
| 		false,    // durable |  | ||||||
| 		false,    // autoDelete |  | ||||||
| 		false,    // internal |  | ||||||
| 		false,    // noWait |  | ||||||
| 		nil,      // args |  | ||||||
| 	) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) DeclareQueue(queue string) error { |  | ||||||
| 	_, err := r.channel.QueueDeclare( |  | ||||||
| 		queue, // name |  | ||||||
| 		false, // durable |  | ||||||
| 		true,  // autoDelete |  | ||||||
| 		false, // exclusive |  | ||||||
| 		false, // noWait |  | ||||||
| 		nil,   // args |  | ||||||
| 	) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) DeclareDurableQueue(queue string) error { |  | ||||||
| 	_, err := r.channel.QueueDeclare( |  | ||||||
| 		queue, // name |  | ||||||
| 		true,  // durable |  | ||||||
| 		false, // autoDelete |  | ||||||
| 		false, // exclusive |  | ||||||
| 		false, // noWait |  | ||||||
| 		nil,   // args |  | ||||||
| 	) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) DeclareReplyQueue(queue string) error { |  | ||||||
| 	_, err := r.channel.QueueDeclare( |  | ||||||
| 		queue, // name |  | ||||||
| 		false, // durable |  | ||||||
| 		true,  // autoDelete |  | ||||||
| 		true,  // exclusive |  | ||||||
| 		false, // noWait |  | ||||||
| 		nil,   // args |  | ||||||
| 	) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) ConsumeQueue(queue string) (<-chan amqp.Delivery, error) { |  | ||||||
| 	return r.channel.Consume( |  | ||||||
| 		queue,  // queue |  | ||||||
| 		r.uuid, // consumer |  | ||||||
| 		true,   // autoAck |  | ||||||
| 		false,  // exclusive |  | ||||||
| 		false,  // nolocal |  | ||||||
| 		false,  // nowait |  | ||||||
| 		nil,    // args |  | ||||||
| 	) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) BindQueue(queue, exchange string) error { |  | ||||||
| 	return r.channel.QueueBind( |  | ||||||
| 		queue,    // name |  | ||||||
| 		queue,    // key |  | ||||||
| 		exchange, // exchange |  | ||||||
| 		false,    // noWait |  | ||||||
| 		nil,      // args |  | ||||||
| 	) |  | ||||||
| } |  | ||||||
| @@ -1,147 +0,0 @@ | |||||||
| package rabbitmq |  | ||||||
|  |  | ||||||
| // |  | ||||||
| // All credit to Mondo |  | ||||||
| // |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/streadway/amqp" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	DefaultExchange  = "micro" |  | ||||||
| 	DefaultRabbitURL = "amqp://guest:guest@127.0.0.1:5672" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type rabbitMQConn struct { |  | ||||||
| 	Connection      *amqp.Connection |  | ||||||
| 	Channel         *rabbitMQChannel |  | ||||||
| 	ExchangeChannel *rabbitMQChannel |  | ||||||
| 	notify          chan bool |  | ||||||
| 	exchange        string |  | ||||||
| 	url             string |  | ||||||
|  |  | ||||||
| 	connected bool |  | ||||||
|  |  | ||||||
| 	mtx    sync.Mutex |  | ||||||
| 	close  chan bool |  | ||||||
| 	closed bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newRabbitMQConn(exchange string, urls []string) *rabbitMQConn { |  | ||||||
| 	var url string |  | ||||||
|  |  | ||||||
| 	if len(urls) > 0 && strings.HasPrefix(urls[0], "amqp://") { |  | ||||||
| 		url = urls[0] |  | ||||||
| 	} else { |  | ||||||
| 		url = DefaultRabbitURL |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(exchange) == 0 { |  | ||||||
| 		exchange = DefaultExchange |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &rabbitMQConn{ |  | ||||||
| 		exchange: exchange, |  | ||||||
| 		url:      url, |  | ||||||
| 		notify:   make(chan bool, 1), |  | ||||||
| 		close:    make(chan bool), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) Init() chan bool { |  | ||||||
| 	go r.Connect(r.notify) |  | ||||||
| 	return r.notify |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) Connect(connected chan bool) { |  | ||||||
| 	for { |  | ||||||
| 		if err := r.tryToConnect(); err != nil { |  | ||||||
| 			time.Sleep(1 * time.Second) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		connected <- true |  | ||||||
| 		r.connected = true |  | ||||||
| 		notifyClose := make(chan *amqp.Error) |  | ||||||
| 		r.Connection.NotifyClose(notifyClose) |  | ||||||
|  |  | ||||||
| 		// Block until we get disconnected, or shut down |  | ||||||
| 		select { |  | ||||||
| 		case <-notifyClose: |  | ||||||
| 			// Spin around and reconnect |  | ||||||
| 			r.connected = false |  | ||||||
| 		case <-r.close: |  | ||||||
| 			// Shut down connection |  | ||||||
| 			if err := r.Connection.Close(); err != nil { |  | ||||||
| 			} |  | ||||||
| 			r.connected = false |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) IsConnected() bool { |  | ||||||
| 	return r.connected |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) Close() { |  | ||||||
| 	r.mtx.Lock() |  | ||||||
| 	defer r.mtx.Unlock() |  | ||||||
|  |  | ||||||
| 	if r.closed { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	close(r.close) |  | ||||||
| 	r.closed = true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) tryToConnect() error { |  | ||||||
| 	var err error |  | ||||||
| 	r.Connection, err = amqp.Dial(r.url) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	r.Channel, err = newRabbitChannel(r.Connection) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	r.Channel.DeclareExchange(r.exchange) |  | ||||||
| 	r.ExchangeChannel, err = newRabbitChannel(r.Connection) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) Consume(queue string) (*rabbitMQChannel, <-chan amqp.Delivery, error) { |  | ||||||
| 	consumerChannel, err := newRabbitChannel(r.Connection) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err = consumerChannel.DeclareQueue(queue) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	deliveries, err := consumerChannel.ConsumeQueue(queue) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err = consumerChannel.BindQueue(queue, r.exchange) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return consumerChannel, deliveries, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) Publish(exchange, key string, msg amqp.Publishing) error { |  | ||||||
| 	return r.ExchangeChannel.Publish(exchange, key, msg) |  | ||||||
| } |  | ||||||
| @@ -1,91 +0,0 @@ | |||||||
| package rabbitmq |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/micro/go-micro/broker" |  | ||||||
| 	"github.com/streadway/amqp" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type rbroker struct { |  | ||||||
| 	conn  *rabbitMQConn |  | ||||||
| 	addrs []string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type subscriber struct { |  | ||||||
| 	topic string |  | ||||||
| 	ch    *rabbitMQChannel |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *subscriber) Topic() string { |  | ||||||
| 	return s.topic |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (s *subscriber) Unsubscribe() error { |  | ||||||
| 	return s.ch.Close() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rbroker) Publish(topic string, msg *broker.Message) error { |  | ||||||
| 	m := amqp.Publishing{ |  | ||||||
| 		Body:    msg.Body, |  | ||||||
| 		Headers: amqp.Table{}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for k, v := range msg.Header { |  | ||||||
| 		m.Headers[k] = v |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return r.conn.Publish("", topic, m) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| 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 |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	fn := func(msg amqp.Delivery) { |  | ||||||
| 		header := make(map[string]string) |  | ||||||
| 		for k, v := range msg.Headers { |  | ||||||
| 			header[k], _ = v.(string) |  | ||||||
| 		} |  | ||||||
| 		handler(&broker.Message{ |  | ||||||
| 			Header: header, |  | ||||||
| 			Body:   msg.Body, |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	go func() { |  | ||||||
| 		for d := range sub { |  | ||||||
| 			go fn(d) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	return &subscriber{ch: ch, topic: topic}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rbroker) Address() string { |  | ||||||
| 	if len(r.addrs) > 0 { |  | ||||||
| 		return r.addrs[0] |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rbroker) Init() error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rbroker) Connect() error { |  | ||||||
| 	<-r.conn.Init() |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rbroker) Disconnect() error { |  | ||||||
| 	r.conn.Close() |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewBroker(addrs []string, opt ...broker.Option) broker.Broker { |  | ||||||
| 	return &rbroker{ |  | ||||||
| 		conn:  newRabbitMQConn("", addrs), |  | ||||||
| 		addrs: addrs, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										27
									
								
								cmd/cmd.go
									
									
									
									
									
								
							
							
						
						
									
										27
									
								
								cmd/cmd.go
									
									
									
									
									
								
							| @@ -15,21 +15,6 @@ import ( | |||||||
| 	"github.com/micro/go-micro/registry" | 	"github.com/micro/go-micro/registry" | ||||||
| 	"github.com/micro/go-micro/server" | 	"github.com/micro/go-micro/server" | ||||||
| 	"github.com/micro/go-micro/transport" | 	"github.com/micro/go-micro/transport" | ||||||
|  |  | ||||||
| 	// brokers |  | ||||||
| 	"github.com/micro/go-micro/broker/http" |  | ||||||
| 	"github.com/micro/go-micro/broker/nats" |  | ||||||
| 	"github.com/micro/go-micro/broker/rabbitmq" |  | ||||||
|  |  | ||||||
| 	// registries |  | ||||||
| 	"github.com/micro/go-micro/registry/consul" |  | ||||||
| 	"github.com/micro/go-micro/registry/etcd" |  | ||||||
| 	"github.com/micro/go-micro/registry/memory" |  | ||||||
|  |  | ||||||
| 	// transport |  | ||||||
| 	thttp "github.com/micro/go-micro/transport/http" |  | ||||||
| 	tnats "github.com/micro/go-micro/transport/nats" |  | ||||||
| 	trmq "github.com/micro/go-micro/transport/rabbitmq" |  | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var ( | var ( | ||||||
| @@ -132,21 +117,15 @@ var ( | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	Brokers = map[string]func([]string, ...broker.Option) broker.Broker{ | 	Brokers = map[string]func([]string, ...broker.Option) broker.Broker{ | ||||||
| 		"http":     http.NewBroker, | 		"http": broker.NewBroker, | ||||||
| 		"nats":     nats.NewBroker, |  | ||||||
| 		"rabbitmq": rabbitmq.NewBroker, |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	Registries = map[string]func([]string, ...registry.Option) registry.Registry{ | 	Registries = map[string]func([]string, ...registry.Option) registry.Registry{ | ||||||
| 		"consul": consul.NewRegistry, | 		"consul": registry.NewRegistry, | ||||||
| 		"etcd":   etcd.NewRegistry, |  | ||||||
| 		"memory": memory.NewRegistry, |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	Transports = map[string]func([]string, ...transport.Option) transport.Transport{ | 	Transports = map[string]func([]string, ...transport.Option) transport.Transport{ | ||||||
| 		"http":     thttp.NewTransport, | 		"http": transport.NewTransport, | ||||||
| 		"rabbitmq": trmq.NewTransport, |  | ||||||
| 		"nats":     tnats.NewTransport, |  | ||||||
| 	} | 	} | ||||||
| ) | ) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,11 +0,0 @@ | |||||||
| package consul |  | ||||||
|  |  | ||||||
| // This is a hack |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/micro/go-micro/registry" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func NewRegistry(addrs []string, opt ...registry.Option) registry.Registry { |  | ||||||
| 	return registry.NewRegistry(addrs, opt...) |  | ||||||
| } |  | ||||||
| @@ -1,193 +0,0 @@ | |||||||
| package etcd |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"errors" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	etcd "github.com/coreos/etcd/client" |  | ||||||
| 	"github.com/micro/go-micro/registry" |  | ||||||
| 	"golang.org/x/net/context" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	prefix = "/micro-registry" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type etcdRegistry struct { |  | ||||||
| 	client etcd.KeysAPI |  | ||||||
|  |  | ||||||
| 	sync.RWMutex |  | ||||||
| 	services map[string][]*registry.Service |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func encode(s *registry.Service) string { |  | ||||||
| 	b, _ := json.Marshal(s) |  | ||||||
| 	return string(b) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func decode(ds string) *registry.Service { |  | ||||||
| 	var s *registry.Service |  | ||||||
| 	json.Unmarshal([]byte(ds), &s) |  | ||||||
| 	return s |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func nodePath(s, id string) string { |  | ||||||
| 	service := strings.Replace(s, "/", "-", -1) |  | ||||||
| 	node := strings.Replace(id, "/", "-", -1) |  | ||||||
| 	return filepath.Join(prefix, service, node) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func servicePath(s string) string { |  | ||||||
| 	return filepath.Join(prefix, strings.Replace(s, "/", "-", -1)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (e *etcdRegistry) Deregister(s *registry.Service) error { |  | ||||||
| 	if len(s.Nodes) == 0 { |  | ||||||
| 		return errors.New("Require at least one node") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, node := range s.Nodes { |  | ||||||
| 		_, err := e.client.Delete(context.Background(), nodePath(s.Name, node.Id), &etcd.DeleteOptions{Recursive: false}) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	e.client.Delete(context.Background(), servicePath(s.Name), &etcd.DeleteOptions{Dir: true}) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (e *etcdRegistry) Register(s *registry.Service) error { |  | ||||||
| 	if len(s.Nodes) == 0 { |  | ||||||
| 		return errors.New("Require at least one node") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	service := ®istry.Service{ |  | ||||||
| 		Name:      s.Name, |  | ||||||
| 		Version:   s.Version, |  | ||||||
| 		Metadata:  s.Metadata, |  | ||||||
| 		Endpoints: s.Endpoints, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	e.client.Set(context.Background(), servicePath(s.Name), "", &etcd.SetOptions{Dir: true}) |  | ||||||
|  |  | ||||||
| 	for _, node := range s.Nodes { |  | ||||||
| 		service.Nodes = []*registry.Node{node} |  | ||||||
| 		_, err := e.client.Set(context.Background(), nodePath(service.Name, node.Id), encode(service), &etcd.SetOptions{}) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (e *etcdRegistry) GetService(name string) ([]*registry.Service, error) { |  | ||||||
| 	e.RLock() |  | ||||||
| 	service, ok := e.services[name] |  | ||||||
| 	e.RUnlock() |  | ||||||
|  |  | ||||||
| 	if ok { |  | ||||||
| 		return service, nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	rsp, err := e.client.Get(context.Background(), servicePath(name), &etcd.GetOptions{}) |  | ||||||
| 	if err != nil && !strings.HasPrefix(err.Error(), "100: Key not found") { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	serviceMap := map[string]*registry.Service{} |  | ||||||
|  |  | ||||||
| 	for _, n := range rsp.Node.Nodes { |  | ||||||
| 		if n.Dir { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		sn := decode(n.Value) |  | ||||||
|  |  | ||||||
| 		s, ok := serviceMap[sn.Version] |  | ||||||
| 		if !ok { |  | ||||||
| 			s = ®istry.Service{ |  | ||||||
| 				Name:      sn.Name, |  | ||||||
| 				Version:   sn.Version, |  | ||||||
| 				Metadata:  sn.Metadata, |  | ||||||
| 				Endpoints: sn.Endpoints, |  | ||||||
| 			} |  | ||||||
| 			serviceMap[s.Version] = s |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for _, node := range sn.Nodes { |  | ||||||
| 			s.Nodes = append(s.Nodes, node) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var services []*registry.Service |  | ||||||
| 	for _, service := range serviceMap { |  | ||||||
| 		services = append(services, service) |  | ||||||
| 	} |  | ||||||
| 	return services, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (e *etcdRegistry) ListServices() ([]*registry.Service, error) { |  | ||||||
| 	e.RLock() |  | ||||||
| 	serviceMap := e.services |  | ||||||
| 	e.RUnlock() |  | ||||||
|  |  | ||||||
| 	var services []*registry.Service |  | ||||||
|  |  | ||||||
| 	if len(serviceMap) > 0 { |  | ||||||
| 		for _, service := range serviceMap { |  | ||||||
| 			services = append(services, service...) |  | ||||||
| 		} |  | ||||||
| 		return services, nil |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	rsp, err := e.client.Get(context.Background(), prefix, &etcd.GetOptions{Recursive: true, Sort: true}) |  | ||||||
| 	if err != nil && !strings.HasPrefix(err.Error(), "100: Key not found") { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, node := range rsp.Node.Nodes { |  | ||||||
| 		service := ®istry.Service{} |  | ||||||
| 		for _, n := range node.Nodes { |  | ||||||
| 			i := decode(n.Value) |  | ||||||
| 			service.Name = i.Name |  | ||||||
| 		} |  | ||||||
| 		services = append(services, service) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return services, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (e *etcdRegistry) Watch() (registry.Watcher, error) { |  | ||||||
| 	// todo: fix watcher |  | ||||||
| 	return newEtcdWatcher(e) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewRegistry(addrs []string, opt ...registry.Option) registry.Registry { |  | ||||||
| 	var cAddrs []string |  | ||||||
|  |  | ||||||
| 	for _, addr := range addrs { |  | ||||||
| 		if len(addr) == 0 { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		cAddrs = append(cAddrs, addr) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(cAddrs) == 0 { |  | ||||||
| 		cAddrs = []string{"http://127.0.0.1:2379"} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	c, _ := etcd.New(etcd.Config{ |  | ||||||
| 		Endpoints: cAddrs, |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	e := &etcdRegistry{ |  | ||||||
| 		client:   etcd.NewKeysAPI(c), |  | ||||||
| 		services: make(map[string][]*registry.Service), |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return e |  | ||||||
| } |  | ||||||
| @@ -1,151 +0,0 @@ | |||||||
| package etcd |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	etcd "github.com/coreos/etcd/client" |  | ||||||
| 	"github.com/micro/go-micro/registry" |  | ||||||
| 	"golang.org/x/net/context" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type etcdWatcher struct { |  | ||||||
| 	registry *etcdRegistry |  | ||||||
| 	stop     chan bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func addNodes(old, neu []*registry.Node) []*registry.Node { |  | ||||||
| 	for _, n := range neu { |  | ||||||
| 		var seen bool |  | ||||||
| 		for i, o := range old { |  | ||||||
| 			if o.Id == n.Id { |  | ||||||
| 				seen = true |  | ||||||
| 				old[i] = n |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !seen { |  | ||||||
| 			old = append(old, n) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return old |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func addServices(old, neu []*registry.Service) []*registry.Service { |  | ||||||
| 	for _, s := range neu { |  | ||||||
| 		var seen bool |  | ||||||
| 		for i, o := range old { |  | ||||||
| 			if o.Version == s.Version { |  | ||||||
| 				s.Nodes = addNodes(o.Nodes, s.Nodes) |  | ||||||
| 				seen = true |  | ||||||
| 				old[i] = s |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !seen { |  | ||||||
| 			old = append(old, s) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return old |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func delNodes(old, del []*registry.Node) []*registry.Node { |  | ||||||
| 	var nodes []*registry.Node |  | ||||||
| 	for _, o := range old { |  | ||||||
| 		var rem bool |  | ||||||
| 		for _, n := range del { |  | ||||||
| 			if o.Id == n.Id { |  | ||||||
| 				rem = true |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !rem { |  | ||||||
| 			nodes = append(nodes, o) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nodes |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func delServices(old, del []*registry.Service) []*registry.Service { |  | ||||||
| 	var services []*registry.Service |  | ||||||
| 	for i, o := range old { |  | ||||||
| 		var rem bool |  | ||||||
| 		for _, s := range del { |  | ||||||
| 			if o.Version == s.Version { |  | ||||||
| 				old[i].Nodes = delNodes(o.Nodes, s.Nodes) |  | ||||||
| 				if len(old[i].Nodes) == 0 { |  | ||||||
| 					rem = true |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !rem { |  | ||||||
| 			services = append(services, o) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return services |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newEtcdWatcher(r *etcdRegistry) (registry.Watcher, error) { |  | ||||||
| 	ew := &etcdWatcher{ |  | ||||||
| 		registry: r, |  | ||||||
| 		stop:     make(chan bool), |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	w := r.client.Watcher(prefix, &etcd.WatcherOptions{AfterIndex: 0, Recursive: true}) |  | ||||||
|  |  | ||||||
| 	c := context.Background() |  | ||||||
| 	ctx, cancel := context.WithCancel(c) |  | ||||||
|  |  | ||||||
| 	go func() { |  | ||||||
| 		<-ew.stop |  | ||||||
| 		cancel() |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	go ew.watch(ctx, w) |  | ||||||
|  |  | ||||||
| 	return ew, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (e *etcdWatcher) watch(ctx context.Context, w etcd.Watcher) { |  | ||||||
| 	for { |  | ||||||
| 		rsp, err := w.Next(ctx) |  | ||||||
| 		if err != nil && ctx.Err() != nil { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if rsp.Node.Dir { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		s := decode(rsp.Node.Value) |  | ||||||
| 		if s == nil { |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		e.registry.Lock() |  | ||||||
|  |  | ||||||
| 		service, ok := e.registry.services[s.Name] |  | ||||||
| 		if !ok { |  | ||||||
| 			if rsp.Action == "create" { |  | ||||||
| 				e.registry.services[s.Name] = []*registry.Service{s} |  | ||||||
| 			} |  | ||||||
| 			e.registry.Unlock() |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		switch rsp.Action { |  | ||||||
| 		case "delete": |  | ||||||
| 			services := delServices(service, []*registry.Service{s}) |  | ||||||
| 			if len(services) > 0 { |  | ||||||
| 				e.registry.services[s.Name] = services |  | ||||||
| 			} else { |  | ||||||
| 				delete(e.registry.services, s.Name) |  | ||||||
| 			} |  | ||||||
| 		case "create": |  | ||||||
| 			e.registry.services[s.Name] = addServices(service, []*registry.Service{s}) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		e.registry.Unlock() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (ew *etcdWatcher) Stop() { |  | ||||||
| 	ew.stop <- true |  | ||||||
| } |  | ||||||
| @@ -1,372 +0,0 @@ | |||||||
| package memory |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"fmt" |  | ||||||
| 	"os" |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	log "github.com/golang/glog" |  | ||||||
| 	"github.com/hashicorp/memberlist" |  | ||||||
| 	"github.com/micro/go-micro/registry" |  | ||||||
| 	"github.com/pborman/uuid" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type action int |  | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	addAction action = iota |  | ||||||
| 	delAction |  | ||||||
| 	syncAction |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type broadcast struct { |  | ||||||
| 	msg    []byte |  | ||||||
| 	notify chan<- struct{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type delegate struct { |  | ||||||
| 	broadcasts *memberlist.TransmitLimitedQueue |  | ||||||
| 	updates    chan *update |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type memoryRegistry struct { |  | ||||||
| 	broadcasts *memberlist.TransmitLimitedQueue |  | ||||||
| 	updates    chan *update |  | ||||||
|  |  | ||||||
| 	sync.RWMutex |  | ||||||
| 	services map[string][]*registry.Service |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type update struct { |  | ||||||
| 	Action  action |  | ||||||
| 	Service *registry.Service |  | ||||||
| 	sync    chan *registry.Service |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type watcher struct{} |  | ||||||
|  |  | ||||||
| func (b *broadcast) Invalidates(other memberlist.Broadcast) bool { |  | ||||||
| 	return false |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *broadcast) Message() []byte { |  | ||||||
| 	return b.msg |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *broadcast) Finished() { |  | ||||||
| 	if b.notify != nil { |  | ||||||
| 		close(b.notify) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (d *delegate) NodeMeta(limit int) []byte { |  | ||||||
| 	return []byte{} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (d *delegate) NotifyMsg(b []byte) { |  | ||||||
| 	if len(b) == 0 { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	buf := make([]byte, len(b)) |  | ||||||
| 	copy(buf, b) |  | ||||||
|  |  | ||||||
| 	go func() { |  | ||||||
| 		switch buf[0] { |  | ||||||
| 		case 'd': // data |  | ||||||
| 			var updates []*update |  | ||||||
| 			if err := json.Unmarshal(buf[1:], &updates); err != nil { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 			for _, u := range updates { |  | ||||||
| 				d.updates <- u |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (d *delegate) GetBroadcasts(overhead, limit int) [][]byte { |  | ||||||
| 	return d.broadcasts.GetBroadcasts(overhead, limit) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (d *delegate) LocalState(join bool) []byte { |  | ||||||
| 	if !join { |  | ||||||
| 		return []byte{} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	syncCh := make(chan *registry.Service, 1) |  | ||||||
| 	m := map[string][]*registry.Service{} |  | ||||||
|  |  | ||||||
| 	d.updates <- &update{ |  | ||||||
| 		Action: syncAction, |  | ||||||
| 		sync:   syncCh, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for s := range syncCh { |  | ||||||
| 		m[s.Name] = append(m[s.Name], s) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	b, _ := json.Marshal(m) |  | ||||||
| 	return b |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (d *delegate) MergeRemoteState(buf []byte, join bool) { |  | ||||||
| 	if len(buf) == 0 { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if !join { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var m map[string][]*registry.Service |  | ||||||
| 	if err := json.Unmarshal(buf, &m); err != nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for _, services := range m { |  | ||||||
| 		for _, service := range services { |  | ||||||
| 			d.updates <- &update{ |  | ||||||
| 				Action:  addAction, |  | ||||||
| 				Service: service, |  | ||||||
| 				sync:    nil, |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *memoryRegistry) run() { |  | ||||||
| 	for u := range m.updates { |  | ||||||
| 		switch u.Action { |  | ||||||
| 		case addAction: |  | ||||||
| 			m.Lock() |  | ||||||
| 			if service, ok := m.services[u.Service.Name]; !ok { |  | ||||||
| 				m.services[u.Service.Name] = []*registry.Service{u.Service} |  | ||||||
| 			} else { |  | ||||||
| 				m.services[u.Service.Name] = addServices(service, []*registry.Service{u.Service}) |  | ||||||
| 			} |  | ||||||
| 			m.Unlock() |  | ||||||
| 		case delAction: |  | ||||||
| 			m.Lock() |  | ||||||
| 			if service, ok := m.services[u.Service.Name]; ok { |  | ||||||
| 				if services := delServices(service, []*registry.Service{u.Service}); len(services) == 0 { |  | ||||||
| 					delete(m.services, u.Service.Name) |  | ||||||
| 				} else { |  | ||||||
| 					m.services[u.Service.Name] = services |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			m.Unlock() |  | ||||||
| 		case syncAction: |  | ||||||
| 			if u.sync == nil { |  | ||||||
| 				continue |  | ||||||
| 			} |  | ||||||
| 			m.RLock() |  | ||||||
| 			for _, services := range m.services { |  | ||||||
| 				for _, service := range services { |  | ||||||
| 					u.sync <- service |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			m.RUnlock() |  | ||||||
| 			close(u.sync) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *memoryRegistry) Register(s *registry.Service) error { |  | ||||||
| 	m.Lock() |  | ||||||
| 	if service, ok := m.services[s.Name]; !ok { |  | ||||||
| 		m.services[s.Name] = []*registry.Service{s} |  | ||||||
| 	} else { |  | ||||||
| 		m.services[s.Name] = addServices(service, []*registry.Service{s}) |  | ||||||
| 	} |  | ||||||
| 	m.Unlock() |  | ||||||
|  |  | ||||||
| 	b, _ := json.Marshal([]*update{ |  | ||||||
| 		&update{ |  | ||||||
| 			Action:  addAction, |  | ||||||
| 			Service: s, |  | ||||||
| 		}, |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	m.broadcasts.QueueBroadcast(&broadcast{ |  | ||||||
| 		msg:    append([]byte("d"), b...), |  | ||||||
| 		notify: nil, |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *memoryRegistry) Deregister(s *registry.Service) error { |  | ||||||
| 	m.Lock() |  | ||||||
| 	if service, ok := m.services[s.Name]; ok { |  | ||||||
| 		if services := delServices(service, []*registry.Service{s}); len(services) == 0 { |  | ||||||
| 			delete(m.services, s.Name) |  | ||||||
| 		} else { |  | ||||||
| 			m.services[s.Name] = services |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	m.Unlock() |  | ||||||
|  |  | ||||||
| 	b, _ := json.Marshal([]*update{ |  | ||||||
| 		&update{ |  | ||||||
| 			Action:  delAction, |  | ||||||
| 			Service: s, |  | ||||||
| 		}, |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	m.broadcasts.QueueBroadcast(&broadcast{ |  | ||||||
| 		msg:    append([]byte("d"), b...), |  | ||||||
| 		notify: nil, |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *memoryRegistry) GetService(name string) ([]*registry.Service, error) { |  | ||||||
| 	m.RLock() |  | ||||||
| 	service, ok := m.services[name] |  | ||||||
| 	m.RUnlock() |  | ||||||
| 	if !ok { |  | ||||||
| 		return nil, fmt.Errorf("Service %s not found", name) |  | ||||||
| 	} |  | ||||||
| 	return service, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *memoryRegistry) ListServices() ([]*registry.Service, error) { |  | ||||||
| 	var services []*registry.Service |  | ||||||
| 	m.RLock() |  | ||||||
| 	for _, service := range m.services { |  | ||||||
| 		services = append(services, service...) |  | ||||||
| 	} |  | ||||||
| 	m.RUnlock() |  | ||||||
| 	return services, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (m *memoryRegistry) Watch() (registry.Watcher, error) { |  | ||||||
| 	return &watcher{}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (w *watcher) Stop() { |  | ||||||
| 	return |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func addNodes(old, neu []*registry.Node) []*registry.Node { |  | ||||||
| 	for _, n := range neu { |  | ||||||
| 		var seen bool |  | ||||||
| 		for i, o := range old { |  | ||||||
| 			if o.Id == n.Id { |  | ||||||
| 				seen = true |  | ||||||
| 				old[i] = n |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !seen { |  | ||||||
| 			old = append(old, n) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return old |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func addServices(old, neu []*registry.Service) []*registry.Service { |  | ||||||
| 	for _, s := range neu { |  | ||||||
| 		var seen bool |  | ||||||
| 		for i, o := range old { |  | ||||||
| 			if o.Version == s.Version { |  | ||||||
| 				s.Nodes = addNodes(o.Nodes, s.Nodes) |  | ||||||
| 				seen = true |  | ||||||
| 				old[i] = s |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !seen { |  | ||||||
| 			old = append(old, s) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return old |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func delNodes(old, del []*registry.Node) []*registry.Node { |  | ||||||
| 	var nodes []*registry.Node |  | ||||||
| 	for _, o := range old { |  | ||||||
| 		var rem bool |  | ||||||
| 		for _, n := range del { |  | ||||||
| 			if o.Id == n.Id { |  | ||||||
| 				rem = true |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !rem { |  | ||||||
| 			nodes = append(nodes, o) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nodes |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func delServices(old, del []*registry.Service) []*registry.Service { |  | ||||||
| 	var services []*registry.Service |  | ||||||
| 	for i, o := range old { |  | ||||||
| 		var rem bool |  | ||||||
| 		for _, s := range del { |  | ||||||
| 			if o.Version == s.Version { |  | ||||||
| 				old[i].Nodes = delNodes(o.Nodes, s.Nodes) |  | ||||||
| 				if len(old[i].Nodes) == 0 { |  | ||||||
| 					rem = true |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 		if !rem { |  | ||||||
| 			services = append(services, o) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return services |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewRegistry(addrs []string, opt ...registry.Option) registry.Registry { |  | ||||||
| 	cAddrs := []string{} |  | ||||||
| 	hostname, _ := os.Hostname() |  | ||||||
| 	updates := make(chan *update, 100) |  | ||||||
|  |  | ||||||
| 	for _, addr := range addrs { |  | ||||||
| 		if len(addr) > 0 { |  | ||||||
| 			cAddrs = append(cAddrs, addr) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	broadcasts := &memberlist.TransmitLimitedQueue{ |  | ||||||
| 		NumNodes: func() int { |  | ||||||
| 			return len(cAddrs) |  | ||||||
| 		}, |  | ||||||
| 		RetransmitMult: 3, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	mr := &memoryRegistry{ |  | ||||||
| 		broadcasts: broadcasts, |  | ||||||
| 		services:   make(map[string][]*registry.Service), |  | ||||||
| 		updates:    updates, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	go mr.run() |  | ||||||
|  |  | ||||||
| 	c := memberlist.DefaultLocalConfig() |  | ||||||
| 	c.BindPort = 0 |  | ||||||
| 	c.Name = hostname + "-" + uuid.NewUUID().String() |  | ||||||
| 	c.Delegate = &delegate{ |  | ||||||
| 		updates:    updates, |  | ||||||
| 		broadcasts: broadcasts, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	m, err := memberlist.Create(c) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Fatalf("Error creating memberlist: %v", err) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(cAddrs) > 0 { |  | ||||||
| 		_, err := m.Join(cAddrs) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Fatalf("Error joining members: %v", err) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	log.Infof("Local memberlist node %s:%d\n", m.LocalNode().Addr, m.LocalNode().Port) |  | ||||||
| 	return mr |  | ||||||
| } |  | ||||||
| @@ -1,78 +0,0 @@ | |||||||
| package memory |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
|  |  | ||||||
| 	"github.com/micro/go-micro/registry" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestDelServices(t *testing.T) { |  | ||||||
| 	services := []*registry.Service{ |  | ||||||
| 		{ |  | ||||||
| 			Name:    "foo", |  | ||||||
| 			Version: "1.0.0", |  | ||||||
| 			Nodes: []*registry.Node{ |  | ||||||
| 				{ |  | ||||||
| 					Id:      "foo-123", |  | ||||||
| 					Address: "localhost", |  | ||||||
| 					Port:    9999, |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			Name:    "foo", |  | ||||||
| 			Version: "1.0.0", |  | ||||||
| 			Nodes: []*registry.Node{ |  | ||||||
| 				{ |  | ||||||
| 					Id:      "foo-123", |  | ||||||
| 					Address: "localhost", |  | ||||||
| 					Port:    6666, |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	servs := delServices([]*registry.Service{services[0]}, []*registry.Service{services[1]}) |  | ||||||
| 	if i := len(servs); i > 0 { |  | ||||||
| 		t.Errorf("Expected 0 nodes, got %d: %+v", i, servs) |  | ||||||
| 	} |  | ||||||
| 	t.Logf("Services %+v", servs) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func TestDelNodes(t *testing.T) { |  | ||||||
| 	services := []*registry.Service{ |  | ||||||
| 		{ |  | ||||||
| 			Name:    "foo", |  | ||||||
| 			Version: "1.0.0", |  | ||||||
| 			Nodes: []*registry.Node{ |  | ||||||
| 				{ |  | ||||||
| 					Id:      "foo-123", |  | ||||||
| 					Address: "localhost", |  | ||||||
| 					Port:    9999, |  | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					Id:      "foo-321", |  | ||||||
| 					Address: "localhost", |  | ||||||
| 					Port:    6666, |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 		{ |  | ||||||
| 			Name:    "foo", |  | ||||||
| 			Version: "1.0.0", |  | ||||||
| 			Nodes: []*registry.Node{ |  | ||||||
| 				{ |  | ||||||
| 					Id:      "foo-123", |  | ||||||
| 					Address: "localhost", |  | ||||||
| 					Port:    6666, |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	nodes := delNodes(services[0].Nodes, services[1].Nodes) |  | ||||||
| 	if i := len(nodes); i != 1 { |  | ||||||
| 		t.Errorf("Expected only 1 node, got %d: %+v", i, nodes) |  | ||||||
| 	} |  | ||||||
| 	t.Logf("Nodes %+v", nodes) |  | ||||||
| } |  | ||||||
| @@ -1,13 +0,0 @@ | |||||||
| package transport |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"io" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type buffer struct { |  | ||||||
| 	io.ReadWriter |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (b *buffer) Close() error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
| @@ -1,11 +0,0 @@ | |||||||
| package http |  | ||||||
|  |  | ||||||
| // This is a hack |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/micro/go-micro/transport" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func NewTransport(addrs []string, opt ...transport.Option) transport.Transport { |  | ||||||
| 	return transport.NewTransport(addrs, opt...) |  | ||||||
| } |  | ||||||
| @@ -12,6 +12,10 @@ import ( | |||||||
| 	"sync" | 	"sync" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | type buffer struct { | ||||||
|  | 	io.ReadWriter | ||||||
|  | } | ||||||
|  |  | ||||||
| type httpTransport struct{} | type httpTransport struct{} | ||||||
|  |  | ||||||
| type httpTransportClient struct { | type httpTransportClient struct { | ||||||
| @@ -33,6 +37,10 @@ type httpTransportListener struct { | |||||||
| 	listener net.Listener | 	listener net.Listener | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (b *buffer) Close() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| func (h *httpTransportClient) Send(m *Message) error { | func (h *httpTransportClient) Send(m *Message) error { | ||||||
| 	header := make(http.Header) | 	header := make(http.Header) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,162 +0,0 @@ | |||||||
| package nats |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"encoding/json" |  | ||||||
| 	"errors" |  | ||||||
| 	"strings" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/apcera/nats" |  | ||||||
| 	"github.com/micro/go-micro/transport" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type ntport struct { |  | ||||||
| 	addrs []string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type ntportClient struct { |  | ||||||
| 	conn *nats.Conn |  | ||||||
| 	addr string |  | ||||||
| 	id   string |  | ||||||
| 	sub  *nats.Subscription |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type ntportSocket struct { |  | ||||||
| 	conn *nats.Conn |  | ||||||
| 	m    *nats.Msg |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type ntportListener struct { |  | ||||||
| 	conn *nats.Conn |  | ||||||
| 	addr string |  | ||||||
| 	exit chan bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *ntportClient) Send(m *transport.Message) error { |  | ||||||
| 	b, err := json.Marshal(m) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return n.conn.PublishRequest(n.addr, n.id, b) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *ntportClient) Recv(m *transport.Message) error { |  | ||||||
| 	rsp, err := n.sub.NextMsg(time.Second * 10) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var mr *transport.Message |  | ||||||
| 	if err := json.Unmarshal(rsp.Data, &mr); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	*m = *mr |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *ntportClient) Close() error { |  | ||||||
| 	n.sub.Unsubscribe() |  | ||||||
| 	n.conn.Close() |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *ntportSocket) Recv(m *transport.Message) error { |  | ||||||
| 	if m == nil { |  | ||||||
| 		return errors.New("message passed in is nil") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err := json.Unmarshal(n.m.Data, &m); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *ntportSocket) Send(m *transport.Message) error { |  | ||||||
| 	b, err := json.Marshal(m) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return n.conn.Publish(n.m.Reply, b) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *ntportSocket) Close() error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *ntportListener) Addr() string { |  | ||||||
| 	return n.addr |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *ntportListener) Close() error { |  | ||||||
| 	n.exit <- true |  | ||||||
| 	n.conn.Close() |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *ntportListener) Accept(fn func(transport.Socket)) error { |  | ||||||
| 	s, err := n.conn.Subscribe(n.addr, func(m *nats.Msg) { |  | ||||||
| 		fn(&ntportSocket{ |  | ||||||
| 			conn: n.conn, |  | ||||||
| 			m:    m, |  | ||||||
| 		}) |  | ||||||
| 	}) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	<-n.exit |  | ||||||
| 	return s.Unsubscribe() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *ntport) Dial(addr string, opts ...transport.DialOption) (transport.Client, error) { |  | ||||||
| 	cAddr := nats.DefaultURL |  | ||||||
|  |  | ||||||
| 	if len(n.addrs) > 0 && strings.HasPrefix(n.addrs[0], "nats://") { |  | ||||||
| 		cAddr = n.addrs[0] |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	c, err := nats.Connect(cAddr) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	id := nats.NewInbox() |  | ||||||
| 	sub, err := c.SubscribeSync(id) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &ntportClient{ |  | ||||||
| 		conn: c, |  | ||||||
| 		addr: addr, |  | ||||||
| 		id:   id, |  | ||||||
| 		sub:  sub, |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (n *ntport) Listen(addr string) (transport.Listener, error) { |  | ||||||
| 	cAddr := nats.DefaultURL |  | ||||||
|  |  | ||||||
| 	if len(n.addrs) > 0 && strings.HasPrefix(n.addrs[0], "nats://") { |  | ||||||
| 		cAddr = n.addrs[0] |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	c, err := nats.Connect(cAddr) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &ntportListener{ |  | ||||||
| 		addr: nats.NewInbox(), |  | ||||||
| 		conn: c, |  | ||||||
| 		exit: make(chan bool, 1), |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewTransport(addrs []string, opt ...transport.Option) transport.Transport { |  | ||||||
| 	return &ntport{ |  | ||||||
| 		addrs: addrs, |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,127 +0,0 @@ | |||||||
| package rabbitmq |  | ||||||
|  |  | ||||||
| // |  | ||||||
| // All credit to Mondo |  | ||||||
| // |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"errors" |  | ||||||
|  |  | ||||||
| 	"github.com/nu7hatch/gouuid" |  | ||||||
| 	"github.com/streadway/amqp" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type rabbitMQChannel struct { |  | ||||||
| 	uuid       string |  | ||||||
| 	connection *amqp.Connection |  | ||||||
| 	channel    *amqp.Channel |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newRabbitChannel(conn *amqp.Connection) (*rabbitMQChannel, error) { |  | ||||||
| 	id, err := uuid.NewV4() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	rabbitCh := &rabbitMQChannel{ |  | ||||||
| 		uuid:       id.String(), |  | ||||||
| 		connection: conn, |  | ||||||
| 	} |  | ||||||
| 	if err := rabbitCh.Connect(); err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
| 	return rabbitCh, nil |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) Connect() error { |  | ||||||
| 	var err error |  | ||||||
| 	r.channel, err = r.connection.Channel() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) Close() error { |  | ||||||
| 	if r.channel == nil { |  | ||||||
| 		return errors.New("Channel is nil") |  | ||||||
| 	} |  | ||||||
| 	return r.channel.Close() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) Publish(exchange, key string, message amqp.Publishing) error { |  | ||||||
| 	if r.channel == nil { |  | ||||||
| 		return errors.New("Channel is nil") |  | ||||||
| 	} |  | ||||||
| 	return r.channel.Publish(exchange, key, false, false, message) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) DeclareExchange(exchange string) error { |  | ||||||
| 	return r.channel.ExchangeDeclare( |  | ||||||
| 		exchange, // name |  | ||||||
| 		"topic",  // kind |  | ||||||
| 		false,    // durable |  | ||||||
| 		false,    // autoDelete |  | ||||||
| 		false,    // internal |  | ||||||
| 		false,    // noWait |  | ||||||
| 		nil,      // args |  | ||||||
| 	) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) DeclareQueue(queue string) error { |  | ||||||
| 	_, err := r.channel.QueueDeclare( |  | ||||||
| 		queue, // name |  | ||||||
| 		false, // durable |  | ||||||
| 		true,  // autoDelete |  | ||||||
| 		false, // exclusive |  | ||||||
| 		false, // noWait |  | ||||||
| 		nil,   // args |  | ||||||
| 	) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) DeclareDurableQueue(queue string) error { |  | ||||||
| 	_, err := r.channel.QueueDeclare( |  | ||||||
| 		queue, // name |  | ||||||
| 		true,  // durable |  | ||||||
| 		false, // autoDelete |  | ||||||
| 		false, // exclusive |  | ||||||
| 		false, // noWait |  | ||||||
| 		nil,   // args |  | ||||||
| 	) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) DeclareReplyQueue(queue string) error { |  | ||||||
| 	_, err := r.channel.QueueDeclare( |  | ||||||
| 		queue, // name |  | ||||||
| 		false, // durable |  | ||||||
| 		true,  // autoDelete |  | ||||||
| 		true,  // exclusive |  | ||||||
| 		false, // noWait |  | ||||||
| 		nil,   // args |  | ||||||
| 	) |  | ||||||
| 	return err |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) ConsumeQueue(queue string) (<-chan amqp.Delivery, error) { |  | ||||||
| 	return r.channel.Consume( |  | ||||||
| 		queue,  // queue |  | ||||||
| 		r.uuid, // consumer |  | ||||||
| 		true,   // autoAck |  | ||||||
| 		false,  // exclusive |  | ||||||
| 		false,  // nolocal |  | ||||||
| 		false,  // nowait |  | ||||||
| 		nil,    // args |  | ||||||
| 	) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQChannel) BindQueue(queue, exchange string) error { |  | ||||||
| 	return r.channel.QueueBind( |  | ||||||
| 		queue,    // name |  | ||||||
| 		queue,    // key |  | ||||||
| 		exchange, // exchange |  | ||||||
| 		false,    // noWait |  | ||||||
| 		nil,      // args |  | ||||||
| 	) |  | ||||||
| } |  | ||||||
| @@ -1,142 +0,0 @@ | |||||||
| package rabbitmq |  | ||||||
|  |  | ||||||
| // |  | ||||||
| // All credit to Mondo |  | ||||||
| // |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"github.com/streadway/amqp" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	DefaultExchange  = "micro" |  | ||||||
| 	DefaultRabbitURL = "amqp://guest:guest@127.0.0.1:5672" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type rabbitMQConn struct { |  | ||||||
| 	Connection *amqp.Connection |  | ||||||
| 	Channel    *rabbitMQChannel |  | ||||||
| 	notify     chan bool |  | ||||||
| 	exchange   string |  | ||||||
| 	url        string |  | ||||||
|  |  | ||||||
| 	connected bool |  | ||||||
|  |  | ||||||
| 	mtx    sync.Mutex |  | ||||||
| 	close  chan bool |  | ||||||
| 	closed bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func newRabbitMQConn(exchange string, urls []string) *rabbitMQConn { |  | ||||||
| 	var url string |  | ||||||
|  |  | ||||||
| 	if len(urls) > 0 && strings.HasPrefix(urls[0], "amqp://") { |  | ||||||
| 		url = urls[0] |  | ||||||
| 	} else { |  | ||||||
| 		url = DefaultRabbitURL |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(exchange) == 0 { |  | ||||||
| 		exchange = DefaultExchange |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return &rabbitMQConn{ |  | ||||||
| 		exchange: exchange, |  | ||||||
| 		url:      url, |  | ||||||
| 		notify:   make(chan bool, 1), |  | ||||||
| 		close:    make(chan bool), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) Init() chan bool { |  | ||||||
| 	go r.Connect(r.notify) |  | ||||||
| 	return r.notify |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) Connect(connected chan bool) { |  | ||||||
| 	for { |  | ||||||
| 		if err := r.tryToConnect(); err != nil { |  | ||||||
| 			time.Sleep(1 * time.Second) |  | ||||||
| 			continue |  | ||||||
| 		} |  | ||||||
| 		connected <- true |  | ||||||
| 		r.connected = true |  | ||||||
| 		notifyClose := make(chan *amqp.Error) |  | ||||||
| 		r.Connection.NotifyClose(notifyClose) |  | ||||||
|  |  | ||||||
| 		// Block until we get disconnected, or shut down |  | ||||||
| 		select { |  | ||||||
| 		case <-notifyClose: |  | ||||||
| 			// Spin around and reconnect |  | ||||||
| 			r.connected = false |  | ||||||
| 		case <-r.close: |  | ||||||
| 			// Shut down connection |  | ||||||
| 			if err := r.Connection.Close(); err != nil { |  | ||||||
| 			} |  | ||||||
| 			r.connected = false |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) IsConnected() bool { |  | ||||||
| 	return r.connected |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) Close() { |  | ||||||
| 	r.mtx.Lock() |  | ||||||
| 	defer r.mtx.Unlock() |  | ||||||
|  |  | ||||||
| 	if r.closed { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	close(r.close) |  | ||||||
| 	r.closed = true |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) tryToConnect() error { |  | ||||||
| 	var err error |  | ||||||
| 	r.Connection, err = amqp.Dial(r.url) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	r.Channel, err = newRabbitChannel(r.Connection) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	r.Channel.DeclareExchange(r.exchange) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) Consume(queue string) (<-chan amqp.Delivery, error) { |  | ||||||
| 	consumerChannel, err := newRabbitChannel(r.Connection) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err = consumerChannel.DeclareQueue(queue) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	deliveries, err := consumerChannel.ConsumeQueue(queue) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	err = consumerChannel.BindQueue(queue, r.exchange) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return deliveries, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rabbitMQConn) Publish(exchange, key string, msg amqp.Publishing) error { |  | ||||||
| 	return r.Channel.Publish(exchange, key, msg) |  | ||||||
| } |  | ||||||
| @@ -1,249 +0,0 @@ | |||||||
| package rabbitmq |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
|  |  | ||||||
| 	"errors" |  | ||||||
| 	uuid "github.com/nu7hatch/gouuid" |  | ||||||
| 	"github.com/streadway/amqp" |  | ||||||
|  |  | ||||||
| 	"github.com/micro/go-micro/transport" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| const ( |  | ||||||
| 	directReplyQueue = "amq.rabbitmq.reply-to" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| type rmqtport struct { |  | ||||||
| 	conn  *rabbitMQConn |  | ||||||
| 	addrs []string |  | ||||||
|  |  | ||||||
| 	once    sync.Once |  | ||||||
| 	replyTo string |  | ||||||
|  |  | ||||||
| 	sync.Mutex |  | ||||||
| 	inflight map[string]chan amqp.Delivery |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type rmqtportClient struct { |  | ||||||
| 	rt    *rmqtport |  | ||||||
| 	addr  string |  | ||||||
| 	corId string |  | ||||||
| 	reply chan amqp.Delivery |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type rmqtportSocket struct { |  | ||||||
| 	conn *rabbitMQConn |  | ||||||
| 	d    *amqp.Delivery |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type rmqtportListener struct { |  | ||||||
| 	conn *rabbitMQConn |  | ||||||
| 	addr string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtportClient) Send(m *transport.Message) error { |  | ||||||
| 	if !r.rt.conn.IsConnected() { |  | ||||||
| 		return errors.New("Not connected to AMQP") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	headers := amqp.Table{} |  | ||||||
| 	for k, v := range m.Header { |  | ||||||
| 		headers[k] = v |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	message := amqp.Publishing{ |  | ||||||
| 		CorrelationId: r.corId, |  | ||||||
| 		Timestamp:     time.Now().UTC(), |  | ||||||
| 		Body:          m.Body, |  | ||||||
| 		ReplyTo:       r.rt.replyTo, |  | ||||||
| 		Headers:       headers, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err := r.rt.conn.Publish("micro", r.addr, message); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtportClient) Recv(m *transport.Message) error { |  | ||||||
| 	select { |  | ||||||
| 	case d := <-r.reply: |  | ||||||
| 		mr := &transport.Message{ |  | ||||||
| 			Header: make(map[string]string), |  | ||||||
| 			Body:   d.Body, |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		for k, v := range d.Headers { |  | ||||||
| 			mr.Header[k] = fmt.Sprintf("%v", v) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		*m = *mr |  | ||||||
| 		return nil |  | ||||||
| 	case <-time.After(time.Second * 10): |  | ||||||
| 		return errors.New("timed out") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtportClient) Close() error { |  | ||||||
| 	r.rt.popReq(r.corId) |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtportSocket) Recv(m *transport.Message) error { |  | ||||||
| 	if m == nil { |  | ||||||
| 		return errors.New("message passed in is nil") |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	mr := &transport.Message{ |  | ||||||
| 		Header: make(map[string]string), |  | ||||||
| 		Body:   r.d.Body, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for k, v := range r.d.Headers { |  | ||||||
| 		mr.Header[k] = fmt.Sprintf("%v", v) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	*m = *mr |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtportSocket) Send(m *transport.Message) error { |  | ||||||
| 	msg := amqp.Publishing{ |  | ||||||
| 		CorrelationId: r.d.CorrelationId, |  | ||||||
| 		Timestamp:     time.Now().UTC(), |  | ||||||
| 		Body:          m.Body, |  | ||||||
| 		Headers:       amqp.Table{}, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for k, v := range m.Header { |  | ||||||
| 		msg.Headers[k] = v |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return r.conn.Publish("", r.d.ReplyTo, msg) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtportSocket) Close() error { |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtportListener) Addr() string { |  | ||||||
| 	return r.addr |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtportListener) Close() error { |  | ||||||
| 	r.conn.Close() |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtportListener) Accept(fn func(transport.Socket)) error { |  | ||||||
| 	deliveries, err := r.conn.Consume(r.addr) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	handler := func(d amqp.Delivery) { |  | ||||||
| 		fn(&rmqtportSocket{ |  | ||||||
| 			d:    &d, |  | ||||||
| 			conn: r.conn, |  | ||||||
| 		}) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	for d := range deliveries { |  | ||||||
| 		go handler(d) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtport) putReq(id string) chan amqp.Delivery { |  | ||||||
| 	r.Lock() |  | ||||||
| 	ch := make(chan amqp.Delivery, 1) |  | ||||||
| 	r.inflight[id] = ch |  | ||||||
| 	r.Unlock() |  | ||||||
| 	return ch |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtport) getReq(id string) chan amqp.Delivery { |  | ||||||
| 	r.Lock() |  | ||||||
| 	defer r.Unlock() |  | ||||||
| 	if ch, ok := r.inflight[id]; ok { |  | ||||||
| 		return ch |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtport) popReq(id string) { |  | ||||||
| 	r.Lock() |  | ||||||
| 	defer r.Unlock() |  | ||||||
| 	if _, ok := r.inflight[id]; ok { |  | ||||||
| 		delete(r.inflight, id) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtport) init() { |  | ||||||
| 	<-r.conn.Init() |  | ||||||
| 	if err := r.conn.Channel.DeclareReplyQueue(r.replyTo); err != nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	deliveries, err := r.conn.Channel.ConsumeQueue(r.replyTo) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	go func() { |  | ||||||
| 		for delivery := range deliveries { |  | ||||||
| 			go r.handle(delivery) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtport) handle(delivery amqp.Delivery) { |  | ||||||
| 	ch := r.getReq(delivery.CorrelationId) |  | ||||||
| 	if ch == nil { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	ch <- delivery |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtport) Dial(addr string, opts ...transport.DialOption) (transport.Client, error) { |  | ||||||
| 	id, err := uuid.NewV4() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	r.once.Do(r.init) |  | ||||||
|  |  | ||||||
| 	return &rmqtportClient{ |  | ||||||
| 		rt:    r, |  | ||||||
| 		addr:  addr, |  | ||||||
| 		corId: id.String(), |  | ||||||
| 		reply: r.putReq(id.String()), |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func (r *rmqtport) Listen(addr string) (transport.Listener, error) { |  | ||||||
| 	id, err := uuid.NewV4() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	conn := newRabbitMQConn("", r.addrs) |  | ||||||
| 	<-conn.Init() |  | ||||||
|  |  | ||||||
| 	return &rmqtportListener{ |  | ||||||
| 		addr: id.String(), |  | ||||||
| 		conn: conn, |  | ||||||
| 	}, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NewTransport(addrs []string, opt ...transport.Option) transport.Transport { |  | ||||||
| 	return &rmqtport{ |  | ||||||
| 		conn:     newRabbitMQConn("", addrs), |  | ||||||
| 		addrs:    addrs, |  | ||||||
| 		replyTo:  directReplyQueue, |  | ||||||
| 		inflight: make(map[string]chan amqp.Delivery), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user