updates #207
| @@ -46,6 +46,12 @@ type Broker interface { | ||||
| 	BatchSubscribe(ctx context.Context, topic string, h BatchHandler, opts ...SubscribeOption) (Subscriber, error) | ||||
| 	// String type of broker | ||||
| 	String() string | ||||
| 	// Live returns broker liveness | ||||
| 	Live() bool | ||||
| 	// Ready returns broker readiness | ||||
| 	Ready() bool | ||||
| 	// Health returns broker health | ||||
| 	Health() bool | ||||
| } | ||||
|  | ||||
| type ( | ||||
|   | ||||
| @@ -339,6 +339,18 @@ func (m *memoryBroker) Name() string { | ||||
| 	return m.opts.Name | ||||
| } | ||||
|  | ||||
| func (m *memoryBroker) Live() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (m *memoryBroker) Ready() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (m *memoryBroker) Health() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (m *memoryEvent) Topic() string { | ||||
| 	return m.topic | ||||
| } | ||||
|   | ||||
| @@ -25,6 +25,18 @@ func NewBroker(opts ...Option) *NoopBroker { | ||||
| 	return b | ||||
| } | ||||
|  | ||||
| func (b *NoopBroker) Health() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (b *NoopBroker) Live() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (b *NoopBroker) Ready() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (b *NoopBroker) Name() string { | ||||
| 	return b.opts.Name | ||||
| } | ||||
|   | ||||
| @@ -11,7 +11,6 @@ import ( | ||||
| 	"go.unistack.org/micro/v3/logger" | ||||
| 	"go.unistack.org/micro/v3/metadata" | ||||
| 	"go.unistack.org/micro/v3/meter" | ||||
| 	"go.unistack.org/micro/v3/network/transport" | ||||
| 	"go.unistack.org/micro/v3/options" | ||||
| 	"go.unistack.org/micro/v3/register" | ||||
| 	"go.unistack.org/micro/v3/router" | ||||
| @@ -22,8 +21,6 @@ import ( | ||||
|  | ||||
| // Options holds client options | ||||
| type Options struct { | ||||
| 	// Transport used for transfer messages | ||||
| 	Transport transport.Transport | ||||
| 	// Selector used to select needed address | ||||
| 	Selector selector.Selector | ||||
| 	// Logger used to log messages | ||||
| @@ -194,18 +191,16 @@ func NewOptions(opts ...Option) Options { | ||||
| 			Retry:          DefaultRetry, | ||||
| 			Retries:        DefaultRetries, | ||||
| 			RequestTimeout: DefaultRequestTimeout, | ||||
| 			DialTimeout:    transport.DefaultDialTimeout, | ||||
| 		}, | ||||
| 		Lookup:    LookupRoute, | ||||
| 		PoolSize:  DefaultPoolSize, | ||||
| 		PoolTTL:   DefaultPoolTTL, | ||||
| 		Selector:  random.NewSelector(), | ||||
| 		Logger:    logger.DefaultLogger, | ||||
| 		Broker:    broker.DefaultBroker, | ||||
| 		Meter:     meter.DefaultMeter, | ||||
| 		Tracer:    tracer.DefaultTracer, | ||||
| 		Router:    router.DefaultRouter, | ||||
| 		Transport: transport.DefaultTransport, | ||||
| 		Lookup:   LookupRoute, | ||||
| 		PoolSize: DefaultPoolSize, | ||||
| 		PoolTTL:  DefaultPoolTTL, | ||||
| 		Selector: random.NewSelector(), | ||||
| 		Logger:   logger.DefaultLogger, | ||||
| 		Broker:   broker.DefaultBroker, | ||||
| 		Meter:    meter.DefaultMeter, | ||||
| 		Tracer:   tracer.DefaultTracer, | ||||
| 		Router:   router.DefaultRouter, | ||||
| 	} | ||||
|  | ||||
| 	for _, o := range opts { | ||||
| @@ -278,13 +273,6 @@ func PoolTTL(d time.Duration) Option { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Transport to use for communication e.g http, rabbitmq, etc | ||||
| func Transport(t transport.Transport) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Transport = t | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Register sets the routers register | ||||
| func Register(r register.Register) Option { | ||||
| 	return func(o *Options) { | ||||
| @@ -334,14 +322,6 @@ func TLSConfig(t *tls.Config) Option { | ||||
| 	return func(o *Options) { | ||||
| 		// set the internal tls | ||||
| 		o.TLSConfig = t | ||||
|  | ||||
| 		// set the default transport if one is not | ||||
| 		// already set. Required for Init call below. | ||||
|  | ||||
| 		// set the transport tls | ||||
| 		_ = o.Transport.Init( | ||||
| 			transport.TLSConfig(t), | ||||
| 		) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -507,13 +487,6 @@ func WithAuthToken(t string) CallOption { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithNetwork is a CallOption which sets the network attribute | ||||
| func WithNetwork(n string) CallOption { | ||||
| 	return func(o *CallOptions) { | ||||
| 		o.Network = n | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithRouter sets the router to use for this call | ||||
| func WithRouter(r router.Router) CallOption { | ||||
| 	return func(o *CallOptions) { | ||||
|   | ||||
| @@ -38,4 +38,10 @@ type Cluster interface { | ||||
| 	Broadcast(ctx context.Context, msg Message, filter ...string) error | ||||
| 	// Unicast send message to single member in cluster | ||||
| 	Unicast(ctx context.Context, node Node, msg Message) error | ||||
| 	// Live returns cluster liveness | ||||
| 	Live() bool | ||||
| 	// Ready returns cluster readiness | ||||
| 	Ready() bool | ||||
| 	// Health returns cluster health | ||||
| 	Health() bool | ||||
| } | ||||
|   | ||||
| @@ -4,7 +4,6 @@ package config | ||||
| import ( | ||||
| 	"context" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"reflect" | ||||
| 	"time" | ||||
| ) | ||||
| @@ -139,7 +138,7 @@ var ( | ||||
| 				return nil | ||||
| 			} | ||||
| 			if err := fn(ctx, c); err != nil { | ||||
| 				c.Options().Logger.Error(ctx, fmt.Sprintf("%s BeforeLoad error", c.String()), err) | ||||
| 				c.Options().Logger.Error(ctx, c.String()+" BeforeLoad error", err) | ||||
| 				if !c.Options().AllowFail { | ||||
| 					return err | ||||
| 				} | ||||
| @@ -154,7 +153,7 @@ var ( | ||||
| 				return nil | ||||
| 			} | ||||
| 			if err := fn(ctx, c); err != nil { | ||||
| 				c.Options().Logger.Error(ctx, fmt.Sprintf("%s AfterLoad error", c.String()), err) | ||||
| 				c.Options().Logger.Error(ctx, c.String()+" AfterLoad error", err) | ||||
| 				if !c.Options().AllowFail { | ||||
| 					return err | ||||
| 				} | ||||
| @@ -169,7 +168,7 @@ var ( | ||||
| 				return nil | ||||
| 			} | ||||
| 			if err := fn(ctx, c); err != nil { | ||||
| 				c.Options().Logger.Error(ctx, fmt.Sprintf("%s BeforeSave error", c.String()), err) | ||||
| 				c.Options().Logger.Error(ctx, c.String()+" BeforeSave error", err) | ||||
| 				if !c.Options().AllowFail { | ||||
| 					return err | ||||
| 				} | ||||
| @@ -184,7 +183,7 @@ var ( | ||||
| 				return nil | ||||
| 			} | ||||
| 			if err := fn(ctx, c); err != nil { | ||||
| 				c.Options().Logger.Error(ctx, fmt.Sprintf("%s AfterSave error", c.String()), err) | ||||
| 				c.Options().Logger.Error(ctx, c.String()+" AfterSave error", err) | ||||
| 				if !c.Options().AllowFail { | ||||
| 					return err | ||||
| 				} | ||||
| @@ -199,7 +198,7 @@ var ( | ||||
| 				return nil | ||||
| 			} | ||||
| 			if err := fn(ctx, c); err != nil { | ||||
| 				c.Options().Logger.Error(ctx, fmt.Sprintf("%s BeforeInit error", c.String()), err) | ||||
| 				c.Options().Logger.Error(ctx, c.String()+" BeforeInit error", err) | ||||
| 				if !c.Options().AllowFail { | ||||
| 					return err | ||||
| 				} | ||||
| @@ -214,7 +213,7 @@ var ( | ||||
| 				return nil | ||||
| 			} | ||||
| 			if err := fn(ctx, c); err != nil { | ||||
| 				c.Options().Logger.Error(ctx, fmt.Sprintf("%s AfterInit error", c.String(), err), err) | ||||
| 				c.Options().Logger.Error(ctx, c.String()+" AfterInit error", err) | ||||
| 				if !c.Options().AllowFail { | ||||
| 					return err | ||||
| 				} | ||||
|   | ||||
| @@ -5,6 +5,28 @@ import ( | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func TestMultipleUsage(t *testing.T) { | ||||
| 	ctx := context.TODO() | ||||
| 	md := New(0) | ||||
| 	md.Set("key1_1", "val1_1", "key1_2", "val1_2", "key1_3", "val1_3") | ||||
| 	ctx = NewIncomingContext(ctx, Copy(md)) | ||||
| 	ctx = NewOutgoingContext(ctx, Copy(md)) | ||||
| 	imd, _ := FromIncomingContext(ctx) | ||||
| 	omd, _ := FromOutgoingContext(ctx) | ||||
| 	_ = func(x context.Context) context.Context { | ||||
| 		m, _ := FromIncomingContext(x) | ||||
| 		m.Del("key1_2") | ||||
| 		return ctx | ||||
| 	}(ctx) | ||||
| 	_ = func(x context.Context) context.Context { | ||||
| 		m, _ := FromIncomingContext(x) | ||||
| 		m.Del("key1_3") | ||||
| 		return ctx | ||||
| 	}(ctx) | ||||
| 	t.Logf("imd %#+v", imd) | ||||
| 	t.Logf("omd %#+v", omd) | ||||
| } | ||||
|  | ||||
| func TestMetadataSetMultiple(t *testing.T) { | ||||
| 	md := New(4) | ||||
| 	md.Set("key1", "val1", "key2", "val2", "key3") | ||||
|   | ||||
| @@ -66,6 +66,12 @@ type bro struct { | ||||
|  | ||||
| func (p *bro) Name() string { return p.name } | ||||
|  | ||||
| func (p *bro) Live() bool { return true } | ||||
|  | ||||
| func (p *bro) Ready() bool { return true } | ||||
|  | ||||
| func (p *bro) Health() bool { return true } | ||||
|  | ||||
| func (p *bro) Init(opts ...broker.Option) error { return nil } | ||||
|  | ||||
| // Options returns broker options | ||||
|   | ||||
| @@ -45,6 +45,18 @@ type ( | ||||
| 	tunnelAddr struct{} | ||||
| ) | ||||
|  | ||||
| func (t *tunBroker) Live() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (t *tunBroker) Ready() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (t *tunBroker) Health() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (t *tunBroker) Init(opts ...broker.Option) error { | ||||
| 	for _, o := range opts { | ||||
| 		o(&t.opts) | ||||
|   | ||||
| @@ -29,17 +29,32 @@ var ( | ||||
| // and an abstraction over varying implementations | ||||
| // {consul, etcd, zookeeper, ...} | ||||
| type Register interface { | ||||
| 	// Name returns register name | ||||
| 	Name() string | ||||
| 	// Init initialize register | ||||
| 	Init(...Option) error | ||||
| 	// Options returns options for register | ||||
| 	Options() Options | ||||
| 	// Connect initialize connect to register | ||||
| 	Connect(context.Context) error | ||||
| 	// Disconnect initialize discconection from register | ||||
| 	Disconnect(context.Context) error | ||||
| 	// Register service in registry | ||||
| 	Register(context.Context, *Service, ...RegisterOption) error | ||||
| 	// Deregister service from registry | ||||
| 	Deregister(context.Context, *Service, ...DeregisterOption) error | ||||
| 	// LookupService in registry | ||||
| 	LookupService(context.Context, string, ...LookupOption) ([]*Service, error) | ||||
| 	// ListServices in registry | ||||
| 	ListServices(context.Context, ...ListOption) ([]*Service, error) | ||||
| 	// Watch registry events | ||||
| 	Watch(context.Context, ...WatchOption) (Watcher, error) | ||||
| 	// String returns registry string representation | ||||
| 	String() string | ||||
| 	// Live returns register liveness | ||||
| 	// Live() bool | ||||
| 	// Ready returns register readiness | ||||
| 	// Ready() bool | ||||
| } | ||||
|  | ||||
| // Service holds service register info | ||||
|   | ||||
| @@ -121,6 +121,18 @@ func (n *noopServer) newCodec(contentType string) (codec.Codec, error) { | ||||
| 	return nil, codec.ErrUnknownContentType | ||||
| } | ||||
|  | ||||
| func (n *noopServer) Live() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (n *noopServer) Ready() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (n *noopServer) Health() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (n *noopServer) Handle(handler Handler) error { | ||||
| 	n.h = handler | ||||
| 	return nil | ||||
|   | ||||
| @@ -12,7 +12,6 @@ import ( | ||||
| 	"go.unistack.org/micro/v3/logger" | ||||
| 	"go.unistack.org/micro/v3/metadata" | ||||
| 	"go.unistack.org/micro/v3/meter" | ||||
| 	"go.unistack.org/micro/v3/network/transport" | ||||
| 	"go.unistack.org/micro/v3/options" | ||||
| 	"go.unistack.org/micro/v3/register" | ||||
| 	msync "go.unistack.org/micro/v3/sync" | ||||
| @@ -37,8 +36,6 @@ type Options struct { | ||||
| 	Logger logger.Logger | ||||
| 	// Meter holds the meter | ||||
| 	Meter meter.Meter | ||||
| 	// Transport holds the transport | ||||
| 	Transport transport.Transport | ||||
|  | ||||
| 	/* | ||||
| 		// Router for requests | ||||
| @@ -100,7 +97,6 @@ func NewOptions(opts ...Option) Options { | ||||
| 		Tracer:           tracer.DefaultTracer, | ||||
| 		Broker:           broker.DefaultBroker, | ||||
| 		Register:         register.DefaultRegister, | ||||
| 		Transport:        transport.DefaultTransport, | ||||
| 		Address:          DefaultAddress, | ||||
| 		Name:             DefaultName, | ||||
| 		Version:          DefaultVersion, | ||||
| @@ -209,13 +205,6 @@ func Tracer(t tracer.Tracer) Option { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Transport mechanism for communication e.g http, rabbitmq, etc | ||||
| func Transport(t transport.Transport) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Transport = t | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Metadata associated with the server | ||||
| func Metadata(md metadata.Metadata) Option { | ||||
| 	return func(o *Options) { | ||||
| @@ -249,14 +238,6 @@ func TLSConfig(t *tls.Config) Option { | ||||
| 	return func(o *Options) { | ||||
| 		// set the internal tls | ||||
| 		o.TLSConfig = t | ||||
|  | ||||
| 		// set the default transport if one is not | ||||
| 		// already set. Required for Init call below. | ||||
|  | ||||
| 		// set the transport tls | ||||
| 		_ = o.Transport.Init( | ||||
| 			transport.TLSConfig(t), | ||||
| 		) | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -62,6 +62,12 @@ type Server interface { | ||||
| 	Stop() error | ||||
| 	// Server implementation | ||||
| 	String() string | ||||
| 	// Live returns server liveness | ||||
| 	Live() bool | ||||
| 	// Ready returns server readiness | ||||
| 	Ready() bool | ||||
| 	// Health returns server health | ||||
| 	Health() bool | ||||
| } | ||||
|  | ||||
| type ( | ||||
|   | ||||
							
								
								
									
										75
									
								
								service.go
									
									
									
									
									
								
							
							
						
						
									
										75
									
								
								service.go
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | ||||
| // Package micro is a pluggable framework for microservices | ||||
| package micro // import "go.unistack.org/micro/v3" | ||||
| package micro | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| @@ -72,8 +72,14 @@ type Service interface { | ||||
| 	Start() error | ||||
| 	// Stop the service | ||||
| 	Stop() error | ||||
| 	// The service implementation | ||||
| 	// String service representation | ||||
| 	String() string | ||||
| 	// Live returns service liveness | ||||
| 	Live() bool | ||||
| 	// Ready returns service readiness | ||||
| 	Ready() bool | ||||
| 	// Health returns service health | ||||
| 	Health() bool | ||||
| } | ||||
|  | ||||
| // RegisterHandler is syntactic sugar for registering a handler | ||||
| @@ -101,9 +107,7 @@ func (s *service) Name() string { | ||||
| 	return s.opts.Name | ||||
| } | ||||
|  | ||||
| // Init initialises options. Additionally it calls cmd.Init | ||||
| // which parses command line flags. cmd.Init is only called | ||||
| // on first Init. | ||||
| // Init initialises options. | ||||
| // | ||||
| //nolint:gocyclo | ||||
| func (s *service) Init(opts ...Option) error { | ||||
| @@ -252,6 +256,63 @@ func (s *service) String() string { | ||||
| 	return s.opts.Name | ||||
| } | ||||
|  | ||||
| func (s *service) Live() bool { | ||||
| 	for _, v := range s.opts.Brokers { | ||||
| 		if !v.Live() { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	for _, v := range s.opts.Servers { | ||||
| 		if !v.Live() { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	for _, v := range s.opts.Stores { | ||||
| 		if !v.Live() { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (s *service) Ready() bool { | ||||
| 	for _, v := range s.opts.Brokers { | ||||
| 		if !v.Ready() { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	for _, v := range s.opts.Servers { | ||||
| 		if !v.Ready() { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	for _, v := range s.opts.Stores { | ||||
| 		if !v.Ready() { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (s *service) Health() bool { | ||||
| 	for _, v := range s.opts.Brokers { | ||||
| 		if !v.Health() { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	for _, v := range s.opts.Servers { | ||||
| 		if !v.Health() { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	for _, v := range s.opts.Stores { | ||||
| 		if !v.Health() { | ||||
| 			return false | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| //nolint:gocyclo | ||||
| func (s *service) Start() error { | ||||
| 	var err error | ||||
| @@ -281,10 +342,6 @@ func (s *service) Start() error { | ||||
| 		config.Loggers[0].Info(s.opts.Context, fmt.Sprintf("starting [service] %s version %s", s.Options().Name, s.Options().Version)) | ||||
| 	} | ||||
|  | ||||
| 	if len(s.opts.Servers) == 0 { | ||||
| 		return fmt.Errorf("cant start nil server") | ||||
| 	} | ||||
|  | ||||
| 	for _, reg := range s.opts.Registers { | ||||
| 		if err = reg.Connect(s.opts.Context); err != nil { | ||||
| 			return err | ||||
|   | ||||
| @@ -149,6 +149,18 @@ func (m *memoryStore) Name() string { | ||||
| 	return m.opts.Name | ||||
| } | ||||
|  | ||||
| func (m *memoryStore) Live() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (m *memoryStore) Ready() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (m *memoryStore) Health() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (m *memoryStore) Exists(ctx context.Context, key string, opts ...store.ExistsOption) error { | ||||
| 	if m.opts.LazyConnect { | ||||
| 		if err := m.connect(ctx); err != nil { | ||||
| @@ -279,3 +291,16 @@ func (m *memoryStore) connect(ctx context.Context) error { | ||||
| 	m.isConnected.CompareAndSwap(0, 1) | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (m *memoryStore) Watch(ctx context.Context, opts ...store.WatchOption) (store.Watcher, error) { | ||||
| 	return &watcher{}, nil | ||||
| } | ||||
|  | ||||
| type watcher struct{} | ||||
|  | ||||
| func (w *watcher) Next() (store.Event, error) { | ||||
| 	return nil, nil | ||||
| } | ||||
|  | ||||
| func (w *watcher) Stop() { | ||||
| } | ||||
|   | ||||
| @@ -23,6 +23,18 @@ type noopStore struct { | ||||
| 	isConnected atomic.Int32 | ||||
| } | ||||
|  | ||||
| func (n *noopStore) Live() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (n *noopStore) Ready() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func (n *noopStore) Health() bool { | ||||
| 	return true | ||||
| } | ||||
|  | ||||
| func NewStore(opts ...Option) *noopStore { | ||||
| 	options := NewOptions(opts...) | ||||
| 	return &noopStore{opts: options} | ||||
|   | ||||
| @@ -45,7 +45,14 @@ type Store interface { | ||||
| 	Disconnect(ctx context.Context) error | ||||
| 	// String returns the name of the implementation. | ||||
| 	String() string | ||||
| 	// Watch returns events watcher | ||||
| 	Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) | ||||
| 	// Live returns store liveness | ||||
| 	Live() bool | ||||
| 	// Ready returns store readiness | ||||
| 	Ready() bool | ||||
| 	// Health returns store health | ||||
| 	Health() bool | ||||
| } | ||||
|  | ||||
| type ( | ||||
|   | ||||
| @@ -70,3 +70,15 @@ func (w *NamespaceStore) String() string { | ||||
| func (w *NamespaceStore) Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) { | ||||
| 	return w.s.Watch(ctx, opts...) | ||||
| } | ||||
|  | ||||
| func (w *NamespaceStore) Live() bool { | ||||
| 	return w.s.Live() | ||||
| } | ||||
|  | ||||
| func (w *NamespaceStore) Ready() bool { | ||||
| 	return w.s.Ready() | ||||
| } | ||||
|  | ||||
| func (w *NamespaceStore) Health() bool { | ||||
| 	return w.s.Health() | ||||
| } | ||||
|   | ||||
| @@ -1,40 +0,0 @@ | ||||
| // Package io is for io management | ||||
| package io | ||||
|  | ||||
| import ( | ||||
| 	"io" | ||||
|  | ||||
| 	"go.unistack.org/micro/v3/network/transport" | ||||
| ) | ||||
|  | ||||
| type rwc struct { | ||||
| 	socket transport.Socket | ||||
| } | ||||
|  | ||||
| func (r *rwc) Read(p []byte) (n int, err error) { | ||||
| 	m := new(transport.Message) | ||||
| 	if err := r.socket.Recv(m); err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	copy(p, m.Body) | ||||
| 	return len(m.Body), nil | ||||
| } | ||||
|  | ||||
| func (r *rwc) Write(p []byte) (n int, err error) { | ||||
| 	err = r.socket.Send(&transport.Message{ | ||||
| 		Body: p, | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return 0, err | ||||
| 	} | ||||
| 	return len(p), nil | ||||
| } | ||||
|  | ||||
| func (r *rwc) Close() error { | ||||
| 	return r.socket.Close() | ||||
| } | ||||
|  | ||||
| // NewRWC returns a new ReadWriteCloser | ||||
| func NewRWC(sock transport.Socket) io.ReadWriteCloser { | ||||
| 	return &rwc{sock} | ||||
| } | ||||
| @@ -1,118 +0,0 @@ | ||||
| package pool | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"go.unistack.org/micro/v3/network/transport" | ||||
| 	"go.unistack.org/micro/v3/util/id" | ||||
| ) | ||||
|  | ||||
| type pool struct { | ||||
| 	tr    transport.Transport | ||||
| 	conns map[string][]*poolConn | ||||
| 	size  int | ||||
| 	ttl   time.Duration | ||||
| 	sync.Mutex | ||||
| } | ||||
|  | ||||
| type poolConn struct { | ||||
| 	created time.Time | ||||
| 	transport.Client | ||||
| 	id string | ||||
| } | ||||
|  | ||||
| func newPool(options Options) *pool { | ||||
| 	return &pool{ | ||||
| 		size:  options.Size, | ||||
| 		tr:    options.Transport, | ||||
| 		ttl:   options.TTL, | ||||
| 		conns: make(map[string][]*poolConn), | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (p *pool) Close() error { | ||||
| 	p.Lock() | ||||
| 	for k, c := range p.conns { | ||||
| 		for _, conn := range c { | ||||
| 			conn.Client.Close() | ||||
| 		} | ||||
| 		delete(p.conns, k) | ||||
| 	} | ||||
| 	p.Unlock() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // NoOp the Close since we manage it | ||||
| func (p *poolConn) Close() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (p *poolConn) ID() string { | ||||
| 	return p.id | ||||
| } | ||||
|  | ||||
| func (p *poolConn) Created() time.Time { | ||||
| 	return p.created | ||||
| } | ||||
|  | ||||
| func (p *pool) Get(ctx context.Context, addr string, opts ...transport.DialOption) (Conn, error) { | ||||
| 	p.Lock() | ||||
| 	conns := p.conns[addr] | ||||
|  | ||||
| 	// while we have conns check age and then return one | ||||
| 	// otherwise we'll create a new conn | ||||
| 	for len(conns) > 0 { | ||||
| 		conn := conns[len(conns)-1] | ||||
| 		conns = conns[:len(conns)-1] | ||||
| 		p.conns[addr] = conns | ||||
|  | ||||
| 		// if conn is old kill it and move on | ||||
| 		if d := time.Since(conn.Created()); d > p.ttl { | ||||
| 			conn.Client.Close() | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		// we got a good conn, lets unlock and return it | ||||
| 		p.Unlock() | ||||
|  | ||||
| 		return conn, nil | ||||
| 	} | ||||
|  | ||||
| 	p.Unlock() | ||||
|  | ||||
| 	// create new conn | ||||
| 	c, err := p.tr.Dial(ctx, addr, opts...) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	id, err := id.New() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return &poolConn{ | ||||
| 		Client:  c, | ||||
| 		id:      id, | ||||
| 		created: time.Now(), | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (p *pool) Release(conn Conn, err error) error { | ||||
| 	// don't store the conn if it has errored | ||||
| 	if err != nil { | ||||
| 		return conn.(*poolConn).Client.Close() | ||||
| 	} | ||||
|  | ||||
| 	// otherwise put it back for reuse | ||||
| 	p.Lock() | ||||
| 	conns := p.conns[conn.Remote()] | ||||
| 	if len(conns) >= p.size { | ||||
| 		p.Unlock() | ||||
| 		return conn.(*poolConn).Client.Close() | ||||
| 	} | ||||
| 	p.conns[conn.Remote()] = append(conns, conn.(*poolConn)) | ||||
| 	p.Unlock() | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
| @@ -1,92 +0,0 @@ | ||||
| //go:build ignore | ||||
| // +build ignore | ||||
|  | ||||
| package pool | ||||
|  | ||||
| import ( | ||||
| 	"testing" | ||||
| 	"time" | ||||
|  | ||||
| 	"go.unistack.org/micro/v3/network/transport" | ||||
| 	"go.unistack.org/micro/v3/network/transport/memory" | ||||
| ) | ||||
|  | ||||
| func testPool(t *testing.T, size int, ttl time.Duration) { | ||||
| 	// mock transport | ||||
| 	tr := memory.NewTransport() | ||||
|  | ||||
| 	options := Options{ | ||||
| 		TTL:       ttl, | ||||
| 		Size:      size, | ||||
| 		Transport: tr, | ||||
| 	} | ||||
| 	// zero pool | ||||
| 	p := newPool(options) | ||||
|  | ||||
| 	// listen | ||||
| 	l, err := tr.Listen(":0") | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	defer l.Close() | ||||
|  | ||||
| 	// accept loop | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			if err := l.Accept(func(s transport.Socket) { | ||||
| 				for { | ||||
| 					var msg transport.Message | ||||
| 					if err := s.Recv(&msg); err != nil { | ||||
| 						return | ||||
| 					} | ||||
| 					if err := s.Send(&msg); err != nil { | ||||
| 						return | ||||
| 					} | ||||
| 				} | ||||
| 			}); err != nil { | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	for i := 0; i < 10; i++ { | ||||
| 		// get a conn | ||||
| 		c, err := p.Get(l.Addr()) | ||||
| 		if err != nil { | ||||
| 			t.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		msg := &transport.Message{ | ||||
| 			Body: []byte(`hello world`), | ||||
| 		} | ||||
|  | ||||
| 		if err := c.Send(msg); err != nil { | ||||
| 			t.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		var rcv transport.Message | ||||
|  | ||||
| 		if err := c.Recv(&rcv); err != nil { | ||||
| 			t.Fatal(err) | ||||
| 		} | ||||
|  | ||||
| 		if string(rcv.Body) != string(msg.Body) { | ||||
| 			t.Fatalf("got %v, expected %v", rcv.Body, msg.Body) | ||||
| 		} | ||||
|  | ||||
| 		// release the conn | ||||
| 		p.Release(c, nil) | ||||
|  | ||||
| 		p.Lock() | ||||
| 		if i := len(p.conns[l.Addr()]); i > size { | ||||
| 			p.Unlock() | ||||
| 			t.Fatalf("pool size %d is greater than expected %d", i, size) | ||||
| 		} | ||||
| 		p.Unlock() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func TestClientPool(t *testing.T) { | ||||
| 	testPool(t, 0, time.Minute) | ||||
| 	testPool(t, 2, time.Minute) | ||||
| } | ||||
| @@ -1,38 +0,0 @@ | ||||
| package pool | ||||
|  | ||||
| import ( | ||||
| 	"time" | ||||
|  | ||||
| 	"go.unistack.org/micro/v3/network/transport" | ||||
| ) | ||||
|  | ||||
| // Options struct | ||||
| type Options struct { | ||||
| 	Transport transport.Transport | ||||
| 	TTL       time.Duration | ||||
| 	Size      int | ||||
| } | ||||
|  | ||||
| // Option func signature | ||||
| type Option func(*Options) | ||||
|  | ||||
| // Size sets the size | ||||
| func Size(i int) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Size = i | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Transport sets the transport | ||||
| func Transport(t transport.Transport) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Transport = t | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TTL specifies ttl | ||||
| func TTL(t time.Duration) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.TTL = t | ||||
| 	} | ||||
| } | ||||
| @@ -1,38 +0,0 @@ | ||||
| // Package pool is a connection pool | ||||
| package pool | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"time" | ||||
|  | ||||
| 	"go.unistack.org/micro/v3/network/transport" | ||||
| ) | ||||
|  | ||||
| // Pool is an interface for connection pooling | ||||
| type Pool interface { | ||||
| 	// Close the pool | ||||
| 	Close() error | ||||
| 	// Get a connection | ||||
| 	Get(ctx context.Context, addr string, opts ...transport.DialOption) (Conn, error) | ||||
| 	// Release the connection | ||||
| 	Release(c Conn, status error) error | ||||
| } | ||||
|  | ||||
| // Conn conn pool interface | ||||
| type Conn interface { | ||||
| 	// unique id of connection | ||||
| 	ID() string | ||||
| 	// time it was created | ||||
| 	Created() time.Time | ||||
| 	// embedded connection | ||||
| 	transport.Client | ||||
| } | ||||
|  | ||||
| // NewPool creates new connection pool | ||||
| func NewPool(opts ...Option) Pool { | ||||
| 	options := Options{} | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 	} | ||||
| 	return newPool(options) | ||||
| } | ||||
| @@ -23,7 +23,7 @@ func TestMarshalYAML(t *testing.T) { | ||||
|  | ||||
| func TestUnmarshalYAML(t *testing.T) { | ||||
| 	type str struct { | ||||
| 		TTL Duration `yaml:"ttl"` | ||||
| 		TTL *Duration `yaml:"ttl"` | ||||
| 	} | ||||
| 	v := &str{} | ||||
| 	var err error | ||||
| @@ -31,14 +31,14 @@ func TestUnmarshalYAML(t *testing.T) { | ||||
| 	err = yaml.Unmarshal([]byte(`{"ttl":"10ms"}`), v) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} else if v.TTL != 10000000 { | ||||
| 	} else if *(v.TTL) != 10000000 { | ||||
| 		t.Fatalf("invalid duration %v != 10000000", v.TTL) | ||||
| 	} | ||||
|  | ||||
| 	err = yaml.Unmarshal([]byte(`{"ttl":"1y"}`), v) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} else if v.TTL != 31622400000000000 { | ||||
| 	} else if *(v.TTL) != 31622400000000000 { | ||||
| 		t.Fatalf("invalid duration %v != 31622400000000000", v.TTL) | ||||
| 	} | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user