many improvements with options and noop stuff
* add many options helpers * fix noop client to allow publish messages to topic in broker * fix noop server to allow registering in registry * fix noop server to allow subscribe to topic in broker * fix new service initialization Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
parent
a59aae760f
commit
14c97d59c1
@ -21,13 +21,8 @@ type httpServer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(address string, opts ...server.Option) server.Server {
|
func NewServer(address string, opts ...server.Option) server.Server {
|
||||||
var options server.Options
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &httpServer{
|
return &httpServer{
|
||||||
opts: options,
|
opts: server.NewOptions(opts...),
|
||||||
mux: http.NewServeMux(),
|
mux: http.NewServeMux(),
|
||||||
address: address,
|
address: address,
|
||||||
exit: make(chan chan error),
|
exit: make(chan chan error),
|
||||||
|
@ -21,6 +21,14 @@ type Options struct {
|
|||||||
Wrappers []Wrapper
|
Wrappers []Wrapper
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewOptions(opts ...Option) Options {
|
||||||
|
options := Options{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
type Wrapper func(h http.Handler) http.Handler
|
type Wrapper func(h http.Handler) http.Handler
|
||||||
|
|
||||||
func WrapHandler(w ...Wrapper) Option {
|
func WrapHandler(w ...Wrapper) Option {
|
||||||
|
@ -17,7 +17,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultAuth Auth = newAuth()
|
DefaultAuth Auth = &NoopAuth{opts: NewOptions()}
|
||||||
// ErrInvalidToken is when the token provided is not valid
|
// ErrInvalidToken is when the token provided is not valid
|
||||||
ErrInvalidToken = errors.New("invalid token provided")
|
ErrInvalidToken = errors.New("invalid token provided")
|
||||||
// ErrForbidden is when a user does not have the necessary scope to access a resource
|
// ErrForbidden is when a user does not have the necessary scope to access a resource
|
||||||
|
33
auth/noop.go
33
auth/noop.go
@ -4,40 +4,29 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newAuth(opts ...Option) Auth {
|
type NoopAuth struct {
|
||||||
var options Options
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &noop{
|
|
||||||
opts: options,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type noop struct {
|
|
||||||
opts Options
|
opts Options
|
||||||
}
|
}
|
||||||
|
|
||||||
// String returns the name of the implementation
|
// String returns the name of the implementation
|
||||||
func (n *noop) String() string {
|
func (n *NoopAuth) String() string {
|
||||||
return "noop"
|
return "noop"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init the auth
|
// Init the auth
|
||||||
func (n *noop) Init(opts ...Option) {
|
func (n *NoopAuth) Init(opts ...Option) {
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&n.opts)
|
o(&n.opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Options set for auth
|
// Options set for auth
|
||||||
func (n *noop) Options() Options {
|
func (n *NoopAuth) Options() Options {
|
||||||
return n.opts
|
return n.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a new account
|
// Generate a new account
|
||||||
func (n *noop) Generate(id string, opts ...GenerateOption) (*Account, error) {
|
func (n *NoopAuth) Generate(id string, opts ...GenerateOption) (*Account, error) {
|
||||||
options := NewGenerateOptions(opts...)
|
options := NewGenerateOptions(opts...)
|
||||||
|
|
||||||
return &Account{
|
return &Account{
|
||||||
@ -50,27 +39,27 @@ func (n *noop) Generate(id string, opts ...GenerateOption) (*Account, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Grant access to a resource
|
// Grant access to a resource
|
||||||
func (n *noop) Grant(rule *Rule) error {
|
func (n *NoopAuth) Grant(rule *Rule) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Revoke access to a resource
|
// Revoke access to a resource
|
||||||
func (n *noop) Revoke(rule *Rule) error {
|
func (n *NoopAuth) Revoke(rule *Rule) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rules used to verify requests
|
// Rules used to verify requests
|
||||||
func (n *noop) Rules(opts ...RulesOption) ([]*Rule, error) {
|
func (n *NoopAuth) Rules(opts ...RulesOption) ([]*Rule, error) {
|
||||||
return []*Rule{}, nil
|
return []*Rule{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify an account has access to a resource
|
// Verify an account has access to a resource
|
||||||
func (n *noop) Verify(acc *Account, res *Resource, opts ...VerifyOption) error {
|
func (n *NoopAuth) Verify(acc *Account, res *Resource, opts ...VerifyOption) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inspect a token
|
// Inspect a token
|
||||||
func (n *noop) Inspect(token string) (*Account, error) {
|
func (n *NoopAuth) Inspect(token string) (*Account, error) {
|
||||||
uid, err := uuid.NewRandom()
|
uid, err := uuid.NewRandom()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -79,6 +68,6 @@ func (n *noop) Inspect(token string) (*Account, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Token generation using an account id and secret
|
// Token generation using an account id and secret
|
||||||
func (n *noop) Token(opts ...TokenOption) (*Token, error) {
|
func (n *NoopAuth) Token(opts ...TokenOption) (*Token, error) {
|
||||||
return &Token{}, nil
|
return &Token{}, nil
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
// Package broker is an interface used for asynchronous messaging
|
// Package broker is an interface used for asynchronous messaging
|
||||||
package broker
|
package broker
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultBroker Broker = newBroker()
|
DefaultBroker Broker = &NoopBroker{opts: NewOptions()}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Broker is an interface used for asynchronous messaging.
|
// Broker is an interface used for asynchronous messaging.
|
||||||
@ -10,10 +12,10 @@ type Broker interface {
|
|||||||
Init(...Option) error
|
Init(...Option) error
|
||||||
Options() Options
|
Options() Options
|
||||||
Address() string
|
Address() string
|
||||||
Connect() error
|
Connect(context.Context) error
|
||||||
Disconnect() error
|
Disconnect(context.Context) error
|
||||||
Publish(topic string, m *Message, opts ...PublishOption) error
|
Publish(context.Context, string, *Message, ...PublishOption) error
|
||||||
Subscribe(topic string, h Handler, opts ...SubscribeOption) (Subscriber, error)
|
Subscribe(context.Context, string, Handler, ...SubscribeOption) (Subscriber, error)
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,5 +41,5 @@ type Message struct {
|
|||||||
type Subscriber interface {
|
type Subscriber interface {
|
||||||
Options() SubscribeOptions
|
Options() SubscribeOptions
|
||||||
Topic() string
|
Topic() string
|
||||||
Unsubscribe() error
|
Unsubscribe(context.Context) error
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package broker
|
package broker
|
||||||
|
|
||||||
type noopBroker struct {
|
import "context"
|
||||||
|
|
||||||
|
type NoopBroker struct {
|
||||||
opts Options
|
opts Options
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9,7 +11,7 @@ type noopSubscriber struct {
|
|||||||
opts SubscribeOptions
|
opts SubscribeOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopBroker) Init(opts ...Option) error {
|
func (n *NoopBroker) Init(opts ...Option) error {
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&n.opts)
|
o(&n.opts)
|
||||||
}
|
}
|
||||||
@ -17,27 +19,27 @@ func (n *noopBroker) Init(opts ...Option) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopBroker) Options() Options {
|
func (n *NoopBroker) Options() Options {
|
||||||
return n.opts
|
return n.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopBroker) Address() string {
|
func (n *NoopBroker) Address() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopBroker) Connect() error {
|
func (n *NoopBroker) Connect(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopBroker) Disconnect() error {
|
func (n *NoopBroker) Disconnect(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopBroker) Publish(topic string, m *Message, opts ...PublishOption) error {
|
func (n *NoopBroker) Publish(ctx context.Context, topic string, m *Message, opts ...PublishOption) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopBroker) Subscribe(topic string, h Handler, opts ...SubscribeOption) (Subscriber, error) {
|
func (n *NoopBroker) Subscribe(ctx context.Context, topic string, h Handler, opts ...SubscribeOption) (Subscriber, error) {
|
||||||
options := NewSubscribeOptions()
|
options := NewSubscribeOptions()
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
@ -47,7 +49,7 @@ func (n *noopBroker) Subscribe(topic string, h Handler, opts ...SubscribeOption)
|
|||||||
return &noopSubscriber{topic: topic, opts: options}, nil
|
return &noopSubscriber{topic: topic, opts: options}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopBroker) String() string {
|
func (n *NoopBroker) String() string {
|
||||||
return "noop"
|
return "noop"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,16 +61,6 @@ func (n *noopSubscriber) Topic() string {
|
|||||||
return n.topic
|
return n.topic
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopSubscriber) Unsubscribe() error {
|
func (n *noopSubscriber) Unsubscribe(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newBroker returns a new noop broker
|
|
||||||
func newBroker(opts ...Option) Broker {
|
|
||||||
options := NewOptions()
|
|
||||||
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
return &noopBroker{opts: options}
|
|
||||||
}
|
|
||||||
|
@ -39,6 +39,12 @@ func NewOptions(opts ...Option) Options {
|
|||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Context(ctx context.Context) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Context = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type PublishOptions struct {
|
type PublishOptions struct {
|
||||||
// Other options for implementations of the interface
|
// Other options for implementations of the interface
|
||||||
// can be stored in a context
|
// can be stored in a context
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultClient Client = newClient()
|
DefaultClient Client = &NoopClient{opts: NewOptions()}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Client is the interface used to make requests to services.
|
// Client is the interface used to make requests to services.
|
||||||
|
@ -2,17 +2,16 @@ package client
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
raw "github.com/unistack-org/micro-codec-bytes"
|
raw "github.com/unistack-org/micro-codec-bytes"
|
||||||
"github.com/unistack-org/micro/v3/broker"
|
"github.com/unistack-org/micro/v3/broker"
|
||||||
"github.com/unistack-org/micro/v3/codec"
|
"github.com/unistack-org/micro/v3/codec"
|
||||||
|
"github.com/unistack-org/micro/v3/codec/json"
|
||||||
"github.com/unistack-org/micro/v3/errors"
|
"github.com/unistack-org/micro/v3/errors"
|
||||||
"github.com/unistack-org/micro/v3/metadata"
|
"github.com/unistack-org/micro/v3/metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type noopClient struct {
|
type NoopClient struct {
|
||||||
once atomic.Value
|
|
||||||
opts Options
|
opts Options
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,30 +118,30 @@ func (n *noopMessage) ContentType() string {
|
|||||||
return n.opts.ContentType
|
return n.opts.ContentType
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) Init(opts ...Option) error {
|
func (n *NoopClient) Init(opts ...Option) error {
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&n.opts)
|
o(&n.opts)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) Options() Options {
|
func (n *NoopClient) Options() Options {
|
||||||
return n.opts
|
return n.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) String() string {
|
func (n *NoopClient) String() string {
|
||||||
return "noop"
|
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 ...CallOption) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) NewRequest(service, endpoint string, req interface{}, opts ...RequestOption) Request {
|
func (n *NoopClient) NewRequest(service, endpoint string, req interface{}, opts ...RequestOption) Request {
|
||||||
return &noopRequest{}
|
return &noopRequest{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) NewMessage(topic string, msg interface{}, opts ...MessageOption) Message {
|
func (n *NoopClient) NewMessage(topic string, msg interface{}, opts ...MessageOption) Message {
|
||||||
options := MessageOptions{}
|
options := MessageOptions{}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
@ -151,25 +150,18 @@ func (n *noopClient) NewMessage(topic string, msg interface{}, opts ...MessageOp
|
|||||||
return &noopMessage{topic: topic, payload: msg, opts: options}
|
return &noopMessage{topic: topic, payload: msg, opts: options}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) {
|
func (n *NoopClient) Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) {
|
||||||
return &noopStream{}, nil
|
return &noopStream{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOption) error {
|
func (n *NoopClient) Publish(ctx context.Context, p Message, opts ...PublishOption) error {
|
||||||
var options PublishOptions
|
|
||||||
var body []byte
|
var body []byte
|
||||||
|
|
||||||
// fail early on connect error
|
if err := n.opts.Broker.Connect(ctx); err != nil {
|
||||||
if !n.once.Load().(bool) {
|
return errors.InternalServerError("go.micro.client", err.Error())
|
||||||
if err := n.opts.Broker.Connect(); err != nil {
|
|
||||||
return errors.InternalServerError("go.micro.client", err.Error())
|
|
||||||
}
|
|
||||||
n.once.Store(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range opts {
|
options := NewPublishOptions(opts...)
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
md, ok := metadata.FromContext(ctx)
|
md, ok := metadata.FromContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -182,6 +174,11 @@ func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOpti
|
|||||||
if d, ok := p.Payload().(*raw.Frame); ok {
|
if d, ok := p.Payload().(*raw.Frame); ok {
|
||||||
body = d.Data
|
body = d.Data
|
||||||
} else {
|
} else {
|
||||||
|
cf := n.opts.Broker.Options().Codec
|
||||||
|
if cf == nil {
|
||||||
|
cf = json.Marshaler{}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// use codec for payload
|
// use codec for payload
|
||||||
cf, err := n.opts.Codecs[p.ContentType()]
|
cf, err := n.opts.Codecs[p.ContentType()]
|
||||||
@ -190,7 +187,7 @@ func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOpti
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
// set the body
|
// set the body
|
||||||
b, err := n.opts.Broker.Options().Codec.Marshal(p.Payload())
|
b, err := cf.Marshal(p.Payload())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.InternalServerError("go.micro.client", err.Error())
|
return errors.InternalServerError("go.micro.client", err.Error())
|
||||||
}
|
}
|
||||||
@ -204,7 +201,7 @@ func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOpti
|
|||||||
topic = options.Exchange
|
topic = options.Exchange
|
||||||
}
|
}
|
||||||
|
|
||||||
return n.opts.Broker.Publish(topic, &broker.Message{
|
return n.opts.Broker.Publish(ctx, topic, &broker.Message{
|
||||||
Header: md,
|
Header: md,
|
||||||
Body: body,
|
Body: body,
|
||||||
}, broker.PublishContext(options.Context))
|
}, broker.PublishContext(options.Context))
|
||||||
@ -217,5 +214,5 @@ func newClient(opts ...Option) Client {
|
|||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
}
|
}
|
||||||
return &noopClient{opts: options}
|
return &NoopClient{opts: options}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,11 @@ import (
|
|||||||
"github.com/unistack-org/micro/v3/broker"
|
"github.com/unistack-org/micro/v3/broker"
|
||||||
"github.com/unistack-org/micro/v3/codec"
|
"github.com/unistack-org/micro/v3/codec"
|
||||||
"github.com/unistack-org/micro/v3/logger"
|
"github.com/unistack-org/micro/v3/logger"
|
||||||
|
"github.com/unistack-org/micro/v3/network/transport"
|
||||||
"github.com/unistack-org/micro/v3/registry"
|
"github.com/unistack-org/micro/v3/registry"
|
||||||
"github.com/unistack-org/micro/v3/router"
|
"github.com/unistack-org/micro/v3/router"
|
||||||
"github.com/unistack-org/micro/v3/selector"
|
"github.com/unistack-org/micro/v3/selector"
|
||||||
"github.com/unistack-org/micro/v3/selector/random"
|
"github.com/unistack-org/micro/v3/selector/random"
|
||||||
"github.com/unistack-org/micro/v3/network/transport"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
@ -48,6 +48,14 @@ type Options struct {
|
|||||||
Context context.Context
|
Context context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewCallOptions(opts ...CallOption) CallOptions {
|
||||||
|
options := CallOptions{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
type CallOptions struct {
|
type CallOptions struct {
|
||||||
// Address of remote hosts
|
// Address of remote hosts
|
||||||
Address []string
|
Address []string
|
||||||
@ -84,6 +92,20 @@ type CallOptions struct {
|
|||||||
Context context.Context
|
Context context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Context(ctx context.Context) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Context = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPublishOptions(opts ...PublishOption) PublishOptions {
|
||||||
|
options := PublishOptions{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
type PublishOptions struct {
|
type PublishOptions struct {
|
||||||
// Exchange is the routing exchange for the message
|
// Exchange is the routing exchange for the message
|
||||||
Exchange string
|
Exchange string
|
||||||
@ -92,10 +114,26 @@ type PublishOptions struct {
|
|||||||
Context context.Context
|
Context context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewMessageOptions(opts ...MessageOption) MessageOptions {
|
||||||
|
options := MessageOptions{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
type MessageOptions struct {
|
type MessageOptions struct {
|
||||||
ContentType string
|
ContentType string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewRequestOptions(opts ...RequestOption) RequestOptions {
|
||||||
|
options := RequestOptions{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
type RequestOptions struct {
|
type RequestOptions struct {
|
||||||
ContentType string
|
ContentType string
|
||||||
Stream bool
|
Stream bool
|
||||||
|
@ -25,12 +25,13 @@ func (c *Codec) ReadBody(b interface{}) error {
|
|||||||
if b == nil {
|
if b == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if pb, ok := b.(proto.Message); ok {
|
switch m := b.(type) {
|
||||||
|
case proto.Message:
|
||||||
buf, err := ioutil.ReadAll(c.Conn)
|
buf, err := ioutil.ReadAll(c.Conn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return jsonpb.Unmarshal(buf, pb)
|
return jsonpb.Unmarshal(buf, m)
|
||||||
}
|
}
|
||||||
return c.Decoder.Decode(b)
|
return c.Decoder.Decode(b)
|
||||||
}
|
}
|
||||||
|
@ -11,20 +11,20 @@ type Profile interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultProfile Profile = new(noop)
|
DefaultProfile Profile = &NoopProfile{}
|
||||||
)
|
)
|
||||||
|
|
||||||
type noop struct{}
|
type NoopProfile struct{}
|
||||||
|
|
||||||
func (p *noop) Start() error {
|
func (p *NoopProfile) Start() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *noop) Stop() error {
|
func (p *NoopProfile) Stop() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *noop) String() string {
|
func (p *NoopProfile) String() string {
|
||||||
return "noop"
|
return "noop"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ var (
|
|||||||
// Logger is a generic logging interface
|
// Logger is a generic logging interface
|
||||||
type Logger interface {
|
type Logger interface {
|
||||||
// Init initialises options
|
// Init initialises options
|
||||||
Init(options ...Option) error
|
Init(opts ...Option) error
|
||||||
// V compare provided verbosity level with current log level
|
// V compare provided verbosity level with current log level
|
||||||
V(level Level) bool
|
V(level Level) bool
|
||||||
// The Logger options
|
// The Logger options
|
||||||
|
@ -20,6 +20,14 @@ type Options struct {
|
|||||||
Context context.Context
|
Context context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewOptions(opts ...Option) Options {
|
||||||
|
options := Options{}
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
// WithFields set default fields for the logger
|
// WithFields set default fields for the logger
|
||||||
func WithFields(fields map[string]interface{}) Option {
|
func WithFields(fields map[string]interface{}) Option {
|
||||||
return func(args *Options) {
|
return func(args *Options) {
|
||||||
|
5
micro.go
5
micro.go
@ -4,6 +4,7 @@ package micro
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/unistack-org/micro/v3/broker"
|
||||||
"github.com/unistack-org/micro/v3/client"
|
"github.com/unistack-org/micro/v3/client"
|
||||||
"github.com/unistack-org/micro/v3/server"
|
"github.com/unistack-org/micro/v3/server"
|
||||||
)
|
)
|
||||||
@ -17,13 +18,15 @@ type Service interface {
|
|||||||
// The service name
|
// The service name
|
||||||
Name() string
|
Name() string
|
||||||
// Init initialises options
|
// Init initialises options
|
||||||
Init(...Option)
|
Init(...Option) error
|
||||||
// Options returns the current options
|
// Options returns the current options
|
||||||
Options() Options
|
Options() Options
|
||||||
// Client is used to call services
|
// Client is used to call services
|
||||||
Client() client.Client
|
Client() client.Client
|
||||||
// Server is for handling requests and events
|
// Server is for handling requests and events
|
||||||
Server() server.Server
|
Server() server.Server
|
||||||
|
// Broker is for broker usage
|
||||||
|
Broker() broker.Broker
|
||||||
// Run the service
|
// Run the service
|
||||||
Run() error
|
Run() error
|
||||||
// The service implementation
|
// The service implementation
|
||||||
|
70
network/transport/noop.go
Normal file
70
network/transport/noop.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package transport
|
||||||
|
|
||||||
|
type NoopTransport struct {
|
||||||
|
opts Options
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NoopTransport) Init(opts ...Option) error {
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&t.opts)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NoopTransport) Options() Options {
|
||||||
|
return t.opts
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NoopTransport) Dial(addr string, opts ...DialOption) (Client, error) {
|
||||||
|
options := NewDialOptions(opts...)
|
||||||
|
return &noopClient{opts: options}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NoopTransport) Listen(addr string, opts ...ListenOption) (Listener, error) {
|
||||||
|
options := NewListenOptions(opts...)
|
||||||
|
return &noopListener{opts: options}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NoopTransport) String() string {
|
||||||
|
return "noop"
|
||||||
|
}
|
||||||
|
|
||||||
|
type noopClient struct {
|
||||||
|
opts DialOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *noopClient) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *noopClient) Local() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *noopClient) Remote() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *noopClient) Recv(*Message) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *noopClient) Send(*Message) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type noopListener struct {
|
||||||
|
opts ListenOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopListener) Addr() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopListener) Accept(fn func(Socket)) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *noopListener) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
@ -106,6 +106,12 @@ func Logger(l logger.Logger) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Context(ctx context.Context) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Context = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Codec sets the codec used for encoding where the transport
|
// Codec sets the codec used for encoding where the transport
|
||||||
// does not support message headers
|
// does not support message headers
|
||||||
func Codec(c codec.Marshaler) Option {
|
func Codec(c codec.Marshaler) Option {
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultTransport Transport
|
DefaultTransport Transport = &NoopTransport{opts: NewOptions()}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Transport is an interface which is used for communication between
|
// Transport is an interface which is used for communication between
|
||||||
|
@ -49,15 +49,15 @@ func (t *tunBroker) Address() string {
|
|||||||
return t.tunnel.Address()
|
return t.tunnel.Address()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunBroker) Connect() error {
|
func (t *tunBroker) Connect(ctx context.Context) error {
|
||||||
return t.tunnel.Connect()
|
return t.tunnel.Connect()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunBroker) Disconnect() error {
|
func (t *tunBroker) Disconnect(ctx context.Context) error {
|
||||||
return t.tunnel.Close()
|
return t.tunnel.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunBroker) Publish(topic string, m *broker.Message, opts ...broker.PublishOption) error {
|
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
|
// TODO: this is probably inefficient, we might want to just maintain an open connection
|
||||||
// it may be easier to add broadcast to the tunnel
|
// it may be easier to add broadcast to the tunnel
|
||||||
c, err := t.tunnel.Dial(topic, tunnel.DialMode(tunnel.Multicast))
|
c, err := t.tunnel.Dial(topic, tunnel.DialMode(tunnel.Multicast))
|
||||||
@ -72,21 +72,16 @@ func (t *tunBroker) Publish(topic string, m *broker.Message, opts ...broker.Publ
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunBroker) Subscribe(topic string, h broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
func (t *tunBroker) Subscribe(ctx context.Context, topic string, h broker.Handler, opts ...broker.SubscribeOption) (broker.Subscriber, error) {
|
||||||
l, err := t.tunnel.Listen(topic, tunnel.ListenMode(tunnel.Multicast))
|
l, err := t.tunnel.Listen(topic, tunnel.ListenMode(tunnel.Multicast))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var options broker.SubscribeOptions
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
tunSub := &tunSubscriber{
|
tunSub := &tunSubscriber{
|
||||||
topic: topic,
|
topic: topic,
|
||||||
handler: h,
|
handler: h,
|
||||||
opts: options,
|
opts: broker.NewSubscribeOptions(opts...),
|
||||||
closed: make(chan bool),
|
closed: make(chan bool),
|
||||||
listener: l,
|
listener: l,
|
||||||
}
|
}
|
||||||
@ -150,7 +145,7 @@ func (t *tunSubscriber) Topic() string {
|
|||||||
return t.topic
|
return t.topic
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tunSubscriber) Unsubscribe() error {
|
func (t *tunSubscriber) Unsubscribe(ctx context.Context) error {
|
||||||
select {
|
select {
|
||||||
case <-t.closed:
|
case <-t.closed:
|
||||||
return nil
|
return nil
|
||||||
@ -177,12 +172,7 @@ func (t *tunEvent) Error() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewBroker(opts ...broker.Option) (broker.Broker, error) {
|
func NewBroker(opts ...broker.Option) (broker.Broker, error) {
|
||||||
options := broker.Options{
|
options := broker.NewOptions(opts...)
|
||||||
Context: context.Background(),
|
|
||||||
}
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
t, ok := options.Context.Value(tunnelKey{}).(tunnel.Tunnel)
|
t, ok := options.Context.Value(tunnelKey{}).(tunnel.Tunnel)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -1,53 +1,59 @@
|
|||||||
package registry
|
package registry
|
||||||
|
|
||||||
import "fmt"
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
type noopRegistry struct {
|
type NoopRegistry struct {
|
||||||
opts Options
|
opts Options
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopRegistry) Init(opts ...Option) error {
|
func (n *NoopRegistry) Init(opts ...Option) error {
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&n.opts)
|
o(&n.opts)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopRegistry) Options() Options {
|
func (n *NoopRegistry) Options() Options {
|
||||||
return n.opts
|
return n.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopRegistry) Register(*Service, ...RegisterOption) error {
|
func (n *NoopRegistry) Connect(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopRegistry) Deregister(*Service, ...DeregisterOption) error {
|
func (n *NoopRegistry) Disconnect(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopRegistry) GetService(string, ...GetOption) ([]*Service, error) {
|
func (n *NoopRegistry) Register(*Service, ...RegisterOption) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NoopRegistry) Deregister(*Service, ...DeregisterOption) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NoopRegistry) GetService(string, ...GetOption) ([]*Service, error) {
|
||||||
return []*Service{}, nil
|
return []*Service{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopRegistry) ListServices(...ListOption) ([]*Service, error) {
|
func (n *NoopRegistry) ListServices(...ListOption) ([]*Service, error) {
|
||||||
return []*Service{}, nil
|
return []*Service{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopRegistry) Watch(...WatchOption) (Watcher, error) {
|
func (n *NoopRegistry) Watch(...WatchOption) (Watcher, error) {
|
||||||
return nil, fmt.Errorf("not implemented")
|
return nil, fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopRegistry) String() string {
|
func (n *NoopRegistry) String() string {
|
||||||
return "noop"
|
return "noop"
|
||||||
}
|
}
|
||||||
|
|
||||||
// newRegistry returns a new noop registry
|
// NewRegistry returns a new noop registry
|
||||||
func newRegistry(opts ...Option) Registry {
|
func NewRegistry(opts ...Option) Registry {
|
||||||
options := NewOptions()
|
options := NewOptions(opts...)
|
||||||
|
return &NoopRegistry{opts: options}
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &noopRegistry{opts: options}
|
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,8 @@ type RegisterOptions struct {
|
|||||||
Context context.Context
|
Context context.Context
|
||||||
// Domain to register the service in
|
// Domain to register the service in
|
||||||
Domain string
|
Domain string
|
||||||
|
// Attempts specify attempts for register
|
||||||
|
Attempts int
|
||||||
}
|
}
|
||||||
|
|
||||||
type WatchOptions struct {
|
type WatchOptions struct {
|
||||||
@ -54,6 +56,8 @@ type DeregisterOptions struct {
|
|||||||
Context context.Context
|
Context context.Context
|
||||||
// Domain the service was registered in
|
// Domain the service was registered in
|
||||||
Domain string
|
Domain string
|
||||||
|
// Atempts specify max attempts for deregister
|
||||||
|
Attempts int
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetOptions struct {
|
type GetOptions struct {
|
||||||
@ -95,6 +99,13 @@ func Logger(l logger.Logger) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Context sets the context
|
||||||
|
func Context(ctx context.Context) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Context = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Specify TLS Config
|
// Specify TLS Config
|
||||||
func TLSConfig(t *tls.Config) Option {
|
func TLSConfig(t *tls.Config) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
@ -102,6 +113,12 @@ func TLSConfig(t *tls.Config) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RegisterAttempts(t int) RegisterOption {
|
||||||
|
return func(o *RegisterOptions) {
|
||||||
|
o.Attempts = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func RegisterTTL(t time.Duration) RegisterOption {
|
func RegisterTTL(t time.Duration) RegisterOption {
|
||||||
return func(o *RegisterOptions) {
|
return func(o *RegisterOptions) {
|
||||||
o.TTL = t
|
o.TTL = t
|
||||||
@ -139,6 +156,12 @@ func WatchDomain(d string) WatchOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeregisterTimeout(t int) DeregisterOption {
|
||||||
|
return func(o *DeregisterOptions) {
|
||||||
|
o.Attempts = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func DeregisterContext(ctx context.Context) DeregisterOption {
|
func DeregisterContext(ctx context.Context) DeregisterOption {
|
||||||
return func(o *DeregisterOptions) {
|
return func(o *DeregisterOptions) {
|
||||||
o.Context = ctx
|
o.Context = ctx
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
package registry
|
package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -13,7 +14,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultRegistry Registry = newRegistry()
|
DefaultRegistry Registry = NewRegistry()
|
||||||
// ErrNotFound returned when GetService is called and no services found
|
// ErrNotFound returned when GetService is called and no services found
|
||||||
ErrNotFound = errors.New("service not found")
|
ErrNotFound = errors.New("service not found")
|
||||||
// ErrWatcherStopped returned when when watcher is stopped
|
// ErrWatcherStopped returned when when watcher is stopped
|
||||||
@ -26,6 +27,8 @@ var (
|
|||||||
type Registry interface {
|
type Registry interface {
|
||||||
Init(...Option) error
|
Init(...Option) error
|
||||||
Options() Options
|
Options() Options
|
||||||
|
Connect(context.Context) error
|
||||||
|
Disconnect(context.Context) error
|
||||||
Register(*Service, ...RegisterOption) error
|
Register(*Service, ...RegisterOption) error
|
||||||
Deregister(*Service, ...DeregisterOption) error
|
Deregister(*Service, ...DeregisterOption) error
|
||||||
GetService(string, ...GetOption) ([]*Service, error)
|
GetService(string, ...GetOption) ([]*Service, error)
|
||||||
|
@ -65,7 +65,7 @@ func QueryLink(link string) QueryOption {
|
|||||||
// NewQuery creates new query and returns it
|
// NewQuery creates new query and returns it
|
||||||
func NewQuery(opts ...QueryOption) QueryOptions {
|
func NewQuery(opts ...QueryOption) QueryOptions {
|
||||||
// default options
|
// default options
|
||||||
qopts := QueryOptions{
|
options := QueryOptions{
|
||||||
Service: "*",
|
Service: "*",
|
||||||
Address: "*",
|
Address: "*",
|
||||||
Gateway: "*",
|
Gateway: "*",
|
||||||
@ -75,8 +75,8 @@ func NewQuery(opts ...QueryOption) QueryOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&qopts)
|
o(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
return qopts
|
return options
|
||||||
}
|
}
|
||||||
|
@ -1,94 +1,59 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import "context"
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
type HandlerOption func(*HandlerOptions)
|
"github.com/unistack-org/micro/v3/registry"
|
||||||
|
)
|
||||||
|
|
||||||
type HandlerOptions struct {
|
type rpcHandler struct {
|
||||||
Internal bool
|
name string
|
||||||
Metadata map[string]map[string]string
|
handler interface{}
|
||||||
Context context.Context
|
endpoints []*registry.Endpoint
|
||||||
|
opts HandlerOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHandlerOptions(opts ...HandlerOption) HandlerOptions {
|
func newRpcHandler(handler interface{}, opts ...HandlerOption) Handler {
|
||||||
options := HandlerOptions{
|
options := NewHandlerOptions(opts...)
|
||||||
Context: context.Background(),
|
|
||||||
|
typ := reflect.TypeOf(handler)
|
||||||
|
hdlr := reflect.ValueOf(handler)
|
||||||
|
name := reflect.Indirect(hdlr).Type().Name()
|
||||||
|
|
||||||
|
var endpoints []*registry.Endpoint
|
||||||
|
|
||||||
|
for m := 0; m < typ.NumMethod(); m++ {
|
||||||
|
if e := registry.ExtractEndpoint(typ.Method(m)); e != nil {
|
||||||
|
e.Name = name + "." + e.Name
|
||||||
|
|
||||||
|
for k, v := range options.Metadata[e.Name] {
|
||||||
|
e.Metadata[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
endpoints = append(endpoints, e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, o := range opts {
|
return &rpcHandler{
|
||||||
o(&options)
|
name: name,
|
||||||
}
|
handler: handler,
|
||||||
|
endpoints: endpoints,
|
||||||
return options
|
opts: options,
|
||||||
}
|
|
||||||
|
|
||||||
type SubscriberOption func(*SubscriberOptions)
|
|
||||||
|
|
||||||
type SubscriberOptions struct {
|
|
||||||
// AutoAck defaults to true. When a handler returns
|
|
||||||
// with a nil error the message is acked.
|
|
||||||
AutoAck bool
|
|
||||||
Queue string
|
|
||||||
Internal bool
|
|
||||||
Context context.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSubscriberOptions(opts ...SubscriberOption) SubscriberOptions {
|
|
||||||
options := SubscriberOptions{
|
|
||||||
AutoAck: true,
|
|
||||||
Context: context.Background(),
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
|
|
||||||
// EndpointMetadata is a Handler option that allows metadata to be added to
|
|
||||||
// individual endpoints.
|
|
||||||
func EndpointMetadata(name string, md map[string]string) HandlerOption {
|
|
||||||
return func(o *HandlerOptions) {
|
|
||||||
o.Metadata[name] = md
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal Handler options specifies that a handler is not advertised
|
func (r *rpcHandler) Name() string {
|
||||||
// to the discovery system. In the future this may also limit request
|
return r.name
|
||||||
// to the internal network or authorised user.
|
|
||||||
func InternalHandler(b bool) HandlerOption {
|
|
||||||
return func(o *HandlerOptions) {
|
|
||||||
o.Internal = b
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal Subscriber options specifies that a subscriber is not advertised
|
func (r *rpcHandler) Handler() interface{} {
|
||||||
// to the discovery system.
|
return r.handler
|
||||||
func InternalSubscriber(b bool) SubscriberOption {
|
|
||||||
return func(o *SubscriberOptions) {
|
|
||||||
o.Internal = b
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DisableAutoAck will disable auto acking of messages
|
func (r *rpcHandler) Endpoints() []*registry.Endpoint {
|
||||||
// after they have been handled.
|
return r.endpoints
|
||||||
func DisableAutoAck() SubscriberOption {
|
|
||||||
return func(o *SubscriberOptions) {
|
|
||||||
o.AutoAck = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shared queue name distributed messages across subscribers
|
func (r *rpcHandler) Options() HandlerOptions {
|
||||||
func SubscriberQueue(n string) SubscriberOption {
|
return r.opts
|
||||||
return func(o *SubscriberOptions) {
|
|
||||||
o.Queue = n
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SubscriberContext set context options to allow broker SubscriberOption passed
|
|
||||||
func SubscriberContext(ctx context.Context) SubscriberOption {
|
|
||||||
return func(o *SubscriberOptions) {
|
|
||||||
o.Context = ctx
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
506
server/noop.go
506
server/noop.go
@ -1,108 +1,462 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import "github.com/unistack-org/micro/v3/registry"
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
type noopServer struct {
|
craw "github.com/unistack-org/micro-codec-bytes"
|
||||||
h Handler
|
"github.com/unistack-org/micro/v3/broker"
|
||||||
opts Options
|
"github.com/unistack-org/micro/v3/codec"
|
||||||
|
cjson "github.com/unistack-org/micro/v3/codec/json"
|
||||||
|
cjsonrpc "github.com/unistack-org/micro/v3/codec/jsonrpc"
|
||||||
|
cproto "github.com/unistack-org/micro/v3/codec/proto"
|
||||||
|
cprotorpc "github.com/unistack-org/micro/v3/codec/protorpc"
|
||||||
|
"github.com/unistack-org/micro/v3/logger"
|
||||||
|
"github.com/unistack-org/micro/v3/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
DefaultCodecs = map[string]codec.NewCodec{
|
||||||
|
"application/json": cjson.NewCodec,
|
||||||
|
"application/json-rpc": cjsonrpc.NewCodec,
|
||||||
|
"application/protobuf": cproto.NewCodec,
|
||||||
|
"application/proto-rpc": cprotorpc.NewCodec,
|
||||||
|
"application/octet-stream": craw.NewCodec,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultContentType = "application/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type NoopServer struct {
|
||||||
|
h Handler
|
||||||
|
opts Options
|
||||||
|
rsvc *registry.Service
|
||||||
|
handlers map[string]Handler
|
||||||
|
subscribers map[*subscriber][]broker.Subscriber
|
||||||
|
registered bool
|
||||||
|
started bool
|
||||||
|
exit chan chan error
|
||||||
|
wg *sync.WaitGroup
|
||||||
|
sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
type noopHandler struct {
|
func (n *NoopServer) newCodec(contentType string) (codec.NewCodec, error) {
|
||||||
opts HandlerOptions
|
if cf, ok := n.opts.Codecs[contentType]; ok {
|
||||||
h interface{}
|
return cf, nil
|
||||||
|
}
|
||||||
|
if cf, ok := DefaultCodecs[contentType]; ok {
|
||||||
|
return cf, nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Unsupported Content-Type: %s", contentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
type noopSubscriber struct {
|
func (n *NoopServer) Handle(handler Handler) error {
|
||||||
topic string
|
|
||||||
opts SubscriberOptions
|
|
||||||
h interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopSubscriber) Topic() string {
|
|
||||||
return n.topic
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopSubscriber) Subscriber() interface{} {
|
|
||||||
return n.h
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopSubscriber) Endpoints() []*registry.Endpoint {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopSubscriber) Options() SubscriberOptions {
|
|
||||||
return n.opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopHandler) Endpoints() []*registry.Endpoint {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopHandler) Handler() interface{} {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopHandler) Options() HandlerOptions {
|
|
||||||
return n.opts
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopHandler) Name() string {
|
|
||||||
return "noop"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopServer) Handle(handler Handler) error {
|
|
||||||
n.h = handler
|
n.h = handler
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopServer) Subscribe(subscriber Subscriber) error {
|
func (n *NoopServer) Subscribe(sb Subscriber) error {
|
||||||
// n.s = handler
|
sub, ok := sb.(*subscriber)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("invalid subscriber: expected *subscriber")
|
||||||
|
}
|
||||||
|
if len(sub.handlers) == 0 {
|
||||||
|
return fmt.Errorf("invalid subscriber: no handler functions")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ValidateSubscriber(sb); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n.Lock()
|
||||||
|
if _, ok = n.subscribers[sub]; ok {
|
||||||
|
n.Unlock()
|
||||||
|
return fmt.Errorf("subscriber %v already exists", sub)
|
||||||
|
}
|
||||||
|
|
||||||
|
n.subscribers[sub] = nil
|
||||||
|
n.Unlock()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopServer) NewHandler(h interface{}, opts ...HandlerOption) Handler {
|
func (n *NoopServer) NewHandler(h interface{}, opts ...HandlerOption) Handler {
|
||||||
options := NewHandlerOptions()
|
return newRpcHandler(h, opts...)
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
return &noopHandler{opts: options, h: h}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopServer) NewSubscriber(topic string, h interface{}, opts ...SubscriberOption) Subscriber {
|
func (n *NoopServer) NewSubscriber(topic string, sb interface{}, opts ...SubscriberOption) Subscriber {
|
||||||
options := NewSubscriberOptions()
|
return newSubscriber(topic, sb, opts...)
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
return &noopSubscriber{topic: topic, opts: options, h: h}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopServer) Init(opts ...Option) error {
|
func (n *NoopServer) Init(opts ...Option) error {
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&n.opts)
|
o(&n.opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if n.handlers == nil {
|
||||||
|
n.handlers = make(map[string]Handler)
|
||||||
|
}
|
||||||
|
if n.subscribers == nil {
|
||||||
|
n.subscribers = make(map[*subscriber][]broker.Subscriber)
|
||||||
|
}
|
||||||
|
if n.exit == nil {
|
||||||
|
n.exit = make(chan chan error)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopServer) Start() error {
|
func (n *NoopServer) Options() Options {
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopServer) Stop() error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopServer) Options() Options {
|
|
||||||
return n.opts
|
return n.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopServer) String() string {
|
func (n *NoopServer) String() string {
|
||||||
return "noop"
|
return "noop"
|
||||||
}
|
}
|
||||||
|
|
||||||
func newServer(opts ...Option) Server {
|
func (n *NoopServer) Register() error {
|
||||||
options := NewOptions()
|
n.RLock()
|
||||||
for _, o := range opts {
|
rsvc := n.rsvc
|
||||||
o(&options)
|
config := n.opts
|
||||||
|
n.RUnlock()
|
||||||
|
|
||||||
|
// if service already filled, reuse it and return early
|
||||||
|
if rsvc != nil {
|
||||||
|
if err := DefaultRegisterFunc(rsvc, config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
return &noopServer{opts: options}
|
|
||||||
|
var err error
|
||||||
|
var service *registry.Service
|
||||||
|
var cacheService bool
|
||||||
|
|
||||||
|
service, err = NewRegistryService(n)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n.RLock()
|
||||||
|
// Maps are ordered randomly, sort the keys for consistency
|
||||||
|
var handlerList []string
|
||||||
|
for n, e := range n.handlers {
|
||||||
|
// Only advertise non internal handlers
|
||||||
|
if !e.Options().Internal {
|
||||||
|
handlerList = append(handlerList, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(handlerList)
|
||||||
|
|
||||||
|
var subscriberList []*subscriber
|
||||||
|
for e := range n.subscribers {
|
||||||
|
// Only advertise non internal subscribers
|
||||||
|
if !e.Options().Internal {
|
||||||
|
subscriberList = append(subscriberList, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Slice(subscriberList, func(i, j int) bool {
|
||||||
|
return subscriberList[i].topic > subscriberList[j].topic
|
||||||
|
})
|
||||||
|
|
||||||
|
endpoints := make([]*registry.Endpoint, 0, len(handlerList)+len(subscriberList))
|
||||||
|
for _, h := range handlerList {
|
||||||
|
endpoints = append(endpoints, n.handlers[h].Endpoints()...)
|
||||||
|
}
|
||||||
|
for _, e := range subscriberList {
|
||||||
|
endpoints = append(endpoints, e.Endpoints()...)
|
||||||
|
}
|
||||||
|
n.RUnlock()
|
||||||
|
|
||||||
|
service.Nodes[0].Metadata["protocol"] = "noop"
|
||||||
|
service.Nodes[0].Metadata["transport"] = "noop"
|
||||||
|
service.Endpoints = endpoints
|
||||||
|
|
||||||
|
n.RLock()
|
||||||
|
registered := n.registered
|
||||||
|
n.RUnlock()
|
||||||
|
|
||||||
|
if !registered {
|
||||||
|
if logger.V(logger.InfoLevel) {
|
||||||
|
logger.Infof("Registry [%s] Registering node: %s", config.Registry.String(), service.Nodes[0].Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// register the service
|
||||||
|
if err := DefaultRegisterFunc(service, config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// already registered? don't need to register subscribers
|
||||||
|
if registered {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n.Lock()
|
||||||
|
defer n.Unlock()
|
||||||
|
|
||||||
|
cx := config.Context
|
||||||
|
|
||||||
|
for sb := range n.subscribers {
|
||||||
|
handler := n.createSubHandler(sb, config)
|
||||||
|
var opts []broker.SubscribeOption
|
||||||
|
if queue := sb.Options().Queue; len(queue) > 0 {
|
||||||
|
opts = append(opts, broker.SubscribeGroup(queue))
|
||||||
|
}
|
||||||
|
|
||||||
|
if sb.Options().Context != nil {
|
||||||
|
cx = sb.Options().Context
|
||||||
|
}
|
||||||
|
|
||||||
|
opts = append(opts, broker.SubscribeContext(cx), broker.SubscribeAutoAck(sb.Options().AutoAck))
|
||||||
|
|
||||||
|
if logger.V(logger.InfoLevel) {
|
||||||
|
logger.Infof("Subscribing to topic: %s", sb.Topic())
|
||||||
|
}
|
||||||
|
sub, err := config.Broker.Subscribe(cx, sb.Topic(), handler, opts...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
n.subscribers[sb] = []broker.Subscriber{sub}
|
||||||
|
}
|
||||||
|
|
||||||
|
n.registered = true
|
||||||
|
if cacheService {
|
||||||
|
n.rsvc = service
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NoopServer) Deregister() error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
n.RLock()
|
||||||
|
config := n.opts
|
||||||
|
n.RUnlock()
|
||||||
|
|
||||||
|
service, err := NewRegistryService(n)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if logger.V(logger.InfoLevel) {
|
||||||
|
logger.Infof("Deregistering node: %s", service.Nodes[0].Id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := DefaultDeregisterFunc(service, config); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
n.Lock()
|
||||||
|
n.rsvc = nil
|
||||||
|
|
||||||
|
if !n.registered {
|
||||||
|
n.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n.registered = false
|
||||||
|
|
||||||
|
cx := config.Context
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
for sb, subs := range n.subscribers {
|
||||||
|
for _, sub := range subs {
|
||||||
|
if sb.Options().Context != nil {
|
||||||
|
cx = sb.Options().Context
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
go func(s broker.Subscriber) {
|
||||||
|
defer wg.Done()
|
||||||
|
if logger.V(logger.InfoLevel) {
|
||||||
|
logger.Infof("Unsubscribing from topic: %s", s.Topic())
|
||||||
|
}
|
||||||
|
if err := s.Unsubscribe(cx); err != nil {
|
||||||
|
if logger.V(logger.ErrorLevel) {
|
||||||
|
logger.Errorf("Unsubscribing from topic: %s err: %v", s.Topic(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(sub)
|
||||||
|
}
|
||||||
|
n.subscribers[sb] = nil
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
n.Unlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NoopServer) Start() error {
|
||||||
|
n.RLock()
|
||||||
|
if n.started {
|
||||||
|
n.RUnlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
config := n.Options()
|
||||||
|
n.RUnlock()
|
||||||
|
|
||||||
|
if logger.V(logger.InfoLevel) {
|
||||||
|
logger.Infof("Server [noop] Listening on %s", config.Address)
|
||||||
|
}
|
||||||
|
n.Lock()
|
||||||
|
if len(config.Advertise) == 0 {
|
||||||
|
config.Advertise = config.Address
|
||||||
|
}
|
||||||
|
n.Unlock()
|
||||||
|
|
||||||
|
// only connect if we're subscribed
|
||||||
|
if len(n.subscribers) > 0 {
|
||||||
|
// connect to the broker
|
||||||
|
if err := config.Broker.Connect(config.Context); err != nil {
|
||||||
|
if logger.V(logger.ErrorLevel) {
|
||||||
|
logger.Errorf("Broker [%s] connect error: %v", config.Broker.String(), err)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if logger.V(logger.InfoLevel) {
|
||||||
|
logger.Infof("Broker [%s] Connected to %s", config.Broker.String(), config.Broker.Address())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use RegisterCheck func before register
|
||||||
|
if err := config.RegisterCheck(config.Context); err != nil {
|
||||||
|
if logger.V(logger.ErrorLevel) {
|
||||||
|
logger.Errorf("Server %s-%s register check error: %s", config.Name, config.Id, err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// announce self to the world
|
||||||
|
if err := n.Register(); err != nil {
|
||||||
|
if logger.V(logger.ErrorLevel) {
|
||||||
|
logger.Errorf("Server register error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
t := new(time.Ticker)
|
||||||
|
|
||||||
|
// only process if it exists
|
||||||
|
if config.RegisterInterval > time.Duration(0) {
|
||||||
|
// new ticker
|
||||||
|
t = time.NewTicker(config.RegisterInterval)
|
||||||
|
}
|
||||||
|
|
||||||
|
// return error chan
|
||||||
|
var ch chan error
|
||||||
|
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
// register self on interval
|
||||||
|
case <-t.C:
|
||||||
|
n.RLock()
|
||||||
|
registered := n.registered
|
||||||
|
n.RUnlock()
|
||||||
|
rerr := config.RegisterCheck(config.Context)
|
||||||
|
if rerr != nil && registered {
|
||||||
|
if logger.V(logger.ErrorLevel) {
|
||||||
|
logger.Errorf("Server %s-%s register check error: %s, deregister it", config.Name, config.Id, rerr)
|
||||||
|
}
|
||||||
|
// deregister self in case of error
|
||||||
|
if err := n.Deregister(); err != nil {
|
||||||
|
if logger.V(logger.ErrorLevel) {
|
||||||
|
logger.Errorf("Server %s-%s deregister error: %s", config.Name, config.Id, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if rerr != nil && !registered {
|
||||||
|
if logger.V(logger.ErrorLevel) {
|
||||||
|
logger.Errorf("Server %s-%s register check error: %s", config.Name, config.Id, rerr)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if err := n.Register(); err != nil {
|
||||||
|
if logger.V(logger.ErrorLevel) {
|
||||||
|
logger.Errorf("Server %s-%s register error: %s", config.Name, config.Id, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// wait for exit
|
||||||
|
case ch = <-n.exit:
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// deregister self
|
||||||
|
if err := n.Deregister(); err != nil {
|
||||||
|
if logger.V(logger.ErrorLevel) {
|
||||||
|
logger.Error("Server deregister error: ", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for waitgroup
|
||||||
|
if n.wg != nil {
|
||||||
|
n.wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop the grpc server
|
||||||
|
exit := make(chan bool)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
close(exit)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-exit:
|
||||||
|
}
|
||||||
|
|
||||||
|
// close transport
|
||||||
|
ch <- nil
|
||||||
|
|
||||||
|
if logger.V(logger.InfoLevel) {
|
||||||
|
logger.Infof("Broker [%s] Disconnected from %s", config.Broker.String(), config.Broker.Address())
|
||||||
|
}
|
||||||
|
// disconnect broker
|
||||||
|
if err := config.Broker.Disconnect(config.Context); err != nil {
|
||||||
|
if logger.V(logger.ErrorLevel) {
|
||||||
|
logger.Errorf("Broker [%s] disconnect error: %v", config.Broker.String(), err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// mark the server as started
|
||||||
|
n.Lock()
|
||||||
|
n.started = true
|
||||||
|
n.Unlock()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NoopServer) Stop() error {
|
||||||
|
n.RLock()
|
||||||
|
if !n.started {
|
||||||
|
n.RUnlock()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
n.RUnlock()
|
||||||
|
|
||||||
|
ch := make(chan error)
|
||||||
|
n.exit <- ch
|
||||||
|
|
||||||
|
err := <-ch
|
||||||
|
n.Lock()
|
||||||
|
n.rsvc = nil
|
||||||
|
n.started = false
|
||||||
|
n.Unlock()
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
type noopcodec struct {
|
||||||
|
*bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c noopcodec) Close() error {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,10 @@ type Options struct {
|
|||||||
RegisterTTL time.Duration
|
RegisterTTL time.Duration
|
||||||
// The interval on which to register
|
// The interval on which to register
|
||||||
RegisterInterval time.Duration
|
RegisterInterval time.Duration
|
||||||
|
// RegisterAttempts specify how many times try to register
|
||||||
|
RegisterAttempts int
|
||||||
|
// DeegisterAttempts specify how many times try to deregister
|
||||||
|
DeregisterAttempts int
|
||||||
|
|
||||||
// The router for requests
|
// The router for requests
|
||||||
Router Router
|
Router Router
|
||||||
@ -61,6 +65,8 @@ func NewOptions(opts ...Option) Options {
|
|||||||
RegisterInterval: DefaultRegisterInterval,
|
RegisterInterval: DefaultRegisterInterval,
|
||||||
RegisterTTL: DefaultRegisterTTL,
|
RegisterTTL: DefaultRegisterTTL,
|
||||||
RegisterCheck: DefaultRegisterCheck,
|
RegisterCheck: DefaultRegisterCheck,
|
||||||
|
Logger: logger.DefaultLogger,
|
||||||
|
Tracer: tracer.DefaultTracer,
|
||||||
Broker: broker.DefaultBroker,
|
Broker: broker.DefaultBroker,
|
||||||
Registry: registry.DefaultRegistry,
|
Registry: registry.DefaultRegistry,
|
||||||
Address: DefaultAddress,
|
Address: DefaultAddress,
|
||||||
@ -255,3 +261,94 @@ func WrapSubscriber(w SubscriberWrapper) Option {
|
|||||||
o.SubWrappers = append(o.SubWrappers, w)
|
o.SubWrappers = append(o.SubWrappers, w)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HandlerOption func(*HandlerOptions)
|
||||||
|
|
||||||
|
type HandlerOptions struct {
|
||||||
|
Internal bool
|
||||||
|
Metadata map[string]map[string]string
|
||||||
|
Context context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandlerOptions(opts ...HandlerOption) HandlerOptions {
|
||||||
|
options := HandlerOptions{
|
||||||
|
Context: context.Background(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
type SubscriberOption func(*SubscriberOptions)
|
||||||
|
|
||||||
|
type SubscriberOptions struct {
|
||||||
|
// AutoAck defaults to true. When a handler returns
|
||||||
|
// with a nil error the message is acked.
|
||||||
|
AutoAck bool
|
||||||
|
Queue string
|
||||||
|
Internal bool
|
||||||
|
Context context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSubscriberOptions(opts ...SubscriberOption) SubscriberOptions {
|
||||||
|
options := SubscriberOptions{
|
||||||
|
AutoAck: true,
|
||||||
|
Context: context.Background(),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, o := range opts {
|
||||||
|
o(&options)
|
||||||
|
}
|
||||||
|
|
||||||
|
return options
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndpointMetadata is a Handler option that allows metadata to be added to
|
||||||
|
// individual endpoints.
|
||||||
|
func EndpointMetadata(name string, md map[string]string) HandlerOption {
|
||||||
|
return func(o *HandlerOptions) {
|
||||||
|
o.Metadata[name] = md
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal Handler options specifies that a handler is not advertised
|
||||||
|
// to the discovery system. In the future this may also limit request
|
||||||
|
// to the internal network or authorised user.
|
||||||
|
func InternalHandler(b bool) HandlerOption {
|
||||||
|
return func(o *HandlerOptions) {
|
||||||
|
o.Internal = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal Subscriber options specifies that a subscriber is not advertised
|
||||||
|
// to the discovery system.
|
||||||
|
func InternalSubscriber(b bool) SubscriberOption {
|
||||||
|
return func(o *SubscriberOptions) {
|
||||||
|
o.Internal = b
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisableAutoAck will disable auto acking of messages
|
||||||
|
// after they have been handled.
|
||||||
|
func DisableAutoAck() SubscriberOption {
|
||||||
|
return func(o *SubscriberOptions) {
|
||||||
|
o.AutoAck = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared queue name distributed messages across subscribers
|
||||||
|
func SubscriberQueue(n string) SubscriberOption {
|
||||||
|
return func(o *SubscriberOptions) {
|
||||||
|
o.Queue = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SubscriberContext set context options to allow broker SubscriberOption passed
|
||||||
|
func SubscriberContext(ctx context.Context) SubscriberOption {
|
||||||
|
return func(o *SubscriberOptions) {
|
||||||
|
o.Context = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
89
server/registry.go
Normal file
89
server/registry.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/unistack-org/micro/v3/metadata"
|
||||||
|
"github.com/unistack-org/micro/v3/registry"
|
||||||
|
"github.com/unistack-org/micro/v3/util/addr"
|
||||||
|
"github.com/unistack-org/micro/v3/util/backoff"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultRegisterFunc uses backoff to register service
|
||||||
|
DefaultRegisterFunc = func(service *registry.Service, config Options) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
opts := []registry.RegisterOption{
|
||||||
|
registry.RegisterTTL(config.RegisterTTL),
|
||||||
|
registry.RegisterDomain(config.Namespace),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i <= config.RegisterAttempts; i++ {
|
||||||
|
err = config.Registry.Register(service, opts...)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// backoff then retry
|
||||||
|
time.Sleep(backoff.Do(i + 1))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// DefaultDeregisterFunc uses backoff to deregister service
|
||||||
|
DefaultDeregisterFunc = func(service *registry.Service, config Options) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
opts := []registry.DeregisterOption{
|
||||||
|
registry.DeregisterDomain(config.Namespace),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i <= config.DeregisterAttempts; i++ {
|
||||||
|
err = config.Registry.Deregister(service, opts...)
|
||||||
|
if err == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// backoff then retry
|
||||||
|
time.Sleep(backoff.Do(i + 1))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewRegistryService(s Server) (*registry.Service, error) {
|
||||||
|
opts := s.Options()
|
||||||
|
|
||||||
|
advt := opts.Address
|
||||||
|
if len(opts.Advertise) > 0 {
|
||||||
|
advt = opts.Advertise
|
||||||
|
}
|
||||||
|
|
||||||
|
host, port, err := net.SplitHostPort(advt)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err := addr.Extract(host)
|
||||||
|
if err != nil {
|
||||||
|
addr = host
|
||||||
|
}
|
||||||
|
|
||||||
|
node := ®istry.Node{
|
||||||
|
Id: opts.Name + "-" + opts.Id,
|
||||||
|
Address: net.JoinHostPort(addr, port),
|
||||||
|
}
|
||||||
|
node.Metadata = metadata.Copy(opts.Metadata)
|
||||||
|
|
||||||
|
node.Metadata["server"] = s.String()
|
||||||
|
node.Metadata["broker"] = opts.Broker.String()
|
||||||
|
node.Metadata["registry"] = opts.Registry.String()
|
||||||
|
|
||||||
|
return ®istry.Service{
|
||||||
|
Name: opts.Name,
|
||||||
|
Version: opts.Version,
|
||||||
|
Nodes: []*registry.Node{node},
|
||||||
|
Metadata: metadata.New(0),
|
||||||
|
}, nil
|
||||||
|
}
|
90
server/request.go
Normal file
90
server/request.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
raw "github.com/unistack-org/micro-codec-bytes"
|
||||||
|
"github.com/unistack-org/micro/v3/codec"
|
||||||
|
)
|
||||||
|
|
||||||
|
type rpcRequest struct {
|
||||||
|
service string
|
||||||
|
method string
|
||||||
|
contentType string
|
||||||
|
codec codec.Codec
|
||||||
|
header map[string]string
|
||||||
|
body []byte
|
||||||
|
stream bool
|
||||||
|
payload interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type rpcMessage struct {
|
||||||
|
topic string
|
||||||
|
contentType string
|
||||||
|
payload interface{}
|
||||||
|
header map[string]string
|
||||||
|
body []byte
|
||||||
|
codec codec.Codec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcRequest) ContentType() string {
|
||||||
|
return r.contentType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcRequest) Service() string {
|
||||||
|
return r.service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcRequest) Method() string {
|
||||||
|
return r.method
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcRequest) Endpoint() string {
|
||||||
|
return r.method
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcRequest) Codec() codec.Reader {
|
||||||
|
return r.codec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcRequest) Header() map[string]string {
|
||||||
|
return r.header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcRequest) Read() ([]byte, error) {
|
||||||
|
f := &raw.Frame{}
|
||||||
|
if err := r.codec.ReadBody(f); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return f.Data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcRequest) Stream() bool {
|
||||||
|
return r.stream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcRequest) Body() interface{} {
|
||||||
|
return r.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcMessage) ContentType() string {
|
||||||
|
return r.contentType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcMessage) Topic() string {
|
||||||
|
return r.topic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcMessage) Payload() interface{} {
|
||||||
|
return r.payload
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcMessage) Header() map[string]string {
|
||||||
|
return r.header
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcMessage) Body() []byte {
|
||||||
|
return r.body
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *rpcMessage) Codec() codec.Reader {
|
||||||
|
return r.codec
|
||||||
|
}
|
@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultServer Server = newServer()
|
DefaultServer Server = &NoopServer{opts: NewOptions()}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Server is a simple micro server abstraction
|
// Server is a simple micro server abstraction
|
||||||
|
@ -1,10 +1,20 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime/debug"
|
||||||
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/unistack-org/micro/v3/broker"
|
||||||
|
"github.com/unistack-org/micro/v3/errors"
|
||||||
|
"github.com/unistack-org/micro/v3/logger"
|
||||||
|
"github.com/unistack-org/micro/v3/metadata"
|
||||||
|
"github.com/unistack-org/micro/v3/registry"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -17,6 +27,22 @@ var (
|
|||||||
typeOfError = reflect.TypeOf((*error)(nil)).Elem()
|
typeOfError = reflect.TypeOf((*error)(nil)).Elem()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type handler struct {
|
||||||
|
method reflect.Value
|
||||||
|
reqType reflect.Type
|
||||||
|
ctxType reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
type subscriber struct {
|
||||||
|
topic string
|
||||||
|
rcvr reflect.Value
|
||||||
|
typ reflect.Type
|
||||||
|
subscriber interface{}
|
||||||
|
handlers []*handler
|
||||||
|
endpoints []*registry.Endpoint
|
||||||
|
opts SubscriberOptions
|
||||||
|
}
|
||||||
|
|
||||||
// Is this an exported - upper case - name?
|
// Is this an exported - upper case - name?
|
||||||
func isExported(name string) bool {
|
func isExported(name string) bool {
|
||||||
rune, _ := utf8.DecodeRuneInString(name)
|
rune, _ := utf8.DecodeRuneInString(name)
|
||||||
@ -86,3 +112,200 @@ func ValidateSubscriber(sub Subscriber) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newSubscriber(topic string, sub interface{}, opts ...SubscriberOption) Subscriber {
|
||||||
|
var endpoints []*registry.Endpoint
|
||||||
|
var handlers []*handler
|
||||||
|
|
||||||
|
options := NewSubscriberOptions(opts...)
|
||||||
|
|
||||||
|
if typ := reflect.TypeOf(sub); typ.Kind() == reflect.Func {
|
||||||
|
h := &handler{
|
||||||
|
method: reflect.ValueOf(sub),
|
||||||
|
}
|
||||||
|
|
||||||
|
switch typ.NumIn() {
|
||||||
|
case 1:
|
||||||
|
h.reqType = typ.In(0)
|
||||||
|
case 2:
|
||||||
|
h.ctxType = typ.In(0)
|
||||||
|
h.reqType = typ.In(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers = append(handlers, h)
|
||||||
|
|
||||||
|
endpoints = append(endpoints, ®istry.Endpoint{
|
||||||
|
Name: "Func",
|
||||||
|
Request: registry.ExtractSubValue(typ),
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"topic": topic,
|
||||||
|
"subscriber": "true",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
hdlr := reflect.ValueOf(sub)
|
||||||
|
name := reflect.Indirect(hdlr).Type().Name()
|
||||||
|
|
||||||
|
for m := 0; m < typ.NumMethod(); m++ {
|
||||||
|
method := typ.Method(m)
|
||||||
|
h := &handler{
|
||||||
|
method: method.Func,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch method.Type.NumIn() {
|
||||||
|
case 2:
|
||||||
|
h.reqType = method.Type.In(1)
|
||||||
|
case 3:
|
||||||
|
h.ctxType = method.Type.In(1)
|
||||||
|
h.reqType = method.Type.In(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
handlers = append(handlers, h)
|
||||||
|
|
||||||
|
endpoints = append(endpoints, ®istry.Endpoint{
|
||||||
|
Name: name + "." + method.Name,
|
||||||
|
Request: registry.ExtractSubValue(method.Type),
|
||||||
|
Metadata: map[string]string{
|
||||||
|
"topic": topic,
|
||||||
|
"subscriber": "true",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &subscriber{
|
||||||
|
rcvr: reflect.ValueOf(sub),
|
||||||
|
typ: reflect.TypeOf(sub),
|
||||||
|
topic: topic,
|
||||||
|
subscriber: sub,
|
||||||
|
handlers: handlers,
|
||||||
|
endpoints: endpoints,
|
||||||
|
opts: options,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NoopServer) createSubHandler(sb *subscriber, opts Options) broker.Handler {
|
||||||
|
return func(p broker.Event) (err error) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
if logger.V(logger.ErrorLevel) {
|
||||||
|
logger.Error("panic recovered: ", r)
|
||||||
|
logger.Error(string(debug.Stack()))
|
||||||
|
}
|
||||||
|
err = errors.InternalServerError(n.opts.Name+".subscriber", "panic recovered: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
msg := p.Message()
|
||||||
|
// if we don't have headers, create empty map
|
||||||
|
if msg.Header == nil {
|
||||||
|
msg.Header = make(map[string]string)
|
||||||
|
}
|
||||||
|
|
||||||
|
ct := msg.Header["Content-Type"]
|
||||||
|
if len(ct) == 0 {
|
||||||
|
msg.Header["Content-Type"] = defaultContentType
|
||||||
|
ct = defaultContentType
|
||||||
|
}
|
||||||
|
cf, err := n.newCodec(ct)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
hdr := make(map[string]string, len(msg.Header))
|
||||||
|
for k, v := range msg.Header {
|
||||||
|
hdr[k] = v
|
||||||
|
}
|
||||||
|
delete(hdr, "Content-Type")
|
||||||
|
ctx := metadata.NewContext(sb.opts.Context, hdr)
|
||||||
|
|
||||||
|
results := make(chan error, len(sb.handlers))
|
||||||
|
|
||||||
|
for i := 0; i < len(sb.handlers); i++ {
|
||||||
|
handler := sb.handlers[i]
|
||||||
|
|
||||||
|
var isVal bool
|
||||||
|
var req reflect.Value
|
||||||
|
|
||||||
|
if handler.reqType.Kind() == reflect.Ptr {
|
||||||
|
req = reflect.New(handler.reqType.Elem())
|
||||||
|
} else {
|
||||||
|
req = reflect.New(handler.reqType)
|
||||||
|
isVal = true
|
||||||
|
}
|
||||||
|
if isVal {
|
||||||
|
req = req.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = cf(noopcodec{bytes.NewBuffer(msg.Body)}).ReadBody(req.Interface()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fn := func(ctx context.Context, msg Message) error {
|
||||||
|
var vals []reflect.Value
|
||||||
|
if sb.typ.Kind() != reflect.Func {
|
||||||
|
vals = append(vals, sb.rcvr)
|
||||||
|
}
|
||||||
|
if handler.ctxType != nil {
|
||||||
|
vals = append(vals, reflect.ValueOf(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
vals = append(vals, reflect.ValueOf(msg.Payload()))
|
||||||
|
|
||||||
|
returnValues := handler.method.Call(vals)
|
||||||
|
if rerr := returnValues[0].Interface(); rerr != nil {
|
||||||
|
return rerr.(error)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := len(opts.SubWrappers); i > 0; i-- {
|
||||||
|
fn = opts.SubWrappers[i-1](fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.wg != nil {
|
||||||
|
n.wg.Add(1)
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
if n.wg != nil {
|
||||||
|
defer n.wg.Done()
|
||||||
|
}
|
||||||
|
err := fn(ctx, &rpcMessage{
|
||||||
|
topic: sb.topic,
|
||||||
|
contentType: ct,
|
||||||
|
payload: req.Interface(),
|
||||||
|
header: msg.Header,
|
||||||
|
body: msg.Body,
|
||||||
|
})
|
||||||
|
results <- err
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
var errors []string
|
||||||
|
for i := 0; i < len(sb.handlers); i++ {
|
||||||
|
if rerr := <-results; rerr != nil {
|
||||||
|
errors = append(errors, rerr.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(errors) > 0 {
|
||||||
|
err = fmt.Errorf("subscriber error: %s", strings.Join(errors, "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subscriber) Topic() string {
|
||||||
|
return s.topic
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subscriber) Subscriber() interface{} {
|
||||||
|
return s.subscriber
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subscriber) Endpoints() []*registry.Endpoint {
|
||||||
|
return s.endpoints
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *subscriber) Options() SubscriberOptions {
|
||||||
|
return s.opts
|
||||||
|
}
|
||||||
|
102
service.go
102
service.go
@ -5,9 +5,13 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
cmd "github.com/unistack-org/micro-config-cmd"
|
cmd "github.com/unistack-org/micro-config-cmd"
|
||||||
|
"github.com/unistack-org/micro/v3/broker"
|
||||||
"github.com/unistack-org/micro/v3/client"
|
"github.com/unistack-org/micro/v3/client"
|
||||||
"github.com/unistack-org/micro/v3/logger"
|
"github.com/unistack-org/micro/v3/logger"
|
||||||
|
"github.com/unistack-org/micro/v3/network/transport"
|
||||||
|
"github.com/unistack-org/micro/v3/registry"
|
||||||
"github.com/unistack-org/micro/v3/server"
|
"github.com/unistack-org/micro/v3/server"
|
||||||
|
"github.com/unistack-org/micro/v3/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
@ -31,84 +35,94 @@ func (s *service) Name() string {
|
|||||||
// Init initialises options. Additionally it calls cmd.Init
|
// Init initialises options. Additionally it calls cmd.Init
|
||||||
// which parses command line flags. cmd.Init is only called
|
// which parses command line flags. cmd.Init is only called
|
||||||
// on first Init.
|
// on first Init.
|
||||||
func (s *service) Init(opts ...Option) {
|
func (s *service) Init(opts ...Option) error {
|
||||||
// process options
|
// process options
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&s.opts)
|
o(&s.opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.once.Do(func() {
|
if s.opts.Cmd != nil {
|
||||||
if s.opts.Cmd != nil {
|
// set cmd name
|
||||||
// set cmd name
|
if len(s.opts.Cmd.App().Name) == 0 {
|
||||||
if len(s.opts.Cmd.App().Name) == 0 {
|
s.opts.Cmd.App().Name = s.Server().Options().Name
|
||||||
s.opts.Cmd.App().Name = s.Server().Options().Name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialise the command options
|
|
||||||
if err := s.opts.Cmd.Init(
|
|
||||||
cmd.Auth(&s.opts.Auth),
|
|
||||||
cmd.Broker(&s.opts.Broker),
|
|
||||||
cmd.Registry(&s.opts.Registry),
|
|
||||||
cmd.Runtime(&s.opts.Runtime),
|
|
||||||
cmd.Transport(&s.opts.Transport),
|
|
||||||
cmd.Client(&s.opts.Client),
|
|
||||||
cmd.Config(&s.opts.Config),
|
|
||||||
cmd.Server(&s.opts.Server),
|
|
||||||
cmd.Store(&s.opts.Store),
|
|
||||||
cmd.Profile(&s.opts.Profile),
|
|
||||||
); err != nil {
|
|
||||||
logger.Fatalf("[cmd] init failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
// Initialise the command options
|
||||||
|
if err := s.opts.Cmd.Init(
|
||||||
|
cmd.Auth(&s.opts.Auth),
|
||||||
|
cmd.Broker(&s.opts.Broker),
|
||||||
|
cmd.Registry(&s.opts.Registry),
|
||||||
|
cmd.Runtime(&s.opts.Runtime),
|
||||||
|
cmd.Transport(&s.opts.Transport),
|
||||||
|
cmd.Client(&s.opts.Client),
|
||||||
|
cmd.Config(&s.opts.Config),
|
||||||
|
cmd.Server(&s.opts.Server),
|
||||||
|
cmd.Store(&s.opts.Store),
|
||||||
|
cmd.Profile(&s.opts.Profile),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if s.opts.Registry != nil {
|
if s.opts.Registry != nil {
|
||||||
if err := s.opts.Registry.Init(); err != nil {
|
if err := s.opts.Registry.Init(
|
||||||
logger.Fatalf("[cmd] init failed: %v", err)
|
registry.Context(s.opts.Context),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.opts.Broker != nil {
|
if s.opts.Broker != nil {
|
||||||
if err := s.opts.Broker.Init(); err != nil {
|
if err := s.opts.Broker.Init(
|
||||||
logger.Fatalf("[cmd] init failed: %v", err)
|
broker.Context(s.opts.Context),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.opts.Transport != nil {
|
if s.opts.Transport != nil {
|
||||||
if err := s.opts.Transport.Init(); err != nil {
|
if err := s.opts.Transport.Init(
|
||||||
logger.Fatalf("[cmd] init failed: %v", err)
|
transport.Context(s.opts.Context),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.opts.Store != nil {
|
if s.opts.Store != nil {
|
||||||
if err := s.opts.Store.Init(s.opts.Context); err != nil {
|
if err := s.opts.Store.Init(
|
||||||
logger.Fatalf("[cmd] init failed: %v", err)
|
store.Context(s.opts.Context),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.opts.Server != nil {
|
if s.opts.Server != nil {
|
||||||
if err := s.opts.Server.Init(); err != nil {
|
if err := s.opts.Server.Init(
|
||||||
logger.Fatalf("[cmd] init failed: %v", err)
|
server.Context(s.opts.Context),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.opts.Client != nil {
|
if s.opts.Client != nil {
|
||||||
if err := s.opts.Client.Init(); err != nil {
|
if err := s.opts.Client.Init(
|
||||||
logger.Fatalf("[cmd] init failed: %v", err)
|
client.Context(s.opts.Context),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// execute the command
|
return nil
|
||||||
// TODO: do this in service.Run()
|
|
||||||
if err := s.opts.Cmd.Run(); err != nil {
|
|
||||||
logger.Fatalf("[cmd] run failed: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Options() Options {
|
func (s *service) Options() Options {
|
||||||
return s.opts
|
return s.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *service) Broker() broker.Broker {
|
||||||
|
return s.opts.Broker
|
||||||
|
}
|
||||||
|
|
||||||
func (s *service) Client() client.Client {
|
func (s *service) Client() client.Client {
|
||||||
return s.opts.Client
|
return s.opts.Client
|
||||||
}
|
}
|
||||||
@ -185,6 +199,12 @@ func (s *service) Run() error {
|
|||||||
defer s.opts.Profile.Stop()
|
defer s.opts.Profile.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if s.opts.Cmd != nil {
|
||||||
|
if err := s.opts.Cmd.Run(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := s.Start(); err != nil {
|
if err := s.Start(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -2,49 +2,45 @@ package store
|
|||||||
|
|
||||||
import "context"
|
import "context"
|
||||||
|
|
||||||
type noopStore struct {
|
type NoopStore struct {
|
||||||
opts Options
|
opts Options
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStore(opts ...Option) Store {
|
func (n *NoopStore) Init(opts ...Option) error {
|
||||||
options := NewOptions()
|
|
||||||
for _, o := range opts {
|
|
||||||
o(&options)
|
|
||||||
}
|
|
||||||
return &noopStore{opts: options}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *noopStore) Init(ctx context.Context, opts ...Option) error {
|
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&n.opts)
|
o(&n.opts)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopStore) Options() Options {
|
func (n *NoopStore) Options() Options {
|
||||||
return n.opts
|
return n.opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopStore) String() string {
|
func (n *NoopStore) String() string {
|
||||||
return "noop"
|
return "noop"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopStore) Read(ctx context.Context, key string, opts ...ReadOption) ([]*Record, error) {
|
func (n *NoopStore) Read(ctx context.Context, key string, opts ...ReadOption) ([]*Record, error) {
|
||||||
return []*Record{}, nil
|
return []*Record{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopStore) Write(ctx context.Context, r *Record, opts ...WriteOption) error {
|
func (n *NoopStore) Write(ctx context.Context, r *Record, opts ...WriteOption) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopStore) Delete(ctx context.Context, key string, opts ...DeleteOption) error {
|
func (n *NoopStore) Delete(ctx context.Context, key string, opts ...DeleteOption) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopStore) List(ctx context.Context, opts ...ListOption) ([]string, error) {
|
func (n *NoopStore) List(ctx context.Context, opts ...ListOption) ([]string, error) {
|
||||||
return []string{}, nil
|
return []string{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noopStore) Close(ctx context.Context) error {
|
func (n *NoopStore) Connect(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NoopStore) Disconnect(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,12 @@ func NewOptions(opts ...Option) Options {
|
|||||||
// Option sets values in Options
|
// Option sets values in Options
|
||||||
type Option func(o *Options)
|
type Option func(o *Options)
|
||||||
|
|
||||||
|
func Context(ctx context.Context) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.Context = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Logger sets the logger
|
// Logger sets the logger
|
||||||
func Logger(l logger.Logger) Option {
|
func Logger(l logger.Logger) Option {
|
||||||
return func(o *Options) {
|
return func(o *Options) {
|
||||||
|
@ -11,13 +11,14 @@ import (
|
|||||||
var (
|
var (
|
||||||
// ErrNotFound is returned when a key doesn't exist
|
// ErrNotFound is returned when a key doesn't exist
|
||||||
ErrNotFound = errors.New("not found")
|
ErrNotFound = errors.New("not found")
|
||||||
DefaultStore Store = newStore()
|
DefaultStore Store = &NoopStore{opts: NewOptions()}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Store is a data storage interface
|
// Store is a data storage interface
|
||||||
type Store interface {
|
type Store interface {
|
||||||
// Init initialises the store. It must perform any required setup on the backing storage implementation and check that it is ready for use, returning any errors.
|
// Init initialises the store. It must perform any required setup on the backing storage implementation and check that it is ready for use, returning any errors.
|
||||||
Init(ctx context.Context, opts ...Option) error
|
Init(opts ...Option) error
|
||||||
|
Connect(ctx context.Context) error
|
||||||
// Options allows you to view the current options.
|
// Options allows you to view the current options.
|
||||||
Options() Options
|
Options() Options
|
||||||
// Read takes a single key name and optional ReadOptions. It returns matching []*Record or an error.
|
// Read takes a single key name and optional ReadOptions. It returns matching []*Record or an error.
|
||||||
@ -28,8 +29,8 @@ type Store interface {
|
|||||||
Delete(ctx context.Context, key string, opts ...DeleteOption) error
|
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 returns any keys that match, or an empty list with no error if none matched.
|
||||||
List(ctx context.Context, opts ...ListOption) ([]string, error)
|
List(ctx context.Context, opts ...ListOption) ([]string, error)
|
||||||
// Close the store
|
// Disconnect the store
|
||||||
Close(ctx context.Context) error
|
Disconnect(ctx context.Context) error
|
||||||
// String returns the name of the implementation.
|
// String returns the name of the implementation.
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
36
tracer/context.go
Normal file
36
tracer/context.go
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Package trace provides an interface for distributed tracing
|
||||||
|
package tracer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/unistack-org/micro/v3/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
traceIDKey = "Micro-Trace-Id"
|
||||||
|
spanIDKey = "Micro-Span-Id"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FromContext returns a span from context
|
||||||
|
func FromContext(ctx context.Context) (traceID string, parentSpanID string, isFound bool) {
|
||||||
|
traceID, traceOk := metadata.Get(ctx, traceIDKey)
|
||||||
|
microID, microOk := metadata.Get(ctx, "Micro-Id")
|
||||||
|
if !traceOk && !microOk {
|
||||||
|
isFound = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !traceOk {
|
||||||
|
traceID = microID
|
||||||
|
}
|
||||||
|
parentSpanID, ok := metadata.Get(ctx, spanIDKey)
|
||||||
|
return traceID, parentSpanID, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContext saves the trace and span ids in the context
|
||||||
|
func NewContext(ctx context.Context, traceID, parentSpanID string) context.Context {
|
||||||
|
return metadata.MergeContext(ctx, map[string]string{
|
||||||
|
traceIDKey: traceID,
|
||||||
|
spanIDKey: parentSpanID,
|
||||||
|
}, true)
|
||||||
|
}
|
@ -2,24 +2,24 @@ package tracer
|
|||||||
|
|
||||||
import "context"
|
import "context"
|
||||||
|
|
||||||
type noop struct{}
|
type NoopTracer struct{}
|
||||||
|
|
||||||
func (n *noop) Init(...Option) error {
|
func (n *NoopTracer) Init(...Option) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noop) Start(ctx context.Context, name string) (context.Context, *Span) {
|
func (n *NoopTracer) Start(ctx context.Context, name string) (context.Context, *Span) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noop) Finish(*Span) error {
|
func (n *NoopTracer) Finish(*Span) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *noop) Read(...ReadOption) ([]*Span, error) {
|
func (n *NoopTracer) Read(...ReadOption) ([]*Span, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTracer(opts ...Option) Tracer {
|
func NewTracer(opts ...Option) Tracer {
|
||||||
return &noop{}
|
return &NoopTracer{}
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,10 @@ package tracer
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/metadata"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DefaultTracer Tracer = newTracer()
|
DefaultTracer Tracer = NewTracer()
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tracer is an interface for distributed tracing
|
// Tracer is an interface for distributed tracing
|
||||||
@ -51,31 +49,3 @@ type Span struct {
|
|||||||
// Type
|
// Type
|
||||||
Type SpanType
|
Type SpanType
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
traceIDKey = "Micro-Trace-Id"
|
|
||||||
spanIDKey = "Micro-Span-Id"
|
|
||||||
)
|
|
||||||
|
|
||||||
// FromContext returns a span from context
|
|
||||||
func FromContext(ctx context.Context) (traceID string, parentSpanID string, isFound bool) {
|
|
||||||
traceID, traceOk := metadata.Get(ctx, traceIDKey)
|
|
||||||
microID, microOk := metadata.Get(ctx, "Micro-Id")
|
|
||||||
if !traceOk && !microOk {
|
|
||||||
isFound = false
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !traceOk {
|
|
||||||
traceID = microID
|
|
||||||
}
|
|
||||||
parentSpanID, ok := metadata.Get(ctx, spanIDKey)
|
|
||||||
return traceID, parentSpanID, ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToContext saves the trace and span ids in the context
|
|
||||||
func ToContext(ctx context.Context, traceID, parentSpanID string) context.Context {
|
|
||||||
return metadata.MergeContext(ctx, map[string]string{
|
|
||||||
traceIDKey: traceID,
|
|
||||||
spanIDKey: parentSpanID,
|
|
||||||
}, true)
|
|
||||||
}
|
|
||||||
|
@ -1,12 +1,7 @@
|
|||||||
package registry
|
package registry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/unistack-org/micro/v3/metadata"
|
|
||||||
"github.com/unistack-org/micro/v3/registry"
|
"github.com/unistack-org/micro/v3/registry"
|
||||||
"github.com/unistack-org/micro/v3/server"
|
|
||||||
"github.com/unistack-org/micro/v3/util/addr"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func addNodes(old, neu []*registry.Node) []*registry.Node {
|
func addNodes(old, neu []*registry.Node) []*registry.Node {
|
||||||
@ -60,13 +55,13 @@ func delNodes(old, del []*registry.Node) []*registry.Node {
|
|||||||
// CopyService make a copy of service
|
// CopyService make a copy of service
|
||||||
func CopyService(service *registry.Service) *registry.Service {
|
func CopyService(service *registry.Service) *registry.Service {
|
||||||
// copy service
|
// copy service
|
||||||
s := new(registry.Service)
|
s := ®istry.Service{}
|
||||||
*s = *service
|
*s = *service
|
||||||
|
|
||||||
// copy nodes
|
// copy nodes
|
||||||
nodes := make([]*registry.Node, len(service.Nodes))
|
nodes := make([]*registry.Node, len(service.Nodes))
|
||||||
for j, node := range service.Nodes {
|
for j, node := range service.Nodes {
|
||||||
n := new(registry.Node)
|
n := ®istry.Node{}
|
||||||
*n = *node
|
*n = *node
|
||||||
nodes[j] = n
|
nodes[j] = n
|
||||||
}
|
}
|
||||||
@ -75,7 +70,7 @@ func CopyService(service *registry.Service) *registry.Service {
|
|||||||
// copy endpoints
|
// copy endpoints
|
||||||
eps := make([]*registry.Endpoint, len(service.Endpoints))
|
eps := make([]*registry.Endpoint, len(service.Endpoints))
|
||||||
for j, ep := range service.Endpoints {
|
for j, ep := range service.Endpoints {
|
||||||
e := new(registry.Endpoint)
|
e := ®istry.Endpoint{}
|
||||||
*e = *ep
|
*e = *ep
|
||||||
eps[j] = e
|
eps[j] = e
|
||||||
}
|
}
|
||||||
@ -100,7 +95,7 @@ func Merge(olist []*registry.Service, nlist []*registry.Service) []*registry.Ser
|
|||||||
var seen bool
|
var seen bool
|
||||||
for _, o := range olist {
|
for _, o := range olist {
|
||||||
if o.Version == n.Version {
|
if o.Version == n.Version {
|
||||||
sp := new(registry.Service)
|
sp := ®istry.Service{}
|
||||||
// make copy
|
// make copy
|
||||||
*sp = *o
|
*sp = *o
|
||||||
// set nodes
|
// set nodes
|
||||||
@ -111,7 +106,7 @@ func Merge(olist []*registry.Service, nlist []*registry.Service) []*registry.Ser
|
|||||||
srv = append(srv, sp)
|
srv = append(srv, sp)
|
||||||
break
|
break
|
||||||
} else {
|
} else {
|
||||||
sp := new(registry.Service)
|
sp := ®istry.Service{}
|
||||||
// make copy
|
// make copy
|
||||||
*sp = *o
|
*sp = *o
|
||||||
srv = append(srv, sp)
|
srv = append(srv, sp)
|
||||||
@ -129,7 +124,7 @@ func Remove(old, del []*registry.Service) []*registry.Service {
|
|||||||
var services []*registry.Service
|
var services []*registry.Service
|
||||||
|
|
||||||
for _, o := range old {
|
for _, o := range old {
|
||||||
srv := new(registry.Service)
|
srv := ®istry.Service{}
|
||||||
*srv = *o
|
*srv = *o
|
||||||
|
|
||||||
var rem bool
|
var rem bool
|
||||||
@ -151,38 +146,3 @@ func Remove(old, del []*registry.Service) []*registry.Service {
|
|||||||
|
|
||||||
return services
|
return services
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(s server.Server) (*registry.Service, error) {
|
|
||||||
opts := s.Options()
|
|
||||||
|
|
||||||
advt := opts.Address
|
|
||||||
if len(opts.Advertise) > 0 {
|
|
||||||
advt = opts.Advertise
|
|
||||||
}
|
|
||||||
|
|
||||||
host, port, err := net.SplitHostPort(advt)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
addr, err := addr.Extract(host)
|
|
||||||
if err != nil {
|
|
||||||
addr = host
|
|
||||||
}
|
|
||||||
|
|
||||||
node := ®istry.Node{
|
|
||||||
Id: opts.Name + "-" + opts.Id,
|
|
||||||
Address: net.JoinHostPort(addr, port),
|
|
||||||
}
|
|
||||||
node.Metadata = metadata.Copy(opts.Metadata)
|
|
||||||
|
|
||||||
node.Metadata["server"] = s.String()
|
|
||||||
node.Metadata["broker"] = opts.Broker.String()
|
|
||||||
node.Metadata["registry"] = opts.Registry.String()
|
|
||||||
|
|
||||||
return ®istry.Service{
|
|
||||||
Name: opts.Name,
|
|
||||||
Version: opts.Version,
|
|
||||||
Nodes: []*registry.Node{node},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
@ -42,12 +42,20 @@ func NewSync(opts ...Option) Sync {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *syncStore) Connect(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *syncStore) Disconnect(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *syncStore) Close(ctx context.Context) error {
|
func (c *syncStore) Close(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initialises the storeOptions
|
// Init initialises the storeOptions
|
||||||
func (c *syncStore) Init(ctx context.Context, opts ...store.Option) error {
|
func (c *syncStore) Init(opts ...store.Option) error {
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&c.storeOpts)
|
o(&c.storeOpts)
|
||||||
}
|
}
|
||||||
@ -55,7 +63,7 @@ func (c *syncStore) Init(ctx context.Context, opts ...store.Option) error {
|
|||||||
return fmt.Errorf("the sync has no stores")
|
return fmt.Errorf("the sync has no stores")
|
||||||
}
|
}
|
||||||
for _, s := range c.syncOpts.Stores {
|
for _, s := range c.syncOpts.Stores {
|
||||||
if err := s.Init(ctx); err != nil {
|
if err := s.Init(); err != nil {
|
||||||
return fmt.Errorf("Store %s failed to Init(): %w", s.String(), err)
|
return fmt.Errorf("Store %s failed to Init(): %w", s.String(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user