Add pub/sub to client/server and make broker more low level
This commit is contained in:
parent
cdf2f2cbcd
commit
b91af916f9
@ -1,23 +1,18 @@
|
|||||||
package broker
|
package broker
|
||||||
|
|
||||||
import (
|
|
||||||
"code.google.com/p/go-uuid/uuid"
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Broker interface {
|
type Broker interface {
|
||||||
Address() string
|
Address() string
|
||||||
Connect() error
|
Connect() error
|
||||||
Disconnect() error
|
Disconnect() error
|
||||||
Init() error
|
Init() error
|
||||||
Publish(context.Context, string, []byte) error
|
Publish(string, *Message) error
|
||||||
Subscribe(string, func(context.Context, *Message)) (Subscriber, error)
|
Subscribe(string, Handler) (Subscriber, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Handler func(*Message)
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Id string
|
Header map[string]string
|
||||||
Timestamp int64
|
|
||||||
Topic string
|
|
||||||
Body []byte
|
Body []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,24 +26,14 @@ type options struct{}
|
|||||||
type Option func(*options)
|
type Option func(*options)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Address string
|
DefaultBroker Broker = newHttpBroker([]string{})
|
||||||
Id string
|
|
||||||
DefaultBroker Broker
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewBroker(addrs []string, opt ...Option) Broker {
|
func NewBroker(addrs []string, opt ...Option) Broker {
|
||||||
return newHttpBroker([]string{Address}, opt...)
|
return newHttpBroker(addrs, opt...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init() error {
|
func Init() error {
|
||||||
if len(Id) == 0 {
|
|
||||||
Id = "broker-" + uuid.NewUUID().String()
|
|
||||||
}
|
|
||||||
|
|
||||||
if DefaultBroker == nil {
|
|
||||||
DefaultBroker = newHttpBroker([]string{Address})
|
|
||||||
}
|
|
||||||
|
|
||||||
return DefaultBroker.Init()
|
return DefaultBroker.Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,10 +45,10 @@ func Disconnect() error {
|
|||||||
return DefaultBroker.Disconnect()
|
return DefaultBroker.Disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
func Publish(ctx context.Context, topic string, body []byte) error {
|
func Publish(topic string, msg *Message) error {
|
||||||
return DefaultBroker.Publish(ctx, topic, body)
|
return DefaultBroker.Publish(topic, msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Subscribe(topic string, function func(context.Context, *Message)) (Subscriber, error) {
|
func Subscribe(topic string, handler Handler) (Subscriber, error) {
|
||||||
return DefaultBroker.Subscribe(topic, function)
|
return DefaultBroker.Subscribe(topic, handler)
|
||||||
}
|
}
|
||||||
|
@ -7,21 +7,14 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.google.com/p/go-uuid/uuid"
|
"code.google.com/p/go-uuid/uuid"
|
||||||
log "github.com/golang/glog"
|
log "github.com/golang/glog"
|
||||||
c "github.com/myodc/go-micro/context"
|
|
||||||
"github.com/myodc/go-micro/errors"
|
"github.com/myodc/go-micro/errors"
|
||||||
"github.com/myodc/go-micro/registry"
|
"github.com/myodc/go-micro/registry"
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type httpBroker struct {
|
type httpBroker struct {
|
||||||
@ -39,28 +32,22 @@ type httpSubscriber struct {
|
|||||||
id string
|
id string
|
||||||
topic string
|
topic string
|
||||||
ch chan *httpSubscriber
|
ch chan *httpSubscriber
|
||||||
fn func(context.Context, *Message)
|
fn Handler
|
||||||
svc *registry.Service
|
svc *registry.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
// used in brokers where there is no support for headers
|
|
||||||
type envelope struct {
|
|
||||||
Header map[string]string
|
|
||||||
Message *Message
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultSubPath = "/_sub"
|
DefaultSubPath = "/_sub"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newHttpBroker(addrs []string, opt ...Option) Broker {
|
func newHttpBroker(addrs []string, opt ...Option) Broker {
|
||||||
addr := ":0"
|
addr := ":0"
|
||||||
if len(addrs) > 0 {
|
if len(addrs) > 0 && len(addrs[0]) > 0 {
|
||||||
addr = addrs[0]
|
addr = addrs[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return &httpBroker{
|
return &httpBroker{
|
||||||
id: Id,
|
id: "broker-" + uuid.NewUUID().String(),
|
||||||
address: addr,
|
address: addr,
|
||||||
subscribers: make(map[string][]*httpSubscriber),
|
subscribers: make(map[string][]*httpSubscriber),
|
||||||
unsubscribe: make(chan *httpSubscriber),
|
unsubscribe: make(chan *httpSubscriber),
|
||||||
@ -96,9 +83,6 @@ func (h *httpBroker) start() error {
|
|||||||
go http.Serve(l, h)
|
go http.Serve(l, h)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
ce := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(ce, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case ch := <-h.exit:
|
case ch := <-h.exit:
|
||||||
@ -107,8 +91,6 @@ func (h *httpBroker) start() error {
|
|||||||
h.running = false
|
h.running = false
|
||||||
h.Unlock()
|
h.Unlock()
|
||||||
return
|
return
|
||||||
case <-ce:
|
|
||||||
h.stop()
|
|
||||||
case subscriber := <-h.unsubscribe:
|
case subscriber := <-h.unsubscribe:
|
||||||
h.Lock()
|
h.Lock()
|
||||||
var subscribers []*httpSubscriber
|
var subscribers []*httpSubscriber
|
||||||
@ -150,26 +132,27 @@ func (h *httpBroker) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var e *envelope
|
var m *Message
|
||||||
if err = json.Unmarshal(b, &e); err != nil {
|
if err = json.Unmarshal(b, &m); err != nil {
|
||||||
errr := errors.InternalServerError("go.micro.broker", fmt.Sprintf("Error parsing request body: %v", err))
|
errr := errors.InternalServerError("go.micro.broker", fmt.Sprintf("Error parsing request body: %v", err))
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
w.Write([]byte(errr.Error()))
|
w.Write([]byte(errr.Error()))
|
||||||
return
|
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")
|
errr := errors.InternalServerError("go.micro.broker", "Topic not found")
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
w.Write([]byte(errr.Error()))
|
w.Write([]byte(errr.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := c.WithMetadata(context.Background(), e.Header)
|
|
||||||
|
|
||||||
h.RLock()
|
h.RLock()
|
||||||
for _, subscriber := range h.subscribers[e.Message.Topic] {
|
for _, subscriber := range h.subscribers[topic] {
|
||||||
subscriber.fn(ctx, e.Message)
|
subscriber.fn(m)
|
||||||
}
|
}
|
||||||
h.RUnlock()
|
h.RUnlock()
|
||||||
}
|
}
|
||||||
@ -195,26 +178,14 @@ func (h *httpBroker) Init() error {
|
|||||||
return nil
|
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)
|
s, err := registry.GetService("topic:" + topic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
message := &Message{
|
msg.Header[":topic"] = topic
|
||||||
Id: uuid.NewUUID().String(),
|
b, err := json.Marshal(msg)
|
||||||
Timestamp: time.Now().Unix(),
|
|
||||||
Topic: topic,
|
|
||||||
Body: body,
|
|
||||||
}
|
|
||||||
|
|
||||||
header, _ := c.GetMetadata(ctx)
|
|
||||||
|
|
||||||
b, err := json.Marshal(&envelope{
|
|
||||||
header,
|
|
||||||
message,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -229,7 +200,7 @@ func (h *httpBroker) Publish(ctx context.Context, topic string, body []byte) err
|
|||||||
return nil
|
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
|
// parse address for host, port
|
||||||
parts := strings.Split(h.Address(), ":")
|
parts := strings.Split(h.Address(), ":")
|
||||||
host := strings.Join(parts[:len(parts)-1], ":")
|
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(),
|
id: uuid.NewUUID().String(),
|
||||||
topic: topic,
|
topic: topic,
|
||||||
ch: h.unsubscribe,
|
ch: h.unsubscribe,
|
||||||
fn: function,
|
fn: handler,
|
||||||
svc: service,
|
svc: service,
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Registering subscriber %s", node.Id)
|
|
||||||
if err := registry.Register(service); err != nil {
|
if err := registry.Register(service); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,9 @@ package nats
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.google.com/p/go-uuid/uuid"
|
|
||||||
"github.com/apcera/nats"
|
"github.com/apcera/nats"
|
||||||
"github.com/myodc/go-micro/broker"
|
"github.com/myodc/go-micro/broker"
|
||||||
c "github.com/myodc/go-micro/context"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type nbroker struct {
|
type nbroker struct {
|
||||||
@ -22,12 +17,6 @@ type subscriber struct {
|
|||||||
s *nats.Subscription
|
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 {
|
func (n *subscriber) Topic() string {
|
||||||
return n.s.Subject
|
return n.s.Subject
|
||||||
}
|
}
|
||||||
@ -67,34 +56,21 @@ func (n *nbroker) Init() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *nbroker) Publish(ctx context.Context, topic string, body []byte) error {
|
func (n *nbroker) Publish(topic string, msg *broker.Message) error {
|
||||||
header, _ := c.GetMetadata(ctx)
|
b, err := json.Marshal(msg)
|
||||||
|
|
||||||
message := &broker.Message{
|
|
||||||
Id: uuid.NewUUID().String(),
|
|
||||||
Timestamp: time.Now().Unix(),
|
|
||||||
Topic: topic,
|
|
||||||
Body: body,
|
|
||||||
}
|
|
||||||
|
|
||||||
b, err := json.Marshal(&envelope{
|
|
||||||
header,
|
|
||||||
message,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return n.conn.Publish(topic, b)
|
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) {
|
sub, err := n.conn.Subscribe(topic, func(msg *nats.Msg) {
|
||||||
var e *envelope
|
var m *broker.Message
|
||||||
if err := json.Unmarshal(msg.Data, &e); err != nil {
|
if err := json.Unmarshal(msg.Data, &m); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx := c.WithMetadata(context.Background(), e.Header)
|
handler(m)
|
||||||
function(ctx, e.Message)
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
package rabbitmq
|
package rabbitmq
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"code.google.com/p/go-uuid/uuid"
|
|
||||||
"github.com/myodc/go-micro/broker"
|
"github.com/myodc/go-micro/broker"
|
||||||
c "github.com/myodc/go-micro/context"
|
|
||||||
"github.com/streadway/amqp"
|
"github.com/streadway/amqp"
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type rbroker struct {
|
type rbroker struct {
|
||||||
@ -28,24 +23,20 @@ func (s *subscriber) Unsubscribe() error {
|
|||||||
return s.ch.Close()
|
return s.ch.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *rbroker) Publish(ctx context.Context, topic string, body []byte) error {
|
func (r *rbroker) Publish(topic string, msg *broker.Message) error {
|
||||||
header, _ := c.GetMetadata(ctx)
|
m := amqp.Publishing{
|
||||||
|
Body: msg.Body,
|
||||||
msg := amqp.Publishing{
|
|
||||||
MessageId: uuid.NewUUID().String(),
|
|
||||||
Timestamp: time.Now().UTC(),
|
|
||||||
Body: body,
|
|
||||||
Headers: amqp.Table{},
|
Headers: amqp.Table{},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range header {
|
for k, v := range msg.Header {
|
||||||
msg.Headers[k] = v
|
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)
|
ch, sub, err := r.conn.Consume(topic)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -56,11 +47,8 @@ func (r *rbroker) Subscribe(topic string, function func(context.Context, *broker
|
|||||||
for k, v := range msg.Headers {
|
for k, v := range msg.Headers {
|
||||||
header[k], _ = v.(string)
|
header[k], _ = v.(string)
|
||||||
}
|
}
|
||||||
ctx := c.WithMetadata(context.Background(), header)
|
handler(&broker.Message{
|
||||||
function(ctx, &broker.Message{
|
Header: header,
|
||||||
Id: msg.MessageId,
|
|
||||||
Timestamp: msg.Timestamp.Unix(),
|
|
||||||
Topic: topic,
|
|
||||||
Body: msg.Body,
|
Body: msg.Body,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,32 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/myodc/go-micro/registry"
|
|
||||||
"github.com/myodc/go-micro/transport"
|
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client interface {
|
type Client interface {
|
||||||
NewRequest(string, string, interface{}) Request
|
NewPublication(topic string, msg interface{}) Publication
|
||||||
NewProtoRequest(string, string, interface{}) Request
|
NewRequest(service, method string, req interface{}) Request
|
||||||
NewJsonRequest(string, string, interface{}) Request
|
NewProtoRequest(service, method string, req interface{}) Request
|
||||||
Call(context.Context, Request, interface{}) error
|
NewJsonRequest(service, method string, req interface{}) Request
|
||||||
CallRemote(context.Context, string, Request, interface{}) error
|
Call(ctx context.Context, req Request, rsp interface{}) error
|
||||||
Stream(context.Context, Request, interface{}) (Streamer, error)
|
CallRemote(ctx context.Context, addr string, req Request, rsp interface{}) error
|
||||||
StreamRemote(context.Context, string, Request, interface{}) (Streamer, error)
|
Stream(ctx context.Context, req Request, rspChan interface{}) (Streamer, error)
|
||||||
|
StreamRemote(ctx context.Context, addr string, req Request, rspChan interface{}) (Streamer, error)
|
||||||
|
Publish(ctx context.Context, p Publication) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type Publication interface {
|
||||||
|
Topic() string
|
||||||
|
Message() interface{}
|
||||||
|
ContentType() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Request interface {
|
||||||
|
Service() string
|
||||||
|
Method() string
|
||||||
|
ContentType() string
|
||||||
|
Request() interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Streamer interface {
|
type Streamer interface {
|
||||||
@ -22,29 +35,12 @@ type Streamer interface {
|
|||||||
Close() error
|
Close() error
|
||||||
}
|
}
|
||||||
|
|
||||||
type options struct {
|
|
||||||
registry registry.Registry
|
|
||||||
transport transport.Transport
|
|
||||||
}
|
|
||||||
|
|
||||||
type Option func(*options)
|
type Option func(*options)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultClient Client = newRpcClient()
|
DefaultClient Client = newRpcClient()
|
||||||
)
|
)
|
||||||
|
|
||||||
func Registry(r registry.Registry) Option {
|
|
||||||
return func(o *options) {
|
|
||||||
o.registry = r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Transport(t transport.Transport) Option {
|
|
||||||
return func(o *options) {
|
|
||||||
o.transport = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Call(ctx context.Context, request Request, response interface{}) error {
|
func Call(ctx context.Context, request Request, response interface{}) error {
|
||||||
return DefaultClient.Call(ctx, request, response)
|
return DefaultClient.Call(ctx, request, response)
|
||||||
}
|
}
|
||||||
@ -61,10 +57,18 @@ func StreamRemote(ctx context.Context, address string, request Request, response
|
|||||||
return DefaultClient.StreamRemote(ctx, address, request, responseChan)
|
return DefaultClient.StreamRemote(ctx, address, request, responseChan)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Publish(ctx context.Context, p Publication) error {
|
||||||
|
return DefaultClient.Publish(ctx, p)
|
||||||
|
}
|
||||||
|
|
||||||
func NewClient(opt ...Option) Client {
|
func NewClient(opt ...Option) Client {
|
||||||
return newRpcClient(opt...)
|
return newRpcClient(opt...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewPublication(topic string, message interface{}) Publication {
|
||||||
|
return DefaultClient.NewPublication(topic, message)
|
||||||
|
}
|
||||||
|
|
||||||
func NewRequest(service, method string, request interface{}) Request {
|
func NewRequest(service, method string, request interface{}) Request {
|
||||||
return DefaultClient.NewRequest(service, method, request)
|
return DefaultClient.NewRequest(service, method, request)
|
||||||
}
|
}
|
||||||
|
31
client/options.go
Normal file
31
client/options.go
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/myodc/go-micro/broker"
|
||||||
|
"github.com/myodc/go-micro/registry"
|
||||||
|
"github.com/myodc/go-micro/transport"
|
||||||
|
)
|
||||||
|
|
||||||
|
type options struct {
|
||||||
|
broker broker.Broker
|
||||||
|
registry registry.Registry
|
||||||
|
transport transport.Transport
|
||||||
|
}
|
||||||
|
|
||||||
|
func Broker(b broker.Broker) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.broker = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Registry(r registry.Registry) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.registry = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Transport(t transport.Transport) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.transport = t
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +0,0 @@
|
|||||||
package client
|
|
||||||
|
|
||||||
type Request interface {
|
|
||||||
Service() string
|
|
||||||
Method() string
|
|
||||||
ContentType() string
|
|
||||||
Request() interface{}
|
|
||||||
}
|
|
@ -1,11 +1,14 @@
|
|||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/myodc/go-micro/broker"
|
||||||
c "github.com/myodc/go-micro/context"
|
c "github.com/myodc/go-micro/context"
|
||||||
"github.com/myodc/go-micro/errors"
|
"github.com/myodc/go-micro/errors"
|
||||||
"github.com/myodc/go-micro/registry"
|
"github.com/myodc/go-micro/registry"
|
||||||
@ -13,6 +16,7 @@ import (
|
|||||||
|
|
||||||
rpc "github.com/youtube/vitess/go/rpcplus"
|
rpc "github.com/youtube/vitess/go/rpcplus"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,6 +25,7 @@ type headerRoundTripper struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type rpcClient struct {
|
type rpcClient struct {
|
||||||
|
once sync.Once
|
||||||
opts options
|
opts options
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +44,14 @@ func newRpcClient(opt ...Option) Client {
|
|||||||
opts.transport = transport.DefaultTransport
|
opts.transport = transport.DefaultTransport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.broker == nil {
|
||||||
|
opts.broker = broker.DefaultBroker
|
||||||
|
}
|
||||||
|
|
||||||
|
var once sync.Once
|
||||||
|
|
||||||
return &rpcClient{
|
return &rpcClient{
|
||||||
|
once: once,
|
||||||
opts: opts,
|
opts: opts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,6 +164,48 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, responseChan in
|
|||||||
return r.stream(ctx, address, request, responseChan)
|
return r.stream(ctx, address, request, responseChan)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *rpcClient) Publish(ctx context.Context, p Publication) error {
|
||||||
|
md, ok := c.GetMetadata(ctx)
|
||||||
|
if !ok {
|
||||||
|
md = make(map[string]string)
|
||||||
|
}
|
||||||
|
md["Content-Type"] = p.ContentType()
|
||||||
|
|
||||||
|
// encode message body
|
||||||
|
var body []byte
|
||||||
|
|
||||||
|
switch p.ContentType() {
|
||||||
|
case "application/octet-stream":
|
||||||
|
b, err := proto.Marshal(p.Message().(proto.Message))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
body = b
|
||||||
|
case "application/json":
|
||||||
|
b, err := json.Marshal(p.Message())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
body = b
|
||||||
|
}
|
||||||
|
|
||||||
|
r.once.Do(func() {
|
||||||
|
r.opts.broker.Connect()
|
||||||
|
})
|
||||||
|
|
||||||
|
return r.opts.broker.Publish(p.Topic(), &broker.Message{
|
||||||
|
Header: md,
|
||||||
|
Body: body,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcClient) NewPublication(topic string, message interface{}) Publication {
|
||||||
|
return r.NewProtoPublication(topic, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcClient) NewProtoPublication(topic string, message interface{}) Publication {
|
||||||
|
return newRpcPublication(topic, message, "application/octet-stream")
|
||||||
|
}
|
||||||
func (r *rpcClient) NewRequest(service, method string, request interface{}) Request {
|
func (r *rpcClient) NewRequest(service, method string, request interface{}) Request {
|
||||||
return r.NewProtoRequest(service, method, request)
|
return r.NewProtoRequest(service, method, request)
|
||||||
}
|
}
|
||||||
|
27
client/rpc_publication.go
Normal file
27
client/rpc_publication.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package client
|
||||||
|
|
||||||
|
type rpcPublication struct {
|
||||||
|
topic string
|
||||||
|
contentType string
|
||||||
|
message interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRpcPublication(topic string, message interface{}, contentType string) Publication {
|
||||||
|
return &rpcPublication{
|
||||||
|
message: message,
|
||||||
|
topic: topic,
|
||||||
|
contentType: contentType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcPublication) ContentType() string {
|
||||||
|
return r.contentType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcPublication) Topic() string {
|
||||||
|
return r.topic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcPublication) Message() interface{} {
|
||||||
|
return r.message
|
||||||
|
}
|
@ -10,6 +10,26 @@ import (
|
|||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func pub() {
|
||||||
|
msg := client.NewPublication("topic.go.micro.srv.example", &example.Message{
|
||||||
|
Say: "This is a publication",
|
||||||
|
})
|
||||||
|
|
||||||
|
// create context with metadata
|
||||||
|
ctx := c.WithMetadata(context.Background(), map[string]string{
|
||||||
|
"X-User-Id": "john",
|
||||||
|
"X-From-Id": "script",
|
||||||
|
})
|
||||||
|
|
||||||
|
// publish message
|
||||||
|
if err := client.Publish(ctx, msg); err != nil {
|
||||||
|
fmt.Println("pub err: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Published: %v\n", msg)
|
||||||
|
}
|
||||||
|
|
||||||
func call(i int) {
|
func call(i int) {
|
||||||
// Create new request to service go.micro.srv.example, method Example.Call
|
// Create new request to service go.micro.srv.example, method Example.Call
|
||||||
req := client.NewRequest("go.micro.srv.example", "Example.Call", &example.Request{
|
req := client.NewRequest("go.micro.srv.example", "Example.Call", &example.Request{
|
||||||
@ -26,7 +46,7 @@ func call(i int) {
|
|||||||
|
|
||||||
// Call service
|
// Call service
|
||||||
if err := client.Call(ctx, req, rsp); err != nil {
|
if err := client.Call(ctx, req, rsp); err != nil {
|
||||||
fmt.Println("err: ", err, rsp)
|
fmt.Println("call err: ", err, rsp)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +72,7 @@ func stream() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if stream.Error() != nil {
|
if stream.Error() != nil {
|
||||||
fmt.Println("err:", err)
|
fmt.Println("stream err:", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,4 +87,5 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
stream()
|
stream()
|
||||||
|
pub()
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,6 @@ import (
|
|||||||
log "github.com/golang/glog"
|
log "github.com/golang/glog"
|
||||||
"github.com/myodc/go-micro/broker"
|
"github.com/myodc/go-micro/broker"
|
||||||
"github.com/myodc/go-micro/cmd"
|
"github.com/myodc/go-micro/cmd"
|
||||||
c "github.com/myodc/go-micro/context"
|
|
||||||
|
|
||||||
"golang.org/x/net/context"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -20,24 +17,24 @@ func pub() {
|
|||||||
tick := time.NewTicker(time.Second)
|
tick := time.NewTicker(time.Second)
|
||||||
i := 0
|
i := 0
|
||||||
for _ = range tick.C {
|
for _ = range tick.C {
|
||||||
ctx := c.WithMetadata(context.Background(), map[string]string{
|
msg := &broker.Message{
|
||||||
|
Header: map[string]string{
|
||||||
"id": fmt.Sprintf("%d", i),
|
"id": fmt.Sprintf("%d", i),
|
||||||
})
|
},
|
||||||
|
Body: []byte(fmt.Sprintf("%d: %s", i, time.Now().String())),
|
||||||
msg := fmt.Sprintf("%d: %s", i, time.Now().String())
|
}
|
||||||
if err := broker.Publish(ctx, topic, []byte(msg)); err != nil {
|
if err := broker.Publish(topic, msg); err != nil {
|
||||||
log.Errorf("[pub] failed: %v", err)
|
log.Errorf("[pub] failed: %v", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("[pub] pubbed message:", msg)
|
fmt.Println("[pub] pubbed message:", string(msg.Body))
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sub() {
|
func sub() {
|
||||||
_, err := broker.Subscribe(topic, func(ctx context.Context, msg *broker.Message) {
|
_, err := broker.Subscribe(topic, func(msg *broker.Message) {
|
||||||
md, _ := c.GetMetadata(ctx)
|
fmt.Println("[sub] received message:", string(msg.Body), "header", msg.Header)
|
||||||
fmt.Println("[sub] received message:", string(msg.Body), "context", md)
|
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
log "github.com/golang/glog"
|
log "github.com/golang/glog"
|
||||||
"github.com/myodc/go-micro/cmd"
|
"github.com/myodc/go-micro/cmd"
|
||||||
"github.com/myodc/go-micro/examples/server/handler"
|
"github.com/myodc/go-micro/examples/server/handler"
|
||||||
|
"github.com/myodc/go-micro/examples/server/subscriber"
|
||||||
"github.com/myodc/go-micro/server"
|
"github.com/myodc/go-micro/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,6 +24,21 @@ func main() {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Register Subscribers
|
||||||
|
server.Subscribe(
|
||||||
|
server.NewSubscriber(
|
||||||
|
"topic.go.micro.srv.example",
|
||||||
|
new(subscriber.Example),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
server.Subscribe(
|
||||||
|
server.NewSubscriber(
|
||||||
|
"topic.go.micro.srv.example",
|
||||||
|
subscriber.Handler,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// Run server
|
// Run server
|
||||||
if err := server.Run(); err != nil {
|
if err := server.Run(); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -9,6 +9,7 @@ It is generated from these files:
|
|||||||
go-micro/examples/server/proto/example/example.proto
|
go-micro/examples/server/proto/example/example.proto
|
||||||
|
|
||||||
It has these top-level messages:
|
It has these top-level messages:
|
||||||
|
Message
|
||||||
Request
|
Request
|
||||||
Response
|
Response
|
||||||
StreamingRequest
|
StreamingRequest
|
||||||
@ -21,6 +22,14 @@ import proto "github.com/golang/protobuf/proto"
|
|||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ = proto.Marshal
|
var _ = proto.Marshal
|
||||||
|
|
||||||
|
type Message struct {
|
||||||
|
Say string `protobuf:"bytes,1,opt,name=say" json:"say,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Message) Reset() { *m = Message{} }
|
||||||
|
func (m *Message) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Message) ProtoMessage() {}
|
||||||
|
|
||||||
type Request struct {
|
type Request struct {
|
||||||
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
|
message Message {
|
||||||
|
string say = 1;
|
||||||
|
}
|
||||||
|
|
||||||
message Request {
|
message Request {
|
||||||
string name = 1;
|
string name = 1;
|
||||||
}
|
}
|
||||||
|
18
examples/server/subscriber/subscriber.go
Normal file
18
examples/server/subscriber/subscriber.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package subscriber
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/golang/glog"
|
||||||
|
example "github.com/myodc/go-micro/examples/server/proto/example"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Example struct{}
|
||||||
|
|
||||||
|
func (e *Example) Handle(ctx context.Context, msg *example.Message) error {
|
||||||
|
log.Info("Handler Received message: ", msg.Say)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Handler(msg *example.Message) {
|
||||||
|
log.Info("Function Received message: ", msg.Say)
|
||||||
|
}
|
@ -19,6 +19,7 @@ type Endpoint struct {
|
|||||||
Name string
|
Name string
|
||||||
Request *Value
|
Request *Value
|
||||||
Response *Value
|
Response *Value
|
||||||
|
Metadata map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Value struct {
|
type Value struct {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/myodc/go-micro/registry"
|
"github.com/myodc/go-micro/registry"
|
||||||
@ -37,7 +38,7 @@ func extractEndpoint(method reflect.Method) *registry.Endpoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var rspType, reqType reflect.Type
|
var rspType, reqType reflect.Type
|
||||||
// var stream bool
|
var stream bool
|
||||||
mt := method.Type
|
mt := method.Type
|
||||||
|
|
||||||
switch mt.NumIn() {
|
switch mt.NumIn() {
|
||||||
@ -51,9 +52,9 @@ func extractEndpoint(method reflect.Method) *registry.Endpoint {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// if rspType.Kind() == reflect.Func {
|
if rspType.Kind() == reflect.Func {
|
||||||
// stream = true
|
stream = true
|
||||||
// }
|
}
|
||||||
|
|
||||||
request := extractValue(reqType)
|
request := extractValue(reqType)
|
||||||
response := extractValue(rspType)
|
response := extractValue(rspType)
|
||||||
@ -62,5 +63,21 @@ func extractEndpoint(method reflect.Method) *registry.Endpoint {
|
|||||||
Name: method.Name,
|
Name: method.Name,
|
||||||
Request: request,
|
Request: request,
|
||||||
Response: response,
|
Response: response,
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"stream": fmt.Sprintf("%v", stream),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func extractSubValue(typ reflect.Type) *registry.Value {
|
||||||
|
var reqType reflect.Type
|
||||||
|
switch typ.NumIn() {
|
||||||
|
case 1:
|
||||||
|
reqType = typ.In(0)
|
||||||
|
case 2:
|
||||||
|
reqType = typ.In(1)
|
||||||
|
default:
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return extractValue(reqType)
|
||||||
|
}
|
||||||
|
@ -9,3 +9,9 @@ type Handler interface {
|
|||||||
Handler() interface{}
|
Handler() interface{}
|
||||||
Endpoints() []*registry.Endpoint
|
Endpoints() []*registry.Endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Subscriber interface {
|
||||||
|
Topic() string
|
||||||
|
Subscriber() interface{}
|
||||||
|
Endpoints() []*registry.Endpoint
|
||||||
|
}
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/myodc/go-micro/broker"
|
||||||
"github.com/myodc/go-micro/registry"
|
"github.com/myodc/go-micro/registry"
|
||||||
"github.com/myodc/go-micro/transport"
|
"github.com/myodc/go-micro/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
type options struct {
|
type options struct {
|
||||||
|
broker broker.Broker
|
||||||
registry registry.Registry
|
registry registry.Registry
|
||||||
transport transport.Transport
|
transport transport.Transport
|
||||||
metadata map[string]string
|
metadata map[string]string
|
||||||
@ -22,6 +24,10 @@ func newOptions(opt ...Option) options {
|
|||||||
o(&opts)
|
o(&opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.broker == nil {
|
||||||
|
opts.broker = broker.DefaultBroker
|
||||||
|
}
|
||||||
|
|
||||||
if opts.registry == nil {
|
if opts.registry == nil {
|
||||||
opts.registry = registry.DefaultRegistry
|
opts.registry = registry.DefaultRegistry
|
||||||
}
|
}
|
||||||
@ -93,6 +99,12 @@ func Address(a string) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Broker(b broker.Broker) Option {
|
||||||
|
return func(o *options) {
|
||||||
|
o.broker = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Registry(r registry.Registry) Option {
|
func Registry(r registry.Registry) Option {
|
||||||
return func(o *options) {
|
return func(o *options) {
|
||||||
o.registry = r
|
o.registry = r
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/myodc/go-micro/broker"
|
||||||
c "github.com/myodc/go-micro/context"
|
c "github.com/myodc/go-micro/context"
|
||||||
"github.com/myodc/go-micro/registry"
|
"github.com/myodc/go-micro/registry"
|
||||||
"github.com/myodc/go-micro/transport"
|
"github.com/myodc/go-micro/transport"
|
||||||
@ -22,6 +24,7 @@ type rpcServer struct {
|
|||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
opts options
|
opts options
|
||||||
handlers map[string]Handler
|
handlers map[string]Handler
|
||||||
|
subscribers map[*subscriber][]broker.Subscriber
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRpcServer(opts ...Option) Server {
|
func newRpcServer(opts ...Option) Server {
|
||||||
@ -29,6 +32,7 @@ func newRpcServer(opts ...Option) Server {
|
|||||||
opts: newOptions(opts...),
|
opts: newOptions(opts...),
|
||||||
rpc: rpc.NewServer(),
|
rpc: rpc.NewServer(),
|
||||||
handlers: make(map[string]Handler),
|
handlers: make(map[string]Handler),
|
||||||
|
subscribers: make(map[*subscriber][]broker.Subscriber),
|
||||||
exit: make(chan chan error),
|
exit: make(chan chan error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,6 +88,29 @@ func (s *rpcServer) Handle(h Handler) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *rpcServer) NewSubscriber(topic string, sb interface{}) Subscriber {
|
||||||
|
return newSubscriber(topic, sb)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *rpcServer) Subscribe(sb Subscriber) error {
|
||||||
|
sub, ok := sb.(*subscriber)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid subscriber: expected *subscriber")
|
||||||
|
}
|
||||||
|
if len(sub.handlers) == 0 {
|
||||||
|
return fmt.Errorf("invalid subscriber: no handler functions")
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
_, ok = s.subscribers[sub]
|
||||||
|
if ok {
|
||||||
|
return fmt.Errorf("subscriber %v already exists", s)
|
||||||
|
}
|
||||||
|
s.subscribers[sub] = nil
|
||||||
|
s.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *rpcServer) Register() error {
|
func (s *rpcServer) Register() error {
|
||||||
// parse address for host, port
|
// parse address for host, port
|
||||||
config := s.Config()
|
config := s.Config()
|
||||||
@ -110,6 +137,9 @@ func (s *rpcServer) Register() error {
|
|||||||
for _, e := range s.handlers {
|
for _, e := range s.handlers {
|
||||||
endpoints = append(endpoints, e.Endpoints()...)
|
endpoints = append(endpoints, e.Endpoints()...)
|
||||||
}
|
}
|
||||||
|
for e, _ := range s.subscribers {
|
||||||
|
endpoints = append(endpoints, e.Endpoints()...)
|
||||||
|
}
|
||||||
s.RUnlock()
|
s.RUnlock()
|
||||||
|
|
||||||
service := ®istry.Service{
|
service := ®istry.Service{
|
||||||
@ -120,7 +150,23 @@ func (s *rpcServer) Register() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Registering node: %s", node.Id)
|
log.Infof("Registering node: %s", node.Id)
|
||||||
return config.registry.Register(service)
|
if err := config.registry.Register(service); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
defer s.Unlock()
|
||||||
|
|
||||||
|
for sb, _ := range s.subscribers {
|
||||||
|
handler := createSubHandler(sb)
|
||||||
|
sub, err := config.broker.Subscribe(sb.Topic(), handler)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.subscribers[sb] = []broker.Subscriber{sub}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *rpcServer) Deregister() error {
|
func (s *rpcServer) Deregister() error {
|
||||||
@ -147,7 +193,21 @@ func (s *rpcServer) Deregister() error {
|
|||||||
Nodes: []*registry.Node{node},
|
Nodes: []*registry.Node{node},
|
||||||
}
|
}
|
||||||
|
|
||||||
return config.registry.Deregister(service)
|
log.Infof("Deregistering node: %s", node.Id)
|
||||||
|
if err := config.registry.Deregister(service); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
for sb, subs := range s.subscribers {
|
||||||
|
for _, sub := range subs {
|
||||||
|
log.Infof("Unsubscribing from topic: %s", sub.Topic())
|
||||||
|
sub.Unsubscribe()
|
||||||
|
}
|
||||||
|
s.subscribers[sb] = nil
|
||||||
|
}
|
||||||
|
s.Unlock()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *rpcServer) Start() error {
|
func (s *rpcServer) Start() error {
|
||||||
@ -169,9 +229,11 @@ func (s *rpcServer) Start() error {
|
|||||||
go func() {
|
go func() {
|
||||||
ch := <-s.exit
|
ch := <-s.exit
|
||||||
ch <- ts.Close()
|
ch <- ts.Close()
|
||||||
|
config.broker.Disconnect()
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
// TODO: subscribe to cruft
|
||||||
|
return config.broker.Connect()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *rpcServer) Stop() error {
|
func (s *rpcServer) Stop() error {
|
||||||
|
@ -14,6 +14,8 @@ type Server interface {
|
|||||||
Init(...Option)
|
Init(...Option)
|
||||||
Handle(Handler) error
|
Handle(Handler) error
|
||||||
NewHandler(interface{}) Handler
|
NewHandler(interface{}) Handler
|
||||||
|
NewSubscriber(string, interface{}) Subscriber
|
||||||
|
Subscribe(Subscriber) error
|
||||||
Register() error
|
Register() error
|
||||||
Deregister() error
|
Deregister() error
|
||||||
Start() error
|
Start() error
|
||||||
@ -45,6 +47,10 @@ func NewServer(opt ...Option) Server {
|
|||||||
return newRpcServer(opt...)
|
return newRpcServer(opt...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewSubscriber(topic string, h interface{}) Subscriber {
|
||||||
|
return DefaultServer.NewSubscriber(topic, h)
|
||||||
|
}
|
||||||
|
|
||||||
func NewHandler(h interface{}) Handler {
|
func NewHandler(h interface{}) Handler {
|
||||||
return DefaultServer.NewHandler(h)
|
return DefaultServer.NewHandler(h)
|
||||||
}
|
}
|
||||||
@ -53,6 +59,10 @@ func Handle(h Handler) error {
|
|||||||
return DefaultServer.Handle(h)
|
return DefaultServer.Handle(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Subscribe(s Subscriber) error {
|
||||||
|
return DefaultServer.Subscribe(s)
|
||||||
|
}
|
||||||
|
|
||||||
func Register() error {
|
func Register() error {
|
||||||
return DefaultServer.Register()
|
return DefaultServer.Register()
|
||||||
}
|
}
|
||||||
@ -78,9 +88,6 @@ func Run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Infof("Deregistering %s", DefaultServer.Config().Id())
|
|
||||||
DefaultServer.Deregister()
|
|
||||||
|
|
||||||
return Stop()
|
return Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
154
server/subscriber.go
Normal file
154
server/subscriber.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/myodc/go-micro/broker"
|
||||||
|
c "github.com/myodc/go-micro/context"
|
||||||
|
"github.com/myodc/go-micro/registry"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
method reflect.Value
|
||||||
|
reqType reflect.Type
|
||||||
|
ctxType reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
type subscriber struct {
|
||||||
|
topic string
|
||||||
|
rcvr reflect.Value
|
||||||
|
typ reflect.Type
|
||||||
|
subscriber interface{}
|
||||||
|
handlers []*handler
|
||||||
|
endpoints []*registry.Endpoint
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSubscriber(topic string, sub interface{}) Subscriber {
|
||||||
|
var endpoints []*registry.Endpoint
|
||||||
|
var handlers []*handler
|
||||||
|
|
||||||
|
if typ := reflect.TypeOf(sub); typ.Kind() == reflect.Func {
|
||||||
|
h := &handler{
|
||||||
|
method: reflect.ValueOf(sub),
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typ.NumIn() {
|
||||||
|
case 1:
|
||||||
|
h.reqType = typ.In(0)
|
||||||
|
case 2:
|
||||||
|
h.ctxType = typ.In(0)
|
||||||
|
h.reqType = typ.In(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers = append(handlers, h)
|
||||||
|
|
||||||
|
endpoints = append(endpoints, ®istry.Endpoint{
|
||||||
|
Name: "Func",
|
||||||
|
Request: extractSubValue(typ),
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"topic": topic,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
for m := 0; m < typ.NumMethod(); m++ {
|
||||||
|
method := typ.Method(m)
|
||||||
|
h := &handler{
|
||||||
|
method: method.Func,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch method.Type.NumIn() {
|
||||||
|
case 2:
|
||||||
|
h.reqType = method.Type.In(1)
|
||||||
|
case 3:
|
||||||
|
h.ctxType = method.Type.In(1)
|
||||||
|
h.reqType = method.Type.In(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers = append(handlers, h)
|
||||||
|
|
||||||
|
endpoints = append(endpoints, ®istry.Endpoint{
|
||||||
|
Name: method.Name,
|
||||||
|
Request: extractSubValue(method.Type),
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"topic": topic,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &subscriber{
|
||||||
|
rcvr: reflect.ValueOf(sub),
|
||||||
|
typ: reflect.TypeOf(sub),
|
||||||
|
topic: topic,
|
||||||
|
subscriber: sub,
|
||||||
|
handlers: handlers,
|
||||||
|
endpoints: endpoints,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createSubHandler(sb *subscriber) broker.Handler {
|
||||||
|
return func(msg *broker.Message) {
|
||||||
|
hdr := make(map[string]string)
|
||||||
|
for k, v := range msg.Header {
|
||||||
|
hdr[k] = v
|
||||||
|
}
|
||||||
|
delete(hdr, "Content-Type")
|
||||||
|
ctx := c.WithMetadata(context.Background(), hdr)
|
||||||
|
rctx := reflect.ValueOf(ctx)
|
||||||
|
|
||||||
|
for _, handler := range sb.handlers {
|
||||||
|
var isVal bool
|
||||||
|
var req reflect.Value
|
||||||
|
var uerr error
|
||||||
|
|
||||||
|
if handler.reqType.Kind() == reflect.Ptr {
|
||||||
|
req = reflect.New(handler.reqType.Elem())
|
||||||
|
} else {
|
||||||
|
req = reflect.New(handler.reqType)
|
||||||
|
isVal = true
|
||||||
|
}
|
||||||
|
|
||||||
|
switch msg.Header["Content-Type"] {
|
||||||
|
case "application/octet-stream":
|
||||||
|
uerr = proto.Unmarshal(msg.Body, req.Interface().(proto.Message))
|
||||||
|
case "application/json":
|
||||||
|
uerr = json.Unmarshal(msg.Body, req.Interface())
|
||||||
|
}
|
||||||
|
|
||||||
|
if uerr != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if isVal {
|
||||||
|
req = req.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
var vals []reflect.Value
|
||||||
|
if sb.typ.Kind() != reflect.Func {
|
||||||
|
vals = append(vals, sb.rcvr)
|
||||||
|
}
|
||||||
|
|
||||||
|
if handler.ctxType != nil {
|
||||||
|
vals = append(vals, rctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
vals = append(vals, req)
|
||||||
|
go handler.method.Call(vals)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subscriber) Topic() string {
|
||||||
|
return s.topic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subscriber) Subscriber() interface{} {
|
||||||
|
return s.subscriber
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subscriber) Endpoints() []*registry.Endpoint {
|
||||||
|
return s.endpoints
|
||||||
|
}
|
@ -137,7 +137,6 @@ func (h *httpTransportSocket) Send(m *Message) error {
|
|||||||
ProtoMajor: 1,
|
ProtoMajor: 1,
|
||||||
ProtoMinor: 1,
|
ProtoMinor: 1,
|
||||||
ContentLength: int64(len(m.Body)),
|
ContentLength: int64(len(m.Body)),
|
||||||
// Request: h.r,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range m.Header {
|
for k, v := range m.Header {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package transport
|
package transport
|
||||||
|
|
||||||
type Message struct {
|
type Message struct {
|
||||||
Id string
|
|
||||||
Header map[string]string
|
Header map[string]string
|
||||||
Body []byte
|
Body []byte
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user