Compare commits
27 Commits
Author | SHA1 | Date | |
---|---|---|---|
c320d8e518 | |||
b5f8316b57 | |||
d7ddd912a8 | |||
c020d90cb4 | |||
db47b62159 | |||
8254456c8b | |||
c2808679c3 | |||
f418235c16 | |||
67ba7b3753 | |||
e48d7cadf9 | |||
c906186011 | |||
dc0ff91b83 | |||
e739c2d438 | |||
bf4a036652 | |||
f83a29eb67 | |||
aef7f53d88 | |||
02c8e4fb7f | |||
f5693bd940 | |||
701afb7bea | |||
019b407e74 | |||
f29a346434 | |||
27db1876c0 | |||
f66ac9736b | |||
ed7972a1fa | |||
2cc004b01c | |||
df951e5daf | |||
5bec0cef03 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,6 +1,8 @@
|
|||||||
# Develop tools
|
# Develop tools
|
||||||
/.vscode/
|
/.vscode/
|
||||||
/.idea/
|
/.idea/
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
|
||||||
# Binaries for programs and plugins
|
# Binaries for programs and plugins
|
||||||
*.exe
|
*.exe
|
||||||
@@ -13,6 +15,7 @@
|
|||||||
_obj
|
_obj
|
||||||
_test
|
_test
|
||||||
_build
|
_build
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
# Architecture specific extensions/prefixes
|
# Architecture specific extensions/prefixes
|
||||||
*.[568vq]
|
*.[568vq]
|
||||||
|
@@ -4,6 +4,7 @@ package broker // import "go.unistack.org/micro/v3/broker"
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/codec"
|
"go.unistack.org/micro/v3/codec"
|
||||||
"go.unistack.org/micro/v3/metadata"
|
"go.unistack.org/micro/v3/metadata"
|
||||||
@@ -17,6 +18,8 @@ var (
|
|||||||
ErrNotConnected = errors.New("broker not connected")
|
ErrNotConnected = errors.New("broker not connected")
|
||||||
// ErrDisconnected returns when broker disconnected
|
// ErrDisconnected returns when broker disconnected
|
||||||
ErrDisconnected = errors.New("broker disconnected")
|
ErrDisconnected = errors.New("broker disconnected")
|
||||||
|
// DefaultGracefulTimeout
|
||||||
|
DefaultGracefulTimeout = 5 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
// Broker is an interface used for asynchronous messaging.
|
// Broker is an interface used for asynchronous messaging.
|
||||||
|
@@ -9,6 +9,7 @@ import (
|
|||||||
"go.unistack.org/micro/v3/logger"
|
"go.unistack.org/micro/v3/logger"
|
||||||
"go.unistack.org/micro/v3/meter"
|
"go.unistack.org/micro/v3/meter"
|
||||||
"go.unistack.org/micro/v3/register"
|
"go.unistack.org/micro/v3/register"
|
||||||
|
"go.unistack.org/micro/v3/sync"
|
||||||
"go.unistack.org/micro/v3/tracer"
|
"go.unistack.org/micro/v3/tracer"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,17 +37,22 @@ type Options struct {
|
|||||||
Name string
|
Name string
|
||||||
// Addrs holds the broker address
|
// Addrs holds the broker address
|
||||||
Addrs []string
|
Addrs []string
|
||||||
|
|
||||||
|
Wait *sync.WaitGroup
|
||||||
|
|
||||||
|
GracefulTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewOptions create new Options
|
// NewOptions create new Options
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
Register: register.DefaultRegister,
|
Register: register.DefaultRegister,
|
||||||
Logger: logger.DefaultLogger,
|
Logger: logger.DefaultLogger,
|
||||||
Context: context.Background(),
|
Context: context.Background(),
|
||||||
Meter: meter.DefaultMeter,
|
Meter: meter.DefaultMeter,
|
||||||
Codec: codec.DefaultCodec,
|
Codec: codec.DefaultCodec,
|
||||||
Tracer: tracer.DefaultTracer,
|
Tracer: tracer.DefaultTracer,
|
||||||
|
GracefulTimeout: DefaultGracefulTimeout,
|
||||||
}
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
|
41
cluster/cluster.go
Normal file
41
cluster/cluster.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package cluster
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"go.unistack.org/micro/v3/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Message sent to member in cluster
|
||||||
|
type Message interface {
|
||||||
|
// Header returns message headers
|
||||||
|
Header() metadata.Metadata
|
||||||
|
// Body returns broker message may be []byte slice or some go struct or interface
|
||||||
|
Body() interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Node interface {
|
||||||
|
// Name returns node name
|
||||||
|
Name() string
|
||||||
|
// Address returns node address
|
||||||
|
Address() string
|
||||||
|
// Metadata returns node metadata
|
||||||
|
Metadata() metadata.Metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cluster interface used for cluster communication across nodes
|
||||||
|
type Cluster interface {
|
||||||
|
// Join is used to take an existing members and performing state sync
|
||||||
|
Join(ctx context.Context, addr ...string) error
|
||||||
|
// Leave broadcast a leave message and stop listeners
|
||||||
|
Leave(ctx context.Context) error
|
||||||
|
// Ping is used to probe live status of the node
|
||||||
|
Ping(ctx context.Context, node Node, payload []byte) error
|
||||||
|
// Members returns the cluster members
|
||||||
|
Members() ([]Node, error)
|
||||||
|
// Broadcast send message for all members in cluster, if filter is not nil, nodes may be filtered
|
||||||
|
// by key/value pairs
|
||||||
|
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
|
||||||
|
}
|
@@ -61,7 +61,6 @@ func (s *slogLogger) renameAttr(_ []string, a slog.Attr) slog.Attr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type slogLogger struct {
|
type slogLogger struct {
|
||||||
slog *slog.Logger
|
|
||||||
leveler *slog.LevelVar
|
leveler *slog.LevelVar
|
||||||
handler slog.Handler
|
handler slog.Handler
|
||||||
opts logger.Options
|
opts logger.Options
|
||||||
@@ -88,8 +87,7 @@ func (s *slogLogger) Clone(opts ...logger.Option) logger.Logger {
|
|||||||
AddSource: l.opts.AddSource,
|
AddSource: l.opts.AddSource,
|
||||||
}
|
}
|
||||||
l.leveler.Set(loggerToSlogLevel(l.opts.Level))
|
l.leveler.Set(loggerToSlogLevel(l.opts.Level))
|
||||||
l.slog = slog.New(slog.NewJSONHandler(options.Out, handleOpt)).With(options.Fields...)
|
l.handler = slog.New(slog.NewJSONHandler(options.Out, handleOpt)).With(options.Fields...).Handler()
|
||||||
l.handler = l.slog.Handler()
|
|
||||||
|
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
@@ -122,8 +120,7 @@ func (s *slogLogger) Fields(attrs ...interface{}) logger.Logger {
|
|||||||
AddSource: l.opts.AddSource,
|
AddSource: l.opts.AddSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
l.slog = slog.New(slog.NewJSONHandler(l.opts.Out, handleOpt)).With(attrs...)
|
l.handler = slog.New(slog.NewJSONHandler(l.opts.Out, handleOpt)).With(attrs...).Handler()
|
||||||
l.handler = l.slog.Handler()
|
|
||||||
|
|
||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
@@ -146,8 +143,7 @@ func (s *slogLogger) Init(opts ...logger.Option) error {
|
|||||||
AddSource: s.opts.AddSource,
|
AddSource: s.opts.AddSource,
|
||||||
}
|
}
|
||||||
s.leveler.Set(loggerToSlogLevel(s.opts.Level))
|
s.leveler.Set(loggerToSlogLevel(s.opts.Level))
|
||||||
s.slog = slog.New(slog.NewJSONHandler(s.opts.Out, handleOpt)).With(s.opts.Fields...)
|
s.handler = slog.New(slog.NewJSONHandler(s.opts.Out, handleOpt)).With(s.opts.Fields...).Handler()
|
||||||
s.handler = s.slog.Handler()
|
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@@ -98,11 +98,12 @@ func (md Metadata) Del(keys ...string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Copy makes a copy of the metadata
|
// Copy makes a copy of the metadata
|
||||||
func Copy(md Metadata) Metadata {
|
func Copy(md Metadata, exclude ...string) Metadata {
|
||||||
nmd := New(len(md))
|
nmd := New(len(md))
|
||||||
for key, val := range md {
|
for key, val := range md {
|
||||||
nmd.Set(key, val)
|
nmd.Set(key, val)
|
||||||
}
|
}
|
||||||
|
nmd.Del(exclude...)
|
||||||
return nmd
|
return nmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -190,3 +190,14 @@ func TestMetadataContext(t *testing.T) {
|
|||||||
t.Errorf("Expected metadata length 1 got %d", i)
|
t.Errorf("Expected metadata length 1 got %d", i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCopy(t *testing.T) {
|
||||||
|
md := New(2)
|
||||||
|
md.Set("key1", "val1", "key2", "val2")
|
||||||
|
nmd := Copy(md, "key2")
|
||||||
|
if len(nmd) != 1 {
|
||||||
|
t.Fatal("Copy exclude not works")
|
||||||
|
} else if nmd["Key1"] != "val1" {
|
||||||
|
t.Fatal("Copy exclude not works")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
22
semconv/broker.go
Normal file
22
semconv/broker.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package semconv
|
||||||
|
|
||||||
|
var (
|
||||||
|
// PublishMessageDurationSeconds specifies meter metric name
|
||||||
|
PublishMessageDurationSeconds = "publish_message_duration_seconds"
|
||||||
|
// PublishMessageLatencyMicroseconds specifies meter metric name
|
||||||
|
PublishMessageLatencyMicroseconds = "publish_message_latency_microseconds"
|
||||||
|
// PublishMessageTotal specifies meter metric name
|
||||||
|
PublishMessageTotal = "publish_message_total"
|
||||||
|
// PublishMessageInflight specifies meter metric name
|
||||||
|
PublishMessageInflight = "publish_message_inflight"
|
||||||
|
// SubscribeMessageDurationSeconds specifies meter metric name
|
||||||
|
SubscribeMessageDurationSeconds = "subscribe_message_duration_seconds"
|
||||||
|
// SubscribeMessageLatencyMicroseconds specifies meter metric name
|
||||||
|
SubscribeMessageLatencyMicroseconds = "subscribe_message_latency_microseconds"
|
||||||
|
// SubscribeMessageTotal specifies meter metric name
|
||||||
|
SubscribeMessageTotal = "subscribe_message_total"
|
||||||
|
// SubscribeMessageInflight specifies meter metric name
|
||||||
|
SubscribeMessageInflight = "subscribe_message_inflight"
|
||||||
|
// BrokerGroupLag specifies broker lag
|
||||||
|
BrokerGroupLag = "broker_group_lag"
|
||||||
|
)
|
12
semconv/client.go
Normal file
12
semconv/client.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package semconv
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ClientRequestDurationSeconds specifies meter metric name
|
||||||
|
ClientRequestDurationSeconds = "client_request_duration_seconds"
|
||||||
|
// ClientRequestLatencyMicroseconds specifies meter metric name
|
||||||
|
ClientRequestLatencyMicroseconds = "client_request_latency_microseconds"
|
||||||
|
// ClientRequestTotal specifies meter metric name
|
||||||
|
ClientRequestTotal = "client_request_total"
|
||||||
|
// ClientRequestInflight specifies meter metric name
|
||||||
|
ClientRequestInflight = "client_request_inflight"
|
||||||
|
)
|
12
semconv/server.go
Normal file
12
semconv/server.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package semconv
|
||||||
|
|
||||||
|
var (
|
||||||
|
// ServerRequestDurationSeconds specifies meter metric name
|
||||||
|
ServerRequestDurationSeconds = "server_request_duration_seconds"
|
||||||
|
// ServerRequestLatencyMicroseconds specifies meter metric name
|
||||||
|
ServerRequestLatencyMicroseconds = "server_request_latency_microseconds"
|
||||||
|
// ServerRequestTotal specifies meter metric name
|
||||||
|
ServerRequestTotal = "server_request_total"
|
||||||
|
// ServerRequestInflight specifies meter metric name
|
||||||
|
ServerRequestInflight = "server_request_inflight"
|
||||||
|
)
|
@@ -15,6 +15,7 @@ import (
|
|||||||
"go.unistack.org/micro/v3/network/transport"
|
"go.unistack.org/micro/v3/network/transport"
|
||||||
"go.unistack.org/micro/v3/options"
|
"go.unistack.org/micro/v3/options"
|
||||||
"go.unistack.org/micro/v3/register"
|
"go.unistack.org/micro/v3/register"
|
||||||
|
msync "go.unistack.org/micro/v3/sync"
|
||||||
"go.unistack.org/micro/v3/tracer"
|
"go.unistack.org/micro/v3/tracer"
|
||||||
"go.unistack.org/micro/v3/util/id"
|
"go.unistack.org/micro/v3/util/id"
|
||||||
)
|
)
|
||||||
@@ -47,7 +48,7 @@ type Options struct {
|
|||||||
// Listener may be passed if already created
|
// Listener may be passed if already created
|
||||||
Listener net.Listener
|
Listener net.Listener
|
||||||
// Wait group
|
// Wait group
|
||||||
Wait *sync.WaitGroup
|
Wait *msync.WaitGroup
|
||||||
// TLSConfig specifies tls.Config for secure serving
|
// TLSConfig specifies tls.Config for secure serving
|
||||||
TLSConfig *tls.Config
|
TLSConfig *tls.Config
|
||||||
// Metadata holds the server metadata
|
// Metadata holds the server metadata
|
||||||
@@ -282,7 +283,7 @@ func Wait(wg *sync.WaitGroup) Option {
|
|||||||
if wg == nil {
|
if wg == nil {
|
||||||
wg = new(sync.WaitGroup)
|
wg = new(sync.WaitGroup)
|
||||||
}
|
}
|
||||||
o.Wait = wg
|
o.Wait = msync.WrapWaitGroup(wg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -331,7 +332,6 @@ func GracefulTimeout(td time.Duration) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// HandlerOptions struct
|
// HandlerOptions struct
|
||||||
type HandlerOptions struct {
|
type HandlerOptions struct {
|
||||||
// Context holds external options
|
// Context holds external options
|
||||||
|
@@ -144,6 +144,10 @@ type ReadOptions struct {
|
|||||||
Context context.Context
|
Context context.Context
|
||||||
// Namespace holds namespace
|
// Namespace holds namespace
|
||||||
Namespace string
|
Namespace string
|
||||||
|
// Name holds mnemonic name
|
||||||
|
Name string
|
||||||
|
// Timeout specifies max timeout for operation
|
||||||
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewReadOptions fills ReadOptions struct with opts slice
|
// NewReadOptions fills ReadOptions struct with opts slice
|
||||||
@@ -158,6 +162,20 @@ func NewReadOptions(opts ...ReadOption) ReadOptions {
|
|||||||
// ReadOption sets values in ReadOptions
|
// ReadOption sets values in ReadOptions
|
||||||
type ReadOption func(r *ReadOptions)
|
type ReadOption func(r *ReadOptions)
|
||||||
|
|
||||||
|
// ReadTimeout pass timeout to ReadOptions
|
||||||
|
func ReadTimeout(td time.Duration) ReadOption {
|
||||||
|
return func(o *ReadOptions) {
|
||||||
|
o.Timeout = td
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadName pass name to ReadOptions
|
||||||
|
func ReadName(name string) ReadOption {
|
||||||
|
return func(o *ReadOptions) {
|
||||||
|
o.Name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ReadContext pass context.Context to ReadOptions
|
// ReadContext pass context.Context to ReadOptions
|
||||||
func ReadContext(ctx context.Context) ReadOption {
|
func ReadContext(ctx context.Context) ReadOption {
|
||||||
return func(o *ReadOptions) {
|
return func(o *ReadOptions) {
|
||||||
@@ -180,6 +198,10 @@ type WriteOptions struct {
|
|||||||
Metadata metadata.Metadata
|
Metadata metadata.Metadata
|
||||||
// Namespace holds namespace
|
// Namespace holds namespace
|
||||||
Namespace string
|
Namespace string
|
||||||
|
// Name holds mnemonic name
|
||||||
|
Name string
|
||||||
|
// Timeout specifies max timeout for operation
|
||||||
|
Timeout time.Duration
|
||||||
// TTL specifies key TTL
|
// TTL specifies key TTL
|
||||||
TTL time.Duration
|
TTL time.Duration
|
||||||
}
|
}
|
||||||
@@ -224,12 +246,30 @@ func WriteNamespace(ns string) WriteOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteName pass name to WriteOptions
|
||||||
|
func WriteName(name string) WriteOption {
|
||||||
|
return func(o *WriteOptions) {
|
||||||
|
o.Name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteTimeout pass timeout to WriteOptions
|
||||||
|
func WriteTimeout(td time.Duration) WriteOption {
|
||||||
|
return func(o *WriteOptions) {
|
||||||
|
o.Timeout = td
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteOptions configures an individual Delete operation
|
// DeleteOptions configures an individual Delete operation
|
||||||
type DeleteOptions struct {
|
type DeleteOptions struct {
|
||||||
// Context holds external options
|
// Context holds external options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
// Namespace holds namespace
|
// Namespace holds namespace
|
||||||
Namespace string
|
Namespace string
|
||||||
|
// Name holds mnemonic name
|
||||||
|
Name string
|
||||||
|
// Timeout specifies max timeout for operation
|
||||||
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDeleteOptions fills DeleteOptions struct with opts slice
|
// NewDeleteOptions fills DeleteOptions struct with opts slice
|
||||||
@@ -258,14 +298,32 @@ func DeleteNamespace(ns string) DeleteOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteName pass name to DeleteOptions
|
||||||
|
func DeleteName(name string) DeleteOption {
|
||||||
|
return func(o *DeleteOptions) {
|
||||||
|
o.Name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteTimeout pass timeout to DeleteOptions
|
||||||
|
func DeleteTimeout(td time.Duration) DeleteOption {
|
||||||
|
return func(o *DeleteOptions) {
|
||||||
|
o.Timeout = td
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ListOptions configures an individual List operation
|
// ListOptions configures an individual List operation
|
||||||
type ListOptions struct {
|
type ListOptions struct {
|
||||||
Context context.Context
|
Context context.Context
|
||||||
Prefix string
|
Prefix string
|
||||||
Suffix string
|
Suffix string
|
||||||
Namespace string
|
Namespace string
|
||||||
Limit uint
|
// Name holds mnemonic name
|
||||||
Offset uint
|
Name string
|
||||||
|
Limit uint
|
||||||
|
Offset uint
|
||||||
|
// Timeout specifies max timeout for operation
|
||||||
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewListOptions fills ListOptions struct with opts slice
|
// NewListOptions fills ListOptions struct with opts slice
|
||||||
@@ -322,12 +380,23 @@ func ListNamespace(ns string) ListOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListTimeout pass timeout to ListOptions
|
||||||
|
func ListTimeout(td time.Duration) ListOption {
|
||||||
|
return func(o *ListOptions) {
|
||||||
|
o.Timeout = td
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ExistsOptions holds options for Exists method
|
// ExistsOptions holds options for Exists method
|
||||||
type ExistsOptions struct {
|
type ExistsOptions struct {
|
||||||
// Context holds external options
|
// Context holds external options
|
||||||
Context context.Context
|
Context context.Context
|
||||||
// Namespace contains namespace
|
// Namespace contains namespace
|
||||||
Namespace string
|
Namespace string
|
||||||
|
// Name holds mnemonic name
|
||||||
|
Name string
|
||||||
|
// Timeout specifies max timeout for operation
|
||||||
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExistsOption specifies Exists call options
|
// ExistsOption specifies Exists call options
|
||||||
@@ -358,6 +427,20 @@ func ExistsNamespace(ns string) ExistsOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExistsName pass name to exist options
|
||||||
|
func ExistsName(name string) ExistsOption {
|
||||||
|
return func(o *ExistsOptions) {
|
||||||
|
o.Name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExistsTimeout timeout to ListOptions
|
||||||
|
func ExistsTimeout(td time.Duration) ExistsOption {
|
||||||
|
return func(o *ExistsOptions) {
|
||||||
|
o.Timeout = td
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// WrapStore adds a store Wrapper to a list of options passed into the store
|
// WrapStore adds a store Wrapper to a list of options passed into the store
|
||||||
func WrapStore(w Wrapper) Option {
|
func WrapStore(w Wrapper) Option {
|
||||||
|
69
sync/waitgroup.go
Normal file
69
sync/waitgroup.go
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
package sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WaitGroup struct {
|
||||||
|
wg *sync.WaitGroup
|
||||||
|
c int
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func WrapWaitGroup(wg *sync.WaitGroup) *WaitGroup {
|
||||||
|
g := &WaitGroup{
|
||||||
|
wg: wg,
|
||||||
|
}
|
||||||
|
return g
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWaitGroup() *WaitGroup {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
return WrapWaitGroup(&wg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *WaitGroup) Add(n int) {
|
||||||
|
g.mu.Lock()
|
||||||
|
g.c += n
|
||||||
|
g.wg.Add(n)
|
||||||
|
g.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *WaitGroup) Done() {
|
||||||
|
g.mu.Lock()
|
||||||
|
g.c += -1
|
||||||
|
g.wg.Add(-1)
|
||||||
|
g.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *WaitGroup) Wait() {
|
||||||
|
g.wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *WaitGroup) WaitContext(ctx context.Context) {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
g.wg.Wait()
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
g.mu.Lock()
|
||||||
|
g.wg.Add(-g.c)
|
||||||
|
<-done
|
||||||
|
g.wg.Add(g.c)
|
||||||
|
g.mu.Unlock()
|
||||||
|
return
|
||||||
|
case <-done:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *WaitGroup) Waiters() int {
|
||||||
|
g.mu.Lock()
|
||||||
|
c := g.c
|
||||||
|
g.mu.Unlock()
|
||||||
|
return c
|
||||||
|
}
|
37
sync/waitgroup_test.go
Normal file
37
sync/waitgroup_test.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package sync
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestWaitGroupContext(t *testing.T) {
|
||||||
|
wg := NewWaitGroup()
|
||||||
|
_ = t
|
||||||
|
wg.Add(1)
|
||||||
|
ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
wg.WaitContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWaitGroupReuse(t *testing.T) {
|
||||||
|
wg := NewWaitGroup()
|
||||||
|
defer func() {
|
||||||
|
if wg.Waiters() != 0 {
|
||||||
|
t.Fatal("lost goroutines")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
defer wg.Done()
|
||||||
|
ctx, cancel := context.WithTimeout(context.TODO(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
wg.WaitContext(ctx)
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
defer wg.Done()
|
||||||
|
ctx, cancel = context.WithTimeout(context.TODO(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
wg.WaitContext(ctx)
|
||||||
|
}
|
@@ -100,13 +100,13 @@ type EventOption func(o *EventOptions)
|
|||||||
|
|
||||||
func WithEventLabels(kv ...interface{}) EventOption {
|
func WithEventLabels(kv ...interface{}) EventOption {
|
||||||
return func(o *EventOptions) {
|
return func(o *EventOptions) {
|
||||||
o.Labels = kv
|
o.Labels = append(o.Labels, kv...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WithSpanLabels(kv ...interface{}) SpanOption {
|
func WithSpanLabels(kv ...interface{}) SpanOption {
|
||||||
return func(o *SpanOptions) {
|
return func(o *SpanOptions) {
|
||||||
o.Labels = kv
|
o.Labels = append(o.Labels, kv...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +159,8 @@ func NewSpanOptions(opts ...SpanOption) SpanOptions {
|
|||||||
// NewOptions returns default options
|
// NewOptions returns default options
|
||||||
func NewOptions(opts ...Option) Options {
|
func NewOptions(opts ...Option) Options {
|
||||||
options := Options{
|
options := Options{
|
||||||
Logger: logger.DefaultLogger,
|
Logger: logger.DefaultLogger,
|
||||||
|
Context: context.Background(),
|
||||||
}
|
}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
|
@@ -3,8 +3,6 @@ package tracer // import "go.unistack.org/micro/v3/tracer"
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"go.unistack.org/micro/v3/logger"
|
"go.unistack.org/micro/v3/logger"
|
||||||
)
|
)
|
||||||
@@ -70,37 +68,3 @@ type Span interface {
|
|||||||
// SpanID returns span id
|
// SpanID returns span id
|
||||||
SpanID() string
|
SpanID() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort labels alphabeticaly by label name
|
|
||||||
type byKey []interface{}
|
|
||||||
|
|
||||||
func (k byKey) Len() int { return len(k) / 2 }
|
|
||||||
func (k byKey) Less(i, j int) bool { return fmt.Sprintf("%s", k[i*2]) < fmt.Sprintf("%s", k[j*2]) }
|
|
||||||
func (k byKey) Swap(i, j int) {
|
|
||||||
k[i*2], k[j*2] = k[j*2], k[i*2]
|
|
||||||
k[i*2+1], k[j*2+1] = k[j*2+1], k[i*2+1]
|
|
||||||
}
|
|
||||||
|
|
||||||
func UniqLabels(labels []interface{}) []interface{} {
|
|
||||||
if len(labels)%2 == 1 {
|
|
||||||
labels = labels[:len(labels)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(labels) > 2 {
|
|
||||||
sort.Sort(byKey(labels))
|
|
||||||
|
|
||||||
idx := 0
|
|
||||||
for {
|
|
||||||
if labels[idx] == labels[idx+2] {
|
|
||||||
copy(labels[idx:], labels[idx+2:])
|
|
||||||
labels = labels[:len(labels)-2]
|
|
||||||
} else {
|
|
||||||
idx += 2
|
|
||||||
}
|
|
||||||
if idx+2 >= len(labels) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return labels
|
|
||||||
}
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package reflect // import "go.unistack.org/micro/v3/util/reflect"
|
package reflect
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -45,15 +46,23 @@ func SliceAppend(b bool) Option {
|
|||||||
|
|
||||||
// Merge merges map[string]interface{} to destination struct
|
// Merge merges map[string]interface{} to destination struct
|
||||||
func Merge(dst interface{}, mp map[string]interface{}, opts ...Option) error {
|
func Merge(dst interface{}, mp map[string]interface{}, opts ...Option) error {
|
||||||
var err error
|
|
||||||
var sval reflect.Value
|
|
||||||
var fname string
|
|
||||||
|
|
||||||
options := Options{}
|
options := Options{}
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
o(&options)
|
o(&options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if unmarshaler, ok := dst.(json.Unmarshaler); ok {
|
||||||
|
buf, err := json.Marshal(mp)
|
||||||
|
if err == nil {
|
||||||
|
err = unmarshaler.UnmarshalJSON(buf)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var sval reflect.Value
|
||||||
|
var fname string
|
||||||
|
|
||||||
dviface := reflect.ValueOf(dst)
|
dviface := reflect.ValueOf(dst)
|
||||||
if dviface.Kind() == reflect.Ptr {
|
if dviface.Kind() == reflect.Ptr {
|
||||||
dviface = dviface.Elem()
|
dviface = dviface.Elem()
|
||||||
|
40
util/sort/sort.go
Normal file
40
util/sort/sort.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package sort
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
// sort labels alphabeticaly by label name
|
||||||
|
type byKey []interface{}
|
||||||
|
|
||||||
|
func (k byKey) Len() int { return len(k) / 2 }
|
||||||
|
func (k byKey) Less(i, j int) bool { return fmt.Sprintf("%s", k[i*2]) < fmt.Sprintf("%s", k[j*2]) }
|
||||||
|
func (k byKey) Swap(i, j int) {
|
||||||
|
k[i*2], k[j*2] = k[j*2], k[i*2]
|
||||||
|
k[i*2+1], k[j*2+1] = k[j*2+1], k[i*2+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func Uniq(labels []interface{}) []interface{} {
|
||||||
|
if len(labels)%2 == 1 {
|
||||||
|
labels = labels[:len(labels)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(labels) > 2 {
|
||||||
|
sort.Sort(byKey(labels))
|
||||||
|
|
||||||
|
idx := 0
|
||||||
|
for {
|
||||||
|
if labels[idx] == labels[idx+2] {
|
||||||
|
copy(labels[idx:], labels[idx+2:])
|
||||||
|
labels = labels[:len(labels)-2]
|
||||||
|
} else {
|
||||||
|
idx += 2
|
||||||
|
}
|
||||||
|
if idx+2 >= len(labels) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return labels
|
||||||
|
}
|
Reference in New Issue
Block a user