diff --git a/broker/broker.go b/broker/broker.go index 9761bf5e..53099b7a 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -5,12 +5,12 @@ import ( "context" "errors" - "go.unistack.org/micro/v4/codec" "go.unistack.org/micro/v4/metadata" + "go.unistack.org/micro/v4/options" ) // DefaultBroker default memory broker -var DefaultBroker Broker // = NewBroker() +var DefaultBroker Broker = NewBroker() var ( // ErrNotConnected returns when broker used but not connected yet @@ -26,7 +26,7 @@ type Broker interface { // Name returns broker instance name Name() string // Init initilize broker - Init(opts ...Option) error + Init(opts ...options.Option) error // Options returns broker options Options() Options // Address return configured address @@ -35,12 +35,10 @@ type Broker interface { Connect(ctx context.Context) error // Disconnect disconnect from broker Disconnect(ctx context.Context) error - // NewMessage creates new broker message - NewMessage(endpoint string, req interface{}, opts ...MessageOption) Message - // Publish message to broker topic - Publish(ctx context.Context, msg interface{}, opts ...PublishOption) error + // Publish message, msg can be single broker.Message or []broker.Message + Publish(ctx context.Context, msg interface{}, opts ...options.Option) error // Subscribe subscribes to topic message via handler - Subscribe(ctx context.Context, topic string, handler interface{}, opts ...SubscribeOption) (Subscriber, error) + Subscribe(ctx context.Context, topic string, handler interface{}, opts ...options.Option) (Subscriber, error) // String type of broker String() string } @@ -49,9 +47,11 @@ type Broker interface { type Message interface { // Context for the message Context() context.Context - // Topic returns event topic + // Topic Topic() string - // Body returns broker message + // Header returns message headers + Header() metadata.Metadata + // Body returns broker message may be []byte slice or some go struct Body() interface{} // Ack acknowledge message Ack() error @@ -60,21 +60,6 @@ type Message interface { Error() error } -// RawMessage is used to transfer data -type RawMessage struct { - // Header contains message metadata - Header metadata.Metadata - // Body contains message body - Body codec.RawMessage -} - -// NewMessage create broker message with topic filled -func NewRawMessage(topic string) *RawMessage { - m := &RawMessage{Header: metadata.New(2)} - m.Header.Set(metadata.HeaderTopic, topic) - return m -} - // Subscriber is a convenience return type for the Subscribe method type Subscriber interface { // Options returns subscriber options @@ -84,3 +69,9 @@ type Subscriber interface { // Unsubscribe from topic Unsubscribe(ctx context.Context) error } + +// MessageHandler func signature for single message processing +type MessageHandler func(Message) error + +// MessagesHandler func signature for batch message processing +type MessagesHandler func([]Message) error diff --git a/broker/context.go b/broker/context.go index 90d453f9..e337db6a 100644 --- a/broker/context.go +++ b/broker/context.go @@ -22,33 +22,3 @@ func NewContext(ctx context.Context, s Broker) context.Context { } return context.WithValue(ctx, brokerKey{}, s) } - -// SetSubscribeOption returns a function to setup a context with given value -func SetSubscribeOption(k, v interface{}) SubscribeOption { - return func(o *SubscribeOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} - -// SetOption returns a function to setup a context with given value -func SetOption(k, v interface{}) Option { - return func(o *Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} - -// SetPublishOption returns a function to setup a context with given value -func SetPublishOption(k, v interface{}) PublishOption { - return func(o *PublishOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} diff --git a/broker/context_test.go b/broker/context_test.go index cd4feb5e..03d6f3b7 100644 --- a/broker/context_test.go +++ b/broker/context_test.go @@ -37,36 +37,3 @@ func TestNewNilContext(t *testing.T) { t.Fatal("NewContext not works") } } - -func TestSetSubscribeOption(t *testing.T) { - type key struct{} - o := SetSubscribeOption(key{}, "test") - opts := &SubscribeOptions{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetSubscribeOption not works") - } -} - -func TestSetPublishOption(t *testing.T) { - type key struct{} - o := SetPublishOption(key{}, "test") - opts := &PublishOptions{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetPublishOption not works") - } -} - -func TestSetOption(t *testing.T) { - type key struct{} - o := SetOption(key{}, "test") - opts := &Options{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetOption not works") - } -} diff --git a/broker/memory.go b/broker/memory.go index 8add9780..832097cd 100644 --- a/broker/memory.go +++ b/broker/memory.go @@ -1,20 +1,23 @@ -//go:build ignore - package broker import ( "context" + "fmt" "sync" "time" + "go.unistack.org/micro/v4/codec" "go.unistack.org/micro/v4/logger" + "go.unistack.org/micro/v4/metadata" + "go.unistack.org/micro/v4/options" + "go.unistack.org/micro/v4/semconv" maddr "go.unistack.org/micro/v4/util/addr" "go.unistack.org/micro/v4/util/id" mnet "go.unistack.org/micro/v4/util/net" "go.unistack.org/micro/v4/util/rand" ) -type memoryBroker struct { +type MemoryBroker struct { subscribers map[string][]*memorySubscriber addr string opts Options @@ -22,15 +25,15 @@ type memoryBroker struct { connected bool } -func (m *memoryBroker) Options() Options { +func (m *MemoryBroker) Options() Options { return m.opts } -func (m *memoryBroker) Address() string { +func (m *MemoryBroker) Address() string { return m.addr } -func (m *memoryBroker) Connect(ctx context.Context) error { +func (m *MemoryBroker) Connect(ctx context.Context) error { m.Lock() defer m.Unlock() @@ -54,30 +57,33 @@ func (m *memoryBroker) Connect(ctx context.Context) error { return nil } -func (m *memoryBroker) Disconnect(ctx context.Context) error { +func (m *MemoryBroker) Disconnect(ctx context.Context) error { m.Lock() defer m.Unlock() - if !m.connected { - return nil + select { + case <-ctx.Done(): + return ctx.Err() + default: + if m.connected { + m.connected = false + } } - m.connected = false return nil } -func (m *memoryBroker) Init(opts ...Option) error { +func (m *MemoryBroker) Init(opts ...options.Option) error { + var err error for _, o := range opts { - o(&m.opts) + if err = o(&m.opts); err != nil { + return err + } } return nil } -func (m *memoryBroker) NewMessage(endpoint string, req interface{}, opts ...MessageOption) Message { - return &memoryMessage{} -} - -func (m *memoryBroker) Publish(ctx context.Context, message interface{}, opts ...PublishOption) error { +func (m *MemoryBroker) Publish(ctx context.Context, message interface{}, opts ...options.Option) error { m.RLock() if !m.connected { m.RUnlock() @@ -92,30 +98,34 @@ func (m *memoryBroker) Publish(ctx context.Context, message interface{}, opts .. return ctx.Err() default: options := NewPublishOptions(opts...) - var msgs []*memoryMessage + var msgs []Message switch v := message.(type) { - case *memoryMessage: - msgs = []*memoryMessage{v} - case []*memoryMessage: + case []Message: msgs = v + case Message: + msgs = append(msgs, v) default: return ErrInvalidMessage } msgTopicMap := make(map[string][]*memoryMessage) for _, msg := range msgs { p := &memoryMessage{opts: options} - /* - if mb, ok := msg.Body().(*codec.Frame); ok { - p.message = v.Body - } else { - p.topic, _ = v.Header.Get(metadata.HeaderTopic) - p.message, err = m.opts.Codec.Marshal(v) - if err != nil { - return err - } + p.topic, _ = msg.Header().Get(metadata.HeaderTopic) + if v, ok := msg.Body().(*codec.Frame); ok { + p.body = msg.Body() + } else if len(m.opts.Codecs) == 0 { + p.body = msg.Body() + } else { + cf, ok := m.opts.Codecs[options.ContentType] + if !ok { + return fmt.Errorf("%s: %s", codec.ErrUnknownContentType, options.ContentType) } - */ - msgTopicMap[msg.Topic()] = append(msgTopicMap[p.topic], p) + p.body, err = cf.Marshal(v) + if err != nil { + return err + } + } + msgTopicMap[p.topic] = append(msgTopicMap[p.topic], p) } eh := m.opts.ErrorHandler @@ -123,55 +133,83 @@ func (m *memoryBroker) Publish(ctx context.Context, message interface{}, opts .. for t, ms := range msgTopicMap { ts := time.Now() - m.opts.Meter.Counter(PublishMessageInflight, "endpoint", t).Add(len(ms)) - m.opts.Meter.Counter(SubscribeMessageInflight, "endpoint", t).Add(len(ms)) + m.opts.Meter.Counter(semconv.PublishMessageInflight, "endpoint", t).Add(len(ms)) + m.opts.Meter.Counter(semconv.SubscribeMessageInflight, "endpoint", t).Add(len(ms)) m.RLock() subs, ok := m.subscribers[t] m.RUnlock() if !ok { - m.opts.Meter.Counter(PublishMessageTotal, "endpoint", t, "status", "failure").Add(len(ms)) - m.opts.Meter.Counter(PublishMessageInflight, "endpoint", t).Add(-len(ms)) - m.opts.Meter.Counter(SubscribeMessageInflight, "endpoint", t).Add(-len(ms)) + m.opts.Meter.Counter(semconv.PublishMessageTotal, "endpoint", t, "status", "failure").Add(len(ms)) + m.opts.Meter.Counter(semconv.PublishMessageInflight, "endpoint", t).Add(-len(ms)) + m.opts.Meter.Counter(semconv.SubscribeMessageInflight, "endpoint", t).Add(-len(ms)) continue } - m.opts.Meter.Counter(PublishMessageTotal, "endpoint", t, "status", "success").Add(len(ms)) + m.opts.Meter.Counter(semconv.PublishMessageTotal, "endpoint", t, "status", "success").Add(len(ms)) for _, sub := range subs { if sub.opts.ErrorHandler != nil { eh = sub.opts.ErrorHandler } - for _, p := range ms { - if err = sub.handler(p); err != nil { - m.opts.Meter.Counter(SubscribeMessageTotal, "endpoint", t, "status", "failure").Inc() + switch mh := sub.handler.(type) { + case MessagesHandler: + mhs := make([]Message, 0, len(ms)) + for _, m := range ms { + mhs = append(mhs, m) + } + if err = mh(mhs); err != nil { + m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "failure").Add(len(ms)) if eh != nil { - _ = eh(p) + switch meh := eh.(type) { + case MessagesHandler: + _ = meh(mhs) + case MessageHandler: + for _, me := range mhs { + _ = meh(me) + } + } } else if m.opts.Logger.V(logger.ErrorLevel) { m.opts.Logger.Error(m.opts.Context, err.Error()) } - } else { - if sub.opts.AutoAck { - if err = p.Ack(); err != nil { - m.opts.Logger.Errorf(m.opts.Context, "ack failed: %v", err) - m.opts.Meter.Counter(SubscribeMessageTotal, "endpoint", t, "status", "failure").Inc() - } else { - m.opts.Meter.Counter(SubscribeMessageTotal, "endpoint", t, "status", "success").Inc() + } + case MessageHandler: + for _, p := range ms { + if err = mh(p); err != nil { + m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "failure").Inc() + if eh != nil { + switch meh := eh.(type) { + case MessageHandler: + _ = meh(p) + case MessagesHandler: + _ = meh([]Message{p}) + } + } else if m.opts.Logger.V(logger.ErrorLevel) { + m.opts.Logger.Error(m.opts.Context, err.Error()) } } else { - m.opts.Meter.Counter(SubscribeMessageTotal, "endpoint", t, "status", "success").Inc() + if sub.opts.AutoAck { + if err = p.Ack(); err != nil { + m.opts.Logger.Errorf(m.opts.Context, "ack failed: %v", err) + m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "failure").Inc() + } else { + m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "success").Inc() + } + } else { + m.opts.Meter.Counter(semconv.SubscribeMessageTotal, "endpoint", t, "status", "success").Inc() + } } + m.opts.Meter.Counter(semconv.PublishMessageInflight, "endpoint", t).Add(-1) + m.opts.Meter.Counter(semconv.SubscribeMessageInflight, "endpoint", t).Add(-1) } - m.opts.Meter.Counter(PublishMessageInflight, "endpoint", t).Add(-1) - m.opts.Meter.Counter(SubscribeMessageInflight, "endpoint", t).Add(-1) } - } + te := time.Since(ts) - m.opts.Meter.Summary(PublishMessageLatencyMicroseconds, "endpoint", t).Update(te.Seconds()) - m.opts.Meter.Histogram(PublishMessageDurationSeconds, "endpoint", t).Update(te.Seconds()) - m.opts.Meter.Summary(SubscribeMessageLatencyMicroseconds, "endpoint", t).Update(te.Seconds()) - m.opts.Meter.Histogram(SubscribeMessageDurationSeconds, "endpoint", t).Update(te.Seconds()) + m.opts.Meter.Summary(semconv.PublishMessageLatencyMicroseconds, "endpoint", t).Update(te.Seconds()) + m.opts.Meter.Histogram(semconv.PublishMessageDurationSeconds, "endpoint", t).Update(te.Seconds()) + m.opts.Meter.Summary(semconv.SubscribeMessageLatencyMicroseconds, "endpoint", t).Update(te.Seconds()) + m.opts.Meter.Histogram(semconv.SubscribeMessageDurationSeconds, "endpoint", t).Update(te.Seconds()) } } @@ -179,7 +217,7 @@ func (m *memoryBroker) Publish(ctx context.Context, message interface{}, opts .. return nil } -func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler interface{}, opts ...SubscribeOption) (Subscriber, error) { +func (m *MemoryBroker) Subscribe(ctx context.Context, topic string, handler interface{}, opts ...options.Option) (Subscriber, error) { m.RLock() if !m.connected { m.RUnlock() @@ -224,26 +262,31 @@ func (m *memoryBroker) Subscribe(ctx context.Context, topic string, handler inte return sub, nil } -func (m *memoryBroker) String() string { +func (m *MemoryBroker) String() string { return "memory" } -func (m *memoryBroker) Name() string { +func (m *MemoryBroker) Name() string { return m.opts.Name } type memoryMessage struct { - err error - body interface{} - topic string - opts PublishOptions - ctx context.Context + err error + body interface{} + topic string + header metadata.Metadata + opts PublishOptions + ctx context.Context } func (m *memoryMessage) Topic() string { return m.topic } +func (m *memoryMessage) Header() metadata.Metadata { + return m.header +} + func (m *memoryMessage) Body() interface{} { return m.body } @@ -283,8 +326,8 @@ func (m *memorySubscriber) Unsubscribe(ctx context.Context) error { } // NewBroker return new memory broker -func NewBroker(opts ...Option) *memoryBroker { - return &memoryBroker{ +func NewBroker(opts ...options.Option) *MemoryBroker { + return &MemoryBroker{ opts: NewOptions(opts...), subscribers: make(map[string][]*memorySubscriber), } diff --git a/broker/memory_test.go b/broker/memory_test.go index 575ec26e..d8bc0a9c 100644 --- a/broker/memory_test.go +++ b/broker/memory_test.go @@ -19,29 +19,35 @@ func TestMemoryBatchBroker(t *testing.T) { topic := "test" count := 10 - fn := func(evts Events) error { - return evts.Ack() + fn := func(evts []Message) error { + var err error + for _, evt := range evts { + if err = evt.Ack(); err != nil { + return err + } + } + return nil } - sub, err := b.BatchSubscribe(ctx, topic, fn) + sub, err := b.Subscribe(ctx, topic, fn) if err != nil { t.Fatalf("Unexpected error subscribing %v", err) } - msgs := make([]*Message, 0, count) + msgs := make([]Message, 0, count) for i := 0; i < count; i++ { - message := &Message{ - Header: map[string]string{ + message := &memoryMessage{ + header: map[string]string{ metadata.HeaderTopic: topic, "foo": "bar", "id": fmt.Sprintf("%d", i), }, - Body: []byte(`"hello world"`), + body: []byte(`"hello world"`), } msgs = append(msgs, message) } - if err := b.BatchPublish(ctx, msgs); err != nil { + if err := b.Publish(ctx, msgs); err != nil { t.Fatalf("Unexpected error publishing %v", err) } @@ -65,8 +71,8 @@ func TestMemoryBroker(t *testing.T) { topic := "test" count := 10 - fn := func(p Event) error { - return nil + fn := func(p Message) error { + return p.Ack() } sub, err := b.Subscribe(ctx, topic, fn) @@ -74,24 +80,20 @@ func TestMemoryBroker(t *testing.T) { t.Fatalf("Unexpected error subscribing %v", err) } - msgs := make([]*Message, 0, count) + msgs := make([]Message, 0, count) for i := 0; i < count; i++ { - message := &Message{ - Header: map[string]string{ + message := &memoryMessage{ + header: map[string]string{ metadata.HeaderTopic: topic, "foo": "bar", "id": fmt.Sprintf("%d", i), }, - Body: []byte(`"hello world"`), + body: []byte(`"hello world"`), } msgs = append(msgs, message) - - if err := b.Publish(ctx, topic, message); err != nil { - t.Fatalf("Unexpected error publishing %d err: %v", i, err) - } } - if err := b.BatchPublish(ctx, msgs); err != nil { + if err := b.Publish(ctx, msgs); err != nil { t.Fatalf("Unexpected error publishing %v", err) } diff --git a/broker/options.go b/broker/options.go index 07234d9e..baa72ee9 100644 --- a/broker/options.go +++ b/broker/options.go @@ -9,29 +9,11 @@ import ( "go.unistack.org/micro/v4/logger" "go.unistack.org/micro/v4/metadata" "go.unistack.org/micro/v4/meter" + "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/register" "go.unistack.org/micro/v4/tracer" ) -var ( - // PublishMessageDurationSeconds specifies meter metric name - PublishMessageDurationSeconds = "publish_message_duration_seconds" - // PublishMessageLatencyMicroseconds specifies meter metric name - PublishMessageLatencyMicroseconds = "publish_message_latency_microseconds" - // PublishMessageTotal specifies meter metric name - PublishMessageTotal = "publish_message_total" - // PublishMessageInflight specifies meter metric name - PublishMessageInflight = "publish_message_inflight" - // SubscribeMessageDurationSeconds specifies meter metric name - SubscribeMessageDurationSeconds = "subscribe_message_duration_seconds" - // SubscribeMessageLatencyMicroseconds specifies meter metric name - SubscribeMessageLatencyMicroseconds = "subscribe_message_latency_microseconds" - // SubscribeMessageTotal specifies meter metric name - SubscribeMessageTotal = "subscribe_message_total" - // SubscribeMessageInflight specifies meter metric name - SubscribeMessageInflight = "subscribe_message_inflight" -) - // Options struct type Options struct { // Tracer used for tracing @@ -48,19 +30,16 @@ type Options struct { Context context.Context // TLSConfig holds tls.TLSConfig options TLSConfig *tls.Config - // ErrorHandler used when broker can't unmarshal incoming message - ErrorHandler func(Message) + // ErrorHandler used when broker have error while processing message + ErrorHandler interface{} // Name holds the broker name Name string - // Addrs holds the broker address - Addrs []string + // Address holds the broker address + Address []string } -// Option func -type Option func(*Options) - // NewOptions create new Options -func NewOptions(opts ...Option) Options { +func NewOptions(opts ...options.Option) Options { options := Options{ Register: register.DefaultRegister, Logger: logger.DefaultLogger, @@ -75,49 +54,22 @@ func NewOptions(opts ...Option) Options { return options } -// Context sets the context option -func Context(ctx context.Context) Option { - return func(o *Options) { - o.Context = ctx - } -} - -// MessageOption func -type MessageOption func(*MessageOptions) - -// MessageOptions struct -type MessageOptions struct { - Metadata metadata.Metadata - ContentType string -} - -// MessageMetadata pass additional message metadata -func MessageMetadata(md metadata.Metadata) MessageOption { - return func(o *MessageOptions) { - o.Metadata = md - } -} - -// MessageContentType pass ContentType for message data -func MessageContentType(ct string) MessageOption { - return func(o *MessageOptions) { - o.ContentType = ct - } -} - -// PublishOption func -type PublishOption func(*PublishOptions) - // PublishOptions struct type PublishOptions struct { // Context holds external options Context context.Context // BodyOnly flag says the message contains raw body bytes BodyOnly bool + // Message metadata usually passed as message headers + Metadata metadata.Metadata + // Content-Type of message for marshal + ContentType string + // Topic destination + Topic string } // NewPublishOptions creates PublishOptions struct -func NewPublishOptions(opts ...PublishOption) PublishOptions { +func NewPublishOptions(opts ...options.Option) PublishOptions { options := PublishOptions{ Context: context.Background(), } @@ -127,12 +79,19 @@ func NewPublishOptions(opts ...PublishOption) PublishOptions { return options } +// PublishTopic pass topic for messages +func PublishTopic(t string) options.Option { + return func(src interface{}) error { + return options.Set(src, t, ".Topic") + } +} + // SubscribeOptions struct type SubscribeOptions struct { // Context holds external options Context context.Context - // ErrorHandler used when broker can't unmarshal incoming message - ErrorHandler func(Message) + // ErrorHandler used when broker have error while processing message + ErrorHandler interface{} // QueueGroup holds consumer group QueueGroup string // AutoAck flag specifies auto ack of incoming message when no error happens @@ -145,99 +104,16 @@ type SubscribeOptions struct { BatchWait time.Duration } -// PublishBodyOnly publish only body of the message -func PublishBodyOnly(b bool) PublishOption { - return func(o *PublishOptions) { - o.BodyOnly = b - } -} - -// PublishContext sets the context -func PublishContext(ctx context.Context) PublishOption { - return func(o *PublishOptions) { - o.Context = ctx - } -} - -// Addrs sets the host addresses to be used by the broker -func Addrs(addrs ...string) Option { - return func(o *Options) { - o.Addrs = addrs - } -} - -// Codec sets the codec used for encoding/decoding used where -// a broker does not support headers -// Codec to be used to encode/decode requests for a given content type -func Codec(contentType string, c codec.Codec) Option { - return func(o *Options) { - o.Codecs[contentType] = c - } -} - // ErrorHandler will catch all broker errors that cant be handled // in normal way, for example Codec errors -func ErrorHandler(h func(Message)) Option { - return func(o *Options) { - o.ErrorHandler = h +func ErrorHandler(h interface{}) options.Option { + return func(src interface{}) error { + return options.Set(src, h, ".ErrorHandler") } } -// SubscribeErrorHandler will catch all broker errors that cant be handled -// in normal way, for example Codec errors -func SubscribeErrorHandler(h func(Message)) SubscribeOption { - return func(o *SubscribeOptions) { - o.ErrorHandler = h - } -} - -// Register sets register option -func Register(r register.Register) Option { - return func(o *Options) { - o.Register = r - } -} - -// TLSConfig sets the TLS Config -func TLSConfig(t *tls.Config) Option { - return func(o *Options) { - o.TLSConfig = t - } -} - -// Logger sets the logger -func Logger(l logger.Logger) Option { - return func(o *Options) { - o.Logger = l - } -} - -// Tracer to be used for tracing -func Tracer(t tracer.Tracer) Option { - return func(o *Options) { - o.Tracer = t - } -} - -// Meter sets the meter -func Meter(m meter.Meter) Option { - return func(o *Options) { - o.Meter = m - } -} - -// Name sets the name -func Name(n string) Option { - return func(o *Options) { - o.Name = n - } -} - -// SubscribeOption func signature -type SubscribeOption func(*SubscribeOptions) - // NewSubscribeOptions creates new SubscribeOptions -func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions { +func NewSubscribeOptions(opts ...options.Option) SubscribeOptions { options := SubscribeOptions{ AutoAck: true, Context: context.Background(), @@ -248,52 +124,38 @@ func NewSubscribeOptions(opts ...SubscribeOption) SubscribeOptions { return options } -// SubscribeContext set context -func SubscribeContext(ctx context.Context) SubscribeOption { - return func(o *SubscribeOptions) { - o.Context = ctx - } -} - // SubscribeAutoAck contol auto acking of messages // after they have been handled. -func SubscribeAutoAck(b bool) SubscribeOption { - return func(o *SubscribeOptions) { - o.AutoAck = b +func SubscribeAutoAck(b bool) options.Option { + return func(src interface{}) error { + return options.Set(src, b, ".AutoAck") } } -// SubscribeBodyOnly consumes only body of the message -func SubscribeBodyOnly(b bool) SubscribeOption { - return func(o *SubscribeOptions) { - o.BodyOnly = b +// BodyOnly transfer only body without +func BodyOnly(b bool) options.Option { + return func(src interface{}) error { + return options.Set(src, b, ".BodyOnly") } } // SubscribeBatchSize specifies max batch size -func SubscribeBatchSize(n int) SubscribeOption { - return func(o *SubscribeOptions) { - o.BatchSize = n +func SubscribeBatchSize(n int) options.Option { + return func(src interface{}) error { + return options.Set(src, n, ".BatchSize") } } // SubscribeBatchWait specifies max batch wait time -func SubscribeBatchWait(td time.Duration) SubscribeOption { - return func(o *SubscribeOptions) { - o.BatchWait = td +func SubscribeBatchWait(td time.Duration) options.Option { + return func(src interface{}) error { + return options.Set(src, td, ".BatchWait") } } // SubscribeQueueGroup sets the shared queue name distributed messages across subscribers -func SubscribeQueueGroup(n string) SubscribeOption { - return func(o *SubscribeOptions) { - o.QueueGroup = n - } -} - -// SubscribeAutoAck control auto ack processing for handler -func SubscribeAuthAck(b bool) SubscribeOption { - return func(o *SubscribeOptions) { - o.AutoAck = b +func SubscribeQueueGroup(n string) options.Option { + return func(src interface{}) error { + return options.Set(src, n, ".QueueGroup") } } diff --git a/client/client.go b/client/client.go index d52e0bec..9ecdb014 100644 --- a/client/client.go +++ b/client/client.go @@ -6,6 +6,7 @@ import ( "time" "go.unistack.org/micro/v4/codec" + "go.unistack.org/micro/v4/options" ) var ( @@ -21,6 +22,8 @@ var ( DefaultRetries = 0 // DefaultRequestTimeout is the default request timeout DefaultRequestTimeout = time.Second * 5 + // DefaultDialTimeout the default dial timeout + DefaultDialTimeout = time.Second * 5 // DefaultPoolSize sets the connection pool size DefaultPoolSize = 100 // DefaultPoolTTL sets the connection pool ttl @@ -32,11 +35,11 @@ var ( // It also supports bidirectional streaming of requests. type Client interface { Name() string - Init(opts ...Option) error + Init(opts ...options.Option) error Options() Options - NewRequest(service string, endpoint string, req interface{}, opts ...RequestOption) Request - Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error - Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) + NewRequest(service string, endpoint string, req interface{}, opts ...options.Option) Request + Call(ctx context.Context, req Request, rsp interface{}, opts ...options.Option) error + Stream(ctx context.Context, req Request, opts ...options.Option) (Stream, error) String() string } @@ -97,18 +100,3 @@ type Stream interface { // CloseSend closes the send direction of the stream CloseSend() error } - -// Option used by the Client -type Option func(*Options) - -// CallOption used by Call or Stream -type CallOption func(*CallOptions) - -// PublishOption used by Publish -type PublishOption func(*PublishOptions) - -// MessageOption used by NewMessage -type MessageOption func(*MessageOptions) - -// RequestOption used by NewRequest -type RequestOption func(*RequestOptions) diff --git a/client/client_call_options.go b/client/client_call_options.go index 903642a5..e631740d 100644 --- a/client/client_call_options.go +++ b/client/client_call_options.go @@ -2,22 +2,24 @@ package client import ( "context" + + "go.unistack.org/micro/v4/options" ) type clientCallOptions struct { Client - opts []CallOption + opts []options.Option } -func (s *clientCallOptions) Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error { +func (s *clientCallOptions) Call(ctx context.Context, req Request, rsp interface{}, opts ...options.Option) error { return s.Client.Call(ctx, req, rsp, append(s.opts, opts...)...) } -func (s *clientCallOptions) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) { +func (s *clientCallOptions) Stream(ctx context.Context, req Request, opts ...options.Option) (Stream, error) { return s.Client.Stream(ctx, req, append(s.opts, opts...)...) } // NewClientCallOptions add CallOption to every call -func NewClientCallOptions(c Client, opts ...CallOption) Client { +func NewClientCallOptions(c Client, opts ...options.Option) Client { return &clientCallOptions{c, opts} } diff --git a/client/client_call_options_test.go b/client/client_call_options_test.go index 2cffdf36..533ac424 100644 --- a/client/client_call_options_test.go +++ b/client/client_call_options_test.go @@ -4,6 +4,8 @@ import ( "context" "testing" "time" + + "go.unistack.org/micro/v4/options" ) func TestNewClientCallOptions(t *testing.T) { @@ -13,11 +15,11 @@ func TestNewClientCallOptions(t *testing.T) { return fn } c := NewClientCallOptions(NewClient(), - WithAddress("127.0.0.1"), + options.Address("127.0.0.1"), WithCallWrapper(w), - WithRequestTimeout(1*time.Millisecond), - WithRetries(0), - WithBackoff(BackoffInterval(10*time.Millisecond, 100*time.Millisecond)), + RequestTimeout(1*time.Millisecond), + Retries(0), + Backoff(BackoffInterval(10*time.Millisecond, 100*time.Millisecond)), ) _ = c.Call(context.TODO(), c.NewRequest("service", "endpoint", nil), nil) if !flag { diff --git a/client/context.go b/client/context.go index f4da4464..d5a63b00 100644 --- a/client/context.go +++ b/client/context.go @@ -22,33 +22,3 @@ func NewContext(ctx context.Context, c Client) context.Context { } return context.WithValue(ctx, clientKey{}, c) } - -// SetPublishOption returns a function to setup a context with given value -func SetPublishOption(k, v interface{}) PublishOption { - return func(o *PublishOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} - -// SetCallOption returns a function to setup a context with given value -func SetCallOption(k, v interface{}) CallOption { - return func(o *CallOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} - -// SetOption returns a function to setup a context with given value -func SetOption(k, v interface{}) Option { - return func(o *Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} diff --git a/client/context_test.go b/client/context_test.go index e562529e..0cfb461a 100644 --- a/client/context_test.go +++ b/client/context_test.go @@ -38,36 +38,3 @@ func TestNewNilContext(t *testing.T) { t.Fatal("NewContext not works") } } - -func TestSetPublishOption(t *testing.T) { - type key struct{} - o := SetPublishOption(key{}, "test") - opts := &PublishOptions{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetPublishOption not works") - } -} - -func TestSetCallOption(t *testing.T) { - type key struct{} - o := SetCallOption(key{}, "test") - opts := &CallOptions{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetCallOption not works") - } -} - -func TestSetOption(t *testing.T) { - type key struct{} - o := SetOption(key{}, "test") - opts := &Options{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetOption not works") - } -} diff --git a/client/noop.go b/client/noop.go index 83393a71..b964f4bb 100644 --- a/client/noop.go +++ b/client/noop.go @@ -8,7 +8,9 @@ import ( "go.unistack.org/micro/v4/codec" "go.unistack.org/micro/v4/errors" "go.unistack.org/micro/v4/metadata" + "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/selector" + "go.unistack.org/micro/v4/semconv" ) // DefaultCodecs will be used to encode/decode data @@ -20,12 +22,6 @@ type noopClient struct { opts Options } -type noopMessage struct { - topic string - payload interface{} - opts MessageOptions -} - type noopRequest struct { body interface{} codec codec.Codec @@ -37,16 +33,12 @@ type noopRequest struct { } // NewClient returns new noop client -func NewClient(opts ...Option) Client { +func NewClient(opts ...options.Option) Client { nc := &noopClient{opts: NewOptions(opts...)} // wrap in reverse c := Client(nc) - for i := len(nc.opts.Wrappers); i > 0; i-- { - c = nc.opts.Wrappers[i-1](c) - } - return c } @@ -141,22 +133,6 @@ func (n *noopStream) CloseSend() error { return nil } -func (n *noopMessage) Topic() string { - return n.topic -} - -func (n *noopMessage) Payload() interface{} { - return n.payload -} - -func (n *noopMessage) ContentType() string { - return n.opts.ContentType -} - -func (n *noopMessage) Metadata() metadata.Metadata { - return n.opts.Metadata -} - func (n *noopClient) newCodec(contentType string) (codec.Codec, error) { if cf, ok := n.opts.Codecs[contentType]; ok { return cf, nil @@ -167,7 +143,7 @@ func (n *noopClient) newCodec(contentType string) (codec.Codec, error) { return nil, codec.ErrUnknownContentType } -func (n *noopClient) Init(opts ...Option) error { +func (n *noopClient) Init(opts ...options.Option) error { for _, o := range opts { o(&n.opts) } @@ -182,7 +158,7 @@ func (n *noopClient) String() string { return "noop" } -func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error { +func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opts ...options.Option) error { // make a copy of call opts callOpts := n.opts.CallOptions for _, opt := range opts { @@ -199,7 +175,7 @@ func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opt } else { // got a deadline so no need to setup context // but we need to set the timeout we pass along - opt := WithRequestTimeout(time.Until(d)) + opt := RequestTimeout(time.Until(d)) opt(&callOpts) } @@ -286,7 +262,7 @@ func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opt ts := time.Now() endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint()) - n.opts.Meter.Counter(ClientRequestInflight, "endpoint", endpoint).Inc() + n.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", endpoint).Inc() for i := 0; i <= callOpts.Retries; i++ { go func() { ch <- call(i) @@ -315,14 +291,14 @@ func (n *noopClient) Call(ctx context.Context, req Request, rsp interface{}, opt } if gerr != nil { - n.opts.Meter.Counter(ClientRequestTotal, "endpoint", endpoint, "status", "failure").Inc() + n.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", endpoint, "status", "failure").Inc() } else { - n.opts.Meter.Counter(ClientRequestTotal, "endpoint", endpoint, "status", "success").Inc() + n.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", endpoint, "status", "success").Inc() } - n.opts.Meter.Counter(ClientRequestInflight, "endpoint", endpoint).Dec() + n.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", endpoint).Dec() te := time.Since(ts) - n.opts.Meter.Summary(ClientRequestLatencyMicroseconds, "endpoint", endpoint).Update(te.Seconds()) - n.opts.Meter.Histogram(ClientRequestDurationSeconds, "endpoint", endpoint).Update(te.Seconds()) + n.opts.Meter.Summary(semconv.ClientRequestLatencyMicroseconds, "endpoint", endpoint).Update(te.Seconds()) + n.opts.Meter.Histogram(semconv.ClientRequestDurationSeconds, "endpoint", endpoint).Update(te.Seconds()) return gerr } @@ -331,11 +307,11 @@ func (n *noopClient) call(ctx context.Context, addr string, req Request, rsp int return nil } -func (n *noopClient) NewRequest(service, endpoint string, req interface{}, opts ...RequestOption) Request { +func (n *noopClient) NewRequest(service, endpoint string, req interface{}, opts ...options.Option) Request { return &noopRequest{service: service, endpoint: endpoint} } -func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) { +func (n *noopClient) Stream(ctx context.Context, req Request, opts ...options.Option) (Stream, error) { var err error // make a copy of call opts @@ -354,7 +330,7 @@ func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption } else { // got a deadline so no need to setup context // but we need to set the timeout we pass along - o := WithStreamTimeout(time.Until(d)) + o := StreamTimeout(time.Until(d)) o(&callOpts) } @@ -423,12 +399,12 @@ func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption // ts := time.Now() endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint()) - n.opts.Meter.Counter(ClientRequestInflight, "endpoint", endpoint).Inc() + n.opts.Meter.Counter(semconv.ClientRequestInflight, "endpoint", endpoint).Inc() stream, cerr := n.stream(ctx, node, req, callOpts) if cerr != nil { - n.opts.Meter.Counter(ClientRequestTotal, "endpoint", endpoint, "status", "failure").Inc() + n.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", endpoint, "status", "failure").Inc() } else { - n.opts.Meter.Counter(ClientRequestTotal, "endpoint", endpoint, "status", "success").Inc() + n.opts.Meter.Counter(semconv.ClientRequestTotal, "endpoint", endpoint, "status", "success").Inc() } // record the result of the call to inform future routing decisions diff --git a/client/options.go b/client/options.go index 82994ec7..e831d084 100644 --- a/client/options.go +++ b/client/options.go @@ -10,29 +10,15 @@ import ( "go.unistack.org/micro/v4/logger" "go.unistack.org/micro/v4/metadata" "go.unistack.org/micro/v4/meter" - "go.unistack.org/micro/v4/network/transport" - "go.unistack.org/micro/v4/register" + "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/router" "go.unistack.org/micro/v4/selector" "go.unistack.org/micro/v4/selector/random" "go.unistack.org/micro/v4/tracer" ) -var ( - // ClientRequestDurationSeconds specifies meter metric name - ClientRequestDurationSeconds = "client_request_duration_seconds" - // ClientRequestLatencyMicroseconds specifies meter metric name - ClientRequestLatencyMicroseconds = "client_request_latency_microseconds" - // ClientRequestTotal specifies meter metric name - ClientRequestTotal = "client_request_total" - // ClientRequestInflight specifies meter metric name - ClientRequestInflight = "client_request_inflight" -) - // Options holds client options type Options struct { - // Transport used for transfer messages - Transport transport.Transport // Selector used to select needed address Selector selector.Selector // Logger used to log messages @@ -57,8 +43,6 @@ type Options struct { ContentType string // Name is the client name Name string - // Wrappers contains wrappers - Wrappers []Wrapper // CallOptions contains default CallOptions CallOptions CallOptions // PoolSize connection pool size @@ -67,10 +51,12 @@ type Options struct { PoolTTL time.Duration // ContextDialer used to connect ContextDialer func(context.Context, string) (net.Conn, error) + // Hooks may contains Client func wrapper + Hooks options.Hooks } // NewCallOptions creates new call options struct -func NewCallOptions(opts ...CallOption) CallOptions { +func NewCallOptions(opts ...options.Option) CallOptions { options := CallOptions{} for _, o := range opts { o(&options) @@ -119,58 +105,14 @@ type CallOptions struct { } // ContextDialer pass ContextDialer to client -func ContextDialer(fn func(context.Context, string) (net.Conn, error)) Option { - return func(o *Options) { - o.ContextDialer = fn +func ContextDialer(fn func(context.Context, string) (net.Conn, error)) options.Option { + return func(src interface{}) error { + return options.Set(src, fn, ".ContextDialer") } } -// Context pass context to client -func Context(ctx context.Context) Option { - return func(o *Options) { - o.Context = ctx - } -} - -// NewPublishOptions create new PublishOptions struct from option -func NewPublishOptions(opts ...PublishOption) PublishOptions { - options := PublishOptions{} - for _, o := range opts { - o(&options) - } - return options -} - -// PublishOptions holds publish options -type PublishOptions struct { - // Context used for external options - Context context.Context - // Exchange topic exchange name - Exchange string - // BodyOnly will publish only message body - BodyOnly bool -} - -// NewMessageOptions creates message options struct -func NewMessageOptions(opts ...MessageOption) MessageOptions { - options := MessageOptions{Metadata: metadata.New(1)} - for _, o := range opts { - o(&options) - } - return options -} - -// MessageOptions holds client message options -type MessageOptions struct { - // Metadata additional metadata - Metadata metadata.Metadata - // ContentType specify content-type of message - // deprecated - ContentType string -} - // NewRequestOptions creates new RequestOptions struct -func NewRequestOptions(opts ...RequestOption) RequestOptions { +func NewRequestOptions(opts ...options.Option) RequestOptions { options := RequestOptions{} for _, o := range opts { o(&options) @@ -189,7 +131,7 @@ type RequestOptions struct { } // NewOptions creates new options struct -func NewOptions(opts ...Option) Options { +func NewOptions(opts ...options.Option) Options { options := Options{ Context: context.Background(), ContentType: DefaultContentType, @@ -200,17 +142,16 @@ func NewOptions(opts ...Option) Options { Retry: DefaultRetry, Retries: DefaultRetries, RequestTimeout: DefaultRequestTimeout, - DialTimeout: transport.DefaultDialTimeout, + DialTimeout: DefaultDialTimeout, }, - Lookup: LookupRoute, - PoolSize: DefaultPoolSize, - PoolTTL: DefaultPoolTTL, - Selector: random.NewSelector(), - Logger: logger.DefaultLogger, - Meter: meter.DefaultMeter, - Tracer: tracer.DefaultTracer, - Router: router.DefaultRouter, - Transport: transport.DefaultTransport, + Lookup: LookupRoute, + PoolSize: DefaultPoolSize, + PoolTTL: DefaultPoolTTL, + Selector: random.NewSelector(), + Logger: logger.DefaultLogger, + Meter: meter.DefaultMeter, + Tracer: tracer.DefaultTracer, + Router: router.DefaultRouter, } for _, o := range opts { @@ -220,374 +161,131 @@ func NewOptions(opts ...Option) Options { return options } -// Tracer to be used for tracing -func Tracer(t tracer.Tracer) Option { - return func(o *Options) { - o.Tracer = t - } -} - -// Logger to be used for log mesages -func Logger(l logger.Logger) Option { - return func(o *Options) { - o.Logger = l - } -} - -// Meter to be used for metrics -func Meter(m meter.Meter) Option { - return func(o *Options) { - o.Meter = m - } -} - -// Codec to be used to encode/decode requests for a given content type -func Codec(contentType string, c codec.Codec) Option { - return func(o *Options) { - o.Codecs[contentType] = c - } -} - -// ContentType used by default if not specified -func ContentType(ct string) Option { - return func(o *Options) { - o.ContentType = ct - } -} - // Proxy sets the proxy address -func Proxy(addr string) Option { - return func(o *Options) { - o.Proxy = addr +func Proxy(addr string) options.Option { + return func(src interface{}) error { + return options.Set(src, addr, ".Proxy") } } // PoolSize sets the connection pool size -func PoolSize(d int) Option { - return func(o *Options) { - o.PoolSize = d +func PoolSize(d int) options.Option { + return func(src interface{}) error { + return options.Set(src, d, ".PoolSize") } } // PoolTTL sets the connection pool ttl -func PoolTTL(d time.Duration) Option { - return func(o *Options) { - o.PoolTTL = d - } -} - -// Transport to use for communication e.g http, rabbitmq, etc -func Transport(t transport.Transport) Option { - return func(o *Options) { - o.Transport = t - } -} - -// Register sets the routers register -func Register(r register.Register) Option { - return func(o *Options) { - if o.Router != nil { - _ = o.Router.Init(router.Register(r)) - } - } -} - -// Router is used to lookup routes for a service -func Router(r router.Router) Option { - return func(o *Options) { - o.Router = r +func PoolTTL(td time.Duration) options.Option { + return func(src interface{}) error { + return options.Set(src, td, ".PoolTTL") } } // Selector is used to select a route -func Selector(s selector.Selector) Option { - return func(o *Options) { - o.Selector = s - } -} - -// Wrap adds a wrapper to the list of options passed into the client -func Wrap(w Wrapper) Option { - return func(o *Options) { - o.Wrappers = append(o.Wrappers, w) - } -} - -// WrapCall adds a wrapper to the list of CallFunc wrappers -func WrapCall(cw ...CallWrapper) Option { - return func(o *Options) { - o.CallOptions.CallWrappers = append(o.CallOptions.CallWrappers, cw...) +func Selector(s selector.Selector) options.Option { + return func(src interface{}) error { + return options.Set(src, s, ".Selector") } } // Backoff is used to set the backoff function used when retrying Calls -func Backoff(fn BackoffFunc) Option { - return func(o *Options) { - o.CallOptions.Backoff = fn - } -} - -// Name sets the client name -func Name(n string) Option { - return func(o *Options) { - o.Name = n +func Backoff(fn BackoffFunc) options.Option { + return func(src interface{}) error { + return options.Set(src, fn, ".Backoff") } } // Lookup sets the lookup function to use for resolving service names -func Lookup(l LookupFunc) Option { - return func(o *Options) { - o.Lookup = l +func Lookup(fn LookupFunc) options.Option { + return func(src interface{}) error { + return options.Set(src, fn, ".Lookup") } } -// TLSConfig specifies a *tls.Config -func TLSConfig(t *tls.Config) Option { - return func(o *Options) { - // set the internal tls - o.TLSConfig = t - - // set the default transport if one is not - // already set. Required for Init call below. - - // set the transport tls - _ = o.Transport.Init( - transport.TLSConfig(t), - ) +// WithCallWrapper sets the retry function to be used when re-trying. +func WithCallWrapper(fn CallWrapper) options.Option { + return func(src interface{}) error { + return options.Set(src, fn, ".CallWrappers") } } // Retries sets the retry count when making the request. -func Retries(i int) Option { - return func(o *Options) { - o.CallOptions.Retries = i +func Retries(n int) options.Option { + return func(src interface{}) error { + return options.Set(src, n, ".Retries") } } // Retry sets the retry function to be used when re-trying. -func Retry(fn RetryFunc) Option { - return func(o *Options) { - o.CallOptions.Retry = fn +func Retry(fn RetryFunc) options.Option { + return func(src interface{}) error { + return options.Set(src, fn, ".Retry") } } // RequestTimeout is the request timeout. -func RequestTimeout(d time.Duration) Option { - return func(o *Options) { - o.CallOptions.RequestTimeout = d +func RequestTimeout(td time.Duration) options.Option { + return func(src interface{}) error { + return options.Set(src, td, ".RequestTimeout") } } // StreamTimeout sets the stream timeout -func StreamTimeout(d time.Duration) Option { - return func(o *Options) { - o.CallOptions.StreamTimeout = d +func StreamTimeout(td time.Duration) options.Option { + return func(src interface{}) error { + return options.Set(src, td, ".StreamTimeout") } } // DialTimeout sets the dial timeout -func DialTimeout(d time.Duration) Option { - return func(o *Options) { - o.CallOptions.DialTimeout = d - } -} - -// WithExchange sets the exchange to route a message through -// Deprecated -func WithExchange(e string) PublishOption { - return func(o *PublishOptions) { - o.Exchange = e - } -} - -// PublishExchange sets the exchange to route a message through -func PublishExchange(e string) PublishOption { - return func(o *PublishOptions) { - o.Exchange = e - } -} - -// WithBodyOnly publish only message body -// DERECATED -func WithBodyOnly(b bool) PublishOption { - return func(o *PublishOptions) { - o.BodyOnly = b - } -} - -// PublishBodyOnly publish only message body -func PublishBodyOnly(b bool) PublishOption { - return func(o *PublishOptions) { - o.BodyOnly = b - } -} - -// PublishContext sets the context in publish options -func PublishContext(ctx context.Context) PublishOption { - return func(o *PublishOptions) { - o.Context = ctx - } -} - -// WithContextDialer pass ContextDialer to client call -func WithContextDialer(fn func(context.Context, string) (net.Conn, error)) CallOption { - return func(o *CallOptions) { - o.ContextDialer = fn - } -} - -// WithContentType specifies call content type -func WithContentType(ct string) CallOption { - return func(o *CallOptions) { - o.ContentType = ct - } -} - -// WithAddress sets the remote addresses to use rather than using service discovery -func WithAddress(a ...string) CallOption { - return func(o *CallOptions) { - o.Address = a - } -} - -// WithCallWrapper is a CallOption which adds to the existing CallFunc wrappers -func WithCallWrapper(cw ...CallWrapper) CallOption { - return func(o *CallOptions) { - o.CallWrappers = append(o.CallWrappers, cw...) - } -} - -// WithBackoff is a CallOption which overrides that which -// set in Options.CallOptions -func WithBackoff(fn BackoffFunc) CallOption { - return func(o *CallOptions) { - o.Backoff = fn - } -} - -// WithRetry is a CallOption which overrides that which -// set in Options.CallOptions -func WithRetry(fn RetryFunc) CallOption { - return func(o *CallOptions) { - o.Retry = fn - } -} - -// WithRetries is a CallOption which overrides that which -// set in Options.CallOptions -func WithRetries(i int) CallOption { - return func(o *CallOptions) { - o.Retries = i +func DialTimeout(td time.Duration) options.Option { + return func(src interface{}) error { + return options.Set(src, td, ".DialTimeout") } } // WithResponseMetadata is a CallOption which adds metadata.Metadata to Options.CallOptions -func WithResponseMetadata(md *metadata.Metadata) CallOption { - return func(o *CallOptions) { - o.ResponseMetadata = md +func ResponseMetadata(md *metadata.Metadata) options.Option { + return func(src interface{}) error { + return options.Set(src, md, ".ResponseMetadata") } } // WithRequestMetadata is a CallOption which adds metadata.Metadata to Options.CallOptions -func WithRequestMetadata(md metadata.Metadata) CallOption { - return func(o *CallOptions) { - o.RequestMetadata = md +func RequestMetadata(md metadata.Metadata) options.Option { + return func(src interface{}) error { + return options.Set(src, metadata.Copy(md), ".RequestMetadata") } } -// WithRequestTimeout is a CallOption which overrides that which -// set in Options.CallOptions -func WithRequestTimeout(d time.Duration) CallOption { - return func(o *CallOptions) { - o.RequestTimeout = d - } -} - -// WithStreamTimeout sets the stream timeout -func WithStreamTimeout(d time.Duration) CallOption { - return func(o *CallOptions) { - o.StreamTimeout = d - } -} - -// WithDialTimeout is a CallOption which overrides that which -// set in Options.CallOptions -func WithDialTimeout(d time.Duration) CallOption { - return func(o *CallOptions) { - o.DialTimeout = d - } -} - -// WithAuthToken is a CallOption which overrides the +// AuthToken is a CallOption which overrides the // authorization header with the services own auth token -func WithAuthToken(t string) CallOption { - return func(o *CallOptions) { - o.AuthToken = t +func AuthToken(t string) options.Option { + return func(src interface{}) error { + return options.Set(src, t, ".AuthToken") } } -// WithNetwork is a CallOption which sets the network attribute -func WithNetwork(n string) CallOption { - return func(o *CallOptions) { - o.Network = n - } -} - -// WithRouter sets the router to use for this call -func WithRouter(r router.Router) CallOption { - return func(o *CallOptions) { - o.Router = r - } -} - -// WithSelector sets the selector to use for this call -func WithSelector(s selector.Selector) CallOption { - return func(o *CallOptions) { - o.Selector = s +// Network is a CallOption which sets the network attribute +func Network(n string) options.Option { + return func(src interface{}) error { + return options.Set(src, n, ".Network") } } +/* // WithSelectOptions sets the options to pass to the selector for this call -func WithSelectOptions(sops ...selector.SelectOption) CallOption { +func WithSelectOptions(sops ...selector.SelectOption) options.Option { return func(o *CallOptions) { o.SelectOptions = sops } } - -// WithMessageContentType sets the message content type -// Deprecated -func WithMessageContentType(ct string) MessageOption { - return func(o *MessageOptions) { - o.Metadata.Set(metadata.HeaderContentType, ct) - o.ContentType = ct - } -} - -// MessageContentType sets the message content type -func MessageContentType(ct string) MessageOption { - return func(o *MessageOptions) { - o.Metadata.Set(metadata.HeaderContentType, ct) - o.ContentType = ct - } -} - -// MessageMetadata sets the message metadata -func MessageMetadata(k, v string) MessageOption { - return func(o *MessageOptions) { - o.Metadata.Set(k, v) - } -} +*/ // StreamingRequest specifies that request is streaming -func StreamingRequest(b bool) RequestOption { - return func(o *RequestOptions) { - o.Stream = b - } -} - -// RequestContentType specifies request content type -func RequestContentType(ct string) RequestOption { - return func(o *RequestOptions) { - o.ContentType = ct +func StreamingRequest(b bool) options.Option { + return func(src interface{}) error { + return options.Set(src, b, ".Stream") } } diff --git a/config/config.go b/config/config.go index 743dcefb..1736d446 100644 --- a/config/config.go +++ b/config/config.go @@ -6,6 +6,8 @@ import ( "errors" "reflect" "time" + + "go.unistack.org/micro/v4/options" ) type Validator interface { @@ -37,15 +39,15 @@ type Config interface { // Name returns name of config Name() string // Init the config - Init(opts ...Option) error + Init(opts ...options.Option) error // Options in the config Options() Options // Load config from sources - Load(context.Context, ...LoadOption) error + Load(context.Context, ...options.Option) error // Save config to sources - Save(context.Context, ...SaveOption) error + Save(context.Context, ...options.Option) error // Watch a config for changes - Watch(context.Context, ...WatchOption) (Watcher, error) + Watch(context.Context, ...options.Option) (Watcher, error) // String returns config type name String() string } @@ -59,7 +61,7 @@ type Watcher interface { } // Load loads config from config sources -func Load(ctx context.Context, cs []Config, opts ...LoadOption) error { +func Load(ctx context.Context, cs []Config, opts ...options.Option) error { var err error for _, c := range cs { if err = c.Init(); err != nil { diff --git a/config/context.go b/config/context.go index bc2bf52f..adb61bfb 100644 --- a/config/context.go +++ b/config/context.go @@ -22,43 +22,3 @@ func NewContext(ctx context.Context, c Config) context.Context { } return context.WithValue(ctx, configKey{}, c) } - -// SetOption returns a function to setup a context with given value -func SetOption(k, v interface{}) Option { - return func(o *Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} - -// SetSaveOption returns a function to setup a context with given value -func SetSaveOption(k, v interface{}) SaveOption { - return func(o *SaveOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} - -// SetLoadOption returns a function to setup a context with given value -func SetLoadOption(k, v interface{}) LoadOption { - return func(o *LoadOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} - -// SetWatchOption returns a function to setup a context with given value -func SetWatchOption(k, v interface{}) WatchOption { - return func(o *WatchOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} diff --git a/config/context_test.go b/config/context_test.go index 2976dcbc..b648b1e8 100644 --- a/config/context_test.go +++ b/config/context_test.go @@ -40,47 +40,3 @@ func TestNewContext(t *testing.T) { t.Fatal("NewContext not works") } } - -func TestSetOption(t *testing.T) { - type key struct{} - o := SetOption(key{}, "test") - opts := &Options{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetOption not works") - } -} - -func TestSetSaveOption(t *testing.T) { - type key struct{} - o := SetSaveOption(key{}, "test") - opts := &SaveOptions{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetSaveOption not works") - } -} - -func TestSetLoadOption(t *testing.T) { - type key struct{} - o := SetLoadOption(key{}, "test") - opts := &LoadOptions{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetLoadOption not works") - } -} - -func TestSetWatchOption(t *testing.T) { - type key struct{} - o := SetWatchOption(key{}, "test") - opts := &WatchOptions{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetWatchOption not works") - } -} diff --git a/config/default.go b/config/default.go index 5153dbb6..9032b02b 100644 --- a/config/default.go +++ b/config/default.go @@ -9,6 +9,7 @@ import ( "github.com/google/uuid" "github.com/imdario/mergo" + "go.unistack.org/micro/v4/options" mid "go.unistack.org/micro/v4/util/id" rutil "go.unistack.org/micro/v4/util/reflect" mtime "go.unistack.org/micro/v4/util/time" @@ -22,7 +23,7 @@ func (c *defaultConfig) Options() Options { return c.opts } -func (c *defaultConfig) Init(opts ...Option) error { +func (c *defaultConfig) Init(opts ...options.Option) error { for _, o := range opts { o(&c.opts) } @@ -38,7 +39,7 @@ func (c *defaultConfig) Init(opts ...Option) error { return nil } -func (c *defaultConfig) Load(ctx context.Context, opts ...LoadOption) error { +func (c *defaultConfig) Load(ctx context.Context, opts ...options.Option) error { if err := DefaultBeforeLoad(ctx, c); err != nil && !c.opts.AllowFail { return err } @@ -291,7 +292,7 @@ func fillValues(valueOf reflect.Value, tname string) error { return nil } -func (c *defaultConfig) Save(ctx context.Context, opts ...SaveOption) error { +func (c *defaultConfig) Save(ctx context.Context, opts ...options.Option) error { if err := DefaultBeforeSave(ctx, c); err != nil { return err } @@ -311,12 +312,12 @@ func (c *defaultConfig) Name() string { return c.opts.Name } -func (c *defaultConfig) Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) { +func (c *defaultConfig) Watch(ctx context.Context, opts ...options.Option) (Watcher, error) { return nil, ErrWatcherNotImplemented } // NewConfig returns new default config source -func NewConfig(opts ...Option) Config { +func NewConfig(opts ...options.Option) Config { options := NewOptions(opts...) if len(options.StructTag) == 0 { options.StructTag = "default" diff --git a/config/options.go b/config/options.go index c64e9361..9dd7443b 100644 --- a/config/options.go +++ b/config/options.go @@ -7,6 +7,7 @@ import ( "go.unistack.org/micro/v4/codec" "go.unistack.org/micro/v4/logger" "go.unistack.org/micro/v4/meter" + "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/tracer" ) @@ -44,11 +45,8 @@ type Options struct { AllowFail bool } -// Option function signature -type Option func(o *Options) - // NewOptions new options struct with filed values -func NewOptions(opts ...Option) Options { +func NewOptions(opts ...options.Option) Options { options := Options{ Logger: logger.DefaultLogger, Meter: meter.DefaultMeter, @@ -62,9 +60,6 @@ func NewOptions(opts ...Option) Options { return options } -// LoadOption function signature -type LoadOption func(o *LoadOptions) - // LoadOptions struct type LoadOptions struct { Struct interface{} @@ -74,7 +69,7 @@ type LoadOptions struct { } // NewLoadOptions create LoadOptions struct with provided opts -func NewLoadOptions(opts ...LoadOption) LoadOptions { +func NewLoadOptions(opts ...options.Option) LoadOptions { options := LoadOptions{} for _, o := range opts { o(&options) @@ -83,44 +78,27 @@ func NewLoadOptions(opts ...LoadOption) LoadOptions { } // LoadOverride override values when load -func LoadOverride(b bool) LoadOption { - return func(o *LoadOptions) { - o.Override = b +func LoadOverride(b bool) options.Option { + return func(src interface{}) error { + return options.Set(src, b, ".Override") } } // LoadAppend override values when load -func LoadAppend(b bool) LoadOption { - return func(o *LoadOptions) { - o.Append = b +func LoadAppend(b bool) options.Option { + return func(src interface{}) error { + return options.Set(src, b, ".Append") } } -// LoadStruct override struct for loading -func LoadStruct(src interface{}) LoadOption { - return func(o *LoadOptions) { - o.Struct = src - } -} - -// SaveOption function signature -type SaveOption func(o *SaveOptions) - // SaveOptions struct type SaveOptions struct { Struct interface{} Context context.Context } -// SaveStruct override struct for save to config -func SaveStruct(src interface{}) SaveOption { - return func(o *SaveOptions) { - o.Struct = src - } -} - // NewSaveOptions fill SaveOptions struct -func NewSaveOptions(opts ...SaveOption) SaveOptions { +func NewSaveOptions(opts ...options.Option) SaveOptions { options := SaveOptions{} for _, o := range opts { o(&options) @@ -129,100 +107,65 @@ func NewSaveOptions(opts ...SaveOption) SaveOptions { } // AllowFail allows config source to fail -func AllowFail(b bool) Option { - return func(o *Options) { - o.AllowFail = b +func AllowFail(b bool) options.Option { + return func(src interface{}) error { + return options.Set(src, b, ".AllowFail") } } // BeforeInit run funcs before config Init -func BeforeInit(fn ...func(context.Context, Config) error) Option { - return func(o *Options) { - o.BeforeInit = fn +func BeforeInit(fn ...func(context.Context, Config) error) options.Option { + return func(src interface{}) error { + return options.Set(src, fn, ".BeforeInit") } } // AfterInit run funcs after config Init -func AfterInit(fn ...func(context.Context, Config) error) Option { - return func(o *Options) { - o.AfterInit = fn +func AfterInit(fn ...func(context.Context, Config) error) options.Option { + return func(src interface{}) error { + return options.Set(src, fn, ".AfterInit") } } // BeforeLoad run funcs before config load -func BeforeLoad(fn ...func(context.Context, Config) error) Option { - return func(o *Options) { - o.BeforeLoad = fn +func BeforeLoad(fn ...func(context.Context, Config) error) options.Option { + return func(src interface{}) error { + return options.Set(src, fn, ".BeforeLoad") } } // AfterLoad run funcs after config load -func AfterLoad(fn ...func(context.Context, Config) error) Option { - return func(o *Options) { - o.AfterLoad = fn +func AfterLoad(fn ...func(context.Context, Config) error) options.Option { + return func(src interface{}) error { + return options.Set(src, fn, ".AfterLoad") } } // BeforeSave run funcs before save -func BeforeSave(fn ...func(context.Context, Config) error) Option { - return func(o *Options) { - o.BeforeSave = fn +func BeforeSave(fn ...func(context.Context, Config) error) options.Option { + return func(src interface{}) error { + return options.Set(src, fn, ".BeforeSave") } } // AfterSave run fncs after save -func AfterSave(fn ...func(context.Context, Config) error) Option { - return func(o *Options) { - o.AfterSave = fn - } -} - -// Context pass context -func Context(ctx context.Context) Option { - return func(o *Options) { - o.Context = ctx - } -} - -// Codec sets the source codec -func Codec(c codec.Codec) Option { - return func(o *Options) { - o.Codec = c - } -} - -// Logger sets the logger -func Logger(l logger.Logger) Option { - return func(o *Options) { - o.Logger = l - } -} - -// Tracer to be used for tracing -func Tracer(t tracer.Tracer) Option { - return func(o *Options) { - o.Tracer = t +func AfterSave(fn ...func(context.Context, Config) error) options.Option { + return func(src interface{}) error { + return options.Set(src, fn, ".AfterSave") } } // Struct used as config -func Struct(v interface{}) Option { - return func(o *Options) { - o.Struct = v +func Struct(v interface{}) options.Option { + return func(src interface{}) error { + return options.Set(src, v, ".Struct") } } // StructTag sets the struct tag that used for filling -func StructTag(name string) Option { - return func(o *Options) { - o.StructTag = name - } -} - -// Name sets the name -func Name(n string) Option { - return func(o *Options) { - o.Name = n +func StructTag(name string) options.Option { + return func(src interface{}) error { + return options.Set(src, name, ".StructTag") } } @@ -240,11 +183,8 @@ type WatchOptions struct { Coalesce bool } -// WatchOption func signature -type WatchOption func(*WatchOptions) - // NewWatchOptions create WatchOptions struct with provided opts -func NewWatchOptions(opts ...WatchOption) WatchOptions { +func NewWatchOptions(opts ...options.Option) WatchOptions { options := WatchOptions{ Context: context.Background(), MinInterval: DefaultWatcherMinInterval, @@ -256,31 +196,20 @@ func NewWatchOptions(opts ...WatchOption) WatchOptions { return options } -// WatchContext pass context -func WatchContext(ctx context.Context) WatchOption { - return func(o *WatchOptions) { - o.Context = ctx - } -} - -// WatchCoalesce controls watch event combining -func WatchCoalesce(b bool) WatchOption { - return func(o *WatchOptions) { - o.Coalesce = b +// Coalesce controls watch event combining +func Coalesce(b bool) options.Option { + return func(src interface{}) error { + return options.Set(src, b, ".Coalesce") } } // WatchInterval specifies min and max time.Duration for pulling changes -func WatchInterval(min, max time.Duration) WatchOption { - return func(o *WatchOptions) { - o.MinInterval = min - o.MaxInterval = max - } -} - -// WatchStruct overrides struct for fill -func WatchStruct(src interface{}) WatchOption { - return func(o *WatchOptions) { - o.Struct = src +func WatchInterval(min, max time.Duration) options.Option { + return func(src interface{}) error { + var err error + if err = options.Set(src, min, ".MinInterval"); err == nil { + err = options.Set(src, max, ".MaxInterval") + } + return err } } diff --git a/flow/context.go b/flow/context.go index 1387429f..9489bcb0 100644 --- a/flow/context.go +++ b/flow/context.go @@ -22,13 +22,3 @@ func NewContext(ctx context.Context, f Flow) context.Context { } return context.WithValue(ctx, flowKey{}, f) } - -// SetOption returns a function to setup a context with given value -func SetOption(k, v interface{}) Option { - return func(o *Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} diff --git a/flow/context_test.go b/flow/context_test.go index 03262697..c451a2b9 100644 --- a/flow/context_test.go +++ b/flow/context_test.go @@ -40,14 +40,3 @@ func TestNewContext(t *testing.T) { t.Fatal("NewContext not works") } } - -func TestSetOption(t *testing.T) { - type key struct{} - o := SetOption(key{}, "test") - opts := &Options{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetOption not works") - } -} diff --git a/flow/default.go b/flow/default.go index 299211b5..8eee3bda 100644 --- a/flow/default.go +++ b/flow/default.go @@ -10,6 +10,8 @@ import ( "go.unistack.org/micro/v4/codec" "go.unistack.org/micro/v4/logger" "go.unistack.org/micro/v4/metadata" + "go.unistack.org/micro/v4/options" + moptions "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/store" "go.unistack.org/micro/v4/util/id" ) @@ -163,7 +165,7 @@ func (w *microWorkflow) Resume(ctx context.Context, id string) error { return workflowStore.Write(ctx, "status", &codec.Frame{Data: []byte(StatusRunning.String())}) } -func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (string, error) { +func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...options.Option) (string, error) { w.Lock() if !w.init { if err := w.g.Validate(); err != nil { @@ -200,13 +202,13 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...Execu nctx, cancel := context.WithCancel(ctx) defer cancel() - nopts := make([]ExecuteOption, 0, len(opts)+5) + nopts := make([]moptions.Option, 0, len(opts)+5) nopts = append(nopts, - ExecuteClient(w.opts.Client), - ExecuteTracer(w.opts.Tracer), - ExecuteLogger(w.opts.Logger), - ExecuteMeter(w.opts.Meter), + moptions.Client(w.opts.Client), + moptions.Tracer(w.opts.Tracer), + moptions.Logger(w.opts.Logger), + moptions.Meter(w.opts.Meter), ) nopts = append(nopts, opts...) done := make(chan struct{}) @@ -349,7 +351,7 @@ func (w *microWorkflow) Execute(ctx context.Context, req *Message, opts ...Execu } // NewFlow create new flow -func NewFlow(opts ...Option) Flow { +func NewFlow(opts ...options.Option) Flow { options := NewOptions(opts...) return µFlow{opts: options} } @@ -358,7 +360,7 @@ func (f *microFlow) Options() Options { return f.opts } -func (f *microFlow) Init(opts ...Option) error { +func (f *microFlow) Init(opts ...options.Option) error { for _, o := range opts { o(&f.opts) } @@ -487,17 +489,17 @@ func (s *microCallStep) SetStatus(status Status) { s.status = status } -func (s *microCallStep) Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (*Message, error) { +func (s *microCallStep) Execute(ctx context.Context, req *Message, opts ...options.Option) (*Message, error) { options := NewExecuteOptions(opts...) if options.Client == nil { return nil, ErrMissingClient } rsp := &codec.Frame{} - copts := []client.CallOption{client.WithRetries(0)} + copts := []moptions.Option{client.Retries(0)} if options.Timeout > 0 { copts = append(copts, - client.WithRequestTimeout(options.Timeout), - client.WithDialTimeout(options.Timeout)) + client.RequestTimeout(options.Timeout), + client.DialTimeout(options.Timeout)) } nctx := metadata.NewOutgoingContext(ctx, req.Header) err := options.Client.Call(nctx, options.Client.NewRequest(s.service, s.method, &codec.Frame{Data: req.Body}), rsp, copts...) @@ -570,18 +572,18 @@ func (s *microPublishStep) SetStatus(status Status) { s.status = status } -func (s *microPublishStep) Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (*Message, error) { +func (s *microPublishStep) Execute(ctx context.Context, req *Message, opts ...options.Option) (*Message, error) { return nil, nil } // NewCallStep create new step with client.Call -func NewCallStep(service string, name string, method string, opts ...StepOption) Step { +func NewCallStep(service string, name string, method string, opts ...options.Option) Step { options := NewStepOptions(opts...) return µCallStep{service: service, method: name + "." + method, opts: options} } // NewPublishStep create new step with client.Publish -func NewPublishStep(topic string, opts ...StepOption) Step { +func NewPublishStep(topic string, opts ...options.Option) Step { options := NewStepOptions(opts...) return µPublishStep{topic: topic, opts: options} } diff --git a/flow/flow.go b/flow/flow.go index 4f36c5f8..0bfba6c6 100644 --- a/flow/flow.go +++ b/flow/flow.go @@ -8,6 +8,7 @@ import ( "sync/atomic" "go.unistack.org/micro/v4/metadata" + "go.unistack.org/micro/v4/options" ) var ( @@ -51,7 +52,7 @@ type Step interface { // Endpoint returns rpc endpoint service_name.service_method or broker topic Endpoint() string // Execute step run - Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (*Message, error) + Execute(ctx context.Context, req *Message, opts ...options.Option) (*Message, error) // Requires returns dependent steps Requires() []string // Options returns step options @@ -118,7 +119,7 @@ type Workflow interface { // ID returns id of the workflow ID() string // Execute workflow with args, return execution id and error - Execute(ctx context.Context, req *Message, opts ...ExecuteOption) (string, error) + Execute(ctx context.Context, req *Message, opts ...options.Option) (string, error) // RemoveSteps remove steps from workflow RemoveSteps(steps ...Step) error // AppendSteps append steps to workflow @@ -140,7 +141,7 @@ type Flow interface { // Options returns options Options() Options // Init initialize - Init(...Option) error + Init(...options.Option) error // WorkflowCreate creates new workflow with specific id and steps WorkflowCreate(ctx context.Context, id string, steps ...Step) (Workflow, error) // WorkflowSave saves workflow diff --git a/flow/options.go b/flow/options.go index a40f3abd..723ca21e 100644 --- a/flow/options.go +++ b/flow/options.go @@ -7,13 +7,11 @@ import ( "go.unistack.org/micro/v4/client" "go.unistack.org/micro/v4/logger" "go.unistack.org/micro/v4/meter" + "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/store" "go.unistack.org/micro/v4/tracer" ) -// Option func -type Option func(*Options) - // Options server struct type Options struct { // Context holds the external options and can be used for flow shutdown @@ -31,7 +29,7 @@ type Options struct { } // NewOptions returns new options struct with default or passed values -func NewOptions(opts ...Option) Options { +func NewOptions(opts ...options.Option) Options { options := Options{ Context: context.Background(), Logger: logger.DefaultLogger, @@ -47,66 +45,12 @@ func NewOptions(opts ...Option) Options { return options } -// Logger sets the logger option -func Logger(l logger.Logger) Option { - return func(o *Options) { - o.Logger = l - } -} - -// Meter sets the meter option -func Meter(m meter.Meter) Option { - return func(o *Options) { - o.Meter = m - } -} - -// Client to use for sync/async communication -func Client(c client.Client) Option { - return func(o *Options) { - o.Client = c - } -} - -// Context specifies a context for the service. -// Can be used to signal shutdown of the flow -// or can be used for extra option values. -func Context(ctx context.Context) Option { - return func(o *Options) { - o.Context = ctx - } -} - -// Tracer mechanism for distributed tracking -func Tracer(t tracer.Tracer) Option { - return func(o *Options) { - o.Tracer = t - } -} - -// Store used for intermediate results -func Store(s store.Store) Option { - return func(o *Options) { - o.Store = s - } -} - -// WorkflowOption func signature -type WorkflowOption func(*WorkflowOptions) - // WorkflowOptions holds workflow options type WorkflowOptions struct { Context context.Context ID string } -// WorkflowID set workflow id -func WorkflowID(id string) WorkflowOption { - return func(o *WorkflowOptions) { - o.ID = id - } -} - // ExecuteOptions holds execute options type ExecuteOptions struct { // Client holds the client.Client @@ -129,67 +73,22 @@ type ExecuteOptions struct { Async bool } -// ExecuteOption func signature -type ExecuteOption func(*ExecuteOptions) - -// ExecuteClient pass client.Client to ExecuteOption -func ExecuteClient(c client.Client) ExecuteOption { - return func(o *ExecuteOptions) { - o.Client = c +// Reverse says that dag must be run in reverse order +func Reverse(b bool) options.Option { + return func(src interface{}) error { + return options.Set(src, b, ".Reverse") } } -// ExecuteTracer pass tracer.Tracer to ExecuteOption -func ExecuteTracer(t tracer.Tracer) ExecuteOption { - return func(o *ExecuteOptions) { - o.Tracer = t - } -} - -// ExecuteLogger pass logger.Logger to ExecuteOption -func ExecuteLogger(l logger.Logger) ExecuteOption { - return func(o *ExecuteOptions) { - o.Logger = l - } -} - -// ExecuteMeter pass meter.Meter to ExecuteOption -func ExecuteMeter(m meter.Meter) ExecuteOption { - return func(o *ExecuteOptions) { - o.Meter = m - } -} - -// ExecuteContext pass context.Context ot ExecuteOption -func ExecuteContext(ctx context.Context) ExecuteOption { - return func(o *ExecuteOptions) { - o.Context = ctx - } -} - -// ExecuteReverse says that dag must be run in reverse order -func ExecuteReverse(b bool) ExecuteOption { - return func(o *ExecuteOptions) { - o.Reverse = b - } -} - -// ExecuteTimeout pass timeout time.Duration for execution -func ExecuteTimeout(td time.Duration) ExecuteOption { - return func(o *ExecuteOptions) { - o.Timeout = td - } -} - -// ExecuteAsync says that caller does not wait for execution complete -func ExecuteAsync(b bool) ExecuteOption { - return func(o *ExecuteOptions) { - o.Async = b +// Async says that caller does not wait for execution complete +func Async(b bool) options.Option { + return func(src interface{}) error { + return options.Set(src, b, ".Async") } } // NewExecuteOptions create new ExecuteOptions struct -func NewExecuteOptions(opts ...ExecuteOption) ExecuteOptions { +func NewExecuteOptions(opts ...options.Option) ExecuteOptions { options := ExecuteOptions{ Client: client.DefaultClient, Logger: logger.DefaultLogger, @@ -211,11 +110,8 @@ type StepOptions struct { Requires []string } -// StepOption func signature -type StepOption func(*StepOptions) - // NewStepOptions create new StepOptions struct -func NewStepOptions(opts ...StepOption) StepOptions { +func NewStepOptions(opts ...options.Option) StepOptions { options := StepOptions{ Context: context.Background(), } @@ -225,23 +121,16 @@ func NewStepOptions(opts ...StepOption) StepOptions { return options } -// StepID sets the step id for dag -func StepID(id string) StepOption { - return func(o *StepOptions) { - o.ID = id +// Requires specifies required steps +func Requires(steps ...string) options.Option { + return func(src interface{}) error { + return options.Set(src, steps, ".Requires") } } -// StepRequires specifies required steps -func StepRequires(steps ...string) StepOption { - return func(o *StepOptions) { - o.Requires = steps - } -} - -// StepFallback set the step to run on error -func StepFallback(step string) StepOption { - return func(o *StepOptions) { - o.Fallback = step +// Fallback set the step to run on error +func Fallback(step string) options.Option { + return func(src interface{}) error { + return options.Set(src, step, ".Fallback") } } diff --git a/logger/context.go b/logger/context.go index 6cb6b65f..931f4cfe 100644 --- a/logger/context.go +++ b/logger/context.go @@ -20,13 +20,3 @@ func NewContext(ctx context.Context, l Logger) context.Context { } return context.WithValue(ctx, loggerKey{}, l) } - -// SetOption returns a function to setup a context with given value -func SetOption(k, v interface{}) Option { - return func(o *Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} diff --git a/logger/context_test.go b/logger/context_test.go index 7d636818..8824098b 100644 --- a/logger/context_test.go +++ b/logger/context_test.go @@ -40,14 +40,3 @@ func TestNewContext(t *testing.T) { t.Fatal("NewContext not works") } } - -func TestSetOption(t *testing.T) { - type key struct{} - o := SetOption(key{}, "test") - opts := &Options{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetOption not works") - } -} diff --git a/logger/default.go b/logger/default.go index 45be6c64..f747ee9d 100644 --- a/logger/default.go +++ b/logger/default.go @@ -9,6 +9,8 @@ import ( "strings" "sync" "time" + + "go.unistack.org/micro/v4/options" ) type defaultLogger struct { @@ -18,7 +20,7 @@ type defaultLogger struct { } // Init(opts...) should only overwrite provided options -func (l *defaultLogger) Init(opts ...Option) error { +func (l *defaultLogger) Init(opts ...options.Option) error { l.Lock() for _, o := range opts { o(&l.opts) @@ -33,7 +35,7 @@ func (l *defaultLogger) String() string { return "micro" } -func (l *defaultLogger) Clone(opts ...Option) Logger { +func (l *defaultLogger) Clone(opts ...options.Option) Logger { newopts := NewOptions(opts...) oldopts := l.opts for _, o := range opts { @@ -221,7 +223,7 @@ func (l *defaultLogger) Options() Options { } // NewLogger builds a new logger based on options -func NewLogger(opts ...Option) Logger { +func NewLogger(opts ...options.Option) Logger { l := &defaultLogger{ opts: NewOptions(opts...), } diff --git a/logger/logger.go b/logger/logger.go index f3232750..7064bdbd 100644 --- a/logger/logger.go +++ b/logger/logger.go @@ -4,6 +4,8 @@ package logger // import "go.unistack.org/micro/v4/logger" import ( "context" "os" + + "go.unistack.org/micro/v4/options" ) var ( @@ -18,9 +20,9 @@ var ( // Logger is a generic logging interface type Logger interface { // Init initialises options - Init(opts ...Option) error + Init(opts ...options.Option) error // Clone create logger copy with new options - Clone(opts ...Option) Logger + Clone(opts ...options.Option) Logger // V compare provided verbosity level with current log level V(level Level) bool // Level sets the log level for logger @@ -130,7 +132,7 @@ func V(level Level) bool { } // Init initialize logger -func Init(opts ...Option) error { +func Init(opts ...options.Option) error { return DefaultLogger.Init(opts...) } diff --git a/logger/options.go b/logger/options.go index 7ea04e74..ce68ccca 100644 --- a/logger/options.go +++ b/logger/options.go @@ -4,10 +4,9 @@ import ( "context" "io" "os" -) -// Option func -type Option func(*Options) + "go.unistack.org/micro/v4/options" +) // Options holds logger options type Options struct { @@ -26,7 +25,7 @@ type Options struct { } // NewOptions creates new options struct -func NewOptions(opts ...Option) Options { +func NewOptions(opts ...options.Option) Options { options := Options{ Level: DefaultLevel, Fields: make([]interface{}, 0, 6), @@ -41,43 +40,29 @@ func NewOptions(opts ...Option) Options { } // WithFields set default fields for the logger -func WithFields(fields ...interface{}) Option { - return func(o *Options) { - o.Fields = fields +func WithFields(fields ...interface{}) options.Option { + return func(src interface{}) error { + return options.Set(src, fields, ".Fields") } } // WithLevel set default level for the logger -func WithLevel(level Level) Option { - return func(o *Options) { - o.Level = level +func WithLevel(lvl Level) options.Option { + return func(src interface{}) error { + return options.Set(src, lvl, ".Level") } } // WithOutput set default output writer for the logger -func WithOutput(out io.Writer) Option { - return func(o *Options) { - o.Out = out +func WithOutput(out io.Writer) options.Option { + return func(src interface{}) error { + return options.Set(src, out, ".Out") } } // WithCallerSkipCount set frame count to skip -func WithCallerSkipCount(c int) Option { - return func(o *Options) { - o.CallerSkipCount = c - } -} - -// WithContext set context -func WithContext(ctx context.Context) Option { - return func(o *Options) { - o.Context = ctx - } -} - -// WithName sets the name -func WithName(n string) Option { - return func(o *Options) { - o.Name = n +func WithCallerSkipCount(c int) options.Option { + return func(src interface{}) error { + return options.Set(src, c, ".CallerSkipCount") } } diff --git a/logger/wrapper/wrapper.go b/logger/wrapper/wrapper.go index c2631bd2..af8050e0 100644 --- a/logger/wrapper/wrapper.go +++ b/logger/wrapper/wrapper.go @@ -29,15 +29,6 @@ var ( return labels } - // DefaultClientPublishObserver called by wrapper in client Publish - DefaultClientPublishObserver = func(ctx context.Context, msg client.Message, opts []client.PublishOption, err error) []string { - labels := []string{"endpoint", msg.Topic()} - if err != nil { - labels = append(labels, "error", err.Error()) - } - return labels - } - // DefaultServerHandlerObserver called by wrapper in server Handler DefaultServerHandlerObserver = func(ctx context.Context, req server.Request, rsp interface{}, err error) []string { labels := []string{"service", req.Service(), "endpoint", req.Endpoint()} @@ -47,15 +38,6 @@ var ( return labels } - // DefaultServerSubscriberObserver called by wrapper in server Subscriber - DefaultServerSubscriberObserver = func(ctx context.Context, msg server.Message, err error) []string { - labels := []string{"endpoint", msg.Topic()} - if err != nil { - labels = append(labels, "error", err.Error()) - } - return labels - } - // DefaultClientCallFuncObserver called by wrapper in client CallFunc DefaultClientCallFuncObserver = func(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions, err error) []string { labels := []string{"service", req.Service(), "endpoint", req.Endpoint()} @@ -71,10 +53,9 @@ var ( type lWrapper struct { client.Client - serverHandler server.HandlerFunc - serverSubscriber server.SubscriberFunc - clientCallFunc client.CallFunc - opts Options + serverHandler server.HandlerFunc + clientCallFunc client.CallFunc + opts Options } type ( @@ -82,14 +63,10 @@ type ( ClientCallObserver func(context.Context, client.Request, interface{}, []client.CallOption, error) []string // ClientStreamObserver func signature ClientStreamObserver func(context.Context, client.Request, []client.CallOption, client.Stream, error) []string - // ClientPublishObserver func signature - ClientPublishObserver func(context.Context, client.Message, []client.PublishOption, error) []string // ClientCallFuncObserver func signature ClientCallFuncObserver func(context.Context, string, client.Request, interface{}, client.CallOptions, error) []string // ServerHandlerObserver func signature ServerHandlerObserver func(context.Context, server.Request, interface{}, error) []string - // ServerSubscriberObserver func signature - ServerSubscriberObserver func(context.Context, server.Message, error) []string ) // Options struct for wrapper @@ -98,14 +75,10 @@ type Options struct { Logger logger.Logger // ServerHandlerObservers funcs ServerHandlerObservers []ServerHandlerObserver - // ServerSubscriberObservers funcs - ServerSubscriberObservers []ServerSubscriberObserver // ClientCallObservers funcs ClientCallObservers []ClientCallObserver // ClientStreamObservers funcs ClientStreamObservers []ClientStreamObserver - // ClientPublishObservers funcs - ClientPublishObservers []ClientPublishObserver // ClientCallFuncObservers funcs ClientCallFuncObservers []ClientCallFuncObserver // SkipEndpoints @@ -122,15 +95,13 @@ type Option func(*Options) // NewOptions creates Options from Option slice func NewOptions(opts ...Option) Options { options := Options{ - Logger: logger.DefaultLogger, - Level: logger.TraceLevel, - ClientCallObservers: []ClientCallObserver{DefaultClientCallObserver}, - ClientStreamObservers: []ClientStreamObserver{DefaultClientStreamObserver}, - ClientPublishObservers: []ClientPublishObserver{DefaultClientPublishObserver}, - ClientCallFuncObservers: []ClientCallFuncObserver{DefaultClientCallFuncObserver}, - ServerHandlerObservers: []ServerHandlerObserver{DefaultServerHandlerObserver}, - ServerSubscriberObservers: []ServerSubscriberObserver{DefaultServerSubscriberObserver}, - SkipEndpoints: DefaultSkipEndpoints, + Logger: logger.DefaultLogger, + Level: logger.TraceLevel, + ClientCallObservers: []ClientCallObserver{DefaultClientCallObserver}, + ClientStreamObservers: []ClientStreamObserver{DefaultClientStreamObserver}, + ClientCallFuncObservers: []ClientCallFuncObserver{DefaultClientCallFuncObserver}, + ServerHandlerObservers: []ServerHandlerObserver{DefaultServerHandlerObserver}, + SkipEndpoints: DefaultSkipEndpoints, } for _, o := range opts { @@ -175,13 +146,6 @@ func WithClientStreamObservers(ob ...ClientStreamObserver) Option { } } -// WithClientPublishObservers funcs -func WithClientPublishObservers(ob ...ClientPublishObserver) Option { - return func(o *Options) { - o.ClientPublishObservers = ob - } -} - // WithClientCallFuncObservers funcs func WithClientCallFuncObservers(ob ...ClientCallFuncObserver) Option { return func(o *Options) { @@ -196,13 +160,6 @@ func WithServerHandlerObservers(ob ...ServerHandlerObserver) Option { } } -// WithServerSubscriberObservers funcs -func WithServerSubscriberObservers(ob ...ServerSubscriberObserver) Option { - return func(o *Options) { - o.ServerSubscriberObservers = ob - } -} - // SkipEndpoins func SkipEndpoints(eps ...string) Option { return func(o *Options) { @@ -256,29 +213,6 @@ func (l *lWrapper) Stream(ctx context.Context, req client.Request, opts ...clien return stream, err } -func (l *lWrapper) Publish(ctx context.Context, msg client.Message, opts ...client.PublishOption) error { - err := l.Client.Publish(ctx, msg, opts...) - - endpoint := msg.Topic() - for _, ep := range l.opts.SkipEndpoints { - if ep == endpoint { - return err - } - } - - if !l.opts.Enabled { - return err - } - - var labels []string - for _, o := range l.opts.ClientPublishObservers { - labels = append(labels, o(ctx, msg, opts, err)...) - } - l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level) - - return err -} - func (l *lWrapper) ServerHandler(ctx context.Context, req server.Request, rsp interface{}) error { err := l.serverHandler(ctx, req, rsp) @@ -302,29 +236,6 @@ func (l *lWrapper) ServerHandler(ctx context.Context, req server.Request, rsp in return err } -func (l *lWrapper) ServerSubscriber(ctx context.Context, msg server.Message) error { - err := l.serverSubscriber(ctx, msg) - - endpoint := msg.Topic() - for _, ep := range l.opts.SkipEndpoints { - if ep == endpoint { - return err - } - } - - if !l.opts.Enabled { - return err - } - - var labels []string - for _, o := range l.opts.ServerSubscriberObservers { - labels = append(labels, o(ctx, msg, err)...) - } - l.opts.Logger.Fields(labels).Log(ctx, l.opts.Level) - - return err -} - // NewClientWrapper accepts an open options and returns a Client Wrapper func NewClientWrapper(opts ...Option) client.Wrapper { return func(c client.Client) client.Client { @@ -384,16 +295,3 @@ func NewServerHandlerWrapper(opts ...Option) server.HandlerWrapper { return l.ServerHandler } } - -// NewServerSubscriberWrapper accepts an options and returns a Subscriber Wrapper -func NewServerSubscriberWrapper(opts ...Option) server.SubscriberWrapper { - return func(h server.SubscriberFunc) server.SubscriberFunc { - options := NewOptions() - for _, o := range opts { - o(&options) - } - - l := &lWrapper{opts: options, serverSubscriber: h} - return l.ServerSubscriber - } -} diff --git a/meter/context.go b/meter/context.go index dff0bd2b..725e4a70 100644 --- a/meter/context.go +++ b/meter/context.go @@ -22,13 +22,3 @@ func NewContext(ctx context.Context, c Meter) context.Context { } return context.WithValue(ctx, meterKey{}, c) } - -// SetOption returns a function to setup a context with given value -func SetOption(k, v interface{}) Option { - return func(o *Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} diff --git a/meter/context_test.go b/meter/context_test.go index 7059c598..b80fc8d0 100644 --- a/meter/context_test.go +++ b/meter/context_test.go @@ -40,14 +40,3 @@ func TestNewContext(t *testing.T) { t.Fatal("NewContext not works") } } - -func TestSetOption(t *testing.T) { - type key struct{} - o := SetOption(key{}, "test") - opts := &Options{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetOption not works") - } -} diff --git a/meter/meter.go b/meter/meter.go index 059fc2f8..19306bc1 100644 --- a/meter/meter.go +++ b/meter/meter.go @@ -7,6 +7,8 @@ import ( "strconv" "strings" "time" + + "go.unistack.org/micro/v4/options" ) var ( @@ -31,9 +33,9 @@ type Meter interface { // Name returns meter name Name() string // Init initialize meter - Init(opts ...Option) error + Init(opts ...options.Option) error // Clone create meter copy with new options - Clone(opts ...Option) Meter + Clone(opts ...options.Option) Meter // Counter get or create counter Counter(name string, labels ...string) Counter // FloatCounter get or create float counter @@ -41,7 +43,7 @@ type Meter interface { // Gauge get or create gauge Gauge(name string, fn func() float64, labels ...string) Gauge // Set create new meter metrics set - Set(opts ...Option) Meter + Set(opts ...options.Option) Meter // Histogram get or create histogram Histogram(name string, labels ...string) Histogram // Summary get or create summary @@ -49,7 +51,7 @@ type Meter interface { // SummaryExt get or create summary with spcified quantiles and window time SummaryExt(name string, window time.Duration, quantiles []float64, labels ...string) Summary // Write writes metrics to io.Writer - Write(w io.Writer, opts ...Option) error + Write(w io.Writer, opts ...options.Option) error // Options returns meter options Options() Options // String return meter type diff --git a/meter/noop.go b/meter/noop.go index a1b47a94..1f4cd720 100644 --- a/meter/noop.go +++ b/meter/noop.go @@ -3,6 +3,8 @@ package meter import ( "io" "time" + + "go.unistack.org/micro/v4/options" ) // NoopMeter is an noop implementation of Meter @@ -11,12 +13,12 @@ type noopMeter struct { } // NewMeter returns a configured noop reporter: -func NewMeter(opts ...Option) Meter { +func NewMeter(opts ...options.Option) Meter { return &noopMeter{opts: NewOptions(opts...)} } // Clone return old meter with new options -func (r *noopMeter) Clone(opts ...Option) Meter { +func (r *noopMeter) Clone(opts ...options.Option) Meter { options := r.opts for _, o := range opts { o(&options) @@ -29,7 +31,7 @@ func (r *noopMeter) Name() string { } // Init initialize options -func (r *noopMeter) Init(opts ...Option) error { +func (r *noopMeter) Init(opts ...options.Option) error { for _, o := range opts { o(&r.opts) } @@ -67,7 +69,7 @@ func (r *noopMeter) Histogram(name string, labels ...string) Histogram { } // Set implements the Meter interface -func (r *noopMeter) Set(opts ...Option) Meter { +func (r *noopMeter) Set(opts ...options.Option) Meter { m := &noopMeter{opts: r.opts} for _, o := range opts { @@ -77,7 +79,7 @@ func (r *noopMeter) Set(opts ...Option) Meter { return m } -func (r *noopMeter) Write(w io.Writer, opts ...Option) error { +func (r *noopMeter) Write(_ io.Writer, _ ...options.Option) error { return nil } diff --git a/meter/options.go b/meter/options.go index be077429..e3c0cd15 100644 --- a/meter/options.go +++ b/meter/options.go @@ -2,13 +2,13 @@ package meter import ( "context" + "reflect" "go.unistack.org/micro/v4/logger" + "go.unistack.org/micro/v4/options" + rutil "go.unistack.org/micro/v4/util/reflect" ) -// Option powers the configuration for metrics implementations: -type Option func(*Options) - // Options for metrics implementations type Options struct { // Logger used for logging @@ -34,7 +34,7 @@ type Options struct { } // NewOptions prepares a set of options: -func NewOptions(opt ...Option) Options { +func NewOptions(opt ...options.Option) Options { opts := Options{ Address: DefaultAddress, Path: DefaultPath, @@ -52,37 +52,23 @@ func NewOptions(opt ...Option) Options { } // LabelPrefix sets the labels prefix -func LabelPrefix(pref string) Option { - return func(o *Options) { - o.LabelPrefix = pref +func LabelPrefix(pref string) options.Option { + return func(src interface{}) error { + return options.Set(src, pref, ".LabelPrefix") } } // MetricPrefix sets the metric prefix -func MetricPrefix(pref string) Option { - return func(o *Options) { - o.MetricPrefix = pref - } -} - -// Context sets the metrics context -func Context(ctx context.Context) Option { - return func(o *Options) { - o.Context = ctx +func MetricPrefix(pref string) options.Option { + return func(src interface{}) error { + return options.Set(src, pref, ".MetricPrefix") } } // Path used to serve metrics over HTTP -func Path(value string) Option { - return func(o *Options) { - o.Path = value - } -} - -// Address is the listen address to serve metrics -func Address(value string) Option { - return func(o *Options) { - o.Address = value +func Path(path string) options.Option { + return func(src interface{}) error { + return options.Set(src, path, ".Path") } } @@ -95,37 +81,34 @@ func TimingObjectives(value map[float64]float64) Option { } */ -// Logger sets the logger -func Logger(l logger.Logger) Option { - return func(o *Options) { - o.Logger = l - } -} - // Labels sets the meter labels -func Labels(ls ...string) Option { - return func(o *Options) { - o.Labels = append(o.Labels, ls...) - } -} - -// Name sets the name -func Name(n string) Option { - return func(o *Options) { - o.Name = n +func Labels(ls ...string) options.Option { + return func(src interface{}) error { + v, err := options.Get(src, ".Labels") + if err != nil { + return err + } else if rutil.IsZero(v) { + v = reflect.MakeSlice(reflect.TypeOf(v), 0, len(ls)).Interface() + } + cv := reflect.ValueOf(v) + for _, l := range ls { + reflect.Append(cv, reflect.ValueOf(l)) + } + err = options.Set(src, cv, ".Labels") + return err } } // WriteProcessMetrics enable process metrics output for write -func WriteProcessMetrics(b bool) Option { - return func(o *Options) { - o.WriteProcessMetrics = b +func WriteProcessMetrics(b bool) options.Option { + return func(src interface{}) error { + return options.Set(src, b, ".WriteProcessMetrics") } } // WriteFDMetrics enable fd metrics output for write -func WriteFDMetrics(b bool) Option { - return func(o *Options) { - o.WriteFDMetrics = b +func WriteFDMetrics(b bool) options.Option { + return func(src interface{}) error { + return options.Set(src, b, ".WriteFDMetrics") } } diff --git a/meter/wrapper/wrapper.go b/meter/wrapper/wrapper.go index 8bb48e02..d3379ce7 100644 --- a/meter/wrapper/wrapper.go +++ b/meter/wrapper/wrapper.go @@ -7,6 +7,7 @@ import ( "go.unistack.org/micro/v4/client" "go.unistack.org/micro/v4/meter" + "go.unistack.org/micro/v4/semconv" "go.unistack.org/micro/v4/server" ) @@ -117,21 +118,21 @@ func (w *wrapper) CallFunc(ctx context.Context, addr string, req client.Request, labels := make([]string, 0, 4) labels = append(labels, labelEndpoint, endpoint) - w.opts.Meter.Counter(ClientRequestInflight, labels...).Inc() + w.opts.Meter.Counter(semconv.ClientRequestInflight, labels...).Inc() ts := time.Now() err := w.callFunc(ctx, addr, req, rsp, opts) te := time.Since(ts) - w.opts.Meter.Counter(ClientRequestInflight, labels...).Dec() + w.opts.Meter.Counter(semconv.ClientRequestInflight, labels...).Dec() - w.opts.Meter.Summary(ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds()) - w.opts.Meter.Histogram(ClientRequestDurationSeconds, labels...).Update(te.Seconds()) + w.opts.Meter.Summary(semconv.ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds()) + w.opts.Meter.Histogram(semconv.ClientRequestDurationSeconds, labels...).Update(te.Seconds()) if err == nil { labels = append(labels, labelStatus, labelSuccess) } else { labels = append(labels, labelStatus, labelFailure) } - w.opts.Meter.Counter(ClientRequestTotal, labels...).Inc() + w.opts.Meter.Counter(semconv.ClientRequestTotal, labels...).Inc() return err } @@ -147,21 +148,21 @@ func (w *wrapper) Call(ctx context.Context, req client.Request, rsp interface{}, labels := make([]string, 0, 4) labels = append(labels, labelEndpoint, endpoint) - w.opts.Meter.Counter(ClientRequestInflight, labels...).Inc() + w.opts.Meter.Counter(semconv.ClientRequestInflight, labels...).Inc() ts := time.Now() err := w.Client.Call(ctx, req, rsp, opts...) te := time.Since(ts) - w.opts.Meter.Counter(ClientRequestInflight, labels...).Dec() + w.opts.Meter.Counter(semconv.ClientRequestInflight, labels...).Dec() - w.opts.Meter.Summary(ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds()) - w.opts.Meter.Histogram(ClientRequestDurationSeconds, labels...).Update(te.Seconds()) + w.opts.Meter.Summary(semconv.ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds()) + w.opts.Meter.Histogram(semconv.ClientRequestDurationSeconds, labels...).Update(te.Seconds()) if err == nil { labels = append(labels, labelStatus, labelSuccess) } else { labels = append(labels, labelStatus, labelFailure) } - w.opts.Meter.Counter(ClientRequestTotal, labels...).Inc() + w.opts.Meter.Counter(semconv.ClientRequestTotal, labels...).Inc() return err } @@ -177,29 +178,25 @@ func (w *wrapper) Stream(ctx context.Context, req client.Request, opts ...client labels := make([]string, 0, 4) labels = append(labels, labelEndpoint, endpoint) - w.opts.Meter.Counter(ClientRequestInflight, labels...).Inc() + w.opts.Meter.Counter(semconv.ClientRequestInflight, labels...).Inc() ts := time.Now() stream, err := w.Client.Stream(ctx, req, opts...) te := time.Since(ts) - w.opts.Meter.Counter(ClientRequestInflight, labels...).Dec() + w.opts.Meter.Counter(semconv.ClientRequestInflight, labels...).Dec() - w.opts.Meter.Summary(ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds()) - w.opts.Meter.Histogram(ClientRequestDurationSeconds, labels...).Update(te.Seconds()) + w.opts.Meter.Summary(semconv.ClientRequestLatencyMicroseconds, labels...).Update(te.Seconds()) + w.opts.Meter.Histogram(semconv.ClientRequestDurationSeconds, labels...).Update(te.Seconds()) if err == nil { labels = append(labels, labelStatus, labelSuccess) } else { labels = append(labels, labelStatus, labelFailure) } - w.opts.Meter.Counter(ClientRequestTotal, labels...).Inc() + w.opts.Meter.Counter(semconv.ClientRequestTotal, labels...).Inc() return stream, err } -func (w *wrapper) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error { - return w.Client.Publish(ctx, p, opts...) -} - // NewServerHandlerWrapper create new server handler wrapper func NewServerHandlerWrapper(opts ...Option) server.HandlerWrapper { handler := &wrapper{ @@ -220,21 +217,21 @@ func (w *wrapper) HandlerFunc(fn server.HandlerFunc) server.HandlerFunc { labels := make([]string, 0, 4) labels = append(labels, labelEndpoint, endpoint) - w.opts.Meter.Counter(ServerRequestInflight, labels...).Inc() + w.opts.Meter.Counter(semconv.ServerRequestInflight, labels...).Inc() ts := time.Now() err := fn(ctx, req, rsp) te := time.Since(ts) - w.opts.Meter.Counter(ServerRequestInflight, labels...).Dec() + w.opts.Meter.Counter(semconv.ServerRequestInflight, labels...).Dec() - w.opts.Meter.Summary(ServerRequestLatencyMicroseconds, labels...).Update(te.Seconds()) - w.opts.Meter.Histogram(ServerRequestDurationSeconds, labels...).Update(te.Seconds()) + w.opts.Meter.Summary(semconv.ServerRequestLatencyMicroseconds, labels...).Update(te.Seconds()) + w.opts.Meter.Histogram(semconv.ServerRequestDurationSeconds, labels...).Update(te.Seconds()) if err == nil { labels = append(labels, labelStatus, labelSuccess) } else { labels = append(labels, labelStatus, labelFailure) } - w.opts.Meter.Counter(ServerRequestTotal, labels...).Inc() + w.opts.Meter.Counter(semconv.ServerRequestTotal, labels...).Inc() return err } diff --git a/network/network.go b/network/network.go deleted file mode 100644 index 25c9c221..00000000 --- a/network/network.go +++ /dev/null @@ -1,55 +0,0 @@ -// Package network is for creating internetworks -package network // import "go.unistack.org/micro/v4/network" - -import ( - "go.unistack.org/micro/v4/client" - "go.unistack.org/micro/v4/server" -) - -// Error is network node errors -type Error interface { - // Count is current count of errors - Count() int - // Msg is last error message - Msg() string -} - -// Status is node status -type Status interface { - // Error reports error status - Error() Error -} - -// Node is network node -type Node interface { - // Id is node id - Id() string - // Address is node bind address - Address() string - // Peers returns node peers - Peers() []Node - // Network is the network node is in - Network() Network - // Status returns node status - Status() Status -} - -// Network is micro network -type Network interface { - // Node is network node - Node - // Initialise options - Init(...Option) error - // Options returns the network options - Options() Options - // Name of the network - Name() string - // Connect starts the resolver and tunnel server - Connect() error - // Close stops the tunnel and resolving - Close() error - // Client is micro client - Client() client.Client - // Server is micro server - Server() server.Server -} diff --git a/network/options.go b/network/options.go deleted file mode 100644 index e7b196c2..00000000 --- a/network/options.go +++ /dev/null @@ -1,135 +0,0 @@ -package network - -import ( - "go.unistack.org/micro/v4/logger" - "go.unistack.org/micro/v4/meter" - "go.unistack.org/micro/v4/network/tunnel" - "go.unistack.org/micro/v4/proxy" - "go.unistack.org/micro/v4/router" - "go.unistack.org/micro/v4/tracer" - "go.unistack.org/micro/v4/util/id" -) - -// Option func -type Option func(*Options) - -// Options configure network -type Options struct { - // Router used for routing - Router router.Router - // Proxy holds the proxy - Proxy proxy.Proxy - // Logger used for logging - Logger logger.Logger - // Meter used for metrics - Meter meter.Meter - // Tracer used for tracing - Tracer tracer.Tracer - // Tunnel used for transfer data - Tunnel tunnel.Tunnel - // ID of the node - ID string - // Name of the network - Name string - // Address to bind to - Address string - // Advertise sets the address to advertise - Advertise string - // Nodes is a list of nodes to connect to - Nodes []string -} - -// ID sets the id of the network node -func ID(id string) Option { - return func(o *Options) { - o.ID = id - } -} - -// Name sets the network name -func Name(n string) Option { - return func(o *Options) { - o.Name = n - } -} - -// Address sets the network address -func Address(a string) Option { - return func(o *Options) { - o.Address = a - } -} - -// Advertise sets the address to advertise -func Advertise(a string) Option { - return func(o *Options) { - o.Advertise = a - } -} - -// Nodes is a list of nodes to connect to -func Nodes(n ...string) Option { - return func(o *Options) { - o.Nodes = n - } -} - -// Tunnel sets the network tunnel -func Tunnel(t tunnel.Tunnel) Option { - return func(o *Options) { - o.Tunnel = t - } -} - -// Router sets the network router -func Router(r router.Router) Option { - return func(o *Options) { - o.Router = r - } -} - -// Proxy sets the network proxy -func Proxy(p proxy.Proxy) Option { - return func(o *Options) { - o.Proxy = p - } -} - -// Logger sets the network logger -func Logger(l logger.Logger) Option { - return func(o *Options) { - o.Logger = l - } -} - -// Meter sets the meter -func Meter(m meter.Meter) Option { - return func(o *Options) { - o.Meter = m - } -} - -// Tracer to be used for tracing -func Tracer(t tracer.Tracer) Option { - return func(o *Options) { - o.Tracer = t - } -} - -// NewOptions returns network default options -func NewOptions(opts ...Option) Options { - options := Options{ - ID: id.Must(), - Name: "go.micro", - Address: ":0", - Logger: logger.DefaultLogger, - Meter: meter.DefaultMeter, - Tracer: tracer.DefaultTracer, - } - - for _, o := range opts { - o(&options) - } - - return options -} diff --git a/network/transport/context.go b/network/transport/context.go deleted file mode 100644 index 73e4681d..00000000 --- a/network/transport/context.go +++ /dev/null @@ -1,34 +0,0 @@ -package transport - -import ( - "context" -) - -type transportKey struct{} - -// FromContext get transport from context -func FromContext(ctx context.Context) (Transport, bool) { - if ctx == nil { - return nil, false - } - c, ok := ctx.Value(transportKey{}).(Transport) - return c, ok -} - -// NewContext put transport in context -func NewContext(ctx context.Context, c Transport) context.Context { - if ctx == nil { - ctx = context.Background() - } - return context.WithValue(ctx, transportKey{}, c) -} - -// SetOption returns a function to setup a context with given value -func SetOption(k, v interface{}) Option { - return func(o *Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} diff --git a/network/transport/memory.go b/network/transport/memory.go deleted file mode 100644 index 6447978f..00000000 --- a/network/transport/memory.go +++ /dev/null @@ -1,258 +0,0 @@ -package transport - -import ( - "context" - "errors" - "fmt" - "net" - "sync" - "time" - - maddr "go.unistack.org/micro/v4/util/addr" - mnet "go.unistack.org/micro/v4/util/net" - "go.unistack.org/micro/v4/util/rand" -) - -type memorySocket struct { - ctx context.Context - recv chan *Message - exit chan bool - lexit chan bool - send chan *Message - local string - remote string - timeout time.Duration - sync.RWMutex -} - -type memoryClient struct { - *memorySocket - opts DialOptions -} - -type memoryListener struct { - lopts ListenOptions - ctx context.Context - exit chan bool - conn chan *memorySocket - addr string - topts Options - sync.RWMutex -} - -type memoryTransport struct { - listeners map[string]*memoryListener - opts Options - sync.RWMutex -} - -func (ms *memorySocket) Recv(m *Message) error { - ms.RLock() - defer ms.RUnlock() - - ctx := ms.ctx - if ms.timeout > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ms.ctx, ms.timeout) - defer cancel() - } - - select { - case <-ctx.Done(): - return ctx.Err() - case <-ms.exit: - return errors.New("connection closed") - case <-ms.lexit: - return errors.New("server connection closed") - case cm := <-ms.recv: - *m = *cm - } - return nil -} - -func (ms *memorySocket) Local() string { - return ms.local -} - -func (ms *memorySocket) Remote() string { - return ms.remote -} - -func (ms *memorySocket) Send(m *Message) error { - ms.RLock() - defer ms.RUnlock() - - ctx := ms.ctx - if ms.timeout > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ms.ctx, ms.timeout) - defer cancel() - } - - select { - case <-ctx.Done(): - return ctx.Err() - case <-ms.exit: - return errors.New("connection closed") - case <-ms.lexit: - return errors.New("server connection closed") - case ms.send <- m: - } - return nil -} - -func (ms *memorySocket) Close() error { - ms.Lock() - defer ms.Unlock() - select { - case <-ms.exit: - return nil - default: - close(ms.exit) - } - return nil -} - -func (m *memoryListener) Addr() string { - return m.addr -} - -func (m *memoryListener) Close() error { - m.Lock() - defer m.Unlock() - select { - case <-m.exit: - return nil - default: - close(m.exit) - } - return nil -} - -func (m *memoryListener) Accept(fn func(Socket)) error { - for { - select { - case <-m.exit: - return nil - case c := <-m.conn: - go fn(&memorySocket{ - lexit: c.lexit, - exit: c.exit, - send: c.recv, - recv: c.send, - local: c.Remote(), - remote: c.Local(), - timeout: m.topts.Timeout, - ctx: m.topts.Context, - }) - } - } -} - -func (m *memoryTransport) Dial(ctx context.Context, addr string, opts ...DialOption) (Client, error) { - m.RLock() - defer m.RUnlock() - - listener, ok := m.listeners[addr] - if !ok { - return nil, errors.New("could not dial " + addr) - } - - options := NewDialOptions(opts...) - - client := &memoryClient{ - &memorySocket{ - send: make(chan *Message), - recv: make(chan *Message), - exit: make(chan bool), - lexit: listener.exit, - local: addr, - remote: addr, - timeout: m.opts.Timeout, - ctx: m.opts.Context, - }, - options, - } - - // pseudo connect - select { - case <-listener.exit: - return nil, errors.New("connection error") - case listener.conn <- client.memorySocket: - } - - return client, nil -} - -func (m *memoryTransport) Listen(ctx context.Context, addr string, opts ...ListenOption) (Listener, error) { - m.Lock() - defer m.Unlock() - - options := NewListenOptions(opts...) - - host, port, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } - - addr, err = maddr.Extract(host) - if err != nil { - return nil, err - } - - // if zero port then randomly assign one - if len(port) > 0 && port == "0" { - var rng rand.Rand - i := rng.Intn(20000) - port = fmt.Sprintf("%d", 10000+i) - } - - // set addr with port - addr = mnet.HostPort(addr, port) - - if _, ok := m.listeners[addr]; ok { - return nil, errors.New("already listening on " + addr) - } - - listener := &memoryListener{ - lopts: options, - topts: m.opts, - addr: addr, - conn: make(chan *memorySocket), - exit: make(chan bool), - ctx: m.opts.Context, - } - - m.listeners[addr] = listener - - return listener, nil -} - -func (m *memoryTransport) Init(opts ...Option) error { - for _, o := range opts { - o(&m.opts) - } - return nil -} - -func (m *memoryTransport) Options() Options { - return m.opts -} - -func (m *memoryTransport) String() string { - return "memory" -} - -func (m *memoryTransport) Name() string { - return m.opts.Name -} - -// NewTransport returns new memory transport with options -func NewTransport(opts ...Option) Transport { - options := NewOptions(opts...) - - return &memoryTransport{ - opts: options, - listeners: make(map[string]*memoryListener), - } -} diff --git a/network/transport/memory_test.go b/network/transport/memory_test.go deleted file mode 100644 index cf712b86..00000000 --- a/network/transport/memory_test.go +++ /dev/null @@ -1,100 +0,0 @@ -package transport - -import ( - "context" - "os" - "testing" -) - -func TestMemoryTransport(t *testing.T) { - tr := NewTransport() - ctx := context.Background() - // bind / listen - l, err := tr.Listen(ctx, "127.0.0.1:8080") - if err != nil { - t.Fatalf("Unexpected error listening %v", err) - } - defer l.Close() - - cherr := make(chan error, 1) - // accept - go func() { - if nerr := l.Accept(func(sock Socket) { - for { - var m Message - if rerr := sock.Recv(&m); rerr != nil { - cherr <- rerr - return - } - if len(os.Getenv("INTEGRATION_TESTS")) == 0 { - t.Logf("Server Received %s", string(m.Body)) - } - if cerr := sock.Send(&Message{ - Body: []byte(`pong`), - }); cerr != nil { - cherr <- cerr - return - } - } - }); nerr != nil { - cherr <- err - } - }() - - // dial - c, err := tr.Dial(ctx, "127.0.0.1:8080") - if err != nil { - t.Fatalf("Unexpected error dialing %v", err) - } - defer c.Close() - - select { - case err := <-cherr: - t.Fatal(err) - default: - // send <=> receive - for i := 0; i < 3; i++ { - if err := c.Send(&Message{ - Body: []byte(`ping`), - }); err != nil { - return - } - var m Message - if err := c.Recv(&m); err != nil { - return - } - if len(os.Getenv("INTEGRATION_TESTS")) == 0 { - t.Logf("Client Received %s", string(m.Body)) - } - } - } -} - -func TestListener(t *testing.T) { - tr := NewTransport() - ctx := context.Background() - // bind / listen on random port - l, err := tr.Listen(ctx, ":0") - if err != nil { - t.Fatalf("Unexpected error listening %v", err) - } - defer l.Close() - - // try again - l2, err := tr.Listen(ctx, ":0") - if err != nil { - t.Fatalf("Unexpected error listening %v", err) - } - defer l2.Close() - - // now make sure it still fails - l3, err := tr.Listen(ctx, ":8080") - if err != nil { - t.Fatalf("Unexpected error listening %v", err) - } - defer l3.Close() - - if _, err := tr.Listen(ctx, ":8080"); err == nil { - t.Fatal("Expected error binding to :8080 got nil") - } -} diff --git a/network/transport/options.go b/network/transport/options.go deleted file mode 100644 index 6ab32d4b..00000000 --- a/network/transport/options.go +++ /dev/null @@ -1,175 +0,0 @@ -package transport - -import ( - "context" - "crypto/tls" - "time" - - "go.unistack.org/micro/v4/codec" - "go.unistack.org/micro/v4/logger" - "go.unistack.org/micro/v4/meter" - "go.unistack.org/micro/v4/tracer" -) - -// Options struct holds the transport options -type Options struct { - // Meter used for metrics - Meter meter.Meter - // Tracer used for tracing - Tracer tracer.Tracer - // Codec used for marshal/unmarshal messages - Codec codec.Codec - // Logger used for logging - Logger logger.Logger - // Context holds external options - Context context.Context - // TLSConfig holds tls.TLSConfig options - TLSConfig *tls.Config - // Name holds the transport name - Name string - // Addrs holds the transport addrs - Addrs []string - // Timeout holds the timeout - Timeout time.Duration -} - -// NewOptions returns new options -func NewOptions(opts ...Option) Options { - options := Options{ - Logger: logger.DefaultLogger, - Meter: meter.DefaultMeter, - Tracer: tracer.DefaultTracer, - Context: context.Background(), - } - - for _, o := range opts { - o(&options) - } - - return options -} - -// DialOptions struct -type DialOptions struct { - // Context holds the external options - Context context.Context - // Timeout holds the timeout - Timeout time.Duration - // Stream flag - Stream bool -} - -// NewDialOptions returns new DialOptions -func NewDialOptions(opts ...DialOption) DialOptions { - options := DialOptions{ - Timeout: DefaultDialTimeout, - Context: context.Background(), - } - - for _, o := range opts { - o(&options) - } - - return options -} - -// ListenOptions struct -type ListenOptions struct { - // TODO: add tls options when listening - // Currently set in global options - // Context holds the external options - Context context.Context - // TLSConfig holds the *tls.Config options - TLSConfig *tls.Config -} - -// NewListenOptions returns new ListenOptions -func NewListenOptions(opts ...ListenOption) ListenOptions { - options := ListenOptions{ - Context: context.Background(), - } - - for _, o := range opts { - o(&options) - } - - return options -} - -// Addrs to use for transport -func Addrs(addrs ...string) Option { - return func(o *Options) { - o.Addrs = addrs - } -} - -// Logger sets the logger -func Logger(l logger.Logger) Option { - return func(o *Options) { - o.Logger = l - } -} - -// Meter sets the meter -func Meter(m meter.Meter) Option { - return func(o *Options) { - o.Meter = m - } -} - -// Context sets the context -func Context(ctx context.Context) Option { - return func(o *Options) { - o.Context = ctx - } -} - -// Codec sets the codec used for encoding where the transport -// does not support message headers -func Codec(c codec.Codec) Option { - return func(o *Options) { - o.Codec = c - } -} - -// Timeout sets the timeout for Send/Recv execution -func Timeout(t time.Duration) Option { - return func(o *Options) { - o.Timeout = t - } -} - -// TLSConfig to be used for the transport. -func TLSConfig(t *tls.Config) Option { - return func(o *Options) { - o.TLSConfig = t - } -} - -// WithStream indicates whether this is a streaming connection -func WithStream() DialOption { - return func(o *DialOptions) { - o.Stream = true - } -} - -// WithTimeout used when dialling the remote side -func WithTimeout(d time.Duration) DialOption { - return func(o *DialOptions) { - o.Timeout = d - } -} - -// Tracer to be used for tracing -func Tracer(t tracer.Tracer) Option { - return func(o *Options) { - o.Tracer = t - } -} - -// Name sets the name -func Name(n string) Option { - return func(o *Options) { - o.Name = n - } -} diff --git a/network/transport/transport.go b/network/transport/transport.go deleted file mode 100644 index b8005a97..00000000 --- a/network/transport/transport.go +++ /dev/null @@ -1,63 +0,0 @@ -// Package transport is an interface for synchronous connection based communication -package transport // import "go.unistack.org/micro/v4/network/transport" - -import ( - "context" - "time" - - "go.unistack.org/micro/v4/metadata" -) - -var ( - // DefaultTransport is the global default transport - DefaultTransport Transport = NewTransport() - // DefaultDialTimeout the default dial timeout - DefaultDialTimeout = time.Second * 5 -) - -// Transport is an interface which is used for communication between -// services. It uses connection based socket send/recv semantics and -// has various implementations; http, grpc, quic. -type Transport interface { - Init(...Option) error - Options() Options - Dial(ctx context.Context, addr string, opts ...DialOption) (Client, error) - Listen(ctx context.Context, addr string, opts ...ListenOption) (Listener, error) - String() string -} - -// Message is used to transfer data -type Message struct { - Header metadata.Metadata - Body []byte -} - -// Socket bastraction interface -type Socket interface { - Recv(*Message) error - Send(*Message) error - Close() error - Local() string - Remote() string -} - -// Client is the socket owner -type Client interface { - Socket -} - -// Listener is the interface for stream oriented messaging -type Listener interface { - Addr() string - Close() error - Accept(func(Socket)) error -} - -// Option is the option signature -type Option func(*Options) - -// DialOption is the option signature -type DialOption func(*DialOptions) - -// ListenOption is the option signature -type ListenOption func(*ListenOptions) diff --git a/network/tunnel/broker/broker.go b/network/tunnel/broker/broker.go deleted file mode 100644 index 75001fb2..00000000 --- a/network/tunnel/broker/broker.go +++ /dev/null @@ -1,356 +0,0 @@ -// Package broker is a tunnel broker -package broker // import "go.unistack.org/micro/v4/network/tunnel/broker" - -import ( - "context" - "fmt" - - "go.unistack.org/micro/v4/broker" - "go.unistack.org/micro/v4/logger" - "go.unistack.org/micro/v4/metadata" - "go.unistack.org/micro/v4/network/transport" - "go.unistack.org/micro/v4/network/tunnel" -) - -type tunBroker struct { - tunnel tunnel.Tunnel - opts broker.Options -} - -type tunSubscriber struct { - listener tunnel.Listener - handler broker.Handler - closed chan bool - topic string - opts broker.SubscribeOptions -} - -type tunBatchSubscriber struct { - listener tunnel.Listener - handler broker.BatchHandler - closed chan bool - topic string - opts broker.SubscribeOptions -} - -type tunEvent struct { - err error - message *broker.Message - topic string -} - -// used to access tunnel from options context -type ( - tunnelKey struct{} - tunnelAddr struct{} -) - -func (t *tunBroker) Init(opts ...broker.Option) error { - for _, o := range opts { - o(&t.opts) - } - return nil -} - -func (t *tunBroker) Name() string { - return t.opts.Name -} - -func (t *tunBroker) Options() broker.Options { - return t.opts -} - -func (t *tunBroker) Address() string { - return t.tunnel.Address() -} - -func (t *tunBroker) Connect(ctx context.Context) error { - return t.tunnel.Connect(ctx) -} - -func (t *tunBroker) Disconnect(ctx context.Context) error { - return t.tunnel.Close(ctx) -} - -func (t *tunBroker) BatchPublish(ctx context.Context, msgs []*broker.Message, opts ...broker.PublishOption) error { - // TODO: this is probably inefficient, we might want to just maintain an open connection - // it may be easier to add broadcast to the tunnel - topicMap := make(map[string]tunnel.Session) - - var err error - for _, msg := range msgs { - topic, _ := msg.Header.Get(metadata.HeaderTopic) - c, ok := topicMap[topic] - if !ok { - c, err = t.tunnel.Dial(ctx, topic, tunnel.DialMode(tunnel.Multicast)) - if err != nil { - return err - } - defer c.Close() - topicMap[topic] = c - } - - if err = c.Send(&transport.Message{ - Header: msg.Header, - Body: msg.Body, - }); err != nil { - // msg.SetError(err) - return err - } - } - - return nil -} - -func (t *tunBroker) Publish(ctx context.Context, topic string, m *broker.Message, opts ...broker.PublishOption) error { - // TODO: this is probably inefficient, we might want to just maintain an open connection - // it may be easier to add broadcast to the tunnel - c, err := t.tunnel.Dial(ctx, topic, tunnel.DialMode(tunnel.Multicast)) - if err != nil { - return err - } - defer c.Close() - - return c.Send(&transport.Message{ - Header: m.Header, - Body: m.Body, - }) -} - -func (t *tunBroker) BatchSubscribe(ctx context.Context, topic string, h broker.BatchHandler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { - l, err := t.tunnel.Listen(ctx, topic, tunnel.ListenMode(tunnel.Multicast)) - if err != nil { - return nil, err - } - - tunSub := &tunBatchSubscriber{ - topic: topic, - handler: h, - opts: broker.NewSubscribeOptions(opts...), - closed: make(chan bool), - listener: l, - } - - // start processing - go tunSub.run() - - return tunSub, nil -} - -func (t *tunBroker) Subscribe(ctx context.Context, topic string, h broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) { - l, err := t.tunnel.Listen(ctx, topic, tunnel.ListenMode(tunnel.Multicast)) - if err != nil { - return nil, err - } - - tunSub := &tunSubscriber{ - topic: topic, - handler: h, - opts: broker.NewSubscribeOptions(opts...), - closed: make(chan bool), - listener: l, - } - - // start processing - go tunSub.run() - - return tunSub, nil -} - -func (t *tunBroker) String() string { - return "tunnel" -} - -func (t *tunBatchSubscriber) run() { - for { - // accept a new connection - c, err := t.listener.Accept() - if err != nil { - select { - case <-t.closed: - return - default: - continue - } - } - - // receive message - m := new(transport.Message) - if err := c.Recv(m); err != nil { - if logger.V(logger.ErrorLevel) { - logger.Error(t.opts.Context, err.Error()) - } - if err = c.Close(); err != nil { - if logger.V(logger.ErrorLevel) { - logger.Error(t.opts.Context, err.Error()) - } - } - continue - } - - // close the connection - c.Close() - - evts := broker.Events{&tunEvent{ - topic: t.topic, - message: &broker.Message{ - Header: m.Header, - Body: m.Body, - }, - }} - // handle the message - go func() { - _ = t.handler(evts) - }() - - } -} - -func (t *tunSubscriber) run() { - for { - // accept a new connection - c, err := t.listener.Accept() - if err != nil { - select { - case <-t.closed: - return - default: - continue - } - } - - // receive message - m := new(transport.Message) - if err := c.Recv(m); err != nil { - if logger.V(logger.ErrorLevel) { - logger.Error(t.opts.Context, err.Error()) - } - if err = c.Close(); err != nil { - if logger.V(logger.ErrorLevel) { - logger.Error(t.opts.Context, err.Error()) - } - } - continue - } - - // close the connection - c.Close() - - // handle the message - go func() { - _ = t.handler(&tunEvent{ - topic: t.topic, - message: &broker.Message{ - Header: m.Header, - Body: m.Body, - }, - }) - }() - } -} - -func (t *tunBatchSubscriber) Options() broker.SubscribeOptions { - return t.opts -} - -func (t *tunBatchSubscriber) Topic() string { - return t.topic -} - -func (t *tunBatchSubscriber) Unsubscribe(ctx context.Context) error { - select { - case <-t.closed: - return nil - default: - close(t.closed) - return t.listener.Close() - } -} - -func (t *tunSubscriber) Options() broker.SubscribeOptions { - return t.opts -} - -func (t *tunSubscriber) Topic() string { - return t.topic -} - -func (t *tunSubscriber) Unsubscribe(ctx context.Context) error { - select { - case <-t.closed: - return nil - default: - close(t.closed) - return t.listener.Close() - } -} - -func (t *tunEvent) Topic() string { - return t.topic -} - -func (t *tunEvent) Message() *broker.Message { - return t.message -} - -func (t *tunEvent) Ack() error { - return nil -} - -func (t *tunEvent) Error() error { - return t.err -} - -func (t *tunEvent) SetError(err error) { - t.err = err -} - -// NewBroker returns new tunnel broker -func NewBroker(opts ...broker.Option) (broker.Broker, error) { - options := broker.NewOptions(opts...) - - t, ok := options.Context.Value(tunnelKey{}).(tunnel.Tunnel) - if !ok { - return nil, fmt.Errorf("tunnel not set") - } - - a, ok := options.Context.Value(tunnelAddr{}).(string) - if ok { - // initialise address - if err := t.Init(tunnel.Address(a)); err != nil { - return nil, err - } - } - - if len(options.Addrs) > 0 { - // initialise nodes - if err := t.Init(tunnel.Nodes(options.Addrs...)); err != nil { - return nil, err - } - } - - return &tunBroker{ - opts: options, - tunnel: t, - }, nil -} - -// WithAddress sets the tunnel address -func WithAddress(a string) broker.Option { - return func(o *broker.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, tunnelAddr{}, a) - } -} - -// WithTunnel sets the internal tunnel -func WithTunnel(t tunnel.Tunnel) broker.Option { - return func(o *broker.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, tunnelKey{}, t) - } -} diff --git a/network/tunnel/options.go b/network/tunnel/options.go deleted file mode 100644 index fbb5570b..00000000 --- a/network/tunnel/options.go +++ /dev/null @@ -1,192 +0,0 @@ -package tunnel - -import ( - "time" - - "go.unistack.org/micro/v4/logger" - "go.unistack.org/micro/v4/meter" - "go.unistack.org/micro/v4/network/transport" - "go.unistack.org/micro/v4/tracer" - "go.unistack.org/micro/v4/util/id" -) - -var ( - // DefaultAddress is default tunnel bind address - DefaultAddress = ":0" - // DefaultToken the shared default token - DefaultToken = "go.micro.tunnel" -) - -// Option func signature -type Option func(*Options) - -// Options provides network configuration options -type Options struct { - // Logger used for logging - Logger logger.Logger - // Meter used for metrics - Meter meter.Meter - // Tracer used for tracing - Tracer tracer.Tracer - // Transport used for communication - Transport transport.Transport - // Token the shared auth token - Token string - // Name holds the tunnel name - Name string - // ID holds the tunnel id - ID string - // Address holds the tunnel address - Address string - // Nodes holds the tunnel nodes - Nodes []string -} - -// DialOption func -type DialOption func(*DialOptions) - -// DialOptions provides dial options -type DialOptions struct { - // Link specifies the link to use - Link string - // specify mode of the session - Mode Mode - // Wait for connection to be accepted - Wait bool - // the dial timeout - Timeout time.Duration -} - -// ListenOption func -type ListenOption func(*ListenOptions) - -// ListenOptions provides listen options -type ListenOptions struct { - // Mode specify mode of the session - Mode Mode - // Timeout the read timeout - Timeout time.Duration -} - -// ID sets the tunnel id -func ID(id string) Option { - return func(o *Options) { - o.ID = id - } -} - -// Logger sets the logger -func Logger(l logger.Logger) Option { - return func(o *Options) { - o.Logger = l - } -} - -// Meter sets the meter -func Meter(m meter.Meter) Option { - return func(o *Options) { - o.Meter = m - } -} - -// Address sets the tunnel address -func Address(a string) Option { - return func(o *Options) { - o.Address = a - } -} - -// Nodes specify remote network nodes -func Nodes(n ...string) Option { - return func(o *Options) { - o.Nodes = n - } -} - -// Token sets the shared token for auth -func Token(t string) Option { - return func(o *Options) { - o.Token = t - } -} - -// Transport listens for incoming connections -func Transport(t transport.Transport) Option { - return func(o *Options) { - o.Transport = t - } -} - -// ListenMode option -func ListenMode(m Mode) ListenOption { - return func(o *ListenOptions) { - o.Mode = m - } -} - -// ListenTimeout for reads and writes on the listener session -func ListenTimeout(t time.Duration) ListenOption { - return func(o *ListenOptions) { - o.Timeout = t - } -} - -// DialMode multicast sets the multicast option to send only to those mapped -func DialMode(m Mode) DialOption { - return func(o *DialOptions) { - o.Mode = m - } -} - -// DialTimeout sets the dial timeout of the connection -func DialTimeout(t time.Duration) DialOption { - return func(o *DialOptions) { - o.Timeout = t - } -} - -// DialLink specifies the link to pin this connection to. -// This is not applicable if the multicast option is set. -func DialLink(id string) DialOption { - return func(o *DialOptions) { - o.Link = id - } -} - -// DialWait specifies whether to wait for the connection -// to be accepted before returning the session -func DialWait(b bool) DialOption { - return func(o *DialOptions) { - o.Wait = b - } -} - -// NewOptions returns router default options with filled values -func NewOptions(opts ...Option) Options { - options := Options{ - ID: id.Must(), - Address: DefaultAddress, - Token: DefaultToken, - Logger: logger.DefaultLogger, - Meter: meter.DefaultMeter, - Tracer: tracer.DefaultTracer, - } - for _, o := range opts { - o(&options) - } - return options -} - -// Tracer to be used for tracing -func Tracer(t tracer.Tracer) Option { - return func(o *Options) { - o.Tracer = t - } -} - -// Name sets the name -func Name(n string) Option { - return func(o *Options) { - o.Name = n - } -} diff --git a/network/tunnel/transport/listener.go b/network/tunnel/transport/listener.go deleted file mode 100644 index eb506e48..00000000 --- a/network/tunnel/transport/listener.go +++ /dev/null @@ -1,30 +0,0 @@ -package transport - -import ( - "go.unistack.org/micro/v4/network/transport" - "go.unistack.org/micro/v4/network/tunnel" -) - -type tunListener struct { - l tunnel.Listener -} - -func (t *tunListener) Addr() string { - return t.l.Channel() -} - -func (t *tunListener) Close() error { - return t.l.Close() -} - -func (t *tunListener) Accept(fn func(socket transport.Socket)) error { - for { - // accept connection - c, err := t.l.Accept() - if err != nil { - return err - } - // execute the function - go fn(c) - } -} diff --git a/network/tunnel/transport/transport.go b/network/tunnel/transport/transport.go deleted file mode 100644 index 87959a79..00000000 --- a/network/tunnel/transport/transport.go +++ /dev/null @@ -1,113 +0,0 @@ -// Package transport provides a tunnel transport -package transport // import "go.unistack.org/micro/v4/network/tunnel/transport" - -import ( - "context" - "fmt" - - "go.unistack.org/micro/v4/network/transport" - "go.unistack.org/micro/v4/network/tunnel" -) - -type tunTransport struct { - tunnel tunnel.Tunnel - options transport.Options -} - -type tunnelKey struct{} - -type transportKey struct{} - -func (t *tunTransport) Init(opts ...transport.Option) error { - for _, o := range opts { - o(&t.options) - } - - // close the existing tunnel - if t.tunnel != nil { - t.tunnel.Close(context.TODO()) - } - - // get the tunnel - tun, ok := t.options.Context.Value(tunnelKey{}).(tunnel.Tunnel) - if !ok { - return fmt.Errorf("tunnel not set") - } - - // get the transport - tr, ok := t.options.Context.Value(transportKey{}).(transport.Transport) - if ok { - _ = tun.Init(tunnel.Transport(tr)) - } - - // set the tunnel - t.tunnel = tun - - return nil -} - -func (t *tunTransport) Dial(ctx context.Context, addr string, opts ...transport.DialOption) (transport.Client, error) { - if err := t.tunnel.Connect(ctx); err != nil { - return nil, err - } - - c, err := t.tunnel.Dial(ctx, addr) - if err != nil { - return nil, err - } - - return c, nil -} - -func (t *tunTransport) Listen(ctx context.Context, addr string, opts ...transport.ListenOption) (transport.Listener, error) { - if err := t.tunnel.Connect(ctx); err != nil { - return nil, err - } - - l, err := t.tunnel.Listen(ctx, addr) - if err != nil { - return nil, err - } - - return &tunListener{l}, nil -} - -func (t *tunTransport) Options() transport.Options { - return t.options -} - -func (t *tunTransport) String() string { - return "tunnel" -} - -// NewTransport honours the initialiser used in -func NewTransport(opts ...transport.Option) transport.Transport { - t := &tunTransport{ - options: transport.Options{}, - } - - // initialise - // t.Init(opts...) - - return t -} - -// WithTunnel sets the internal tunnel -func WithTunnel(t tunnel.Tunnel) transport.Option { - return func(o *transport.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, tunnelKey{}, t) - } -} - -// WithTransport sets the internal transport -func WithTransport(t transport.Transport) transport.Option { - return func(o *transport.Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, transportKey{}, t) - } -} diff --git a/network/tunnel/tunnel.go b/network/tunnel/tunnel.go deleted file mode 100644 index c6d36aef..00000000 --- a/network/tunnel/tunnel.go +++ /dev/null @@ -1,106 +0,0 @@ -// Package tunnel provides gre network tunnelling -package tunnel // import "go.unistack.org/micro/v4/network/transport/tunnel" - -import ( - "context" - "errors" - "time" - - "go.unistack.org/micro/v4/network/transport" -) - -// DefaultTunnel contains default tunnel implementation -var DefaultTunnel Tunnel - -const ( - // Unicast send over one link - Unicast Mode = iota - // Multicast send to all channel listeners - Multicast - // Broadcast send to all links - Broadcast -) - -var ( - // DefaultDialTimeout is the dial timeout if none is specified - DefaultDialTimeout = time.Second * 5 - // ErrDialTimeout is returned by a call to Dial where the timeout occurs - ErrDialTimeout = errors.New("dial timeout") - // ErrDiscoverChan is returned when we failed to receive the "announce" back from a discovery - ErrDiscoverChan = errors.New("failed to discover channel") - // ErrLinkNotFound is returned when a link is specified at dial time and does not exist - ErrLinkNotFound = errors.New("link not found") - // ErrLinkDisconnected is returned when a link we attempt to send to is disconnected - ErrLinkDisconnected = errors.New("link not connected") - // ErrLinkLoopback is returned when attempting to send an outbound message over loopback link - ErrLinkLoopback = errors.New("link is loopback") - // ErrLinkRemote is returned when attempting to send a loopback message over remote link - ErrLinkRemote = errors.New("link is remote") - // ErrReadTimeout is a timeout on session.Recv - ErrReadTimeout = errors.New("read timeout") - // ErrDecryptingData is for when theres a nonce error - ErrDecryptingData = errors.New("error decrypting data") -) - -// Mode of the session -type Mode uint8 - -// Tunnel creates a gre tunnel on top of the micro/transport. -// It establishes multiple streams using the Micro-Tunnel-Channel header -// and Micro-Tunnel-Session header. The tunnel id is a hash of -// the address being requested. -type Tunnel interface { - // Init initializes tunnel with options - Init(opts ...Option) error - // Address returns the address the tunnel is listening on - Address() string - // Connect connects the tunnel - Connect(ctx context.Context) error - // Close closes the tunnel - Close(ctx context.Context) error - // Links returns all the links the tunnel is connected to - Links() []Link - // Dial allows a client to connect to a channel - Dial(ctx context.Context, channel string, opts ...DialOption) (Session, error) - // Listen allows to accept connections on a channel - Listen(ctx context.Context, channel string, opts ...ListenOption) (Listener, error) - // String returns the name of the tunnel implementation - String() string -} - -// Link represents internal links to the tunnel -type Link interface { - // Id returns the link unique Id - Id() string - // Delay is the current load on the link (lower is better) - Delay() int64 - // Length returns the roundtrip time as nanoseconds (lower is better) - Length() int64 - // Current transfer rate as bits per second (lower is better) - Rate() float64 - // Is this a loopback link - Loopback() bool - // State of the link: connected/closed/error - State() string - // honours transport socket - transport.Socket -} - -// Listener provides similar constructs to the transport.Listener -type Listener interface { - Accept() (Session, error) - Channel() string - Close() error -} - -// Session is a unique session created when dialling or accepting connections on the tunnel -type Session interface { - // The unique session id - Id() string - // The channel name - Channel() string - // The link the session is on - Link() string - // a transport socket - transport.Socket -} diff --git a/options.go b/options.go index e564a536..9bb39b8a 100644 --- a/options.go +++ b/options.go @@ -11,6 +11,7 @@ import ( "go.unistack.org/micro/v4/logger" "go.unistack.org/micro/v4/metadata" "go.unistack.org/micro/v4/meter" + "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/register" "go.unistack.org/micro/v4/router" "go.unistack.org/micro/v4/server" @@ -201,7 +202,7 @@ func Logger(l logger.Logger, opts ...LoggerOption) Option { for _, srv := range o.Servers { for _, os := range lopts.servers { if srv.Name() == os || all { - if err = srv.Init(server.Logger(l)); err != nil { + if err = srv.Init(options.Logger(l)); err != nil { return err } } @@ -210,7 +211,7 @@ func Logger(l logger.Logger, opts ...LoggerOption) Option { for _, cli := range o.Clients { for _, oc := range lopts.clients { if cli.Name() == oc || all { - if err = cli.Init(client.Logger(l)); err != nil { + if err = cli.Init(options.Logger(l)); err != nil { return err } } @@ -219,7 +220,7 @@ func Logger(l logger.Logger, opts ...LoggerOption) Option { for _, brk := range o.Brokers { for _, ob := range lopts.brokers { if brk.Name() == ob || all { - if err = brk.Init(broker.Logger(l)); err != nil { + if err = brk.Init(options.Logger(l)); err != nil { return err } } @@ -237,7 +238,7 @@ func Logger(l logger.Logger, opts ...LoggerOption) Option { for _, str := range o.Stores { for _, or := range lopts.stores { if str.Name() == or || all { - if err = str.Init(store.Logger(l)); err != nil { + if err = str.Init(options.Logger(l)); err != nil { return err } } @@ -246,7 +247,7 @@ func Logger(l logger.Logger, opts ...LoggerOption) Option { for _, mtr := range o.Meters { for _, or := range lopts.meters { if mtr.Name() == or || all { - if err = mtr.Init(meter.Logger(l)); err != nil { + if err = mtr.Init(options.Logger(l)); err != nil { return err } } @@ -255,7 +256,7 @@ func Logger(l logger.Logger, opts ...LoggerOption) Option { for _, trc := range o.Tracers { for _, ot := range lopts.tracers { if trc.Name() == ot || all { - if err = trc.Init(tracer.Logger(l)); err != nil { + if err = trc.Init(options.Logger(l)); err != nil { return err } } @@ -329,7 +330,7 @@ func Register(r register.Register, opts ...RegisterOption) Option { for _, srv := range o.Servers { for _, os := range ropts.servers { if srv.Name() == os || all { - if err = srv.Init(server.Register(r)); err != nil { + if err = srv.Init(options.Register(r)); err != nil { return err } } @@ -338,7 +339,7 @@ func Register(r register.Register, opts ...RegisterOption) Option { for _, brk := range o.Brokers { for _, os := range ropts.brokers { if brk.Name() == os || all { - if err = brk.Init(broker.Register(r)); err != nil { + if err = brk.Init(options.Register(r)); err != nil { return err } } @@ -395,7 +396,7 @@ func Tracer(t tracer.Tracer, opts ...TracerOption) Option { for _, srv := range o.Servers { for _, os := range topts.servers { if srv.Name() == os || all { - if err = srv.Init(server.Tracer(t)); err != nil { + if err = srv.Init(options.Tracer(t)); err != nil { return err } } @@ -404,7 +405,7 @@ func Tracer(t tracer.Tracer, opts ...TracerOption) Option { for _, cli := range o.Clients { for _, os := range topts.clients { if cli.Name() == os || all { - if err = cli.Init(client.Tracer(t)); err != nil { + if err = cli.Init(options.Tracer(t)); err != nil { return err } } @@ -413,7 +414,7 @@ func Tracer(t tracer.Tracer, opts ...TracerOption) Option { for _, str := range o.Stores { for _, os := range topts.stores { if str.Name() == os || all { - if err = str.Init(store.Tracer(t)); err != nil { + if err = str.Init(options.Tracer(t)); err != nil { return err } } @@ -422,7 +423,7 @@ func Tracer(t tracer.Tracer, opts ...TracerOption) Option { for _, brk := range o.Brokers { for _, os := range topts.brokers { if brk.Name() == os || all { - if err = brk.Init(broker.Tracer(t)); err != nil { + if err = brk.Init(options.Tracer(t)); err != nil { return err } } @@ -521,7 +522,7 @@ func Router(r router.Router, opts ...RouterOption) Option { for _, cli := range o.Clients { for _, os := range ropts.clients { if cli.Name() == os || all { - if err = cli.Init(client.Router(r)); err != nil { + if err = cli.Init(options.Router(r)); err != nil { return err } } @@ -556,7 +557,7 @@ func Address(addr string) Option { default: return fmt.Errorf("cant set same address for multiple servers") } - return o.Servers[0].Init(server.Address(addr)) + return o.Servers[0].Init(options.Address(addr)) } } diff --git a/options/options.go b/options/options.go new file mode 100644 index 00000000..43f035ec --- /dev/null +++ b/options/options.go @@ -0,0 +1,196 @@ +package options + +import ( + "context" + "crypto/tls" + "reflect" + "time" + + "go.unistack.org/micro/v4/metadata" + rutil "go.unistack.org/micro/v4/util/reflect" +) + +// Option func signature +type Option func(interface{}) error + +// Set assign value to struct by its path +func Set(src interface{}, dst interface{}, path string) error { + return rutil.SetFieldByPath(src, dst, path) +} + +// Get returns value from struct by its path +func Get(src interface{}, path string) (interface{}, error) { + return rutil.StructFieldByPath(src, path) +} + +// Name set Name value +func Name(v ...string) Option { + return func(src interface{}) error { + return Set(src, v, ".Name") + } +} + +// Address set Address value to single string or slice of strings +func Address(v ...string) Option { + return func(src interface{}) error { + return Set(src, v, ".Address") + } +} + +// Broker set Broker value +func Broker(v interface{}) Option { + return func(src interface{}) error { + return Set(src, v, ".Broker") + } +} + +// Logger set Logger value +func Logger(v interface{}) Option { + return func(src interface{}) error { + return Set(src, v, ".Logger") + } +} + +// Meter set Meter value +func Meter(v interface{}) Option { + return func(src interface{}) error { + return Set(src, v, ".Meter") + } +} + +// Tracer set Tracer value +func Tracer(v interface{}) Option { + return func(src interface{}) error { + return Set(src, v, ".Tracer") + } +} + +// Store set Store value +func Store(v interface{}) Option { + return func(src interface{}) error { + return Set(src, v, ".Store") + } +} + +// Register set Register value +func Register(v interface{}) Option { + return func(src interface{}) error { + return Set(src, v, ".Register") + } +} + +// Router set Router value +func Router(v interface{}) Option { + return func(src interface{}) error { + return Set(src, v, ".Router") + } +} + +// Codec set Codec value +func Codec(v interface{}) Option { + return func(src interface{}) error { + return Set(src, v, ".Codec") + } +} + +// Client set Client value +func Client(v interface{}) Option { + return func(src interface{}) error { + return Set(src, v, ".Client") + } +} + +// Codecs to be used to encode/decode requests for a given content type +func Codecs(ct string, v interface{}) Option { + return func(src interface{}) error { + cm, err := Get(src, ".Codecs") + if err != nil { + return err + } else if rutil.IsZero(cm) { + cm = reflect.MakeMap(reflect.TypeOf(cm)).Interface() + } + cv := reflect.ValueOf(cm) + cv.SetMapIndex(reflect.ValueOf(ct), reflect.ValueOf(v)) + return Set(src, cv.Interface(), ".Codecs") + } +} + +// Context set Context value +func Context(v context.Context) Option { + return func(src interface{}) error { + return Set(src, v, ".Context") + } +} + +// TLSConfig set TLSConfig value +func TLSConfig(v *tls.Config) Option { + return func(src interface{}) error { + return Set(src, v, ".TLSConfig") + } +} + +func ContextOption(k, v interface{}) Option { + return func(src interface{}) error { + ctx, err := Get(src, ".Context") + if err != nil { + return err + } + if ctx == nil { + ctx = context.Background() + } + err = Set(src, context.WithValue(ctx.(context.Context), k, v), ".Context") + return err + } +} + +// ContentType pass ContentType for message data +func ContentType(ct string) Option { + return func(src interface{}) error { + return Set(src, ct, ".ContentType") + } +} + +// Metadata pass additional metadata +func Metadata(md metadata.Metadata) Option { + return func(src interface{}) error { + return Set(src, metadata.Copy(md), ".Metadata") + } +} + +// Namespace to use +func Namespace(ns string) Option { + return func(src interface{}) error { + return Set(src, ns, ".Namespace") + } +} + +// Labels sets the labels +func Labels(ls ...interface{}) Option { + return func(src interface{}) error { + v, err := Get(src, ".Labels") + if err != nil { + return err + } else if rutil.IsZero(v) { + v = reflect.MakeSlice(reflect.TypeOf(v), 0, len(ls)).Interface() + } + cv := reflect.ValueOf(v) + for _, l := range ls { + cv = reflect.Append(cv, reflect.ValueOf(l)) + } + return Set(src, cv.Interface(), ".Labels") + } +} + +// Timeout pass timeout time.Duration +func Timeout(td time.Duration) Option { + return func(src interface{}) error { + return Set(src, td, ".Timeout") + } +} + +// ID sets the step ID +func StepID(id string) Option { + return func(src interface{}) error { + return Set(src, id, ".ID") + } +} diff --git a/options/options_test.go b/options/options_test.go new file mode 100644 index 00000000..7d3b6a62 --- /dev/null +++ b/options/options_test.go @@ -0,0 +1,86 @@ +package options_test + +import ( + "testing" + + "go.unistack.org/micro/v4/codec" + "go.unistack.org/micro/v4/options" +) + +func TestAddress(t *testing.T) { + var err error + + type s struct { + Address []string + } + + src := &s{} + var opts []options.Option + opts = append(opts, options.Address("host:port")) + + for _, o := range opts { + if err = o(src); err != nil { + t.Fatal(err) + } + } + + if src.Address[0] != "host:port" { + t.Fatal("failed to set Address") + } +} + +func TestCodecs(t *testing.T) { + var err error + + type s struct { + Codecs map[string]codec.Codec + } + + src := &s{} + var opts []options.Option + c := codec.NewCodec() + opts = append(opts, options.Codecs("text/html", c)) + + for _, o := range opts { + if err = o(src); err != nil { + t.Fatal(err) + } + } + + for k, v := range src.Codecs { + if k != "text/html" || v != c { + continue + } + return + } + + t.Fatalf("failed to set Codecs") +} + +func TestLabels(t *testing.T) { + type str1 struct { + Labels []string + } + type str2 struct { + Labels []interface{} + } + + x1 := &str1{} + + if err := options.Labels("one", "two")(x1); err != nil { + t.Fatal(err) + } + if len(x1.Labels) != 2 { + t.Fatal("failed to set labels") + } + x2 := &str2{} + if err := options.Labels("key", "val")(x2); err != nil { + t.Fatal(err) + } + if len(x2.Labels) != 2 { + t.Fatal("failed to set labels") + } + if x2.Labels[0] != "key" { + t.Fatal("failed to set labels") + } +} diff --git a/proxy/options.go b/proxy/options.go deleted file mode 100644 index f1d01271..00000000 --- a/proxy/options.go +++ /dev/null @@ -1,98 +0,0 @@ -// Package proxy is a transparent proxy built on the micro/server -package proxy - -import ( - "go.unistack.org/micro/v4/client" - "go.unistack.org/micro/v4/logger" - "go.unistack.org/micro/v4/meter" - "go.unistack.org/micro/v4/router" - "go.unistack.org/micro/v4/tracer" -) - -// Options for proxy -type Options struct { - // Tracer used for tracing - Tracer tracer.Tracer - // Client for communication - Client client.Client - // Router for routing - Router router.Router - // Logger used for logging - Logger logger.Logger - // Meter used for metrics - Meter meter.Meter - // Links holds the communication links - Links map[string]client.Client - // Endpoint holds the destination address - Endpoint string -} - -// Option func signature -type Option func(o *Options) - -// NewOptions returns new options struct that filled by opts -func NewOptions(opts ...Option) Options { - options := Options{ - Logger: logger.DefaultLogger, - Meter: meter.DefaultMeter, - Tracer: tracer.DefaultTracer, - } - - for _, o := range opts { - o(&options) - } - - return options -} - -// WithEndpoint sets a proxy endpoint -func WithEndpoint(e string) Option { - return func(o *Options) { - o.Endpoint = e - } -} - -// WithClient sets the client -func WithClient(c client.Client) Option { - return func(o *Options) { - o.Client = c - } -} - -// WithRouter specifies the router to use -func WithRouter(r router.Router) Option { - return func(o *Options) { - o.Router = r - } -} - -// WithLogger specifies the logger to use -func WithLogger(l logger.Logger) Option { - return func(o *Options) { - o.Logger = l - } -} - -// WithMeter specifies the meter to use -func WithMeter(m meter.Meter) Option { - return func(o *Options) { - o.Meter = m - } -} - -// WithLink sets a link for outbound requests -func WithLink(name string, c client.Client) Option { - return func(o *Options) { - if o.Links == nil { - o.Links = make(map[string]client.Client) - } - o.Links[name] = c - } -} - -// Tracer to be used for tracing -func Tracer(t tracer.Tracer) Option { - return func(o *Options) { - o.Tracer = t - } -} diff --git a/proxy/proxy.go b/proxy/proxy.go deleted file mode 100644 index b58bcc74..00000000 --- a/proxy/proxy.go +++ /dev/null @@ -1,21 +0,0 @@ -// Package proxy is a transparent proxy built on the micro/server -package proxy // import "go.unistack.org/micro/v4/proxy" - -import ( - "context" - - "go.unistack.org/micro/v4/server" -) - -// DefaultEndpoint holds default proxy address -var DefaultEndpoint = "localhost:9090" - -// Proxy can be used as a proxy server for micro services -type Proxy interface { - // ProcessMessage handles inbound messages - ProcessMessage(context.Context, server.Message) error - // ServeRequest handles inbound requests - ServeRequest(context.Context, server.Request, server.Response) error - // Name of the proxy protocol - String() string -} diff --git a/semconv/broker.go b/semconv/broker.go new file mode 100644 index 00000000..9b61b523 --- /dev/null +++ b/semconv/broker.go @@ -0,0 +1,20 @@ +package semconv + +var ( + // PublishMessageDurationSeconds specifies meter metric name + PublishMessageDurationSeconds = "publish_message_duration_seconds" + // PublishMessageLatencyMicroseconds specifies meter metric name + PublishMessageLatencyMicroseconds = "publish_message_latency_microseconds" + // PublishMessageTotal specifies meter metric name + PublishMessageTotal = "publish_message_total" + // PublishMessageInflight specifies meter metric name + PublishMessageInflight = "publish_message_inflight" + // SubscribeMessageDurationSeconds specifies meter metric name + SubscribeMessageDurationSeconds = "subscribe_message_duration_seconds" + // SubscribeMessageLatencyMicroseconds specifies meter metric name + SubscribeMessageLatencyMicroseconds = "subscribe_message_latency_microseconds" + // SubscribeMessageTotal specifies meter metric name + SubscribeMessageTotal = "subscribe_message_total" + // SubscribeMessageInflight specifies meter metric name + SubscribeMessageInflight = "subscribe_message_inflight" +) diff --git a/semconv/client.go b/semconv/client.go new file mode 100644 index 00000000..96671471 --- /dev/null +++ b/semconv/client.go @@ -0,0 +1,12 @@ +package semconv + +var ( + // ClientRequestDurationSeconds specifies meter metric name + ClientRequestDurationSeconds = "client_request_duration_seconds" + // ClientRequestLatencyMicroseconds specifies meter metric name + ClientRequestLatencyMicroseconds = "client_request_latency_microseconds" + // ClientRequestTotal specifies meter metric name + ClientRequestTotal = "client_request_total" + // ClientRequestInflight specifies meter metric name + ClientRequestInflight = "client_request_inflight" +) diff --git a/semconv/server.go b/semconv/server.go new file mode 100644 index 00000000..fedb7074 --- /dev/null +++ b/semconv/server.go @@ -0,0 +1,12 @@ +package semconv + +var ( + // ServerRequestDurationSeconds specifies meter metric name + ServerRequestDurationSeconds = "server_request_duration_seconds" + // ServerRequestLatencyMicroseconds specifies meter metric name + ServerRequestLatencyMicroseconds = "server_request_latency_microseconds" + // ServerRequestTotal specifies meter metric name + ServerRequestTotal = "server_request_total" + // ServerRequestInflight specifies meter metric name + ServerRequestInflight = "server_request_inflight" +) diff --git a/server/context.go b/server/context.go index 4fe23bed..57bd033c 100644 --- a/server/context.go +++ b/server/context.go @@ -22,23 +22,3 @@ func NewContext(ctx context.Context, s Server) context.Context { } return context.WithValue(ctx, serverKey{}, s) } - -// SetOption returns a function to setup a context with given value -func SetOption(k, v interface{}) Option { - return func(o *Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} - -// SetHandlerOption returns a function to setup a context with given value -func SetHandlerOption(k, v interface{}) HandlerOption { - return func(o *HandlerOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} diff --git a/server/context_test.go b/server/context_test.go index 351dc17f..9c3ee36a 100644 --- a/server/context_test.go +++ b/server/context_test.go @@ -40,14 +40,3 @@ func TestNewContext(t *testing.T) { t.Fatal("NewContext not works") } } - -func TestSetOption(t *testing.T) { - type key struct{} - o := SetOption(key{}, "test") - opts := &Options{} - o(opts) - - if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { - t.Fatal("SetOption not works") - } -} diff --git a/server/noop.go b/server/noop.go index be6ed396..ebae5fe0 100644 --- a/server/noop.go +++ b/server/noop.go @@ -8,6 +8,7 @@ import ( "go.unistack.org/micro/v4/codec" "go.unistack.org/micro/v4/logger" + "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/register" maddr "go.unistack.org/micro/v4/util/addr" mnet "go.unistack.org/micro/v4/util/net" @@ -19,15 +20,11 @@ var DefaultCodecs = map[string]codec.Codec{ "application/octet-stream": codec.NewCodec(), } -const ( - defaultContentType = "application/json" -) - type noopServer struct { - h Handler + h *rpcHandler wg *sync.WaitGroup rsvc *register.Service - handlers map[string]Handler + handlers map[string]*rpcHandler exit chan chan error opts Options sync.RWMutex @@ -36,10 +33,10 @@ type noopServer struct { } // NewServer returns new noop server -func NewServer(opts ...Option) Server { +func NewServer(opts ...options.Option) Server { n := &noopServer{opts: NewOptions(opts...)} if n.handlers == nil { - n.handlers = make(map[string]Handler) + n.handlers = make(map[string]*rpcHandler) } if n.exit == nil { n.exit = make(chan chan error) @@ -47,18 +44,8 @@ func NewServer(opts ...Option) Server { return n } -func (n *noopServer) newCodec(contentType string) (codec.Codec, error) { - if cf, ok := n.opts.Codecs[contentType]; ok { - return cf, nil - } - if cf, ok := DefaultCodecs[contentType]; ok { - return cf, nil - } - return nil, codec.ErrUnknownContentType -} - -func (n *noopServer) Handle(handler Handler) error { - n.h = handler +func (n *noopServer) Handle(h interface{}, opts ...options.Option) error { + n.h = newRPCHandler(h, opts...) return nil } @@ -66,17 +53,13 @@ func (n *noopServer) Name() string { return n.opts.Name } -func (n *noopServer) NewHandler(h interface{}, opts ...HandlerOption) Handler { - return newRPCHandler(h, opts...) -} - -func (n *noopServer) Init(opts ...Option) error { +func (n *noopServer) Init(opts ...options.Option) error { for _, o := range opts { o(&n.opts) } if n.handlers == nil { - n.handlers = make(map[string]Handler, 1) + n.handlers = make(map[string]*rpcHandler, 1) } if n.exit == nil { @@ -339,14 +322,14 @@ func (n *noopServer) Stop() error { } type rpcHandler struct { - opts HandlerOptions + opts HandleOptions handler interface{} name string endpoints []*register.Endpoint } -func newRPCHandler(handler interface{}, opts ...HandlerOption) Handler { - options := NewHandlerOptions(opts...) +func newRPCHandler(handler interface{}, opts ...options.Option) *rpcHandler { + options := NewHandleOptions(opts...) typ := reflect.TypeOf(handler) hdlr := reflect.ValueOf(handler) @@ -386,6 +369,6 @@ func (r *rpcHandler) Endpoints() []*register.Endpoint { return r.endpoints } -func (r *rpcHandler) Options() HandlerOptions { +func (r *rpcHandler) Options() HandleOptions { return r.opts } diff --git a/server/options.go b/server/options.go index 26b3a42d..143f7eeb 100644 --- a/server/options.go +++ b/server/options.go @@ -11,27 +11,12 @@ import ( "go.unistack.org/micro/v4/logger" "go.unistack.org/micro/v4/metadata" "go.unistack.org/micro/v4/meter" - "go.unistack.org/micro/v4/network/transport" "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/register" "go.unistack.org/micro/v4/tracer" "go.unistack.org/micro/v4/util/id" ) -var ( - // ServerRequestDurationSeconds specifies meter metric name - ServerRequestDurationSeconds = "server_request_duration_seconds" - // ServerRequestLatencyMicroseconds specifies meter metric name - ServerRequestLatencyMicroseconds = "server_request_latency_microseconds" - // ServerRequestTotal specifies meter metric name - ServerRequestTotal = "server_request_total" - // ServerRequestInflight specifies meter metric name - ServerRequestInflight = "server_request_inflight" -) - -// Option func -type Option func(*Options) - // Options server struct type Options struct { // Context holds the external options and can be used for server shutdown @@ -44,8 +29,6 @@ type Options struct { Logger logger.Logger // Meter holds the meter Meter meter.Meter - // Transport holds the transport - Transport transport.Transport // Listener may be passed if already created Listener net.Listener // Wait group @@ -70,8 +53,6 @@ type Options struct { Advertise string // Version holds the server version Version string - // HdlrWrappers holds the handler wrappers - HdlrWrappers []HandlerWrapper // RegisterAttempts holds the number of register attempts before error RegisterAttempts int // RegisterInterval holds he interval for re-register @@ -82,12 +63,12 @@ type Options struct { MaxConn int // DeregisterAttempts holds the number of deregister attempts before error DeregisterAttempts int - // Hooks may contains HandlerWrapper or Server func wrapper + // Hooks may contains HandleWrapper or Server func wrapper Hooks options.Hooks } // NewOptions returns new options struct with default or passed values -func NewOptions(opts ...Option) Options { +func NewOptions(opts ...options.Option) Options { options := Options{ Codecs: make(map[string]codec.Codec), Context: context.Background(), @@ -99,12 +80,10 @@ func NewOptions(opts ...Option) Options { Meter: meter.DefaultMeter, Tracer: tracer.DefaultTracer, Register: register.DefaultRegister, - Transport: transport.DefaultTransport, Address: DefaultAddress, Name: DefaultName, Version: DefaultVersion, ID: id.Must(), - Namespace: DefaultNamespace, } for _, o := range opts { @@ -114,140 +93,45 @@ func NewOptions(opts ...Option) Options { return options } -// Name sets the server name option -func Name(n string) Option { - return func(o *Options) { - o.Name = n - } -} - -// Namespace to register handlers in -func Namespace(n string) Option { - return func(o *Options) { - o.Namespace = n - } -} - -// Logger sets the logger option -func Logger(l logger.Logger) Option { - return func(o *Options) { - o.Logger = l - } -} - -// Meter sets the meter option -func Meter(m meter.Meter) Option { - return func(o *Options) { - o.Meter = m - } -} - // ID unique server id -func ID(id string) Option { - return func(o *Options) { - o.ID = id +func ID(id string) options.Option { + return func(src interface{}) error { + return options.Set(src, id, ".ID") } } // Version of the service -func Version(v string) Option { - return func(o *Options) { - o.Version = v - } -} - -// Address to bind to - host:port -func Address(a string) Option { - return func(o *Options) { - o.Address = a +func Version(v string) options.Option { + return func(src interface{}) error { + return options.Set(src, v, ".Version") } } // Advertise the address to advertise for discovery - host:port -func Advertise(a string) Option { - return func(o *Options) { - o.Advertise = a - } -} - -// Codec to use to encode/decode requests for a given content type -func Codec(contentType string, c codec.Codec) Option { - return func(o *Options) { - o.Codecs[contentType] = c - } -} - -// Context specifies a context for the service. -// Can be used to signal shutdown of the service -// Can be used for extra option values. -func Context(ctx context.Context) Option { - return func(o *Options) { - o.Context = ctx - } -} - -// Register used for discovery -func Register(r register.Register) Option { - return func(o *Options) { - o.Register = r - } -} - -// Tracer mechanism for distributed tracking -func Tracer(t tracer.Tracer) Option { - return func(o *Options) { - o.Tracer = t - } -} - -// Transport mechanism for communication e.g http, rabbitmq, etc -func Transport(t transport.Transport) Option { - return func(o *Options) { - o.Transport = t - } -} - -// Metadata associated with the server -func Metadata(md metadata.Metadata) Option { - return func(o *Options) { - o.Metadata = metadata.Copy(md) +func Advertise(a string) options.Option { + return func(src interface{}) error { + return options.Set(src, a, ".Advertise") } } // RegisterCheck run func before register service -func RegisterCheck(fn func(context.Context) error) Option { - return func(o *Options) { - o.RegisterCheck = fn +func RegisterCheck(fn func(context.Context) error) options.Option { + return func(src interface{}) error { + return options.Set(src, fn, ".RegisterCheck") } } // RegisterTTL registers service with a TTL -func RegisterTTL(t time.Duration) Option { - return func(o *Options) { - o.RegisterTTL = t +func RegisterTTL(td time.Duration) options.Option { + return func(src interface{}) error { + return options.Set(src, td, ".RegisterTTL") } } // RegisterInterval registers service with at interval -func RegisterInterval(t time.Duration) Option { - return func(o *Options) { - o.RegisterInterval = t - } -} - -// TLSConfig specifies a *tls.Config -func TLSConfig(t *tls.Config) Option { - return func(o *Options) { - // set the internal tls - o.TLSConfig = t - - // set the default transport if one is not - // already set. Required for Init call below. - - // set the transport tls - _ = o.Transport.Init( - transport.TLSConfig(t), - ) +func RegisterInterval(td time.Duration) options.Option { + return func(src interface{}) error { + return options.Set(src, td, ".RegisterInterval") } } @@ -255,50 +139,40 @@ func TLSConfig(t *tls.Config) Option { // If `wg` is nil, server only wait for completion of rpc handler. // For user need finer grained control, pass a concrete `wg` here, server will // wait against it on stop. -func Wait(wg *sync.WaitGroup) Option { - return func(o *Options) { - if wg == nil { - wg = new(sync.WaitGroup) - } - o.Wait = wg +func Wait(wg *sync.WaitGroup) options.Option { + if wg == nil { + wg = new(sync.WaitGroup) } -} - -// WrapHandler adds a handler Wrapper to a list of options passed into the server -func WrapHandler(w HandlerWrapper) Option { - return func(o *Options) { - o.HdlrWrappers = append(o.HdlrWrappers, w) + return func(src interface{}) error { + return options.Set(src, wg, ".Wait") } } // MaxConn specifies maximum number of max simultaneous connections to server -func MaxConn(n int) Option { - return func(o *Options) { - o.MaxConn = n +func MaxConn(n int) options.Option { + return func(src interface{}) error { + return options.Set(src, n, ".MaxConn") } } // Listener specifies the net.Listener to use instead of the default -func Listener(l net.Listener) Option { - return func(o *Options) { - o.Listener = l +func Listener(nl net.Listener) options.Option { + return func(src interface{}) error { + return options.Set(src, nl, ".Listener") } } -// HandlerOption func -type HandlerOption func(*HandlerOptions) - -// HandlerOptions struct -type HandlerOptions struct { +// HandleOptions struct +type HandleOptions struct { // Context holds external options Context context.Context // Metadata for handler Metadata map[string]metadata.Metadata } -// NewHandlerOptions creates new HandlerOptions -func NewHandlerOptions(opts ...HandlerOption) HandlerOptions { - options := HandlerOptions{ +// NewHandleOptions creates new HandleOptions +func NewHandleOptions(opts ...options.Option) HandleOptions { + options := HandleOptions{ Context: context.Background(), Metadata: make(map[string]metadata.Metadata), } @@ -309,11 +183,3 @@ func NewHandlerOptions(opts ...HandlerOption) HandlerOptions { return options } - -// EndpointMetadata is a Handler option that allows metadata to be added to -// individual endpoints. -func EndpointMetadata(name string, md metadata.Metadata) HandlerOption { - return func(o *HandlerOptions) { - o.Metadata[name] = metadata.Copy(md) - } -} diff --git a/server/server.go b/server/server.go index 07c65854..ffeb3c70 100644 --- a/server/server.go +++ b/server/server.go @@ -7,7 +7,7 @@ import ( "go.unistack.org/micro/v4/codec" "go.unistack.org/micro/v4/metadata" - "go.unistack.org/micro/v4/register" + "go.unistack.org/micro/v4/options" ) // DefaultServer default server @@ -26,9 +26,7 @@ var ( DefaultRegisterInterval = time.Second * 30 // DefaultRegisterTTL holds register record ttl, must be multiple of DefaultRegisterInterval DefaultRegisterTTL = time.Second * 90 - // DefaultNamespace will be used if no namespace passed - DefaultNamespace = "micro" - // DefaultMaxMsgSize holds default max msg ssize + // DefaultMaxMsgSize holds default max msg size DefaultMaxMsgSize = 1024 * 1024 * 4 // 4Mb // DefaultMaxMsgRecvSize holds default max recv size DefaultMaxMsgRecvSize = 1024 * 1024 * 4 // 4Mb @@ -41,13 +39,11 @@ type Server interface { // Name returns server name Name() string // Initialise options - Init(...Option) error + Init(...options.Option) error // Retrieve the options Options() Options - // Register a handler - Handle(h Handler) error - // Create a new handler - NewHandler(h interface{}, opts ...HandlerOption) Handler + // Create and register new handler + Handle(h interface{}, opts ...options.Option) error // Start the server Start() error // Stop the server @@ -110,21 +106,3 @@ type Stream interface { // Close closes the stream Close() error } - -// Handler interface represents a request handler. It's generated -// by passing any type of public concrete object with endpoints into server.NewHandler. -// Most will pass in a struct. -// -// Example: -// -// type Greeter struct {} -// -// func (g *Greeter) Hello(context, request, response) error { -// return nil -// } -type Handler interface { - Name() string - Handler() interface{} - Endpoints() []*register.Endpoint - Options() HandlerOptions -} diff --git a/service.go b/service.go index 23e6f064..9d8e6c21 100644 --- a/service.go +++ b/service.go @@ -10,6 +10,7 @@ import ( "go.unistack.org/micro/v4/config" "go.unistack.org/micro/v4/logger" "go.unistack.org/micro/v4/meter" + "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/register" "go.unistack.org/micro/v4/router" "go.unistack.org/micro/v4/server" @@ -62,8 +63,8 @@ type Service interface { } // RegisterHandler is syntactic sugar for registering a handler -func RegisterHandler(s server.Server, h interface{}, opts ...server.HandlerOption) error { - return s.Handle(s.NewHandler(h, opts...)) +func RegisterHandler(s server.Server, h interface{}, opts ...options.Option) error { + return s.Handle(h, opts...) } type service struct { @@ -99,13 +100,13 @@ func (s *service) Init(opts ...Option) error { // skip config as the struct not passed continue } - if err = cfg.Init(config.Context(cfg.Options().Context)); err != nil { + if err = cfg.Init(options.Context(cfg.Options().Context)); err != nil { return err } } for _, log := range s.opts.Loggers { - if err = log.Init(logger.WithContext(log.Options().Context)); err != nil { + if err = log.Init(options.Context(log.Options().Context)); err != nil { return err } } @@ -117,25 +118,25 @@ func (s *service) Init(opts ...Option) error { } for _, brk := range s.opts.Brokers { - if err = brk.Init(broker.Context(brk.Options().Context)); err != nil { + if err = brk.Init(options.Context(brk.Options().Context)); err != nil { return err } } for _, str := range s.opts.Stores { - if err = str.Init(store.Context(str.Options().Context)); err != nil { + if err = str.Init(options.Context(str.Options().Context)); err != nil { return err } } for _, srv := range s.opts.Servers { - if err = srv.Init(server.Context(srv.Options().Context)); err != nil { + if err = srv.Init(options.Context(srv.Options().Context)); err != nil { return err } } for _, cli := range s.opts.Clients { - if err = cli.Init(client.Context(cli.Options().Context)); err != nil { + if err = cli.Init(options.Context(cli.Options().Context)); err != nil { return err } } diff --git a/service_test.go b/service_test.go index 025e44c8..0063727e 100644 --- a/service_test.go +++ b/service_test.go @@ -9,6 +9,7 @@ import ( "go.unistack.org/micro/v4/config" "go.unistack.org/micro/v4/logger" "go.unistack.org/micro/v4/meter" + "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/register" "go.unistack.org/micro/v4/router" "go.unistack.org/micro/v4/server" @@ -37,7 +38,7 @@ func TestRegisterHandler(t *testing.T) { type args struct { s server.Server h interface{} - opts []server.HandlerOption + opts []options.Option } h := struct{}{} tests := []struct { diff --git a/store/context.go b/store/context.go index 2cfcb62e..df0de596 100644 --- a/store/context.go +++ b/store/context.go @@ -22,63 +22,3 @@ func NewContext(ctx context.Context, c Store) context.Context { } return context.WithValue(ctx, storeKey{}, c) } - -// SetOption returns a function to setup a context with given value -func SetOption(k, v interface{}) Option { - return func(o *Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} - -// SetReadOption returns a function to setup a context with given value -func SetReadOption(k, v interface{}) ReadOption { - return func(o *ReadOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} - -// SetWriteOption returns a function to setup a context with given value -func SetWriteOption(k, v interface{}) WriteOption { - return func(o *WriteOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} - -// SetListOption returns a function to setup a context with given value -func SetListOption(k, v interface{}) ListOption { - return func(o *ListOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} - -// SetDeleteOption returns a function to setup a context with given value -func SetDeleteOption(k, v interface{}) DeleteOption { - return func(o *DeleteOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} - -// SetExistsOption returns a function to setup a context with given value -func SetExistsOption(k, v interface{}) ExistsOption { - return func(o *ExistsOptions) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} diff --git a/store/context_test.go b/store/context_test.go index bc9973b5..2bda042f 100644 --- a/store/context_test.go +++ b/store/context_test.go @@ -3,6 +3,8 @@ package store import ( "context" "testing" + + "go.unistack.org/micro/v4/options" ) func TestFromNilContext(t *testing.T) { @@ -43,10 +45,7 @@ func TestNewContext(t *testing.T) { func TestSetOption(t *testing.T) { type key struct{} - o := SetOption(key{}, "test") - opts := &Options{} - o(opts) - + opts := NewOptions(options.ContextOption(key{}, "test")) if v, ok := opts.Context.Value(key{}).(string); !ok || v == "" { t.Fatal("SetOption not works") } diff --git a/store/memory.go b/store/memory.go index 0e27ebec..fef70705 100644 --- a/store/memory.go +++ b/store/memory.go @@ -7,10 +7,11 @@ import ( "time" "github.com/patrickmn/go-cache" + "go.unistack.org/micro/v4/options" ) // NewStore returns a memory store -func NewStore(opts ...Option) Store { +func NewStore(opts ...options.Option) Store { return &memoryStore{ opts: NewOptions(opts...), store: cache.New(cache.NoExpiration, 5*time.Minute), @@ -100,7 +101,7 @@ func (m *memoryStore) list(prefix string, limit, offset uint) []string { return allKeys } -func (m *memoryStore) Init(opts ...Option) error { +func (m *memoryStore) Init(opts ...options.Option) error { for _, o := range opts { o(&m.opts) } @@ -115,7 +116,7 @@ func (m *memoryStore) Name() string { return m.opts.Name } -func (m *memoryStore) Exists(ctx context.Context, key string, opts ...ExistsOption) error { +func (m *memoryStore) Exists(ctx context.Context, key string, opts ...options.Option) error { options := NewExistsOptions(opts...) if options.Namespace == "" { options.Namespace = m.opts.Namespace @@ -123,7 +124,7 @@ func (m *memoryStore) Exists(ctx context.Context, key string, opts ...ExistsOpti return m.exists(options.Namespace, key) } -func (m *memoryStore) Read(ctx context.Context, key string, val interface{}, opts ...ReadOption) error { +func (m *memoryStore) Read(ctx context.Context, key string, val interface{}, opts ...options.Option) error { options := NewReadOptions(opts...) if options.Namespace == "" { options.Namespace = m.opts.Namespace @@ -131,7 +132,7 @@ func (m *memoryStore) Read(ctx context.Context, key string, val interface{}, opt return m.get(options.Namespace, key, val) } -func (m *memoryStore) Write(ctx context.Context, key string, val interface{}, opts ...WriteOption) error { +func (m *memoryStore) Write(ctx context.Context, key string, val interface{}, opts ...options.Option) error { options := NewWriteOptions(opts...) if options.Namespace == "" { options.Namespace = m.opts.Namespace @@ -151,7 +152,7 @@ func (m *memoryStore) Write(ctx context.Context, key string, val interface{}, op return nil } -func (m *memoryStore) Delete(ctx context.Context, key string, opts ...DeleteOption) error { +func (m *memoryStore) Delete(ctx context.Context, key string, opts ...options.Option) error { options := NewDeleteOptions(opts...) if options.Namespace == "" { options.Namespace = m.opts.Namespace @@ -165,7 +166,7 @@ func (m *memoryStore) Options() Options { return m.opts } -func (m *memoryStore) List(ctx context.Context, opts ...ListOption) ([]string, error) { +func (m *memoryStore) List(ctx context.Context, opts ...options.Option) ([]string, error) { options := NewListOptions(opts...) if options.Namespace == "" { options.Namespace = m.opts.Namespace diff --git a/store/options.go b/store/options.go index dea99ec0..ac08caa4 100644 --- a/store/options.go +++ b/store/options.go @@ -9,6 +9,7 @@ import ( "go.unistack.org/micro/v4/logger" "go.unistack.org/micro/v4/metadata" "go.unistack.org/micro/v4/meter" + "go.unistack.org/micro/v4/options" "go.unistack.org/micro/v4/tracer" ) @@ -32,16 +33,14 @@ type Options struct { Namespace string // Separator used as key parts separator Separator string - // Addrs contains store address - Addrs []string - // Wrappers store wrapper that called before actual functions - // Wrappers []Wrapper + // Address contains store address + Address []string // Timeout specifies timeout duration for all operations Timeout time.Duration } // NewOptions creates options struct -func NewOptions(opts ...Option) Options { +func NewOptions(opts ...options.Option) Options { options := Options{ Logger: logger.DefaultLogger, Context: context.Background(), @@ -56,85 +55,17 @@ func NewOptions(opts ...Option) Options { return options } -// Option sets values in Options -type Option func(o *Options) - -// TLSConfig specifies a *tls.Config -func TLSConfig(t *tls.Config) Option { - return func(o *Options) { - o.TLSConfig = t - } -} - -// Context pass context to store -func Context(ctx context.Context) Option { - return func(o *Options) { - o.Context = ctx - } -} - -// Codec sets the codec -func Codec(c codec.Codec) Option { - return func(o *Options) { - o.Codec = c - } -} - -// Logger sets the logger -func Logger(l logger.Logger) Option { - return func(o *Options) { - o.Logger = l - } -} - -// Meter sets the meter -func Meter(m meter.Meter) Option { - return func(o *Options) { - o.Meter = m - } -} - -// Name the name of the store -func Name(n string) Option { - return func(o *Options) { - o.Name = n - } -} - // Separator the value used as key parts separator -func Separator(s string) Option { - return func(o *Options) { - o.Separator = s - } -} - -// Namespace sets namespace of the store -func Namespace(ns string) Option { - return func(o *Options) { - o.Namespace = ns - } -} - -// Tracer sets the tracer -func Tracer(t tracer.Tracer) Option { - return func(o *Options) { - o.Tracer = t +func Separator(s string) options.Option { + return func(src interface{}) error { + return options.Set(src, s, "Separator") } } // Timeout sets the timeout -func Timeout(td time.Duration) Option { - return func(o *Options) { - o.Timeout = td - } -} - -// Addrs contains the addresses or other connection information of the backing storage. -// For example, an etcd implementation would contain the nodes of the cluster. -// A SQL implementation could contain one or more connection strings. -func Addrs(addrs ...string) Option { - return func(o *Options) { - o.Addrs = addrs +func Timeout(td time.Duration) options.Option { + return func(src interface{}) error { + return options.Set(src, td, ".Timeout") } } @@ -147,7 +78,7 @@ type ReadOptions struct { } // NewReadOptions fills ReadOptions struct with opts slice -func NewReadOptions(opts ...ReadOption) ReadOptions { +func NewReadOptions(opts ...options.Option) ReadOptions { options := ReadOptions{} for _, o := range opts { o(&options) @@ -155,23 +86,6 @@ func NewReadOptions(opts ...ReadOption) ReadOptions { return options } -// ReadOption sets values in ReadOptions -type ReadOption func(r *ReadOptions) - -// ReadContext pass context.Context to ReadOptions -func ReadContext(ctx context.Context) ReadOption { - return func(o *ReadOptions) { - o.Context = ctx - } -} - -// ReadNamespace pass namespace to ReadOptions -func ReadNamespace(ns string) ReadOption { - return func(o *ReadOptions) { - o.Namespace = ns - } -} - // WriteOptions configures an individual Write operation type WriteOptions struct { // Context holds external options @@ -185,7 +99,7 @@ type WriteOptions struct { } // NewWriteOptions fills WriteOptions struct with opts slice -func NewWriteOptions(opts ...WriteOption) WriteOptions { +func NewWriteOptions(opts ...options.Option) WriteOptions { options := WriteOptions{} for _, o := range opts { o(&options) @@ -193,34 +107,17 @@ func NewWriteOptions(opts ...WriteOption) WriteOptions { return options } -// WriteOption sets values in WriteOptions -type WriteOption func(w *WriteOptions) - -// WriteContext pass context.Context to wirte options -func WriteContext(ctx context.Context) WriteOption { - return func(o *WriteOptions) { - o.Context = ctx - } -} - // WriteMetadata add metadata.Metadata -func WriteMetadata(md metadata.Metadata) WriteOption { - return func(o *WriteOptions) { - o.Metadata = metadata.Copy(md) +func WriteMetadata(md metadata.Metadata) options.Option { + return func(src interface{}) error { + return options.Set(src, metadata.Copy(md), ".Metadata") } } // WriteTTL is the time the record expires -func WriteTTL(d time.Duration) WriteOption { - return func(o *WriteOptions) { - o.TTL = d - } -} - -// WriteNamespace pass namespace to write options -func WriteNamespace(ns string) WriteOption { - return func(o *WriteOptions) { - o.Namespace = ns +func WriteTTL(td time.Duration) options.Option { + return func(src interface{}) error { + return options.Set(src, td, ".TTL") } } @@ -233,7 +130,7 @@ type DeleteOptions struct { } // NewDeleteOptions fills DeleteOptions struct with opts slice -func NewDeleteOptions(opts ...DeleteOption) DeleteOptions { +func NewDeleteOptions(opts ...options.Option) DeleteOptions { options := DeleteOptions{} for _, o := range opts { o(&options) @@ -241,23 +138,6 @@ func NewDeleteOptions(opts ...DeleteOption) DeleteOptions { return options } -// DeleteOption sets values in DeleteOptions -type DeleteOption func(d *DeleteOptions) - -// DeleteContext pass context.Context to delete options -func DeleteContext(ctx context.Context) DeleteOption { - return func(o *DeleteOptions) { - o.Context = ctx - } -} - -// DeleteNamespace pass namespace to delete options -func DeleteNamespace(ns string) DeleteOption { - return func(o *DeleteOptions) { - o.Namespace = ns - } -} - // ListOptions configures an individual List operation type ListOptions struct { Context context.Context @@ -269,7 +149,7 @@ type ListOptions struct { } // NewListOptions fills ListOptions struct with opts slice -func NewListOptions(opts ...ListOption) ListOptions { +func NewListOptions(opts ...options.Option) ListOptions { options := ListOptions{} for _, o := range opts { o(&options) @@ -277,48 +157,31 @@ func NewListOptions(opts ...ListOption) ListOptions { return options } -// ListOption sets values in ListOptions -type ListOption func(l *ListOptions) - -// ListContext pass context.Context to list options -func ListContext(ctx context.Context) ListOption { - return func(o *ListOptions) { - o.Context = ctx - } -} - // ListPrefix returns all keys that are prefixed with key -func ListPrefix(s string) ListOption { - return func(o *ListOptions) { - o.Prefix = s +func ListPrefix(s string) options.Option { + return func(src interface{}) error { + return options.Set(src, s, ".Prefix") } } // ListSuffix returns all keys that end with key -func ListSuffix(s string) ListOption { - return func(o *ListOptions) { - o.Suffix = s +func ListSuffix(s string) options.Option { + return func(src interface{}) error { + return options.Set(src, s, ".Prefix") } } // ListLimit limits the number of returned keys -func ListLimit(n uint) ListOption { - return func(o *ListOptions) { - o.Limit = n +func ListLimit(n uint) options.Option { + return func(src interface{}) error { + return options.Set(src, n, ".Limit") } } // ListOffset use with Limit for pagination -func ListOffset(n uint) ListOption { - return func(o *ListOptions) { - o.Offset = n - } -} - -// ListNamespace pass namespace to list options -func ListNamespace(ns string) ListOption { - return func(o *ListOptions) { - o.Namespace = ns +func ListOffset(n uint) options.Option { + return func(src interface{}) error { + return options.Set(src, n, ".Offset") } } @@ -330,11 +193,8 @@ type ExistsOptions struct { Namespace string } -// ExistsOption specifies Exists call options -type ExistsOption func(*ExistsOptions) - // NewExistsOptions helper for Exists method -func NewExistsOptions(opts ...ExistsOption) ExistsOptions { +func NewExistsOptions(opts ...options.Option) ExistsOptions { options := ExistsOptions{ Context: context.Background(), } @@ -343,26 +203,3 @@ func NewExistsOptions(opts ...ExistsOption) ExistsOptions { } return options } - -// ExistsContext pass context.Context to exist options -func ExistsContext(ctx context.Context) ExistsOption { - return func(o *ExistsOptions) { - o.Context = ctx - } -} - -// ExistsNamespace pass namespace to exist options -func ExistsNamespace(ns string) ExistsOption { - return func(o *ExistsOptions) { - o.Namespace = ns - } -} - -/* -// WrapStore adds a store Wrapper to a list of options passed into the store -func WrapStore(w Wrapper) Option { - return func(o *Options) { - o.Wrappers = append(o.Wrappers, w) - } -} -*/ diff --git a/store/store.go b/store/store.go index 708f97bf..ce908313 100644 --- a/store/store.go +++ b/store/store.go @@ -4,6 +4,8 @@ package store // import "go.unistack.org/micro/v4/store" import ( "context" "errors" + + "go.unistack.org/micro/v4/options" ) var ( @@ -21,21 +23,21 @@ var ( type Store interface { Name() string // Init initialises the store - Init(opts ...Option) error + Init(opts ...options.Option) error // Connect is used when store needs to be connected Connect(ctx context.Context) error // Options allows you to view the current options. Options() Options // Exists check that key exists in store - Exists(ctx context.Context, key string, opts ...ExistsOption) error - // Read reads a single key name to provided value with optional ReadOptions - Read(ctx context.Context, key string, val interface{}, opts ...ReadOption) error - // Write writes a value to key name to the store with optional WriteOption - Write(ctx context.Context, key string, val interface{}, opts ...WriteOption) error - // Delete removes the record with the corresponding key from the store. - Delete(ctx context.Context, key string, opts ...DeleteOption) error - // List returns any keys that match, or an empty list with no error if none matched. - List(ctx context.Context, opts ...ListOption) ([]string, error) + Exists(ctx context.Context, key string, opts ...options.Option) error + // Read reads a single key name to provided value with optional options + Read(ctx context.Context, key string, val interface{}, opts ...options.Option) error + // Write writes a value to key name to the store with optional options + Write(ctx context.Context, key string, val interface{}, opts ...options.Option) error + // Delete removes the record with the corresponding key from the store with optional options + Delete(ctx context.Context, key string, opts ...options.Option) error + // List returns any keys that match, or an empty list with no error if none matched with optional options + List(ctx context.Context, opts ...options.Option) ([]string, error) // Disconnect the store Disconnect(ctx context.Context) error // String returns the name of the implementation. diff --git a/store/wrapper.go b/store/wrapper.go index 84c43026..489135d6 100644 --- a/store/wrapper.go +++ b/store/wrapper.go @@ -2,14 +2,9 @@ package store import ( "context" -) -// LogfFunc function used for Logf method -// type LogfFunc func(ctx context.Context, level Level, msg string, args ...interface{}) -// type Wrapper interface { -// Logf logs message with needed level -// Logf(LogfFunc) LogfFunc -// } + "go.unistack.org/micro/v4/options" +) // NamespaceStore wrap store with namespace type NamespaceStore struct { @@ -23,7 +18,7 @@ func NewNamespaceStore(s Store, ns string) Store { return &NamespaceStore{s: s, ns: ns} } -func (w *NamespaceStore) Init(opts ...Option) error { +func (w *NamespaceStore) Init(opts ...options.Option) error { return w.s.Init(opts...) } @@ -35,24 +30,24 @@ func (w *NamespaceStore) Disconnect(ctx context.Context) error { return w.s.Disconnect(ctx) } -func (w *NamespaceStore) Read(ctx context.Context, key string, val interface{}, opts ...ReadOption) error { - return w.s.Read(ctx, key, val, append(opts, ReadNamespace(w.ns))...) +func (w *NamespaceStore) Read(ctx context.Context, key string, val interface{}, opts ...options.Option) error { + return w.s.Read(ctx, key, val, append(opts, options.Namespace(w.ns))...) } -func (w *NamespaceStore) Write(ctx context.Context, key string, val interface{}, opts ...WriteOption) error { - return w.s.Write(ctx, key, val, append(opts, WriteNamespace(w.ns))...) +func (w *NamespaceStore) Write(ctx context.Context, key string, val interface{}, opts ...options.Option) error { + return w.s.Write(ctx, key, val, append(opts, options.Namespace(w.ns))...) } -func (w *NamespaceStore) Delete(ctx context.Context, key string, opts ...DeleteOption) error { - return w.s.Delete(ctx, key, append(opts, DeleteNamespace(w.ns))...) +func (w *NamespaceStore) Delete(ctx context.Context, key string, opts ...options.Option) error { + return w.s.Delete(ctx, key, append(opts, options.Namespace(w.ns))...) } -func (w *NamespaceStore) Exists(ctx context.Context, key string, opts ...ExistsOption) error { - return w.s.Exists(ctx, key, append(opts, ExistsNamespace(w.ns))...) +func (w *NamespaceStore) Exists(ctx context.Context, key string, opts ...options.Option) error { + return w.s.Exists(ctx, key, append(opts, options.Namespace(w.ns))...) } -func (w *NamespaceStore) List(ctx context.Context, opts ...ListOption) ([]string, error) { - return w.s.List(ctx, append(opts, ListNamespace(w.ns))...) +func (w *NamespaceStore) List(ctx context.Context, opts ...options.Option) ([]string, error) { + return w.s.List(ctx, append(opts, options.Namespace(w.ns))...) } func (w *NamespaceStore) Options() Options { @@ -66,17 +61,3 @@ func (w *NamespaceStore) Name() string { func (w *NamespaceStore) String() string { return w.s.String() } - -// type NamespaceWrapper struct{} - -// func NewNamespaceWrapper() Wrapper { -// return &NamespaceWrapper{} -// } - -/* -func (w *OmitWrapper) Logf(fn LogfFunc) LogfFunc { - return func(ctx context.Context, level Level, msg string, args ...interface{}) { - fn(ctx, level, msg, getArgs(args)...) - } -} -*/ diff --git a/tools.go b/tools.go new file mode 100644 index 00000000..1164e4d9 --- /dev/null +++ b/tools.go @@ -0,0 +1,3 @@ +package micro + +//go:generate go install golang.org/x/tools/cmd/stringer@latest diff --git a/tracer/context.go b/tracer/context.go index 7cbdf533..8ff4e621 100644 --- a/tracer/context.go +++ b/tracer/context.go @@ -46,13 +46,3 @@ func NewSpanContext(ctx context.Context, span Span) context.Context { } return context.WithValue(ctx, spanKey{}, span) } - -// SetOption returns a function to setup a context with given value -func SetOption(k, v interface{}) Option { - return func(o *Options) { - if o.Context == nil { - o.Context = context.Background() - } - o.Context = context.WithValue(o.Context, k, v) - } -} diff --git a/tracer/noop.go b/tracer/noop.go index 69a89a82..77f53191 100644 --- a/tracer/noop.go +++ b/tracer/noop.go @@ -2,6 +2,8 @@ package tracer import ( "context" + + "go.unistack.org/micro/v4/options" ) var _ Tracer = (*noopTracer)(nil) @@ -10,7 +12,7 @@ type noopTracer struct { opts Options } -func (t *noopTracer) Start(ctx context.Context, name string, opts ...SpanOption) (context.Context, Span) { +func (t *noopTracer) Start(ctx context.Context, name string, opts ...options.Option) (context.Context, Span) { span := &noopSpan{ name: name, ctx: ctx, @@ -27,7 +29,7 @@ func (t *noopTracer) Flush(ctx context.Context) error { return nil } -func (t *noopTracer) Init(opts ...Option) error { +func (t *noopTracer) Init(opts ...options.Option) error { for _, o := range opts { o(&t.opts) } @@ -47,7 +49,7 @@ type noopSpan struct { statusMsg string } -func (s *noopSpan) Finish(opts ...SpanOption) { +func (s *noopSpan) Finish(opts ...options.Option) { } func (s *noopSpan) Context() context.Context { @@ -58,7 +60,7 @@ func (s *noopSpan) Tracer() Tracer { return s.tracer } -func (s *noopSpan) AddEvent(name string, opts ...EventOption) { +func (s *noopSpan) AddEvent(name string, opts ...options.Option) { } func (s *noopSpan) SetName(name string) { @@ -87,7 +89,7 @@ func (s *noopSpan) SetStatus(st SpanStatus, msg string) { } // NewTracer returns new memory tracer -func NewTracer(opts ...Option) Tracer { +func NewTracer(opts ...options.Option) Tracer { return &noopTracer{ opts: NewOptions(opts...), } diff --git a/tracer/options.go b/tracer/options.go index ff43b342..c6824cd5 100644 --- a/tracer/options.go +++ b/tracer/options.go @@ -2,8 +2,11 @@ package tracer import ( "context" + "reflect" "go.unistack.org/micro/v4/logger" + "go.unistack.org/micro/v4/options" + rutil "go.unistack.org/micro/v4/util/reflect" ) type SpanStatus int @@ -87,24 +90,29 @@ type SpanOptions struct { Kind SpanKind } -// SpanOption func signature -type SpanOption func(o *SpanOptions) - // EventOptions contains event options type EventOptions struct{} -// EventOption func signature -type EventOption func(o *EventOptions) - -func WithSpanLabels(labels ...interface{}) SpanOption { - return func(o *SpanOptions) { - o.Labels = labels +func WithSpanLabels(ls ...interface{}) options.Option { + return func(src interface{}) error { + v, err := options.Get(src, ".Labels") + if err != nil { + return err + } else if rutil.IsZero(v) { + v = reflect.MakeSlice(reflect.TypeOf(v), 0, len(ls)).Interface() + } + cv := reflect.ValueOf(v) + for _, l := range ls { + reflect.Append(cv, reflect.ValueOf(l)) + } + err = options.Set(src, cv, ".Labels") + return err } } -func WithSpanKind(k SpanKind) SpanOption { - return func(o *SpanOptions) { - o.Kind = k +func WithSpanKind(k SpanKind) options.Option { + return func(src interface{}) error { + return options.Set(src, k, ".Kind") } } @@ -118,18 +126,8 @@ type Options struct { Name string } -// Option func signature -type Option func(o *Options) - -// Logger sets the logger -func Logger(l logger.Logger) Option { - return func(o *Options) { - o.Logger = l - } -} - // NewSpanOptions returns default SpanOptions -func NewSpanOptions(opts ...SpanOption) SpanOptions { +func NewSpanOptions(opts ...options.Option) SpanOptions { options := SpanOptions{ Kind: SpanKindInternal, } @@ -140,7 +138,7 @@ func NewSpanOptions(opts ...SpanOption) SpanOptions { } // NewOptions returns default options -func NewOptions(opts ...Option) Options { +func NewOptions(opts ...options.Option) Options { options := Options{ Logger: logger.DefaultLogger, } @@ -149,10 +147,3 @@ func NewOptions(opts ...Option) Options { } return options } - -// Name sets the name -func Name(n string) Option { - return func(o *Options) { - o.Name = n - } -} diff --git a/tracer/tracer.go b/tracer/tracer.go index f3d914c7..442113c9 100644 --- a/tracer/tracer.go +++ b/tracer/tracer.go @@ -3,6 +3,8 @@ package tracer // import "go.unistack.org/micro/v4/tracer" import ( "context" + + "go.unistack.org/micro/v4/options" ) // DefaultTracer is the global default tracer @@ -13,9 +15,9 @@ type Tracer interface { // Name return tracer name Name() string // Init tracer with options - Init(...Option) error + Init(...options.Option) error // Start a trace - Start(ctx context.Context, name string, opts ...SpanOption) (context.Context, Span) + Start(ctx context.Context, name string, opts ...options.Option) (context.Context, Span) // Flush flushes spans Flush(ctx context.Context) error } @@ -24,9 +26,9 @@ type Span interface { // Tracer return underlining tracer Tracer() Tracer // Finish complete and send span - Finish(opts ...SpanOption) + Finish(opts ...options.Option) // AddEvent add event to span - AddEvent(name string, opts ...EventOption) + AddEvent(name string, opts ...options.Option) // Context return context with span Context() context.Context // SetName set the span name diff --git a/tracer/wrapper/wrapper.go b/tracer/wrapper/wrapper.go index ba2548d2..c47f46f7 100644 --- a/tracer/wrapper/wrapper.go +++ b/tracer/wrapper/wrapper.go @@ -46,23 +46,6 @@ var ( sp.SetLabels(labels...) } - DefaultClientPublishObserver = func(ctx context.Context, msg client.Message, opts []client.PublishOption, sp tracer.Span, err error) { - sp.SetName(fmt.Sprintf("Publish %s", msg.Topic())) - var labels []interface{} - if md, ok := metadata.FromOutgoingContext(ctx); ok { - labels = make([]interface{}, 0, len(md)) - for k, v := range md { - labels = append(labels, k, v) - } - } - if err != nil { - labels = append(labels, "error", err.Error()) - sp.SetStatus(tracer.SpanStatusError, err.Error()) - } - labels = append(labels, "kind", sp.Kind()) - sp.SetLabels(labels...) - } - DefaultServerHandlerObserver = func(ctx context.Context, req server.Request, rsp interface{}, sp tracer.Span, err error) { sp.SetName(fmt.Sprintf("Handler %s.%s", req.Service(), req.Method())) var labels []interface{} @@ -80,23 +63,6 @@ var ( sp.SetLabels(labels...) } - DefaultServerSubscriberObserver = func(ctx context.Context, msg server.Message, sp tracer.Span, err error) { - sp.SetName(fmt.Sprintf("Subscriber %s", msg.Topic())) - var labels []interface{} - if md, ok := metadata.FromIncomingContext(ctx); ok { - labels = make([]interface{}, 0, len(md)) - for k, v := range md { - labels = append(labels, k, v) - } - } - if err != nil { - labels = append(labels, "error", err.Error()) - sp.SetStatus(tracer.SpanStatusError, err.Error()) - } - labels = append(labels, "kind", sp.Kind()) - sp.SetLabels(labels...) - } - DefaultClientCallFuncObserver = func(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions, sp tracer.Span, err error) { sp.SetName(fmt.Sprintf("Call %s.%s", req.Service(), req.Method())) var labels []interface{} @@ -119,19 +85,16 @@ var ( type tWrapper struct { client.Client - serverHandler server.HandlerFunc - serverSubscriber server.SubscriberFunc - clientCallFunc client.CallFunc - opts Options + serverHandler server.HandlerFunc + clientCallFunc client.CallFunc + opts Options } type ( - ClientCallObserver func(context.Context, client.Request, interface{}, []client.CallOption, tracer.Span, error) - ClientStreamObserver func(context.Context, client.Request, []client.CallOption, client.Stream, tracer.Span, error) - ClientPublishObserver func(context.Context, client.Message, []client.PublishOption, tracer.Span, error) - ClientCallFuncObserver func(context.Context, string, client.Request, interface{}, client.CallOptions, tracer.Span, error) - ServerHandlerObserver func(context.Context, server.Request, interface{}, tracer.Span, error) - ServerSubscriberObserver func(context.Context, server.Message, tracer.Span, error) + ClientCallObserver func(context.Context, client.Request, interface{}, []client.CallOption, tracer.Span, error) + ClientStreamObserver func(context.Context, client.Request, []client.CallOption, client.Stream, tracer.Span, error) + ClientCallFuncObserver func(context.Context, string, client.Request, interface{}, client.CallOptions, tracer.Span, error) + ServerHandlerObserver func(context.Context, server.Request, interface{}, tracer.Span, error) ) // Options struct @@ -142,14 +105,10 @@ type Options struct { ClientCallObservers []ClientCallObserver // ClientStreamObservers funcs ClientStreamObservers []ClientStreamObserver - // ClientPublishObservers funcs - ClientPublishObservers []ClientPublishObserver // ClientCallFuncObservers funcs ClientCallFuncObservers []ClientCallFuncObserver // ServerHandlerObservers funcs ServerHandlerObservers []ServerHandlerObserver - // ServerSubscriberObservers funcs - ServerSubscriberObservers []ServerSubscriberObserver // SkipEndpoints SkipEndpoints []string } @@ -160,14 +119,12 @@ type Option func(*Options) // NewOptions create Options from Option slice func NewOptions(opts ...Option) Options { options := Options{ - Tracer: tracer.DefaultTracer, - ClientCallObservers: []ClientCallObserver{DefaultClientCallObserver}, - ClientStreamObservers: []ClientStreamObserver{DefaultClientStreamObserver}, - ClientPublishObservers: []ClientPublishObserver{DefaultClientPublishObserver}, - ClientCallFuncObservers: []ClientCallFuncObserver{DefaultClientCallFuncObserver}, - ServerHandlerObservers: []ServerHandlerObserver{DefaultServerHandlerObserver}, - ServerSubscriberObservers: []ServerSubscriberObserver{DefaultServerSubscriberObserver}, - SkipEndpoints: DefaultSkipEndpoints, + Tracer: tracer.DefaultTracer, + ClientCallObservers: []ClientCallObserver{DefaultClientCallObserver}, + ClientStreamObservers: []ClientStreamObserver{DefaultClientStreamObserver}, + ClientCallFuncObservers: []ClientCallFuncObserver{DefaultClientCallFuncObserver}, + ServerHandlerObservers: []ServerHandlerObserver{DefaultServerHandlerObserver}, + SkipEndpoints: DefaultSkipEndpoints, } for _, o := range opts { @@ -205,13 +162,6 @@ func WithClientStreamObservers(ob ...ClientStreamObserver) Option { } } -// WithClientPublishObservers funcs -func WithClientPublishObservers(ob ...ClientPublishObserver) Option { - return func(o *Options) { - o.ClientPublishObservers = ob - } -} - // WithClientCallFuncObservers funcs func WithClientCallFuncObservers(ob ...ClientCallFuncObserver) Option { return func(o *Options) { @@ -226,13 +176,6 @@ func WithServerHandlerObservers(ob ...ServerHandlerObserver) Option { } } -// WithServerSubscriberObservers funcs -func WithServerSubscriberObservers(ob ...ServerSubscriberObserver) Option { - return func(o *Options) { - o.ServerSubscriberObservers = ob - } -} - func (ot *tWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint()) for _, ep := range ot.opts.SkipEndpoints { @@ -279,22 +222,6 @@ func (ot *tWrapper) Stream(ctx context.Context, req client.Request, opts ...clie return stream, err } -func (ot *tWrapper) Publish(ctx context.Context, msg client.Message, opts ...client.PublishOption) error { - sp, ok := tracer.SpanFromContext(ctx) - if !ok { - ctx, sp = ot.opts.Tracer.Start(ctx, "", tracer.WithSpanKind(tracer.SpanKindProducer)) - } - defer sp.Finish() - - err := ot.Client.Publish(ctx, msg, opts...) - - for _, o := range ot.opts.ClientPublishObservers { - o(ctx, msg, opts, sp, err) - } - - return err -} - func (ot *tWrapper) ServerHandler(ctx context.Context, req server.Request, rsp interface{}) error { endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Method()) for _, ep := range ot.opts.SkipEndpoints { @@ -318,22 +245,6 @@ func (ot *tWrapper) ServerHandler(ctx context.Context, req server.Request, rsp i return err } -func (ot *tWrapper) ServerSubscriber(ctx context.Context, msg server.Message) error { - sp, ok := tracer.SpanFromContext(ctx) - if !ok { - ctx, sp = ot.opts.Tracer.Start(ctx, "", tracer.WithSpanKind(tracer.SpanKindConsumer)) - } - defer sp.Finish() - - err := ot.serverSubscriber(ctx, msg) - - for _, o := range ot.opts.ServerSubscriberObservers { - o(ctx, msg, sp, err) - } - - return err -} - // NewClientWrapper accepts an open tracing Trace and returns a Client Wrapper func NewClientWrapper(opts ...Option) client.Wrapper { return func(c client.Client) client.Client { @@ -393,16 +304,3 @@ func NewServerHandlerWrapper(opts ...Option) server.HandlerWrapper { return ot.ServerHandler } } - -// NewServerSubscriberWrapper accepts an opentracing Tracer and returns a Subscriber Wrapper -func NewServerSubscriberWrapper(opts ...Option) server.SubscriberWrapper { - return func(h server.SubscriberFunc) server.SubscriberFunc { - options := NewOptions() - for _, o := range opts { - o(&options) - } - - ot := &tWrapper{opts: options, serverSubscriber: h} - return ot.ServerSubscriber - } -} diff --git a/util/ctx/ctx.go b/util/ctx/ctx.go new file mode 100644 index 00000000..c952370e --- /dev/null +++ b/util/ctx/ctx.go @@ -0,0 +1,63 @@ +package ctx + +import "context" + +// Mixin mixes a typically shorter-lived (service, etc.) context passed in +// “shortctx” into a long-living context “longctx”, returning a derived “mixed” +// context. The “mixed” context is derived from the long-living context and +// additionally gets the deadline (if any) and cancellation of the mixed-in +// “shortctx”. +// +// Please note that it is essential to always call the additionally returned +// cancel function in order to not leak go routines. Calling this cancel +// function won't cancel the long-living and short-lived contexts, just clean +// up. This follows the established context pattern of [context.WithCancel], +// [context.WithDeadline] and [context.WithTimeout]. +func Mixin(longctx, shortctx context.Context) (mixedctx context.Context, mixedcancel context.CancelFunc) { + if longctx == nil { + panic("wye.Mixin: cannot mix into nil context") + } + if shortctx == nil { + panic("wye.Mixin: cannot mix-in nil context") + } + mixedctx = longctx + shortDone := shortctx.Done() + if shortDone == nil { + // In case the shorter-living context isn't cancellable at all, then we + // cannot cancel it and the cancel function returned must be a "no + // operation". There's no need to mix in something, so we can pass on + // the long-lived context. + mixedcancel = func() {} + return + } + // Nota bene: cancelled contexts "trickle down", so if a context higher up + // the hierarchy was cancelled, this will automatically propagate down to + // all child contexts, and so on. + // + // In case the shorter-lived context has a deadline, we need to carry it + // over into the final mixed context. + if deadline, ok := shortctx.Deadline(); ok { + mixedctx, mixedcancel = context.WithDeadline(mixedctx, deadline) + } + // As the shorter-living context can be cancelled, we will need to supervise + // it so we notice when it gets cancelled and then cancel the mixed context. + mixedctx, mixedcancel = context.WithCancel(mixedctx) + go func(ctx context.Context, cancel context.CancelFunc) { + select { + case <-shortDone: + // In case the shorter-living context was cancelled (but it did + // not pass a deadline) then we need to propagate this mixed + // context. Please note this correctly won't cancel the original + // longer context, because that's a long-living context we + // shouldn't interfere with. + if shortctx.Err() == context.Canceled { + cancel() + } + case <-ctx.Done(): + // The final mixed context was either cancelled itself or its parent + // deadline context met its fate; here, do not touch short-lived + // context. + } + }(mixedctx, mixedcancel) + return +} diff --git a/util/io/io.go b/util/io/io.go deleted file mode 100644 index ad13bf34..00000000 --- a/util/io/io.go +++ /dev/null @@ -1,40 +0,0 @@ -// Package io is for io management -package io // import "go.unistack.org/micro/v4/util/io" - -import ( - "io" - - "go.unistack.org/micro/v4/network/transport" -) - -type rwc struct { - socket transport.Socket -} - -func (r *rwc) Read(p []byte) (n int, err error) { - m := new(transport.Message) - if err := r.socket.Recv(m); err != nil { - return 0, err - } - copy(p, m.Body) - return len(m.Body), nil -} - -func (r *rwc) Write(p []byte) (n int, err error) { - err = r.socket.Send(&transport.Message{ - Body: p, - }) - if err != nil { - return 0, err - } - return len(p), nil -} - -func (r *rwc) Close() error { - return r.socket.Close() -} - -// NewRWC returns a new ReadWriteCloser -func NewRWC(sock transport.Socket) io.ReadWriteCloser { - return &rwc{sock} -} diff --git a/util/pool/default.go b/util/pool/default.go deleted file mode 100644 index 9ccd4d27..00000000 --- a/util/pool/default.go +++ /dev/null @@ -1,118 +0,0 @@ -package pool - -import ( - "context" - "sync" - "time" - - "go.unistack.org/micro/v4/network/transport" - "go.unistack.org/micro/v4/util/id" -) - -type pool struct { - tr transport.Transport - conns map[string][]*poolConn - size int - ttl time.Duration - sync.Mutex -} - -type poolConn struct { - created time.Time - transport.Client - id string -} - -func newPool(options Options) *pool { - return &pool{ - size: options.Size, - tr: options.Transport, - ttl: options.TTL, - conns: make(map[string][]*poolConn), - } -} - -func (p *pool) Close() error { - p.Lock() - for k, c := range p.conns { - for _, conn := range c { - conn.Client.Close() - } - delete(p.conns, k) - } - p.Unlock() - return nil -} - -// NoOp the Close since we manage it -func (p *poolConn) Close() error { - return nil -} - -func (p *poolConn) ID() string { - return p.id -} - -func (p *poolConn) Created() time.Time { - return p.created -} - -func (p *pool) Get(ctx context.Context, addr string, opts ...transport.DialOption) (Conn, error) { - p.Lock() - conns := p.conns[addr] - - // while we have conns check age and then return one - // otherwise we'll create a new conn - for len(conns) > 0 { - conn := conns[len(conns)-1] - conns = conns[:len(conns)-1] - p.conns[addr] = conns - - // if conn is old kill it and move on - if d := time.Since(conn.Created()); d > p.ttl { - conn.Client.Close() - continue - } - - // we got a good conn, lets unlock and return it - p.Unlock() - - return conn, nil - } - - p.Unlock() - - // create new conn - c, err := p.tr.Dial(ctx, addr, opts...) - if err != nil { - return nil, err - } - id, err := id.New() - if err != nil { - return nil, err - } - return &poolConn{ - Client: c, - id: id, - created: time.Now(), - }, nil -} - -func (p *pool) Release(conn Conn, err error) error { - // don't store the conn if it has errored - if err != nil { - return conn.(*poolConn).Client.Close() - } - - // otherwise put it back for reuse - p.Lock() - conns := p.conns[conn.Remote()] - if len(conns) >= p.size { - p.Unlock() - return conn.(*poolConn).Client.Close() - } - p.conns[conn.Remote()] = append(conns, conn.(*poolConn)) - p.Unlock() - - return nil -} diff --git a/util/pool/default_test.go b/util/pool/default_test.go deleted file mode 100644 index 8ef74c4c..00000000 --- a/util/pool/default_test.go +++ /dev/null @@ -1,92 +0,0 @@ -//go:build ignore -// +build ignore - -package pool - -import ( - "testing" - "time" - - "go.unistack.org/micro/v4/network/transport" - "go.unistack.org/micro/v4/network/transport/memory" -) - -func testPool(t *testing.T, size int, ttl time.Duration) { - // mock transport - tr := memory.NewTransport() - - options := Options{ - TTL: ttl, - Size: size, - Transport: tr, - } - // zero pool - p := newPool(options) - - // listen - l, err := tr.Listen(":0") - if err != nil { - t.Fatal(err) - } - defer l.Close() - - // accept loop - go func() { - for { - if err := l.Accept(func(s transport.Socket) { - for { - var msg transport.Message - if err := s.Recv(&msg); err != nil { - return - } - if err := s.Send(&msg); err != nil { - return - } - } - }); err != nil { - return - } - } - }() - - for i := 0; i < 10; i++ { - // get a conn - c, err := p.Get(l.Addr()) - if err != nil { - t.Fatal(err) - } - - msg := &transport.Message{ - Body: []byte(`hello world`), - } - - if err := c.Send(msg); err != nil { - t.Fatal(err) - } - - var rcv transport.Message - - if err := c.Recv(&rcv); err != nil { - t.Fatal(err) - } - - if string(rcv.Body) != string(msg.Body) { - t.Fatalf("got %v, expected %v", rcv.Body, msg.Body) - } - - // release the conn - p.Release(c, nil) - - p.Lock() - if i := len(p.conns[l.Addr()]); i > size { - p.Unlock() - t.Fatalf("pool size %d is greater than expected %d", i, size) - } - p.Unlock() - } -} - -func TestClientPool(t *testing.T) { - testPool(t, 0, time.Minute) - testPool(t, 2, time.Minute) -} diff --git a/util/pool/options.go b/util/pool/options.go deleted file mode 100644 index c6df3949..00000000 --- a/util/pool/options.go +++ /dev/null @@ -1,38 +0,0 @@ -package pool - -import ( - "time" - - "go.unistack.org/micro/v4/network/transport" -) - -// Options struct -type Options struct { - Transport transport.Transport - TTL time.Duration - Size int -} - -// Option func signature -type Option func(*Options) - -// Size sets the size -func Size(i int) Option { - return func(o *Options) { - o.Size = i - } -} - -// Transport sets the transport -func Transport(t transport.Transport) Option { - return func(o *Options) { - o.Transport = t - } -} - -// TTL specifies ttl -func TTL(t time.Duration) Option { - return func(o *Options) { - o.TTL = t - } -} diff --git a/util/pool/pool.go b/util/pool/pool.go deleted file mode 100644 index 329027a7..00000000 --- a/util/pool/pool.go +++ /dev/null @@ -1,38 +0,0 @@ -// Package pool is a connection pool -package pool // import "go.unistack.org/micro/v4/util/pool" - -import ( - "context" - "time" - - "go.unistack.org/micro/v4/network/transport" -) - -// Pool is an interface for connection pooling -type Pool interface { - // Close the pool - Close() error - // Get a connection - Get(ctx context.Context, addr string, opts ...transport.DialOption) (Conn, error) - // Release the connection - Release(c Conn, status error) error -} - -// Conn conn pool interface -type Conn interface { - // unique id of connection - ID() string - // time it was created - Created() time.Time - // embedded connection - transport.Client -} - -// NewPool creates new connection pool -func NewPool(opts ...Option) Pool { - options := Options{} - for _, o := range opts { - o(&options) - } - return newPool(options) -} diff --git a/util/reflect/struct.go b/util/reflect/struct.go index 44ce1fdf..6c4e302c 100644 --- a/util/reflect/struct.go +++ b/util/reflect/struct.go @@ -76,6 +76,9 @@ func ZeroFieldByPath(src interface{}, path string) error { val := reflect.ValueOf(src) for _, p := range strings.Split(path, ".") { + if p == "" { + continue + } val, err = structValueByName(val, p) if err != nil { return err @@ -101,6 +104,9 @@ func SetFieldByPath(src interface{}, dst interface{}, path string) error { val := reflect.ValueOf(src) for _, p := range strings.Split(path, ".") { + if p == "" { + continue + } val, err = structValueByName(val, p) if err != nil { return err @@ -111,7 +117,20 @@ func SetFieldByPath(src interface{}, dst interface{}, path string) error { return ErrInvalidStruct } - val.Set(reflect.ValueOf(dst)) + nv := reflect.ValueOf(dst) + if val.Kind() != nv.Kind() { + switch { + default: + val.Set(nv) + case nv.Kind() == reflect.Slice: + val.Set(nv.Index(0)) + case val.Kind() == reflect.Slice: + val.Set(reflect.MakeSlice(val.Type(), 1, 1)) + val.Index(0).Set(nv) + } + } else { + val.Set(nv) + } return nil } @@ -157,6 +176,9 @@ func structValueByName(sv reflect.Value, tkey string) (reflect.Value, error) { func StructFieldByPath(src interface{}, path string) (interface{}, error) { var err error for _, p := range strings.Split(path, ".") { + if p == "" { + continue + } src, err = StructFieldByName(src, p) if err != nil { return nil, err diff --git a/util/reflect/struct_test.go b/util/reflect/struct_test.go index 510d7a3a..19b15ab8 100644 --- a/util/reflect/struct_test.go +++ b/util/reflect/struct_test.go @@ -62,6 +62,42 @@ func TestStructFieldsNested(t *testing.T) { } } +func TestSetFieldByPathMultiple(t *testing.T) { + var err error + tv := "test_val" + type Str1 struct { + Name []string `json:"name"` + } + val1 := &Str1{} + + err = rutil.SetFieldByPath(val1, tv, ".Name") + if err != nil { + t.Fatal(err) + } + + if len(val1.Name) != 1 { + t.Fatal("assign error") + } else if val1.Name[0] != tv { + t.Fatal("assign error") + } + + type Str2 struct { + Name string `json:"name"` + } + val2 := &Str2{} + + err = rutil.SetFieldByPath(val2, []string{tv}, ".Name") + if err != nil { + t.Fatal(err) + } + + if len(val1.Name) != 1 { + t.Fatal("assign error") + } else if val1.Name[0] != tv { + t.Fatal("assign error") + } +} + func TestSetFieldByPath(t *testing.T) { type NestedStr struct { BBB string `json:"bbb"` diff --git a/util/socket/pool.go b/util/socket/pool.go deleted file mode 100644 index 470fa3c3..00000000 --- a/util/socket/pool.go +++ /dev/null @@ -1,65 +0,0 @@ -package socket - -import ( - "sync" -) - -// Pool holds the socket pool -type Pool struct { - sync.RWMutex - pool map[string]*Socket -} - -// Get socket from pool -func (p *Pool) Get(id string) (*Socket, bool) { - // attempt to get existing socket - p.RLock() - socket, ok := p.pool[id] - if ok { - p.RUnlock() - return socket, ok - } - p.RUnlock() - - // save socket - p.Lock() - defer p.Unlock() - // double checked locking - socket, ok = p.pool[id] - if ok { - return socket, ok - } - // create new socket - socket = New(id) - p.pool[id] = socket - - // return socket - return socket, false -} - -// Release close the socket and delete from pool -func (p *Pool) Release(s *Socket) { - p.Lock() - defer p.Unlock() - - // close the socket - s.Close() - delete(p.pool, s.id) -} - -// Close the pool and delete all the sockets -func (p *Pool) Close() { - p.Lock() - defer p.Unlock() - for id, sock := range p.pool { - sock.Close() - delete(p.pool, id) - } -} - -// NewPool returns a new socket pool -func NewPool() *Pool { - return &Pool{ - pool: make(map[string]*Socket), - } -} diff --git a/util/socket/socket.go b/util/socket/socket.go deleted file mode 100644 index a686c511..00000000 --- a/util/socket/socket.go +++ /dev/null @@ -1,118 +0,0 @@ -// Package socket provides a pseudo socket -package socket // import "go.unistack.org/micro/v4/util/socket" - -import ( - "io" - - "go.unistack.org/micro/v4/network/transport" -) - -// Socket is our pseudo socket for transport.Socket -type Socket struct { - closed chan bool - send chan *transport.Message - recv chan *transport.Message - id string - remote string - local string -} - -// SetLocal sets the local addr -func (s *Socket) SetLocal(l string) { - s.local = l -} - -// SetRemote sets the remote addr -func (s *Socket) SetRemote(r string) { - s.remote = r -} - -// Accept passes a message to the socket which will be processed by the call to Recv -func (s *Socket) Accept(m *transport.Message) error { - select { - case s.recv <- m: - return nil - case <-s.closed: - return io.EOF - } -} - -// Process takes the next message off the send queue created by a call to Send -func (s *Socket) Process(m *transport.Message) error { - select { - case msg := <-s.send: - *m = *msg - case <-s.closed: - // see if we need to drain - select { - case msg := <-s.send: - *m = *msg - return nil - default: - return io.EOF - } - } - return nil -} - -// Remote returns remote addr -func (s *Socket) Remote() string { - return s.remote -} - -// Local returns local addr -func (s *Socket) Local() string { - return s.local -} - -// Send message by via transport -func (s *Socket) Send(m *transport.Message) error { - // send a message - select { - case s.send <- m: - case <-s.closed: - return io.EOF - } - - return nil -} - -// Recv message from transport -func (s *Socket) Recv(m *transport.Message) error { - // receive a message - select { - case msg := <-s.recv: - // set message - *m = *msg - case <-s.closed: - return io.EOF - } - - // return nil - return nil -} - -// Close closes the socket -func (s *Socket) Close() error { - select { - case <-s.closed: - // no op - default: - close(s.closed) - } - return nil -} - -// New returns a new pseudo socket which can be used in the place of a transport socket. -// Messages are sent to the socket via Accept and receives from the socket via Process. -// SetLocal/SetRemote should be called before using the socket. -func New(id string) *Socket { - return &Socket{ - id: id, - closed: make(chan bool), - local: "local", - remote: "remote", - send: make(chan *transport.Message, 128), - recv: make(chan *transport.Message, 128), - } -}