Compare commits

..

28 Commits

Author SHA1 Message Date
ef664607b4 automerge minor version updates
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-12 11:48:09 +03:00
62e482a14b move renovate
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-10 12:44:56 +03:00
a390ebf80f fix renovate.json
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-10 12:43:29 +03:00
9a44960be7 another fix
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-10 10:52:33 +03:00
c846c59b9b fix renovate.json
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-10 10:39:37 +03:00
renovate[bot]
902bf6326b chore(deps): update golangci/golangci-lint-action action to v2 (#14)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-10 00:35:56 +03:00
renovate[bot]
bddf3bf502 chore(deps): update actions/setup-go action to v2 (#13)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-02-09 23:04:23 +03:00
renovate[bot]
284131da98 Add renovate.json (#12)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
Co-authored-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-09 23:01:50 +03:00
927c7ea3c2 metadata: allow to modify metadata via SetXXX functions
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-09 12:46:14 +03:00
0e51a79bb6 metadata: split context to incoming and outgoing
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-09 01:08:45 +03:00
1de9911b73 util/reflect: add missing types for merge
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-06 18:13:43 +03:00
b4092c6619 util/reflect: improve merge for map
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-05 18:27:16 +03:00
024868bfd7 api: encode body param in endpoint
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-02 19:35:16 +03:00
a0bbfd6d02 provide compa options
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-02-02 15:37:12 +03:00
2cb6843773 codec: fix noop codec
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 23:18:12 +03:00
87e1480077 config: add name to each config imp
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 16:18:17 +03:00
bcd7f6a551 codec: fix noop codec to handle *broker.Message
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 16:07:21 +03:00
925b3af46b register: fix options
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 15:06:47 +03:00
ef4efa6a6b rename util
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 14:50:09 +03:00
125646d89b add Name func option
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 14:07:35 +03:00
7af7649448 store: add Name func
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 14:02:54 +03:00
827d467077 micro: rewrite options to support multiple building blocks
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-29 13:17:32 +03:00
ac8a3a12c4 meter: complete interface
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-27 00:54:19 +03:00
286785491c store: improve interface
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-26 02:09:26 +03:00
263ea8910d meter: use plan map and metadata
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-23 00:23:29 +03:00
202a942eef metadata: add Merge func
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-23 00:09:07 +03:00
c7bafecce3 add meter and tracer across all options
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-22 23:32:33 +03:00
c67fe6f330 meter: add option helper and provide default metric name and label prefix
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
2021-01-22 19:18:28 +03:00
83 changed files with 2810 additions and 922 deletions

19
.github/renovate.json vendored Normal file
View File

@@ -0,0 +1,19 @@
{
"extends": [
"config:base"
],
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
"automerge": true
},
{
"groupName": "all deps",
"separateMajorMinor": true,
"groupSlug": "all",
"packagePatterns": [
"*"
]
}
]
}

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: setup
uses: actions/setup-go@v1
uses: actions/setup-go@v2
with:
go-version: 1.15
- name: cache
@@ -49,7 +49,7 @@ jobs:
- name: checkout
uses: actions/checkout@v2
- name: lint
uses: golangci/golangci-lint-action@v1
uses: golangci/golangci-lint-action@v2
continue-on-error: true
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: setup
uses: actions/setup-go@v1
uses: actions/setup-go@v2
with:
go-version: 1.15
- name: cache
@@ -49,7 +49,7 @@ jobs:
- name: checkout
uses: actions/checkout@v2
- name: lint
uses: golangci/golangci-lint-action@v1
uses: golangci/golangci-lint-action@v2
continue-on-error: true
with:
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.

View File

@@ -6,7 +6,7 @@ import (
"strings"
"github.com/unistack-org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
"github.com/unistack-org/micro/v3/server"
)
@@ -56,7 +56,7 @@ type Service struct {
// The endpoint for this service
Endpoint *Endpoint
// Versions of this service
Services []*registry.Service
Services []*register.Service
}
func strip(s string) string {
@@ -98,6 +98,7 @@ func Encode(e *Endpoint) map[string]string {
set("method", strings.Join(e.Method, ","))
set("path", strings.Join(e.Path, ","))
set("host", strings.Join(e.Host, ","))
set("body", e.Body)
return ep
}
@@ -118,6 +119,7 @@ func Decode(e metadata.Metadata) *Endpoint {
ephost, _ := e.Get("host")
ep.Host = []string{ephost}
ep.Handler, _ = e.Get("handler")
ep.Body, _ = e.Get("body")
return ep
}

View File

@@ -11,7 +11,7 @@ import (
"github.com/unistack-org/micro/v3/api"
"github.com/unistack-org/micro/v3/api/handler"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
)
const (
@@ -70,7 +70,7 @@ func (h *httpHandler) getService(r *http.Request) (string, error) {
}
// get the nodes for this service
nodes := make([]*registry.Node, 0, len(service.Services))
nodes := make([]*register.Node, 0, len(service.Services))
for _, srv := range service.Services {
nodes = append(nodes, srv.Nodes...)
}

View File

@@ -12,13 +12,13 @@ import (
"github.com/unistack-org/micro/v3/api/resolver"
"github.com/unistack-org/micro/v3/api/resolver/vpath"
"github.com/unistack-org/micro/v3/api/router"
regRouter "github.com/unistack-org/micro/v3/api/router/registry"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/registry/memory"
regRouter "github.com/unistack-org/micro/v3/api/router/register"
"github.com/unistack-org/micro/v3/register"
"github.com/unistack-org/micro/v3/register/memory"
)
func testHttp(t *testing.T, path, service, ns string) {
r := memory.NewRegistry()
r := memory.NewRegister()
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
@@ -26,9 +26,9 @@ func testHttp(t *testing.T, path, service, ns string) {
}
defer l.Close()
s := &registry.Service{
s := &register.Service{
Name: service,
Nodes: []*registry.Node{
Nodes: []*register.Node{
{
Id: service + "-1",
Address: l.Addr().String(),
@@ -58,7 +58,7 @@ func testHttp(t *testing.T, path, service, ns string) {
// initialise the handler
rt := regRouter.NewRouter(
router.WithHandler("http"),
router.WithRegistry(r),
router.WithRegister(r),
router.WithResolver(vpath.NewResolver(
resolver.WithServicePrefix(ns),
)),

View File

@@ -14,7 +14,7 @@ import (
"github.com/unistack-org/micro/v3/api"
"github.com/unistack-org/micro/v3/api/handler"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
)
const (
@@ -72,7 +72,7 @@ func (wh *webHandler) getService(r *http.Request) (string, error) {
}
// get the nodes
nodes := make([]*registry.Node, 0, len(service.Services))
nodes := make([]*register.Node, 0, len(service.Services))
for _, srv := range service.Services {
nodes = append(nodes, srv.Nodes...)
}

View File

@@ -3,7 +3,7 @@ package resolver
import (
"context"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
)
// Options struct
@@ -58,7 +58,7 @@ func Domain(n string) ResolveOption {
// NewResolveOptions returns new initialised resolve options
func NewResolveOptions(opts ...ResolveOption) ResolveOptions {
options := ResolveOptions{Domain: registry.DefaultDomain}
options := ResolveOptions{Domain: register.DefaultDomain}
for _, o := range opts {
o(&options)
}

View File

@@ -6,12 +6,12 @@ import (
"github.com/unistack-org/micro/v3/api/resolver"
"github.com/unistack-org/micro/v3/api/resolver/vpath"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
)
type Options struct {
Handler string
Registry registry.Registry
Register register.Register
Resolver resolver.Resolver
Logger logger.Logger
Context context.Context
@@ -52,10 +52,10 @@ func WithHandler(h string) Option {
}
}
// WithRegistry sets the registry
func WithRegistry(r registry.Registry) Option {
// WithRegister sets the register
func WithRegister(r register.Register) Option {
return func(o *Options) {
o.Registry = r
o.Register = r
}
}

View File

@@ -6,13 +6,17 @@ import (
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/store"
"github.com/unistack-org/micro/v3/tracer"
)
// NewOptions creates Options struct from slice of options
func NewOptions(opts ...Option) Options {
options := Options{
Tracer: tracer.DefaultTracer,
Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter,
}
for _, o := range opts {
o(&options)
@@ -21,6 +25,7 @@ func NewOptions(opts ...Option) Options {
}
type Options struct {
Name string
// Issuer of the service's account
Issuer string
// ID is the services auth ID
@@ -41,6 +46,10 @@ type Options struct {
Addrs []string
// Logger sets the logger
Logger logger.Logger
// Meter sets tht meter
Meter meter.Meter
// Tracer
Tracer tracer.Tracer
// Context to store other options
Context context.Context
}
@@ -55,6 +64,13 @@ func Addrs(addrs ...string) Option {
}
}
// Name sets the name
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
// Issuer of the services account
func Issuer(i string) Option {
return func(o *Options) {
@@ -288,3 +304,17 @@ func Logger(l logger.Logger) Option {
o.Logger = l
}
}
// Meter sets the meter
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// Tracer sets the meter
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}

View File

@@ -14,6 +14,7 @@ var (
// Broker is an interface used for asynchronous messaging.
type Broker interface {
Name() string
Init(...Option) error
Options() Options
Address() string

View File

@@ -16,6 +16,10 @@ func NewBroker(opts ...Option) Broker {
return &noopBroker{opts: NewOptions(opts...)}
}
func (n *noopBroker) Name() string {
return n.opts.Name
}
// Init initialize broker
func (n *noopBroker) Init(opts ...Option) error {
for _, o := range opts {

View File

@@ -6,36 +6,43 @@ import (
"github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/register"
"github.com/unistack-org/micro/v3/tracer"
)
// Options struct
type Options struct {
Addrs []string
Secure bool
// Codec
Codec codec.Codec
// Logger the logger
Logger logger.Logger
// Handler executed when errors occur processing messages
Name string
// Addrs useed by broker
Addrs []string
// ErrorHandler executed when errors occur processing messages
ErrorHandler Handler
// Codec used to marshal/unmarshal messages
Codec codec.Codec
// Logger the used logger
Logger logger.Logger
// Meter the used for metrics
Meter meter.Meter
// Tracer used for trace
Tracer tracer.Tracer
// TLSConfig for secure communication
TLSConfig *tls.Config
// Registry used for clustering
Registry registry.Registry
// Other options for implementations of the interface
// can be stored in a context
// Register used for clustering
Register register.Register
// Context is used for non default options
Context context.Context
}
// NewOptions create new Options
func NewOptions(opts ...Option) Options {
options := Options{
Registry: registry.DefaultRegistry,
Register: register.DefaultRegister,
Logger: logger.DefaultLogger,
Context: context.Background(),
Meter: meter.DefaultMeter,
Codec: codec.DefaultCodec,
Tracer: tracer.DefaultTracer,
}
for _, o := range opts {
o(&options)
@@ -54,8 +61,7 @@ func Context(ctx context.Context) Option {
type PublishOptions struct {
// BodyOnly says that only body of the message must be published
BodyOnly bool
// Other options for implementations of the interface
// can be stored in a context
// Context for non default options
Context context.Context
}
@@ -77,10 +83,10 @@ type SubscribeOptions struct {
// AutoAck ack messages if handler returns nil err
AutoAck bool
// Handler executed when errors occur processing messages
// ErrorHandler executed when errors occur processing messages
ErrorHandler Handler
// Subscribers with the same group name
// Group for subscriber, Subscribers with the same group name
// will create a shared subscription where each
// receives a subset of messages.
Group string
@@ -88,8 +94,7 @@ type SubscribeOptions struct {
// BodyOnly says that consumed only body of the message
BodyOnly bool
// Other options for implementations of the interface
// can be stored in a context
// Context is used for non default options
Context context.Context
}
@@ -198,17 +203,10 @@ func SubscribeGroup(name string) SubscribeOption {
}
}
// Registry sets registry option
func Registry(r registry.Registry) Option {
// Register sets register option
func Register(r register.Register) Option {
return func(o *Options) {
o.Registry = r
}
}
// Secure communication with the broker
func Secure(b bool) Option {
return func(o *Options) {
o.Secure = b
o.Register = r
}
}
@@ -226,6 +224,27 @@ func Logger(l logger.Logger) Option {
}
}
// Tracer to be used for tracing
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}
// Meter sets the meter
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// Name sets the name
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
// SubscribeContext set context
func SubscribeContext(ctx context.Context) SubscribeOption {
return func(o *SubscribeOptions) {

View File

@@ -18,6 +18,7 @@ var (
// It supports Request/Response via Transport and Publishing via the Broker.
// It also supports bidirectional streaming of requests.
type Client interface {
Name() string
Init(...Option) error
Options() Options
NewMessage(topic string, msg interface{}, opts ...MessageOption) Message

View File

@@ -49,6 +49,10 @@ func NewClient(opts ...Option) Client {
return &noopClient{opts: NewOptions(opts...)}
}
func (n *noopClient) Name() string {
return n.opts.Name
}
func (n *noopRequest) Service() string {
return n.service
}
@@ -183,7 +187,7 @@ func (n *noopClient) Publish(ctx context.Context, p Message, opts ...PublishOpti
options := NewPublishOptions(opts...)
md, ok := metadata.FromContext(ctx)
md, ok := metadata.FromOutgoingContext(ctx)
if !ok {
md = metadata.New(0)
}

View File

@@ -7,15 +7,18 @@ import (
"github.com/unistack-org/micro/v3/broker"
"github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/network/transport"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
"github.com/unistack-org/micro/v3/router"
"github.com/unistack-org/micro/v3/selector"
"github.com/unistack-org/micro/v3/selector/random"
"github.com/unistack-org/micro/v3/tracer"
)
// Options holds client options
type Options struct {
Name string
// Used to select codec
ContentType string
// Proxy address to send requests via
@@ -28,21 +31,21 @@ type Options struct {
Selector selector.Selector
Transport transport.Transport
Logger logger.Logger
Meter meter.Meter
// Lookup used for looking up routes
Lookup LookupFunc
// Connection Pool
PoolSize int
PoolTTL time.Duration
// Middleware for client
Tracer tracer.Tracer
// Wrapper that used client
Wrappers []Wrapper
// Default Call Options
// CallOptions that used by default
CallOptions CallOptions
// Other options for implementations of the interface
// can be stored in a context
// Context is used for non default options
Context context.Context
}
@@ -81,12 +84,9 @@ type CallOptions struct {
AuthToken bool
// Network to lookup the route within
Network string
// Middleware for low level call func
CallWrappers []CallWrapper
// Other options for implementations of the interface
// can be stored in a context
// Context is uded for non default options
Context context.Context
}
@@ -142,7 +142,6 @@ func NewRequestOptions(opts ...RequestOption) RequestOptions {
type RequestOptions struct {
ContentType string
Stream bool
// Other options for implementations of the interface
// can be stored in a context
Context context.Context
@@ -167,6 +166,8 @@ func NewOptions(opts ...Option) Options {
Selector: random.NewSelector(),
Logger: logger.DefaultLogger,
Broker: broker.DefaultBroker,
Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer,
}
for _, o := range opts {
@@ -183,6 +184,13 @@ func Broker(b broker.Broker) Option {
}
}
// Tracer to be used for tracing
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}
// Logger to be used for log mesages
func Logger(l logger.Logger) Option {
return func(o *Options) {
@@ -190,6 +198,13 @@ func Logger(l logger.Logger) Option {
}
}
// Meter to be used for metrics
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// Codec to be used to encode/decode requests for a given content type
func Codec(contentType string, c codec.Codec) Option {
return func(o *Options) {
@@ -232,11 +247,11 @@ func Transport(t transport.Transport) Option {
}
}
// Registry sets the routers registry
func Registry(r registry.Registry) Option {
// Register sets the routers register
func Register(r register.Register) Option {
return func(o *Options) {
if o.Router != nil {
o.Router.Init(router.Registry(r))
o.Router.Init(router.Register(r))
}
}
}
@@ -277,6 +292,13 @@ func Backoff(fn BackoffFunc) Option {
}
}
// Name sets the client name
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
// Lookup sets the lookup function to use for resolving service names
func Lookup(l LookupFunc) Option {
return func(o *Options) {

View File

@@ -25,7 +25,7 @@ var (
var (
// DefaultMaxMsgSize specifies how much data codec can handle
DefaultMaxMsgSize = 1024 * 1024 * 4 // 4Mb
DefaultMaxMsgSize int = 1024 * 1024 * 4 // 4Mb
DefaultCodec Codec = NewCodec()
)
@@ -62,21 +62,6 @@ type Message struct {
Body []byte
}
// Option func
type Option func(*Options)
// Options contains codec options
type Options struct {
MaxMsgSize int64
}
// MaxMsgSize sets the max message size
func MaxMsgSize(n int64) Option {
return func(o *Options) {
o.MaxMsgSize = n
}
}
// NewMessage creates new codec message
func NewMessage(t MessageType) *Message {
return &Message{Type: t, Header: metadata.New(0)}

View File

@@ -1,6 +1,7 @@
package codec
import (
"encoding/json"
"io"
"io/ioutil"
)
@@ -40,7 +41,7 @@ func (c *noopCodec) ReadBody(conn io.Reader, b interface{}) error {
case *Frame:
v.Data = buf
default:
return ErrInvalidMessage
return json.Unmarshal(buf, v)
}
return nil
@@ -64,7 +65,11 @@ func (c *noopCodec) Write(conn io.Writer, m *Message, b interface{}) error {
case []byte:
v = vb
default:
return ErrInvalidMessage
var err error
v, err = json.Marshal(vb)
if err != nil {
return err
}
}
_, err := conn.Write(v)
return err
@@ -98,30 +103,34 @@ func (c *noopCodec) Marshal(v interface{}) ([]byte, error) {
case *Message:
return ve.Body, nil
}
return nil, ErrInvalidMessage
return json.Marshal(v)
}
func (c *noopCodec) Unmarshal(d []byte, v interface{}) error {
var err error
if v == nil {
return nil
}
switch ve := v.(type) {
case string:
ve = string(d)
return nil
case *string:
*ve = string(d)
return nil
case []byte:
ve = d
return nil
case *[]byte:
*ve = d
return nil
case *Frame:
ve.Data = d
return nil
case *Message:
ve.Body = d
default:
err = ErrInvalidMessage
return nil
}
return err
return json.Unmarshal(d, v)
}

61
codec/options.go Normal file
View File

@@ -0,0 +1,61 @@
package codec
import (
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/tracer"
)
// Option func
type Option func(*Options)
// Options contains codec options
type Options struct {
MaxMsgSize int
Meter meter.Meter
Logger logger.Logger
Tracer tracer.Tracer
}
// MaxMsgSize sets the max message size
func MaxMsgSize(n int) Option {
return func(o *Options) {
o.MaxMsgSize = n
}
}
// Logger sets the logger
func Logger(l logger.Logger) Option {
return func(o *Options) {
o.Logger = l
}
}
// Tracer to be used for tracing
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}
// Meter sets the meter
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
func NewOptions(opts ...Option) Options {
options := Options{
Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer,
MaxMsgSize: DefaultMaxMsgSize,
}
for _, o := range opts {
o(&options)
}
return options
}

View File

@@ -22,6 +22,7 @@ var (
// Config is an interface abstraction for dynamic configuration
type Config interface {
Name() string
// Init the config
Init(opts ...Option) error
// Options in the config

View File

@@ -251,6 +251,10 @@ func (c *defaultConfig) String() string {
return "default"
}
func (c *defaultConfig) Name() string {
return c.opts.Name
}
func NewConfig(opts ...Option) Config {
options := NewOptions(opts...)
if len(options.StructTag) == 0 {

View File

@@ -5,9 +5,12 @@ import (
"github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/tracer"
)
type Options struct {
Name string
AllowFail bool
BeforeLoad []func(context.Context, Config) error
AfterLoad []func(context.Context, Config) error
@@ -15,13 +18,17 @@ type Options struct {
AfterSave []func(context.Context, Config) error
// Struct that holds config data
Struct interface{}
// struct tag name
// StructTag name
StructTag string
// logger that will be used
// Logger that will be used
Logger logger.Logger
// codec that used for load/save
// Meter that will be used
Meter meter.Meter
// Tracer used for trace
Tracer tracer.Tracer
// Codec that used for load/save
Codec codec.Codec
// for alternative data
// Context for alternative data
Context context.Context
}
@@ -30,6 +37,8 @@ type Option func(o *Options)
func NewOptions(opts ...Option) Options {
options := Options{
Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer,
Context: context.Background(),
}
for _, o := range opts {
@@ -88,6 +97,13 @@ func Logger(l logger.Logger) Option {
}
}
// Tracer to be used for tracing
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}
// Struct used as config
func Struct(v interface{}) Option {
return func(o *Options) {
@@ -101,3 +117,10 @@ func StructTag(name string) Option {
o.StructTag = name
}
}
// Name sets the name
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}

22
context.go Normal file
View File

@@ -0,0 +1,22 @@
package micro
import "context"
type serviceKey struct{}
// FromContext retrieves a Service from the Context.
func FromContext(ctx context.Context) (Service, bool) {
if ctx == nil {
return nil, false
}
s, ok := ctx.Value(serviceKey{}).(Service)
return s, ok
}
// NewContext returns a new Context with the Service embedded within it.
func NewContext(ctx context.Context, s Service) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, serviceKey{}, s)
}

View File

@@ -6,11 +6,22 @@ import (
"github.com/unistack-org/micro/v3/client"
)
// Event is used to publish messages to a topic
type Event interface {
// Publish publishes a message to the event topic
Publish(ctx context.Context, msg interface{}, opts ...client.PublishOption) error
}
type event struct {
c client.Client
topic string
}
// NewEvent creates a new event publisher
func NewEvent(topic string, c client.Client) Event {
return &event{c, topic}
}
func (e *event) Publish(ctx context.Context, msg interface{}, opts ...client.PublishOption) error {
return e.c.Publish(ctx, e.c.NewMessage(e.topic, msg), opts...)
}

View File

@@ -33,7 +33,7 @@ type Store interface {
type Event struct {
// ID to uniquely identify the event
ID string
// Topic of event, e.g. "registry.service.created"
// Topic of event, e.g. "register.service.created"
Topic string
// Timestamp of the event
Timestamp time.Time

View File

@@ -1,3 +1,5 @@
// +build ignore
package micro
import (
@@ -7,11 +9,28 @@ import (
"github.com/unistack-org/micro/v3/server"
)
// Function is a one time executing Service
type Function interface {
// Inherits Service interface
Service
// Done signals to complete execution
Done() error
// Handle registers an RPC handler
Handle(v interface{}) error
// Subscribe registers a subscriber
Subscribe(topic string, v interface{}) error
}
type function struct {
cancel context.CancelFunc
Service
}
// NewFunction returns a new Function for a one time executing Service
func NewFunction(opts ...Option) Function {
return newFunction(opts...)
}
func fnHandlerWrapper(f Function) server.HandlerWrapper {
return func(h server.HandlerFunc) server.HandlerFunc {
return func(ctx context.Context, req server.Request, rsp interface{}) error {
@@ -45,7 +64,7 @@ func newFunction(opts ...Option) Function {
// make context the last thing
fopts = append(fopts, Context(ctx))
service := newService(fopts...)
service := &service{opts: NewOptions(opts...)}
fn := &function{
cancel: cancel,

View File

@@ -7,18 +7,18 @@ import (
"sync"
"testing"
rmemory "github.com/unistack-org/micro-registry-memory"
rmemory "github.com/unistack-org/micro-register-memory"
)
func TestFunction(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
r := rmemory.NewRegistry()
r := rmemory.NewRegister()
// create service
fn := NewFunction(
Registry(r),
Register(r),
Name("test.function"),
AfterStart(func() error {
wg.Done()

6
go.mod
View File

@@ -7,15 +7,15 @@ require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/ef-ds/deque v1.0.4
github.com/golang/protobuf v1.4.3
github.com/google/uuid v1.1.5
github.com/google/uuid v1.2.0
github.com/imdario/mergo v0.3.11
github.com/kr/text v0.2.0 // indirect
github.com/miekg/dns v1.1.35
github.com/miekg/dns v1.1.38
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c
github.com/stretchr/testify v1.7.0
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
golang.org/x/net v0.0.0-20201224014010-6772e930b67b
golang.org/x/net v0.0.0-20210119194325-5f4716e94777
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
google.golang.org/protobuf v1.25.0
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect

12
go.sum
View File

@@ -31,16 +31,16 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.1.5 h1:kxhtnfFVi+rYdOALN0B3k9UT86zVJKfBimRaciULW4I=
github.com/google/uuid v1.1.5/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/miekg/dns v1.1.35 h1:oTfOaDH+mZkdcgdIjH6yBajRGtIwcwcaR+rt23ZSrJs=
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.38 h1:MtIY+fmHUVVgv1AXzmKMWcwdCYxTRPG1EDjpqF4RCEw=
github.com/miekg/dns v1.1.38/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
@@ -68,8 +68,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 h1:003p0dJM77cxMSyCPFphvZf/Y5/NXf5fzg6ufd1/Oew=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

View File

@@ -11,6 +11,7 @@ type Option func(*Options)
// Options holds logger options
type Options struct {
Name string
// The logging level the logger should log at. default is `InfoLevel`
Level Level
// fields to always be logged
@@ -72,3 +73,10 @@ func WithContext(ctx context.Context) Option {
o.Context = ctx
}
}
// WithName sets the name
func withName(n string) Option {
return func(o *Options) {
o.Name = n
}
}

View File

@@ -5,45 +5,104 @@ import (
"context"
)
type mdIncomingKey struct{}
type mdOutgoingKey struct{}
type mdKey struct{}
// FromIncomingContext returns metadata from incoming ctx
// returned metadata shoud not be modified or race condition happens
func FromIncomingContext(ctx context.Context) (Metadata, bool) {
if ctx == nil {
return nil, false
}
md, ok := ctx.Value(mdIncomingKey{}).(*rawMetadata)
if !ok {
return nil, false
}
return md.md, ok
}
// FromOutgoingContext returns metadata from outgoing ctx
// returned metadata shoud not be modified or race condition happens
func FromOutgoingContext(ctx context.Context) (Metadata, bool) {
if ctx == nil {
return nil, false
}
md, ok := ctx.Value(mdOutgoingKey{}).(*rawMetadata)
if !ok {
return nil, false
}
return md.md, ok
}
// FromContext returns metadata from the given context
// returned metadata shoud not be modified or race condition happens
//
// Deprecated: use FromIncomingContext or FromOutgoingContext
func FromContext(ctx context.Context) (Metadata, bool) {
if ctx == nil {
return nil, false
}
md, ok := ctx.Value(metadataKey{}).(Metadata)
md, ok := ctx.Value(mdKey{}).(*rawMetadata)
if !ok {
return nil, ok
return nil, false
}
nmd := Copy(md)
return nmd, ok
return md.md, ok
}
// NewContext creates a new context with the given metadata
//
// Deprecated: use NewIncomingContext or NewOutgoingContext
func NewContext(ctx context.Context, md Metadata) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, metadataKey{}, Copy(md))
ctx = context.WithValue(ctx, mdKey{}, &rawMetadata{md})
ctx = context.WithValue(ctx, mdIncomingKey{}, &rawMetadata{})
ctx = context.WithValue(ctx, mdOutgoingKey{}, &rawMetadata{})
return ctx
}
// MergeContext merges metadata to existing metadata, overwriting if specified
func MergeContext(ctx context.Context, pmd Metadata, overwrite bool) context.Context {
// SetOutgoingContext modify outgoing context with given metadata
func SetOutgoingContext(ctx context.Context, md Metadata) bool {
if ctx == nil {
return false
}
if omd, ok := ctx.Value(mdOutgoingKey{}).(*rawMetadata); ok {
omd.md = md
return true
}
return false
}
// SetIncomingContext modify incoming context with given metadata
func SetIncomingContext(ctx context.Context, md Metadata) bool {
if ctx == nil {
return false
}
if omd, ok := ctx.Value(mdIncomingKey{}).(*rawMetadata); ok {
omd.md = md
return true
}
return false
}
// NewIncomingContext creates a new context with incoming metadata attached
func NewIncomingContext(ctx context.Context, md Metadata) context.Context {
if ctx == nil {
ctx = context.Background()
}
md, ok := FromContext(ctx)
if !ok {
return context.WithValue(ctx, metadataKey{}, Copy(pmd))
}
nmd := Copy(md)
for key, val := range pmd {
if _, ok := nmd[key]; ok && !overwrite {
// skip
} else if val != "" {
nmd.Set(key, val)
} else {
nmd.Del(key)
}
}
return context.WithValue(ctx, metadataKey{}, nmd)
ctx = context.WithValue(ctx, mdIncomingKey{}, &rawMetadata{md})
ctx = context.WithValue(ctx, mdOutgoingKey{}, &rawMetadata{})
return ctx
}
// NewOutgoingContext creates a new context with outcoming metadata attached
func NewOutgoingContext(ctx context.Context, md Metadata) context.Context {
if ctx == nil {
ctx = context.Background()
}
ctx = context.WithValue(ctx, mdOutgoingKey{}, &rawMetadata{md})
ctx = context.WithValue(ctx, mdIncomingKey{}, &rawMetadata{})
return ctx
}

View File

@@ -2,21 +2,27 @@
package metadata
import (
"context"
"net/textproto"
"sort"
)
type metadataKey struct{}
var (
// HeaderPrefix for all headers passed
HeaderPrefix = "Micro-"
)
// Metadata is our way of representing request headers internally.
// They're used at the RPC level and translate back and forth
// from Transport headers.
type Metadata map[string]string
type rawMetadata struct {
md Metadata
}
var (
// DefaultMetadataSize used when need to init new Metadata
DefaultMetadataSize = 6
// defaultMetadataSize used when need to init new Metadata
defaultMetadataSize = 2
)
type Iterator struct {
@@ -67,12 +73,9 @@ func (md Metadata) Set(key, val string) {
// Del is used to remove value from metadata
func (md Metadata) Del(key string) {
// fast path
if _, ok := md[key]; ok {
delete(md, key)
} else {
// slow path
delete(md, textproto.CanonicalMIMEHeaderKey(key))
}
delete(md, key)
// slow path
delete(md, textproto.CanonicalMIMEHeaderKey(key))
}
// Copy makes a copy of the metadata
@@ -84,39 +87,25 @@ func Copy(md Metadata) Metadata {
return nmd
}
// Del deletes key from metadata
func Del(ctx context.Context, key string) context.Context {
md, ok := FromContext(ctx)
if !ok {
md = New(0)
}
md.Del(key)
return context.WithValue(ctx, metadataKey{}, md)
}
// Set add key with val to metadata
func Set(ctx context.Context, key, val string) context.Context {
md, ok := FromContext(ctx)
if !ok {
md = New(0)
}
md.Set(key, val)
return context.WithValue(ctx, metadataKey{}, md)
}
// Get returns a single value from metadata in the context
func Get(ctx context.Context, key string) (string, bool) {
md, ok := FromContext(ctx)
if !ok {
return "", ok
}
return md.Get(key)
}
// New return new sized metadata
func New(size int) Metadata {
if size == 0 {
size = DefaultMetadataSize
size = defaultMetadataSize
}
return make(Metadata, size)
}
// Merge merges metadata to existing metadata, overwriting if specified
func Merge(omd Metadata, mmd Metadata, overwrite bool) Metadata {
nmd := Copy(omd)
for key, val := range mmd {
if _, ok := nmd[key]; ok && !overwrite {
// skip
} else if val != "" {
nmd.Set(key, val)
} else {
nmd.Del(key)
}
}
return nmd
}

View File

@@ -3,10 +3,45 @@ package metadata
import (
"context"
"fmt"
"reflect"
"testing"
)
func testCtx(ctx context.Context) {
md := New(2)
md.Set("Key1", "Val1_new")
md.Set("Key3", "Val3")
SetOutgoingContext(ctx, md)
}
func TestPassing(t *testing.T) {
ctx := context.TODO()
md1 := New(2)
md1.Set("Key1", "Val1")
md1.Set("Key2", "Val2")
ctx = NewIncomingContext(ctx, md1)
testCtx(ctx)
md, ok := FromOutgoingContext(ctx)
if !ok {
t.Fatalf("missing metadata from outgoing context")
}
fmt.Printf("%#+v\n", md)
}
func TestMerge(t *testing.T) {
omd := Metadata{
"key1": "val1",
}
mmd := Metadata{
"key2": "val2",
}
nmd := Merge(omd, mmd, true)
if len(nmd) != 2 {
t.Fatalf("merge failed: %v", nmd)
}
}
func TestIterator(t *testing.T) {
md := Metadata{
"1Last": "last",
@@ -18,26 +53,27 @@ func TestIterator(t *testing.T) {
var k, v string
for iter.Next(&k, &v) {
fmt.Printf("k: %s, v: %s\n", k, v)
//fmt.Printf("k: %s, v: %s\n", k, v)
}
}
func TestMedataCanonicalKey(t *testing.T) {
ctx := Set(context.TODO(), "x-request-id", "12345")
v, ok := Get(ctx, "x-request-id")
md := New(1)
md.Set("x-request-id", "12345")
v, ok := md.Get("x-request-id")
if !ok {
t.Fatalf("failed to get x-request-id")
} else if v != "12345" {
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
}
v, ok = Get(ctx, "X-Request-Id")
v, ok = md.Get("X-Request-Id")
if !ok {
t.Fatalf("failed to get x-request-id")
} else if v != "12345" {
t.Fatalf("invalid metadata value: %s != %s", "12345", v)
}
v, ok = Get(ctx, "X-Request-ID")
v, ok = md.Get("X-Request-ID")
if !ok {
t.Fatalf("failed to get x-request-id")
} else if v != "12345" {
@@ -47,9 +83,11 @@ func TestMedataCanonicalKey(t *testing.T) {
}
func TestMetadataSet(t *testing.T) {
ctx := Set(context.TODO(), "Key", "val")
md := New(1)
val, ok := Get(ctx, "Key")
md.Set("Key", "val")
val, ok := md.Get("Key")
if !ok {
t.Fatal("key Key not found")
}
@@ -64,15 +102,8 @@ func TestMetadataDelete(t *testing.T) {
"Baz": "empty",
}
ctx := NewContext(context.TODO(), md)
ctx = Del(ctx, "Baz")
emd, ok := FromContext(ctx)
if !ok {
t.Fatal("key Key not found")
}
_, ok = emd["Baz"]
md.Del("Baz")
_, ok := md.Get("Baz")
if ok {
t.Fatal("key Baz not deleted")
}
@@ -123,42 +154,3 @@ func TestMetadataContext(t *testing.T) {
t.Errorf("Expected metadata length 1 got %d", i)
}
}
func TestMergeContext(t *testing.T) {
type args struct {
existing Metadata
append Metadata
overwrite bool
}
tests := []struct {
name string
args args
want Metadata
}{
{
name: "matching key, overwrite false",
args: args{
existing: Metadata{"Foo": "bar", "Sumo": "demo"},
append: Metadata{"Sumo": "demo2"},
overwrite: false,
},
want: Metadata{"Foo": "bar", "Sumo": "demo"},
},
{
name: "matching key, overwrite true",
args: args{
existing: Metadata{"Foo": "bar", "Sumo": "demo"},
append: Metadata{"Sumo": "demo2"},
overwrite: true,
},
want: Metadata{"Foo": "bar", "Sumo": "demo2"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got, _ := FromContext(MergeContext(NewContext(context.TODO(), tt.args.existing), tt.args.append, tt.args.overwrite)); !reflect.DeepEqual(got, tt.want) {
t.Errorf("MergeContext() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -1,22 +0,0 @@
metrics
=======
The metrics package provides a simple metrics "Reporter" interface which allows the user to submit counters, gauges and timings (along with key/value tags).
Implementations
---------------
* Prometheus (pull): will be first
* Prometheus (push): certainly achievable
* InfluxDB: could quite easily be done
* Telegraf: almost identical to the InfluxDB implementation
* Micro: Could we provide metrics over Micro's server interface?
Todo
----
* Include a handler middleware which uses the Reporter interface to generate per-request level metrics
- Throughput
- Errors
- Duration

34
meter/context.go Normal file
View File

@@ -0,0 +1,34 @@
package meter
import (
"context"
)
type meterKey struct{}
// FromContext get meter from context
func FromContext(ctx context.Context) (Meter, bool) {
if ctx == nil {
return nil, false
}
c, ok := ctx.Value(meterKey{}).(Meter)
return c, ok
}
// NewContext put meter in context
func NewContext(ctx context.Context, c Meter) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, meterKey{}, c)
}
// SetOption returns a function to setup a context with given value
func SetOption(k, v interface{}) Option {
return func(o *Options) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}

View File

@@ -2,25 +2,38 @@
package meter
import (
"io"
"sort"
"time"
"github.com/unistack-org/micro/v3/metadata"
)
var (
DefaultReporter Meter = NewMeter()
// DefaultMeter is the default meter
DefaultMeter Meter = NewMeter()
// DefaultAddress data will be made available on this host:port
DefaultAddress = ":9090"
// DefaultPath the meter endpoint where the Meter data will be made available
DefaultPath = "/metrics"
// timingObjectives is the default spread of stats we maintain for timings / histograms:
//defaultTimingObjectives = map[float64]float64{0.0: 0, 0.5: 0.05, 0.75: 0.04, 0.90: 0.03, 0.95: 0.02, 0.98: 0.001, 1: 0}
// default metric prefix
DefaultMetricPrefix = "micro_"
// default label prefix
DefaultLabelPrefix = "micro_"
)
// Meter is an interface for collecting and instrumenting metrics
type Meter interface {
Name() string
Init(...Option) error
Counter(string, metadata.Metadata) Counter
FloatCounter(string, metadata.Metadata) FloatCounter
Gauge(string, func() float64, metadata.Metadata) Gauge
Set(metadata.Metadata) Meter
Histogram(string, metadata.Metadata) Histogram
Summary(string, metadata.Metadata) Summary
SummaryExt(string, time.Duration, []float64, metadata.Metadata) Summary
Counter(string, ...Option) Counter
FloatCounter(string, ...Option) FloatCounter
Gauge(string, func() float64, ...Option) Gauge
Set(...Option) Meter
Histogram(string, ...Option) Histogram
Summary(string, ...Option) Summary
SummaryExt(string, time.Duration, []float64, ...Option) Summary
Write(io.Writer, bool) error
Options() Options
String() string
}
@@ -60,3 +73,55 @@ type Summary interface {
Update(float64)
UpdateDuration(time.Time)
}
type Labels struct {
keys []string
vals []string
}
func (ls Labels) Len() int {
return len(ls.keys)
}
func (ls Labels) Swap(i, j int) {
ls.keys[i], ls.keys[j] = ls.keys[j], ls.keys[i]
ls.vals[i], ls.vals[j] = ls.vals[j], ls.vals[i]
}
func (ls Labels) Less(i, j int) bool {
return ls.vals[i] < ls.vals[j]
}
func (ls Labels) Sort() {
sort.Sort(ls)
}
func (ls Labels) Append(nls Labels) Labels {
for n := range nls.keys {
ls.keys = append(ls.keys, nls.keys[n])
ls.vals = append(ls.vals, nls.vals[n])
}
return ls
}
type LabelIter struct {
labels Labels
cnt int
cur int
}
func (ls Labels) Iter() *LabelIter {
ls.Sort()
return &LabelIter{labels: ls, cnt: len(ls.keys)}
}
func (iter *LabelIter) Next(k, v *string) bool {
if iter.cur+1 > iter.cnt {
return false
}
*k = iter.labels.keys[iter.cur]
*v = iter.labels.vals[iter.cur]
iter.cur++
return true
}

View File

@@ -11,4 +11,53 @@ func TestNoopMeter(t *testing.T) {
assert.NotNil(t, meter)
assert.Equal(t, "/noop", meter.Options().Path)
assert.Implements(t, new(Meter), meter)
cnt := meter.Counter("counter", Label("server", "noop"))
cnt.Inc()
}
func TestLabels(t *testing.T) {
var ls Labels
ls.keys = []string{"type", "server"}
ls.vals = []string{"noop", "http"}
ls.Sort()
if ls.keys[0] != "server" || ls.vals[0] != "http" {
t.Fatalf("sort error: %v", ls)
}
}
func TestLabelsAppend(t *testing.T) {
var ls Labels
ls.keys = []string{"type", "server"}
ls.vals = []string{"noop", "http"}
var nls Labels
nls.keys = []string{"register"}
nls.vals = []string{"gossip"}
ls = ls.Append(nls)
ls.Sort()
if ls.keys[0] != "register" || ls.vals[0] != "gossip" {
t.Fatalf("append error: %v", ls)
}
}
func TestIterator(t *testing.T) {
var ls Labels
ls.keys = []string{"type", "server", "register"}
ls.vals = []string{"noop", "http", "gossip"}
iter := ls.Iter()
var k, v string
cnt := 0
for iter.Next(&k, &v) {
if cnt == 1 && (k != "server" || v != "http") {
t.Fatalf("iter error: %s != %s || %s != %s", k, "server", v, "http")
}
cnt++
}
}

View File

@@ -1,22 +1,23 @@
package meter
import (
"io"
"time"
"github.com/unistack-org/micro/v3/metadata"
)
// NoopMeter is an noop implementation of Meter
type noopMeter struct {
opts Options
md metadata.Metadata
opts Options
labels Labels
}
// NewMeter returns a configured noop reporter:
func NewMeter(opts ...Option) Meter {
return &noopMeter{
opts: NewOptions(opts...),
}
return &noopMeter{opts: NewOptions(opts...)}
}
func (r *noopMeter) Name() string {
return r.opts.Name
}
// Init initialize options
@@ -28,38 +29,52 @@ func (r *noopMeter) Init(opts ...Option) error {
}
// Counter implements the Meter interface
func (r *noopMeter) Counter(name string, md metadata.Metadata) Counter {
return &noopCounter{}
func (r *noopMeter) Counter(name string, opts ...Option) Counter {
options := Options{}
for _, o := range opts {
o(&options)
}
return &noopCounter{labels: options.Labels}
}
// FloatCounter implements the Meter interface
func (r *noopMeter) FloatCounter(name string, md metadata.Metadata) FloatCounter {
func (r *noopMeter) FloatCounter(name string, opts ...Option) FloatCounter {
return &noopFloatCounter{}
}
// Gauge implements the Meter interface
func (r *noopMeter) Gauge(name string, f func() float64, md metadata.Metadata) Gauge {
func (r *noopMeter) Gauge(name string, f func() float64, opts ...Option) Gauge {
return &noopGauge{}
}
// Summary implements the Meter interface
func (r *noopMeter) Summary(name string, md metadata.Metadata) Summary {
func (r *noopMeter) Summary(name string, opts ...Option) Summary {
return &noopSummary{}
}
// SummaryExt implements the Meter interface
func (r *noopMeter) SummaryExt(name string, window time.Duration, quantiles []float64, md metadata.Metadata) Summary {
func (r *noopMeter) SummaryExt(name string, window time.Duration, quantiles []float64, opts ...Option) Summary {
return &noopSummary{}
}
// Histogram implements the Meter interface
func (r *noopMeter) Histogram(name string, md metadata.Metadata) Histogram {
func (r *noopMeter) Histogram(name string, opts ...Option) Histogram {
return &noopHistogram{}
}
// Set implements the Meter interface
func (r *noopMeter) Set(md metadata.Metadata) Meter {
return &noopMeter{opts: r.opts, md: metadata.Copy(md)}
func (r *noopMeter) Set(opts ...Option) Meter {
m := &noopMeter{opts: r.opts}
for _, o := range opts {
o(&m.opts)
}
return m
}
func (r *noopMeter) Write(w io.Writer, withProcessMetrics bool) error {
return nil
}
// Options implements the Meter interface
@@ -72,7 +87,9 @@ func (r *noopMeter) String() string {
return "noop"
}
type noopCounter struct{}
type noopCounter struct {
labels Labels
}
func (r *noopCounter) Add(int) {

View File

@@ -4,16 +4,6 @@ import (
"context"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/metadata"
)
var (
// The Meter data will be made available on this port
DefaultAddress = ":9090"
// This is the endpoint where the Meter data will be made available ("/metrics" is the default)
DefaultPath = "/metrics"
// timingObjectives is the default spread of stats we maintain for timings / histograms:
//defaultTimingObjectives = map[float64]float64{0.0: 0, 0.5: 0.05, 0.75: 0.04, 0.90: 0.03, 0.95: 0.02, 0.98: 0.001, 1: 0}
)
// Option powers the configuration for metrics implementations:
@@ -21,23 +11,26 @@ type Option func(*Options)
// Options for metrics implementations:
type Options struct {
Address string
Path string
Metadata metadata.Metadata
Name string
Address string
Path string
Labels Labels
//TimingObjectives map[float64]float64
Logger logger.Logger
Context context.Context
Logger logger.Logger
Context context.Context
MetricPrefix string
LabelPrefix string
}
// NewOptions prepares a set of options:
func NewOptions(opt ...Option) Options {
opts := Options{
Address: DefaultAddress,
Metadata: metadata.New(3), // 3 elements contains service name, version and id
Path: DefaultPath,
// TimingObjectives: defaultTimingObjectives,
Context: context.Background(),
Logger: logger.DefaultLogger,
Address: DefaultAddress,
Path: DefaultPath,
Context: context.Background(),
Logger: logger.DefaultLogger,
MetricPrefix: DefaultMetricPrefix,
LabelPrefix: DefaultLabelPrefix,
}
for _, o := range opt {
@@ -47,7 +40,7 @@ func NewOptions(opt ...Option) Options {
return opts
}
// Cntext sets the metrics context
// Context sets the metrics context
func Context(ctx context.Context) Option {
return func(o *Options) {
o.Context = ctx
@@ -68,12 +61,14 @@ func Address(value string) Option {
}
}
// Metadata will be added to every metric
func Metadata(md metadata.Metadata) Option {
/*
// Labels be added to every metric
func Labels(labels []string) Option {
return func(o *Options) {
o.Metadata = metadata.Copy(md)
o.Labels = labels
}
}
*/
/*
// TimingObjectives defines the desired spread of statistics for histogram / timing metrics:
@@ -90,3 +85,18 @@ func Logger(l logger.Logger) Option {
o.Logger = l
}
}
// Label sets the label
func Label(key, val string) Option {
return func(o *Options) {
o.Labels.keys = append(o.Labels.keys, key)
o.Labels.vals = append(o.Labels.vals, val)
}
}
// Name sets the name
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}

232
meter/wrapper/wrapper.go Normal file
View File

@@ -0,0 +1,232 @@
// +build ignore
package wrapper
import (
"context"
"fmt"
"time"
"github.com/unistack-org/micro/v3/client"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/server"
)
type Options struct {
Meter meter.Meter
Name string
Version string
ID string
}
type Option func(*Options)
func ServiceName(name string) Option {
return func(o *Options) {
o.Name = name
}
}
func ServiceVersion(version string) Option {
return func(o *Options) {
o.Version = version
}
}
func ServiceID(id string) Option {
return func(o *Options) {
o.ID = id
}
}
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
type wrapper struct {
options Options
callFunc client.CallFunc
client.Client
}
func NewClientWrapper(opts ...Option) client.Wrapper {
return func(c client.Client) client.Client {
handler := &wrapper{
labels: labels,
Client: c,
}
return handler
}
}
func NewCallWrapper(opts ...Option) client.CallWrapper {
labels := getLabels(opts...)
return func(fn client.CallFunc) client.CallFunc {
handler := &wrapper{
labels: labels,
callFunc: fn,
}
return handler.CallFunc
}
}
func (w *wrapper) CallFunc(ctx context.Context, addr string, req client.Request, rsp interface{}, opts client.CallOptions) error {
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
wlabels := append(w.labels, fmt.Sprintf(`%sendpoint="%s"`, DefaultLabelPrefix, endpoint))
timeCounterSummary := metrics.GetOrCreateSummary(getName("client_request_latency_microseconds", wlabels))
timeCounterHistogram := metrics.GetOrCreateSummary(getName("client_request_duration_seconds", wlabels))
ts := time.Now()
err := w.callFunc(ctx, addr, req, rsp, opts)
te := time.Since(ts)
timeCounterSummary.Update(float64(te.Seconds()))
timeCounterHistogram.Update(te.Seconds())
if err == nil {
metrics.GetOrCreateCounter(getName("client_request_total", append(wlabels, fmt.Sprintf(`%sstatus="success"`, DefaultLabelPrefix)))).Inc()
} else {
metrics.GetOrCreateCounter(getName("client_request_total", append(wlabels, fmt.Sprintf(`%sstatus="failure"`, DefaultLabelPrefix)))).Inc()
}
return err
}
func (w *wrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
wlabels := append(w.labels, fmt.Sprintf(`%sendpoint="%s"`, DefaultLabelPrefix, endpoint))
timeCounterSummary := metrics.GetOrCreateSummary(getName("client_request_latency_microseconds", wlabels))
timeCounterHistogram := metrics.GetOrCreateSummary(getName("client_request_duration_seconds", wlabels))
ts := time.Now()
err := w.Client.Call(ctx, req, rsp, opts...)
te := time.Since(ts)
timeCounterSummary.Update(float64(te.Seconds()))
timeCounterHistogram.Update(te.Seconds())
if err == nil {
metrics.GetOrCreateCounter(getName("client_request_total", append(wlabels, fmt.Sprintf(`%sstatus="success"`, DefaultLabelPrefix)))).Inc()
} else {
metrics.GetOrCreateCounter(getName("client_request_total", append(wlabels, fmt.Sprintf(`%sstatus="failure"`, DefaultLabelPrefix)))).Inc()
}
return err
}
func (w *wrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Stream, error) {
endpoint := fmt.Sprintf("%s.%s", req.Service(), req.Endpoint())
wlabels := append(w.labels, fmt.Sprintf(`%sendpoint="%s"`, DefaultLabelPrefix, endpoint))
timeCounterSummary := metrics.GetOrCreateSummary(getName("client_request_latency_microseconds", wlabels))
timeCounterHistogram := metrics.GetOrCreateSummary(getName("client_request_duration_seconds", wlabels))
ts := time.Now()
stream, err := w.Client.Stream(ctx, req, opts...)
te := time.Since(ts)
timeCounterSummary.Update(float64(te.Seconds()))
timeCounterHistogram.Update(te.Seconds())
if err == nil {
metrics.GetOrCreateCounter(getName("client_request_total", append(wlabels, fmt.Sprintf(`%sstatus="success"`, DefaultLabelPrefix)))).Inc()
} else {
metrics.GetOrCreateCounter(getName("client_request_total", append(wlabels, fmt.Sprintf(`%sstatus="failure"`, DefaultLabelPrefix)))).Inc()
}
return stream, err
}
func (w *wrapper) Publish(ctx context.Context, p client.Message, opts ...client.PublishOption) error {
endpoint := p.Topic()
wlabels := append(w.labels, fmt.Sprintf(`%sendpoint="%s"`, DefaultLabelPrefix, endpoint))
timeCounterSummary := metrics.GetOrCreateSummary(getName("publish_message_latency_microseconds", wlabels))
timeCounterHistogram := metrics.GetOrCreateSummary(getName("publish_message_duration_seconds", wlabels))
ts := time.Now()
err := w.Client.Publish(ctx, p, opts...)
te := time.Since(ts)
timeCounterSummary.Update(float64(te.Seconds()))
timeCounterHistogram.Update(te.Seconds())
if err == nil {
metrics.GetOrCreateCounter(getName("publish_message_total", append(wlabels, fmt.Sprintf(`%sstatus="success"`, DefaultLabelPrefix)))).Inc()
} else {
metrics.GetOrCreateCounter(getName("publish_message_total", append(wlabels, fmt.Sprintf(`%sstatus="failure"`, DefaultLabelPrefix)))).Inc()
}
return err
}
func NewHandlerWrapper(opts ...Option) server.HandlerWrapper {
labels := getLabels(opts...)
handler := &wrapper{
labels: labels,
}
return handler.HandlerFunc
}
func (w *wrapper) HandlerFunc(fn server.HandlerFunc) server.HandlerFunc {
return func(ctx context.Context, req server.Request, rsp interface{}) error {
endpoint := req.Endpoint()
wlabels := append(w.labels, fmt.Sprintf(`%sendpoint="%s"`, DefaultLabelPrefix, endpoint))
timeCounterSummary := metrics.GetOrCreateSummary(getName("server_request_latency_microseconds", wlabels))
timeCounterHistogram := metrics.GetOrCreateSummary(getName("server_request_duration_seconds", wlabels))
ts := time.Now()
err := fn(ctx, req, rsp)
te := time.Since(ts)
timeCounterSummary.Update(float64(te.Seconds()))
timeCounterHistogram.Update(te.Seconds())
if err == nil {
metrics.GetOrCreateCounter(getName("server_request_total", append(wlabels, fmt.Sprintf(`%sstatus="success"`, DefaultLabelPrefix)))).Inc()
} else {
metrics.GetOrCreateCounter(getName("server_request_total", append(wlabels, fmt.Sprintf(`%sstatus="failure"`, DefaultLabelPrefix)))).Inc()
}
return err
}
}
func NewSubscriberWrapper(opts ...Option) server.SubscriberWrapper {
labels := getLabels(opts...)
handler := &wrapper{
labels: labels,
}
return handler.SubscriberFunc
}
func (w *wrapper) SubscriberFunc(fn server.SubscriberFunc) server.SubscriberFunc {
return func(ctx context.Context, msg server.Message) error {
endpoint := msg.Topic()
wlabels := append(w.labels, fmt.Sprintf(`%sendpoint="%s"`, DefaultLabelPrefix, endpoint))
timeCounterSummary := metrics.GetOrCreateSummary(getName("subscribe_message_latency_microseconds", wlabels))
timeCounterHistogram := metrics.GetOrCreateSummary(getName("subscribe_message_duration_seconds", wlabels))
ts := time.Now()
err := fn(ctx, msg)
te := time.Since(ts)
timeCounterSummary.Update(float64(te.Seconds()))
timeCounterHistogram.Update(te.Seconds())
if err == nil {
metrics.GetOrCreateCounter(getName("subscribe_message_total", append(wlabels, fmt.Sprintf(`%sstatus="success"`, DefaultLabelPrefix)))).Inc()
} else {
metrics.GetOrCreateCounter(getName("subscribe_message_total", append(wlabels, fmt.Sprintf(`%sstatus="failure"`, DefaultLabelPrefix)))).Inc()
}
return err
}
}

119
micro.go
View File

@@ -1,119 +0,0 @@
// Package micro is a pluggable framework for microservices
package micro
import (
"context"
"github.com/unistack-org/micro/v3/broker"
"github.com/unistack-org/micro/v3/client"
"github.com/unistack-org/micro/v3/server"
)
type serviceKey struct{}
// Service is an interface that wraps the lower level libraries
// within micro. Its a convenience method for building
// and initialising services.
type Service interface {
// The service name
Name() string
// Init initialises options
Init(...Option) error
// Options returns the current options
Options() Options
// Client is used to call services
Client() client.Client
// Server is for handling requests and events
Server() server.Server
// Broker is for broker usage
Broker() broker.Broker
// Run the service
Run() error
// The service implementation
String() string
}
// Function is a one time executing Service
type Function interface {
// Inherits Service interface
Service
// Done signals to complete execution
Done() error
// Handle registers an RPC handler
Handle(v interface{}) error
// Subscribe registers a subscriber
Subscribe(topic string, v interface{}) error
}
/*
// Type Event is a future type for acting on asynchronous events
type Event interface {
// Publish publishes a message to the event topic
Publish(ctx context.Context, msg interface{}, opts ...client.PublishOption) error
// Subscribe to the event
Subscribe(ctx context.Context, v in
}
// Resource is a future type for defining dependencies
type Resource interface {
// Name of the resource
Name() string
// Type of resource
Type() string
// Method of creation
Create() error
}
*/
// Event is used to publish messages to a topic
type Event interface {
// Publish publishes a message to the event topic
Publish(ctx context.Context, msg interface{}, opts ...client.PublishOption) error
}
var (
// HeaderPrefix for all headers passed
HeaderPrefix = "Micro-"
)
// NewService creates and returns a new Service based on the packages within.
func NewService(opts ...Option) Service {
return newService(opts...)
}
// FromContext retrieves a Service from the Context.
func FromContext(ctx context.Context) (Service, bool) {
if ctx == nil {
return nil, false
}
s, ok := ctx.Value(serviceKey{}).(Service)
return s, ok
}
// NewContext returns a new Context with the Service embedded within it.
func NewContext(ctx context.Context, s Service) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, serviceKey{}, s)
}
// NewFunction returns a new Function for a one time executing Service
func NewFunction(opts ...Option) Function {
return newFunction(opts...)
}
// NewEvent creates a new event publisher
func NewEvent(topic string, c client.Client) Event {
return &event{c, topic}
}
// RegisterHandler is syntactic sugar for registering a handler
func RegisterHandler(s server.Server, h interface{}, opts ...server.HandlerOption) error {
return s.Handle(s.NewHandler(h, opts...))
}
// RegisterSubscriber is syntactic sugar for registering a subscriber
func RegisterSubscriber(topic string, s server.Server, h interface{}, opts ...server.SubscriberOption) error {
return s.Subscribe(s.NewSubscriber(topic, h, opts...))
}

View File

@@ -3,9 +3,11 @@ package network
import (
"github.com/google/uuid"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/network/tunnel"
"github.com/unistack-org/micro/v3/proxy"
"github.com/unistack-org/micro/v3/router"
"github.com/unistack-org/micro/v3/tracer"
)
// Option func
@@ -31,6 +33,10 @@ type Options struct {
Proxy proxy.Proxy
// Logger
Logger logger.Logger
// Meter
Meter meter.Meter
// Tracer
Tracer tracer.Tracer
}
// Id sets the id of the network node
@@ -96,11 +102,34 @@ func Logger(l logger.Logger) Option {
}
}
// DefaultOptions returns network default options
func DefaultOptions() Options {
return Options{
// Meter sets the meter
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// Tracer to be used for tracing
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}
// NewOptions returns network default options
func NewOptions(opts ...Option) Options {
options := Options{
Id: uuid.New().String(),
Name: "go.micro",
Address: ":0",
Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer,
}
for _, o := range opts {
o(&options)
}
return options
}

View File

@@ -7,9 +7,12 @@ import (
"github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/tracer"
)
type Options struct {
Name string
// Addrs is the list of intermediary addresses to connect to
Addrs []string
// Codec is the codec interface to use where headers are not supported
@@ -26,6 +29,10 @@ type Options struct {
Timeout time.Duration
// Logger sets the logger
Logger logger.Logger
// Meter sets the meter
Meter meter.Meter
// Tracer
Tracer tracer.Tracer
// Other options for implementations of the interface
// can be stored in a context
Context context.Context
@@ -35,6 +42,8 @@ type Options struct {
func NewOptions(opts ...Option) Options {
options := Options{
Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer,
Context: context.Background(),
}
@@ -112,6 +121,13 @@ func Logger(l logger.Logger) Option {
}
}
// Meter sets the meter
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// Context sets the context
func Context(ctx context.Context) Option {
return func(o *Options) {
@@ -134,14 +150,6 @@ func Timeout(t time.Duration) Option {
}
}
// Use secure communication. If TLSConfig is not specified we
// use InsecureSkipVerify and generate a self signed cert
func Secure(b bool) Option {
return func(o *Options) {
o.Secure = b
}
}
// TLSConfig to be used for the transport.
func TLSConfig(t *tls.Config) Option {
return func(o *Options) {
@@ -162,3 +170,17 @@ func WithTimeout(d time.Duration) DialOption {
o.Timeout = d
}
}
// Tracer to be used for tracing
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}
// Name sets the name
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}

View File

@@ -41,6 +41,10 @@ func (t *tunBroker) Init(opts ...broker.Option) error {
return nil
}
func (t *tunBroker) Name() string {
return t.opts.Name
}
func (t *tunBroker) Options() broker.Options {
return t.opts
}

View File

@@ -5,7 +5,9 @@ import (
"github.com/google/uuid"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/network/transport"
"github.com/unistack-org/micro/v3/tracer"
)
var (
@@ -20,6 +22,7 @@ type Option func(*Options)
// Options provides network configuration options
type Options struct {
Name string
// Id is tunnel id
Id string
// Address is tunnel address
@@ -32,6 +35,10 @@ type Options struct {
Transport transport.Transport
// Logger
Logger logger.Logger
// Meter
Meter meter.Meter
// Tracer
Tracer tracer.Tracer
}
// DialOption func
@@ -74,6 +81,13 @@ func Logger(l logger.Logger) Option {
}
}
// Meter sets the meter
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// Address sets the tunnel address
func Address(a string) Option {
return func(o *Options) {
@@ -152,9 +166,26 @@ func NewOptions(opts ...Option) Options {
Id: uuid.New().String(),
Address: DefaultAddress,
Token: DefaultToken,
Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer,
}
for _, o := range opts {
o(&options)
}
return options
}
// Tracer to be used for tracing
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}
// Name sets the name
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}

View File

@@ -2,37 +2,44 @@ package micro
import (
"context"
"fmt"
"time"
"github.com/unistack-org/micro/v3/auth"
"github.com/unistack-org/micro/v3/broker"
"github.com/unistack-org/micro/v3/client"
"github.com/unistack-org/micro/v3/config"
"github.com/unistack-org/micro/v3/debug/profile"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/register"
"github.com/unistack-org/micro/v3/router"
"github.com/unistack-org/micro/v3/runtime"
"github.com/unistack-org/micro/v3/selector"
"github.com/unistack-org/micro/v3/server"
"github.com/unistack-org/micro/v3/store"
"github.com/unistack-org/micro/v3/tracer"
// "github.com/unistack-org/micro/v3/debug/profile"
// "github.com/unistack-org/micro/v3/runtime"
)
// Options for micro service
type Options struct {
Auth auth.Auth
Broker broker.Broker
Logger logger.Logger
Configs []config.Config
Client client.Client
Server server.Server
Store store.Store
Registry registry.Registry
Router router.Router
Runtime runtime.Runtime
Profile profile.Profile
Name string
Version string
Metadata metadata.Metadata
Auths []auth.Auth
Brokers []broker.Broker
Loggers []logger.Logger
Meters []meter.Meter
Configs []config.Config
Clients []client.Client
Servers []server.Server
Stores []store.Store
Registers []register.Register
Tracers []tracer.Tracer
Routers []router.Router
// Runtime runtime.Runtime
// Profile profile.Profile
// Before and After funcs
BeforeStart []func(context.Context) error
@@ -48,16 +55,18 @@ type Options struct {
// NewOptions returns new Options filled with defaults and overrided by provided opts
func NewOptions(opts ...Option) Options {
options := Options{
Context: context.Background(),
Server: server.DefaultServer,
Client: client.DefaultClient,
Broker: broker.DefaultBroker,
Registry: registry.DefaultRegistry,
Router: router.DefaultRouter,
Auth: auth.DefaultAuth,
Logger: logger.DefaultLogger,
Configs: []config.Config{config.DefaultConfig},
Store: store.DefaultStore,
Context: context.Background(),
Servers: []server.Server{server.DefaultServer},
Clients: []client.Client{client.DefaultClient},
Brokers: []broker.Broker{broker.DefaultBroker},
Registers: []register.Register{register.DefaultRegister},
Routers: []router.Router{router.DefaultRouter},
Auths: []auth.Auth{auth.DefaultAuth},
Loggers: []logger.Logger{logger.DefaultLogger},
Tracers: []tracer.Tracer{tracer.DefaultTracer},
Meters: []meter.Meter{meter.DefaultMeter},
Configs: []config.Config{config.DefaultConfig},
Stores: []store.Store{store.DefaultStore},
//Runtime runtime.Runtime
//Profile profile.Profile
}
@@ -70,264 +79,599 @@ func NewOptions(opts ...Option) Options {
}
// Option func
type Option func(*Options)
type Option func(*Options) error
// Broker to be used for service
func Broker(b broker.Broker) Option {
return func(o *Options) {
o.Broker = b
if o.Client != nil {
// Update Client and Server
o.Client.Init(client.Broker(b))
// Broker to be used for client and server
func Broker(b broker.Broker, opts ...BrokerOption) Option {
return func(o *Options) error {
var err error
bopts := brokerOptions{}
for _, opt := range opts {
opt(&bopts)
}
if o.Server != nil {
o.Server.Init(server.Broker(b))
all := false
if len(opts) == 0 {
all = true
}
for _, srv := range o.Servers {
for _, os := range bopts.servers {
if srv.Name() == os || all {
if err = srv.Init(server.Broker(b)); err != nil {
return err
}
}
}
}
for _, cli := range o.Clients {
for _, oc := range bopts.clients {
if cli.Name() == oc || all {
if err = cli.Init(client.Broker(b)); err != nil {
return err
}
}
}
}
return nil
}
}
type brokerOptions struct {
servers []string
clients []string
}
type BrokerOption func(*brokerOptions)
func BrokerClient(n string) BrokerOption {
return func(o *brokerOptions) {
o.clients = append(o.clients, n)
}
}
func BrokerServer(n string) BrokerOption {
return func(o *brokerOptions) {
o.servers = append(o.servers, n)
}
}
// Client to be used for service
func Client(c client.Client) Option {
return func(o *Options) {
o.Client = c
func Client(c ...client.Client) Option {
return func(o *Options) error {
o.Clients = c
return nil
}
}
// Clients to be used for service
func Clients(c ...client.Client) Option {
return func(o *Options) error {
o.Clients = c
return nil
}
}
// Context specifies a context for the service.
// Can be used to signal shutdown of the service and for extra option values.
func Context(ctx context.Context) Option {
return func(o *Options) {
return func(o *Options) error {
// TODO: Pass context to underline stuff ?
o.Context = ctx
return nil
}
}
/*
// Profile to be used for debug profile
func Profile(p profile.Profile) Option {
return func(o *Options) {
o.Profile = p
}
}
*/
// Server to be used for service
func Server(s server.Server) Option {
return func(o *Options) {
o.Server = s
func Server(s ...server.Server) Option {
return func(o *Options) error {
o.Servers = s
return nil
}
}
// Servers to be used for service
func Servers(s ...server.Server) Option {
return func(o *Options) error {
o.Servers = s
return nil
}
}
// Store sets the store to use
func Store(s store.Store) Option {
return func(o *Options) {
o.Store = s
func Store(s ...store.Store) Option {
return func(o *Options) error {
o.Stores = s
return nil
}
}
// Stores sets the store to use
func Stores(s ...store.Store) Option {
return func(o *Options) error {
o.Stores = s
return nil
}
}
// Logger set the logger to use
func Logger(l logger.Logger) Option {
return func(o *Options) {
o.Logger = l
func Logger(l logger.Logger, opts ...LoggerOption) Option {
return func(o *Options) error {
var err error
lopts := loggerOptions{}
for _, opt := range opts {
opt(&lopts)
}
all := false
if len(opts) == 0 {
all = true
}
for _, srv := range o.Servers {
for _, os := range lopts.servers {
if srv.Name() == os || all {
if err = srv.Init(server.Logger(l)); err != nil {
return err
}
}
}
}
for _, cli := range o.Clients {
for _, oc := range lopts.clients {
if cli.Name() == oc || all {
if err = cli.Init(client.Logger(l)); err != nil {
return err
}
}
}
}
for _, brk := range o.Brokers {
for _, ob := range lopts.brokers {
if brk.Name() == ob || all {
if err = brk.Init(broker.Logger(l)); err != nil {
return err
}
}
}
}
for _, reg := range o.Registers {
for _, or := range lopts.registers {
if reg.Name() == or || all {
if err = reg.Init(register.Logger(l)); err != nil {
return err
}
}
}
}
for _, str := range o.Stores {
for _, or := range lopts.stores {
if str.Name() == or || all {
if err = str.Init(store.Logger(l)); err != nil {
return err
}
}
}
}
for _, mtr := range o.Meters {
for _, or := range lopts.meters {
if mtr.Name() == or || all {
if err = mtr.Init(meter.Logger(l)); err != nil {
return err
}
}
}
}
for _, trc := range o.Tracers {
for _, ot := range lopts.tracers {
if trc.Name() == ot || all {
if err = trc.Init(tracer.Logger(l)); err != nil {
return err
}
}
}
}
return nil
}
}
// Registry sets the registry for the service
type LoggerOption func(*loggerOptions)
type loggerOptions struct {
servers []string
clients []string
brokers []string
registers []string
stores []string
meters []string
tracers []string
}
/*
func LoggerServer(n string) LoggerOption {
}
*/
// Meter set the meter to use
func Meter(m ...meter.Meter) Option {
return func(o *Options) error {
o.Meters = m
return nil
}
}
// Meters set the meter to use
func Meters(m ...meter.Meter) Option {
return func(o *Options) error {
o.Meters = m
return nil
}
}
// Register sets the register for the service
// and the underlying components
func Registry(r registry.Registry) Option {
return func(o *Options) {
o.Registry = r
if o.Router != nil {
// Update router
o.Router.Init(router.Registry(r))
func Register(r register.Register, opts ...RegisterOption) Option {
return func(o *Options) error {
var err error
ropts := registerOptions{}
for _, opt := range opts {
opt(&ropts)
}
if o.Server != nil {
// Update server
o.Server.Init(server.Registry(r))
all := false
if len(opts) == 0 {
all = true
}
if o.Broker != nil {
// Update Broker
o.Broker.Init(broker.Registry(r))
for _, rtr := range o.Routers {
for _, os := range ropts.routers {
if rtr.Name() == os || all {
if err = rtr.Init(router.Register(r)); err != nil {
return err
}
}
}
}
for _, srv := range o.Servers {
for _, os := range ropts.servers {
if srv.Name() == os || all {
if err = srv.Init(server.Register(r)); err != nil {
return err
}
}
}
}
for _, brk := range o.Brokers {
for _, os := range ropts.brokers {
if brk.Name() == os || all {
if err = brk.Init(broker.Register(r)); err != nil {
return err
}
}
}
}
return nil
}
}
// Tracer sets the tracer for the service
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
if o.Server != nil {
//todo client trace
o.Server.Init(server.Tracer(t))
}
type registerOptions struct {
routers []string
servers []string
brokers []string
}
type RegisterOption func(*registerOptions)
func RegisterRouter(n string) RegisterOption {
return func(o *registerOptions) {
o.routers = append(o.routers, n)
}
}
func RegisterServer(n string) RegisterOption {
return func(o *registerOptions) {
o.servers = append(o.servers, n)
}
}
func RegisterBroker(n string) RegisterOption {
return func(o *registerOptions) {
o.brokers = append(o.brokers, n)
}
}
func Tracer(t tracer.Tracer, opts ...TracerOption) Option {
return func(o *Options) error {
var err error
topts := tracerOptions{}
for _, opt := range opts {
opt(&topts)
}
all := false
if len(opts) == 0 {
all = true
}
for _, srv := range o.Servers {
for _, os := range topts.servers {
if srv.Name() == os || all {
if err = srv.Init(server.Tracer(t)); err != nil {
return err
}
}
}
}
for _, cli := range o.Clients {
for _, os := range topts.clients {
if cli.Name() == os || all {
if err = cli.Init(client.Tracer(t)); err != nil {
return err
}
}
}
}
for _, str := range o.Stores {
for _, os := range topts.stores {
if str.Name() == os || all {
if err = str.Init(store.Tracer(t)); err != nil {
return err
}
}
}
}
for _, brk := range o.Brokers {
for _, os := range topts.brokers {
if brk.Name() == os || all {
if err = brk.Init(broker.Tracer(t)); err != nil {
return err
}
}
}
}
return nil
}
}
type tracerOptions struct {
clients []string
servers []string
brokers []string
stores []string
}
type TracerOption func(*tracerOptions)
func TracerClient(n string) TracerOption {
return func(o *tracerOptions) {
o.clients = append(o.clients, n)
}
}
func TracerServer(n string) TracerOption {
return func(o *tracerOptions) {
o.servers = append(o.servers, n)
}
}
func TracerBroker(n string) TracerOption {
return func(o *tracerOptions) {
o.brokers = append(o.brokers, n)
}
}
func TracerStore(n string) TracerOption {
return func(o *tracerOptions) {
o.stores = append(o.stores, n)
}
}
/*
// Auth sets the auth for the service
func Auth(a auth.Auth) Option {
return func(o *Options) {
return func(o *Options) error {
o.Auth = a
if o.Server != nil {
o.Server.Init(server.Auth(a))
}
return nil
}
}
*/
// Config sets the config for the service
func Config(c ...config.Config) Option {
return func(o *Options) error {
o.Configs = c
return nil
}
}
// Configs sets the configs for the service
func Configs(c ...config.Config) Option {
return func(o *Options) {
return func(o *Options) error {
o.Configs = c
return nil
}
}
/*
// Selector sets the selector for the service client
func Selector(s selector.Selector) Option {
return func(o *Options) {
return func(o *Options) error {
if o.Client != nil {
o.Client.Init(client.Selector(s))
}
return nil
}
}
*/
/*
// Runtime sets the runtime
func Runtime(r runtime.Runtime) Option {
return func(o *Options) {
o.Runtime = r
}
}
*/
// Router sets the router
func Router(r router.Router) Option {
return func(o *Options) {
o.Router = r
// Update client
if o.Client != nil {
o.Client.Init(client.Router(r))
func Router(r router.Router, opts ...RouterOption) Option {
return func(o *Options) error {
var err error
ropts := routerOptions{}
for _, opt := range opts {
opt(&ropts)
}
all := false
if len(opts) == 0 {
all = true
}
for _, cli := range o.Clients {
for _, os := range ropts.clients {
if cli.Name() == os || all {
if err = cli.Init(client.Router(r)); err != nil {
return err
}
}
}
}
return nil
}
}
type routerOptions struct {
clients []string
}
type RouterOption func(*routerOptions)
func RouterClient(n string) RouterOption {
return func(o *routerOptions) {
o.clients = append(o.clients, n)
}
}
// Address sets the address of the server
func Address(addr string) Option {
return func(o *Options) {
if o.Server != nil {
o.Server.Init(server.Address(addr))
return func(o *Options) error {
switch len(o.Servers) {
case 0:
return fmt.Errorf("cant set address on nil server")
case 1:
break
default:
return fmt.Errorf("cant set same address for multiple servers")
}
return o.Servers[0].Init(server.Address(addr))
}
}
// Name of the service
func Name(n string) Option {
return func(o *Options) {
if o.Server != nil {
o.Server.Init(server.Name(n))
}
return func(o *Options) error {
o.Name = n
return nil
}
}
// Version of the service
func Version(v string) Option {
return func(o *Options) {
if o.Server != nil {
o.Server.Init(server.Version(v))
}
return func(o *Options) error {
o.Version = v
return nil
}
}
// Metadata associated with the service
func Metadata(md metadata.Metadata) Option {
return func(o *Options) {
if o.Server != nil {
o.Server.Init(server.Metadata(md))
}
return func(o *Options) error {
o.Metadata = metadata.Copy(md)
return nil
}
}
// RegisterTTL specifies the TTL to use when registering the service
func RegisterTTL(t time.Duration) Option {
return func(o *Options) {
if o.Server != nil {
o.Server.Init(server.RegisterTTL(t))
func RegisterTTL(td time.Duration, opts ...RegisterOption) Option {
return func(o *Options) error {
var err error
ropts := registerOptions{}
for _, opt := range opts {
opt(&ropts)
}
all := false
if len(opts) == 0 {
all = true
}
for _, srv := range o.Servers {
for _, os := range ropts.servers {
if srv.Name() == os || all {
if err = srv.Init(server.RegisterTTL(td)); err != nil {
return err
}
}
}
}
return nil
}
}
// RegisterInterval specifies the interval on which to re-register
func RegisterInterval(t time.Duration) Option {
return func(o *Options) {
if o.Server != nil {
o.Server.Init(server.RegisterInterval(t))
func RegisterInterval(td time.Duration, opts ...RegisterOption) Option {
return func(o *Options) error {
var err error
ropts := registerOptions{}
for _, opt := range opts {
opt(&ropts)
}
}
}
// WrapClient is a convenience method for wrapping a Client with
// some middleware component. A list of wrappers can be provided.
// Wrappers are applied in reverse order so the last is executed first.
func WrapClient(w ...client.Wrapper) Option {
return func(o *Options) {
// apply in reverse
for i := len(w); i > 0; i-- {
o.Client = w[i-1](o.Client)
all := false
if len(opts) == 0 {
all = true
}
}
}
// WrapCall is a convenience method for wrapping a Client CallFunc
func WrapCall(w ...client.CallWrapper) Option {
return func(o *Options) {
o.Client.Init(client.WrapCall(w...))
}
}
// WrapHandler adds a handler Wrapper to a list of options passed into the server
func WrapHandler(w ...server.HandlerWrapper) Option {
return func(o *Options) {
var wrappers []server.Option
for _, wrap := range w {
wrappers = append(wrappers, server.WrapHandler(wrap))
for _, srv := range o.Servers {
for _, os := range ropts.servers {
if srv.Name() == os || all {
if err = srv.Init(server.RegisterInterval(td)); err != nil {
return err
}
}
}
}
// Init once
o.Server.Init(wrappers...)
}
}
// WrapSubscriber adds a subscriber Wrapper to a list of options passed into the server
func WrapSubscriber(w ...server.SubscriberWrapper) Option {
return func(o *Options) {
var wrappers []server.Option
for _, wrap := range w {
wrappers = append(wrappers, server.WrapSubscriber(wrap))
}
// Init once
o.Server.Init(wrappers...)
return nil
}
}
// BeforeStart run funcs before service starts
func BeforeStart(fn func(context.Context) error) Option {
return func(o *Options) {
return func(o *Options) error {
o.BeforeStart = append(o.BeforeStart, fn)
return nil
}
}
// BeforeStop run funcs before service stops
func BeforeStop(fn func(context.Context) error) Option {
return func(o *Options) {
return func(o *Options) error {
o.BeforeStop = append(o.BeforeStop, fn)
return nil
}
}
// AfterStart run funcs after service starts
func AfterStart(fn func(context.Context) error) Option {
return func(o *Options) {
return func(o *Options) error {
o.AfterStart = append(o.AfterStart, fn)
return nil
}
}
// AfterStop run funcs after service stops
func AfterStop(fn func(context.Context) error) Option {
return func(o *Options) {
return func(o *Options) error {
o.AfterStop = append(o.AfterStop, fn)
return nil
}
}

View File

@@ -4,7 +4,9 @@ package proxy
import (
"github.com/unistack-org/micro/v3/client"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/router"
"github.com/unistack-org/micro/v3/tracer"
)
// Options for proxy
@@ -19,11 +21,29 @@ type Options struct {
Links map[string]client.Client
// Logger
Logger logger.Logger
// Meter
Meter meter.Meter
// Tracer
Tracer tracer.Tracer
}
// Option func signature
type Option func(o *Options)
func NewOptions(opts ...Option) Options {
options := Options{
Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer,
}
for _, o := range opts {
o(&options)
}
return options
}
// WithEndpoint sets a proxy endpoint
func WithEndpoint(e string) Option {
return func(o *Options) {
@@ -52,6 +72,13 @@ func WithLogger(l logger.Logger) Option {
}
}
// WithMeter specifies the meter to use
func WithMeter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// WithLink sets a link for outbound requests
func WithLink(name string, c client.Client) Option {
return func(o *Options) {
@@ -61,3 +88,10 @@ func WithLink(name string, c client.Client) Option {
o.Links[name] = c
}
}
// Tracer to be used for tracing
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}

View File

@@ -1,26 +1,26 @@
package registry
package register
import (
"context"
)
type registryKey struct{}
type registerKey struct{}
// FromContext get registry from context
func FromContext(ctx context.Context) (Registry, bool) {
// FromContext get register from context
func FromContext(ctx context.Context) (Register, bool) {
if ctx == nil {
return nil, false
}
c, ok := ctx.Value(registryKey{}).(Registry)
c, ok := ctx.Value(registerKey{}).(Register)
return c, ok
}
// NewContext put registry in context
func NewContext(ctx context.Context, c Registry) context.Context {
// NewContext put register in context
func NewContext(ctx context.Context, c Register) context.Context {
if ctx == nil {
ctx = context.Background()
}
return context.WithValue(ctx, registryKey{}, c)
return context.WithValue(ctx, registerKey{}, c)
}
// SetOption returns a function to setup a context with given value

View File

@@ -1,4 +1,4 @@
package registry
package register
import (
"fmt"

View File

@@ -1,4 +1,4 @@
package registry
package register
import (
"context"

85
register/noop.go Normal file
View File

@@ -0,0 +1,85 @@
package register
import (
"context"
)
type noopRegister struct {
opts Options
}
func (n *noopRegister) Name() string {
return n.opts.Name
}
// Init initialize register
func (n *noopRegister) Init(opts ...Option) error {
for _, o := range opts {
o(&n.opts)
}
return nil
}
// Options returns options struct
func (n *noopRegister) Options() Options {
return n.opts
}
// Connect opens connection to register
func (n *noopRegister) Connect(ctx context.Context) error {
return nil
}
// Disconnect close connection to register
func (n *noopRegister) Disconnect(ctx context.Context) error {
return nil
}
// Register registers service
func (n *noopRegister) Register(ctx context.Context, svc *Service, opts ...RegisterOption) error {
return nil
}
// Deregister deregisters service
func (n *noopRegister) Deregister(ctx context.Context, svc *Service, opts ...DeregisterOption) error {
return nil
}
// LookupService returns servive info
func (n *noopRegister) LookupService(ctx context.Context, name string, opts ...LookupOption) ([]*Service, error) {
return []*Service{}, nil
}
// ListServices listing services
func (n *noopRegister) ListServices(ctx context.Context, opts ...ListOption) ([]*Service, error) {
return []*Service{}, nil
}
// Watch is used to watch for service changes
func (n *noopRegister) Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) {
return &noopWatcher{done: make(chan struct{}), opts: NewWatchOptions(opts...)}, nil
}
// String returns register string representation
func (n *noopRegister) String() string {
return "noop"
}
type noopWatcher struct {
opts WatchOptions
done chan struct{}
}
func (n *noopWatcher) Next() (*Result, error) {
<-n.done
return nil, ErrWatcherStopped
}
func (n *noopWatcher) Stop() {
close(n.done)
}
// NewRegister returns a new noop register
func NewRegister(opts ...Option) Register {
return &noopRegister{opts: NewOptions(opts...)}
}

View File

@@ -1,4 +1,4 @@
package registry
package register
import (
"context"
@@ -6,16 +6,22 @@ import (
"time"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/tracer"
)
type Options struct {
Name string
Addrs []string
Timeout time.Duration
Secure bool
TLSConfig *tls.Config
// Logger imp
// Logger that will be used
Logger logger.Logger
// Meter that will be used
Meter meter.Meter
// Tracer
Tracer tracer.Tracer
// Other options for implementations of the interface
// can be stored in a context
Context context.Context
@@ -24,6 +30,8 @@ type Options struct {
func NewOptions(opts ...Option) Options {
options := Options{
Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer,
Context: context.Background(),
}
for _, o := range opts {
@@ -95,14 +103,14 @@ func NewDeregisterOptions(opts ...DeregisterOption) DeregisterOptions {
return options
}
type GetOptions struct {
type LookupOptions struct {
Context context.Context
// Domain to scope the request to
Domain string
}
func NewGetOptions(opts ...GetOption) GetOptions {
options := GetOptions{
func NewLookupOptions(opts ...LookupOption) LookupOptions {
options := LookupOptions{
Domain: DefaultDomain,
Context: context.Background(),
}
@@ -129,7 +137,7 @@ func NewListOptions(opts ...ListOption) ListOptions {
return options
}
// Addrs is the registry addresses to use
// Addrs is the register addresses to use
func Addrs(addrs ...string) Option {
return func(o *Options) {
o.Addrs = addrs
@@ -142,13 +150,6 @@ func Timeout(t time.Duration) Option {
}
}
// Secure communication with the registry
func Secure(b bool) Option {
return func(o *Options) {
o.Secure = b
}
}
// Logger sets the logger
func Logger(l logger.Logger) Option {
return func(o *Options) {
@@ -156,6 +157,20 @@ func Logger(l logger.Logger) Option {
}
}
// Meter sets the meter
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// Tracer sets the tracer
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}
// Context sets the context
func Context(ctx context.Context) Option {
return func(o *Options) {
@@ -231,14 +246,14 @@ func DeregisterDomain(d string) DeregisterOption {
}
}
func GetContext(ctx context.Context) GetOption {
return func(o *GetOptions) {
func LookupContext(ctx context.Context) LookupOption {
return func(o *LookupOptions) {
o.Context = ctx
}
}
func GetDomain(d string) GetOption {
return func(o *GetOptions) {
func LookupDomain(d string) LookupOption {
return func(o *LookupOptions) {
o.Domain = d
}
}
@@ -254,3 +269,10 @@ func ListDomain(d string) ListOption {
o.Domain = d
}
}
// Name sets the name
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}

View File

@@ -1,5 +1,5 @@
// Package registry is an interface for service discovery
package registry
// Package register is an interface for service discovery
package register
import (
"context"
@@ -16,31 +16,32 @@ const (
)
var (
// DefaultRegistry is the global default registry
DefaultRegistry Registry = NewRegistry()
// ErrNotFound returned when GetService is called and no services found
// DefaultRegister is the global default register
DefaultRegister Register = NewRegister()
// ErrNotFound returned when LookupService is called and no services found
ErrNotFound = errors.New("service not found")
// ErrWatcherStopped returned when when watcher is stopped
ErrWatcherStopped = errors.New("watcher stopped")
)
// Registry provides an interface for service discovery
// Register provides an interface for service discovery
// and an abstraction over varying implementations
// {consul, etcd, zookeeper, ...}
type Registry interface {
type Register interface {
Name() string
Init(...Option) error
Options() Options
Connect(context.Context) error
Disconnect(context.Context) error
Register(context.Context, *Service, ...RegisterOption) error
Deregister(context.Context, *Service, ...DeregisterOption) error
GetService(context.Context, string, ...GetOption) ([]*Service, error)
LookupService(context.Context, string, ...LookupOption) ([]*Service, error)
ListServices(context.Context, ...ListOption) ([]*Service, error)
Watch(context.Context, ...WatchOption) (Watcher, error)
String() string
}
// Service holds service registry info
// Service holds service register info
type Service struct {
Name string `json:"name"`
Version string `json:"version"`
@@ -49,14 +50,14 @@ type Service struct {
Nodes []*Node `json:"nodes"`
}
// Node holds node registry info
// Node holds node register info
type Node struct {
Id string `json:"id"`
Address string `json:"address"`
Metadata metadata.Metadata `json:"metadata"`
}
// Endpoint holds endpoint registry info
// Endpoint holds endpoint register info
type Endpoint struct {
Name string `json:"name"`
Request *Value `json:"request"`
@@ -83,8 +84,8 @@ type WatchOption func(*WatchOptions)
// DeregisterOption option is used to deregister service
type DeregisterOption func(*DeregisterOptions)
// GetOption option is used to get service
type GetOption func(*GetOptions)
// LookupOption option is used to get service
type LookupOption func(*LookupOptions)
// ListOption option is used to list services
type ListOption func(*ListOptions)

View File

@@ -1,9 +1,9 @@
package registry
package register
import "time"
// Watcher is an interface that returns updates
// about services within the registry.
// about services within the register.
type Watcher interface {
// Next is a blocking call
Next() (*Result, error)
@@ -17,7 +17,7 @@ type Result struct {
Service *Service
}
// EventType defines registry event type
// EventType defines register event type
type EventType int
const (
@@ -43,14 +43,14 @@ func (t EventType) String() string {
}
}
// Event is registry event
// Event is register event
type Event struct {
// Id is registry id
// Id is register id
Id string
// Type defines type of event
Type EventType
// Timestamp is event timestamp
Timestamp time.Time
// Service is registry service
// Service is register service
Service *Service
}

View File

@@ -1,81 +0,0 @@
package registry
import (
"context"
)
type noopRegistry struct {
opts Options
}
// Init initialize registry
func (n *noopRegistry) Init(opts ...Option) error {
for _, o := range opts {
o(&n.opts)
}
return nil
}
// Options returns options struct
func (n *noopRegistry) Options() Options {
return n.opts
}
// Connect opens connection to registry
func (n *noopRegistry) Connect(ctx context.Context) error {
return nil
}
// Disconnect close connection to registry
func (n *noopRegistry) Disconnect(ctx context.Context) error {
return nil
}
// Register registers service
func (n *noopRegistry) Register(ctx context.Context, svc *Service, opts ...RegisterOption) error {
return nil
}
// Deregister deregisters service
func (n *noopRegistry) Deregister(ctx context.Context, svc *Service, opts ...DeregisterOption) error {
return nil
}
// GetService returns servive info
func (n *noopRegistry) GetService(ctx context.Context, name string, opts ...GetOption) ([]*Service, error) {
return []*Service{}, nil
}
// ListServices listing services
func (n *noopRegistry) ListServices(ctx context.Context, opts ...ListOption) ([]*Service, error) {
return []*Service{}, nil
}
// Watch is used to watch for service changes
func (n *noopRegistry) Watch(ctx context.Context, opts ...WatchOption) (Watcher, error) {
return &noopWatcher{done: make(chan struct{}), opts: NewWatchOptions(opts...)}, nil
}
// String returns registry string representation
func (n *noopRegistry) String() string {
return "noop"
}
type noopWatcher struct {
opts WatchOptions
done chan struct{}
}
func (n *noopWatcher) Next() (*Result, error) {
<-n.done
return nil, ErrWatcherStopped
}
func (n *noopWatcher) Stop() {
close(n.done)
}
// NewRegistry returns a new noop registry
func NewRegistry(opts ...Option) Registry {
return &noopRegistry{opts: NewOptions(opts...)}
}

View File

@@ -1,22 +1,22 @@
// Package registry resolves names using the micro registry
package registry
// Package register resolves names using the micro register
package register
import (
"context"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
"github.com/unistack-org/micro/v3/resolver"
)
// Resolver is a registry network resolver
// Resolver is a register network resolver
type Resolver struct {
// Registry is the registry to use otherwise we use the defaul
Registry registry.Registry
// Register is the register to use otherwise we use the defaul
Register register.Register
}
// Resolve assumes ID is a domain name e.g micro.mu
func (r *Resolver) Resolve(name string) ([]*resolver.Record, error) {
services, err := r.Registry.GetService(context.TODO(), name)
services, err := r.Register.LookupService(context.TODO(), name)
if err != nil {
return nil, err
}

View File

@@ -5,11 +5,12 @@ import (
"github.com/google/uuid"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
)
// Options are router options
type Options struct {
Name string
// Id is router id
Id string
// Address is router address
@@ -18,8 +19,8 @@ type Options struct {
Gateway string
// Network is network address
Network string
// Registry is the local registry
Registry registry.Registry
// Register is the local register
Register register.Register
// Precache routes
Precache bool
// Logger
@@ -63,10 +64,10 @@ func Logger(l logger.Logger) Option {
}
}
// Registry sets the local registry
func Registry(r registry.Registry) Option {
// Register sets the local register
func Register(r register.Register) Option {
return func(o *Options) {
o.Registry = r
o.Register = r
}
}
@@ -77,12 +78,19 @@ func Precache() Option {
}
}
// Name of the router
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
// NewOptions returns router default options
func NewOptions(opts ...Option) Options {
options := Options{
Id: uuid.New().String(),
Network: DefaultNetwork,
Registry: registry.DefaultRegistry,
Register: register.DefaultRegister,
Logger: logger.DefaultLogger,
Context: context.Background(),
}

View File

@@ -18,6 +18,7 @@ var (
// Router is an interface for a routing control plane
type Router interface {
Name() string
// Init initializes the router with options
Init(...Option) error
// Options returns the router options

View File

@@ -3,13 +3,13 @@ package server
import (
"reflect"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
)
type rpcHandler struct {
name string
handler interface{}
endpoints []*registry.Endpoint
endpoints []*register.Endpoint
opts HandlerOptions
}
@@ -20,10 +20,10 @@ func newRpcHandler(handler interface{}, opts ...HandlerOption) Handler {
hdlr := reflect.ValueOf(handler)
name := reflect.Indirect(hdlr).Type().Name()
var endpoints []*registry.Endpoint
var endpoints []*register.Endpoint
for m := 0; m < typ.NumMethod(); m++ {
if e := registry.ExtractEndpoint(typ.Method(m)); e != nil {
if e := register.ExtractEndpoint(typ.Method(m)); e != nil {
e.Name = name + "." + e.Name
for k, v := range options.Metadata[e.Name] {
@@ -50,7 +50,7 @@ func (r *rpcHandler) Handler() interface{} {
return r.handler
}
func (r *rpcHandler) Endpoints() []*registry.Endpoint {
func (r *rpcHandler) Endpoints() []*register.Endpoint {
return r.endpoints
}

View File

@@ -13,7 +13,7 @@ import (
"github.com/unistack-org/micro/v3/broker"
"github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
)
var (
@@ -34,7 +34,7 @@ const (
type noopServer struct {
h Handler
opts Options
rsvc *registry.Service
rsvc *register.Service
handlers map[string]Handler
subscribers map[*subscriber][]broker.Subscriber
registered bool
@@ -64,6 +64,10 @@ func (n *noopServer) Handle(handler Handler) error {
return nil
}
func (n *noopServer) Name() string {
return n.opts.Name
}
func (n *noopServer) Subscribe(sb Subscriber) error {
sub, ok := sb.(*subscriber)
if !ok {
@@ -137,10 +141,10 @@ func (n *noopServer) Register() error {
}
var err error
var service *registry.Service
var service *register.Service
var cacheService bool
service, err = NewRegistryService(n)
service, err = NewRegisterService(n)
if err != nil {
return err
}
@@ -168,7 +172,7 @@ func (n *noopServer) Register() error {
return subscriberList[i].topic > subscriberList[j].topic
})
endpoints := make([]*registry.Endpoint, 0, len(handlerList)+len(subscriberList))
endpoints := make([]*register.Endpoint, 0, len(handlerList)+len(subscriberList))
for _, h := range handlerList {
endpoints = append(endpoints, n.handlers[h].Endpoints()...)
}
@@ -187,7 +191,7 @@ func (n *noopServer) Register() error {
if !registered {
if config.Logger.V(logger.InfoLevel) {
config.Logger.Infof(n.opts.Context, "registry [%s] Registering node: %s", config.Registry.String(), service.Nodes[0].Id)
config.Logger.Infof(n.opts.Context, "register [%s] Registering node: %s", config.Register.String(), service.Nodes[0].Id)
}
}
@@ -244,7 +248,7 @@ func (n *noopServer) Deregister() error {
config := n.opts
n.RUnlock()
service, err := NewRegistryService(n)
service, err := NewRegisterService(n)
if err != nil {
return err
}

View File

@@ -12,8 +12,9 @@ import (
"github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/network/transport"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
"github.com/unistack-org/micro/v3/tracer"
)
@@ -24,10 +25,11 @@ type Option func(*Options)
type Options struct {
Codecs map[string]codec.Codec
Broker broker.Broker
Registry registry.Registry
Register register.Register
Tracer tracer.Tracer
Auth auth.Auth
Logger logger.Logger
Meter meter.Meter
Transport transport.Transport
Metadata metadata.Metadata
Name string
@@ -78,9 +80,10 @@ func NewOptions(opts ...Option) Options {
RegisterTTL: DefaultRegisterTTL,
RegisterCheck: DefaultRegisterCheck,
Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer,
Broker: broker.DefaultBroker,
Registry: registry.DefaultRegistry,
Register: register.DefaultRegister,
Transport: transport.DefaultTransport,
Address: DefaultAddress,
Name: DefaultName,
@@ -117,6 +120,13 @@ func Logger(l logger.Logger) Option {
}
}
// Meter sets the meter option
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// Id unique server id
func Id(id string) Option {
return func(o *Options) {
@@ -168,10 +178,10 @@ func Context(ctx context.Context) Option {
}
}
// Registry used for discovery
func Registry(r registry.Registry) Option {
// Register used for discovery
func Register(r register.Register) Option {
return func(o *Options) {
o.Registry = r
o.Register = r
}
}
@@ -203,7 +213,7 @@ func Metadata(md metadata.Metadata) Option {
}
}
// RegisterCheck run func before registry service
// RegisterCheck run func before register service
func RegisterCheck(fn func(context.Context) error) Option {
return func(o *Options) {
o.RegisterCheck = fn
@@ -235,7 +245,6 @@ func TLSConfig(t *tls.Config) Option {
// set the transport tls
o.Transport.Init(
transport.Secure(true),
transport.TLSConfig(t),
)
}

View File

@@ -5,23 +5,23 @@ import (
"time"
"github.com/unistack-org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
"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(svc *registry.Service, config Options) error {
DefaultRegisterFunc = func(svc *register.Service, config Options) error {
var err error
opts := []registry.RegisterOption{
registry.RegisterTTL(config.RegisterTTL),
registry.RegisterDomain(config.Namespace),
opts := []register.RegisterOption{
register.RegisterTTL(config.RegisterTTL),
register.RegisterDomain(config.Namespace),
}
for i := 0; i <= config.RegisterAttempts; i++ {
err = config.Registry.Register(config.Context, svc, opts...)
err = config.Register.Register(config.Context, svc, opts...)
if err == nil {
break
}
@@ -32,15 +32,15 @@ var (
return err
}
// DefaultDeregisterFunc uses backoff to deregister service
DefaultDeregisterFunc = func(svc *registry.Service, config Options) error {
DefaultDeregisterFunc = func(svc *register.Service, config Options) error {
var err error
opts := []registry.DeregisterOption{
registry.DeregisterDomain(config.Namespace),
opts := []register.DeregisterOption{
register.DeregisterDomain(config.Namespace),
}
for i := 0; i <= config.DeregisterAttempts; i++ {
err = config.Registry.Deregister(config.Context, svc, opts...)
err = config.Register.Deregister(config.Context, svc, opts...)
if err == nil {
break
}
@@ -52,8 +52,8 @@ var (
}
)
// NewRegistryService returns *registry.Service from Server
func NewRegistryService(s Server) (*registry.Service, error) {
// NewRegisterService returns *register.Service from Server
func NewRegisterService(s Server) (*register.Service, error) {
opts := s.Options()
advt := opts.Address
@@ -71,7 +71,7 @@ func NewRegistryService(s Server) (*registry.Service, error) {
addr = host
}
node := &registry.Node{
node := &register.Node{
Id: opts.Name + "-" + opts.Id,
Address: net.JoinHostPort(addr, port),
}
@@ -79,12 +79,12 @@ func NewRegistryService(s Server) (*registry.Service, error) {
node.Metadata["server"] = s.String()
node.Metadata["broker"] = opts.Broker.String()
node.Metadata["registry"] = opts.Registry.String()
node.Metadata["register"] = opts.Register.String()
return &registry.Service{
return &register.Service{
Name: opts.Name,
Version: opts.Version,
Nodes: []*registry.Node{node},
Nodes: []*register.Node{node},
Metadata: metadata.New(0),
}, nil
}

View File

@@ -8,7 +8,7 @@ import (
"github.com/google/uuid"
"github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
)
var (
@@ -29,7 +29,7 @@ var (
DefaultRegisterCheck = func(context.Context) error { return nil }
// DefaultRegisterInterval holds interval for register
DefaultRegisterInterval = time.Second * 30
// DefaultRegisterTTL holds registry record ttl, must be multiple of DefaultRegisterInterval
// DefaultRegisterTTL holds register record ttl, must be multiple of DefaultRegisterInterval
DefaultRegisterTTL = time.Second * 90
// DefaultNamespace will be used if no namespace passed
DefaultNamespace = "micro"
@@ -43,6 +43,8 @@ var (
// Server is a simple micro server abstraction
type Server interface {
// Name returns server name
Name() string
// Initialise options
Init(...Option) error
// Retrieve the options
@@ -147,7 +149,7 @@ type Stream interface {
type Handler interface {
Name() string
Handler() interface{}
Endpoints() []*registry.Endpoint
Endpoints() []*register.Endpoint
Options() HandlerOptions
}
@@ -157,6 +159,6 @@ type Handler interface {
type Subscriber interface {
Topic() string
Subscriber() interface{}
Endpoints() []*registry.Endpoint
Endpoints() []*register.Endpoint
Options() SubscriberOptions
}

View File

@@ -14,7 +14,7 @@ import (
"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"
"github.com/unistack-org/micro/v3/register"
)
const (
@@ -39,7 +39,7 @@ type subscriber struct {
typ reflect.Type
subscriber interface{}
handlers []*handler
endpoints []*registry.Endpoint
endpoints []*register.Endpoint
opts SubscriberOptions
}
@@ -115,7 +115,7 @@ func ValidateSubscriber(sub Subscriber) error {
}
func newSubscriber(topic string, sub interface{}, opts ...SubscriberOption) Subscriber {
var endpoints []*registry.Endpoint
var endpoints []*register.Endpoint
var handlers []*handler
options := NewSubscriberOptions(opts...)
@@ -134,9 +134,9 @@ func newSubscriber(topic string, sub interface{}, opts ...SubscriberOption) Subs
}
handlers = append(handlers, h)
ep := &registry.Endpoint{
ep := &register.Endpoint{
Name: "Func",
Request: registry.ExtractSubValue(typ),
Request: register.ExtractSubValue(typ),
Metadata: metadata.New(2),
}
ep.Metadata.Set("topic", topic)
@@ -161,9 +161,9 @@ func newSubscriber(topic string, sub interface{}, opts ...SubscriberOption) Subs
}
handlers = append(handlers, h)
ep := &registry.Endpoint{
ep := &register.Endpoint{
Name: name + "." + method.Name,
Request: registry.ExtractSubValue(method.Type),
Request: register.ExtractSubValue(method.Type),
Metadata: metadata.New(2),
}
ep.Metadata.Set("topic", topic)
@@ -304,7 +304,7 @@ func (s *subscriber) Subscriber() interface{} {
return s.subscriber
}
func (s *subscriber) Endpoints() []*registry.Endpoint {
func (s *subscriber) Endpoints() []*register.Endpoint {
return s.endpoints
}

View File

@@ -1,8 +1,8 @@
// Package micro is a pluggable framework for microservices
package micro
import (
"fmt"
rtime "runtime"
"sync"
"github.com/unistack-org/micro/v3/auth"
@@ -10,31 +10,86 @@ import (
"github.com/unistack-org/micro/v3/client"
"github.com/unistack-org/micro/v3/config"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/register"
"github.com/unistack-org/micro/v3/router"
"github.com/unistack-org/micro/v3/server"
"github.com/unistack-org/micro/v3/store"
"github.com/unistack-org/micro/v3/tracer"
)
// Service is an interface that wraps the lower level components.
// Its works as container with building blocks for service.
type Service interface {
// The service name
Name() string
// Init initialises options
Init(...Option) error
// Options returns the current options
Options() Options
// Auth is for handling auth
Auth(...string) auth.Auth
// Logger is for logs
Logger(...string) logger.Logger
// Config if for config
Config(...string) config.Config
// Client is for calling services
Client(...string) client.Client
// Broker is for sending and receiving events
Broker(...string) broker.Broker
// Server is for handling requests and events
Server(...string) server.Server
// Store is for key/val store
Store(...string) store.Store
// Register
Register(...string) register.Register
// Tracer
Tracer(...string) tracer.Tracer
// Router
Router(...string) router.Router
// Meter
Meter(...string) meter.Meter
// Runtime
// Runtime(string) (runtime.Runtime, bool)
// Profile
// Profile(string) (profile.Profile, bool)
// Run the service
Run() error
// The service implementation
String() string
}
// RegisterHandler is syntactic sugar for registering a handler
func RegisterHandler(s server.Server, h interface{}, opts ...server.HandlerOption) error {
return s.Handle(s.NewHandler(h, opts...))
}
// RegisterSubscriber is syntactic sugar for registering a subscriber
func RegisterSubscriber(topic string, s server.Server, h interface{}, opts ...server.SubscriberOption) error {
return s.Subscribe(s.NewSubscriber(topic, h, opts...))
}
type service struct {
opts Options
sync.RWMutex
// once sync.Once
}
func newService(opts ...Option) Service {
service := &service{opts: NewOptions(opts...)}
return service
// NewService creates and returns a new Service based on the packages within.
func NewService(opts ...Option) Service {
return &service{opts: NewOptions(opts...)}
}
func (s *service) Name() string {
return s.opts.Server.Options().Name
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.
func (s *service) Init(opts ...Option) error {
var err error
// process options
for _, o := range opts {
o(&s.opts)
@@ -45,60 +100,48 @@ func (s *service) Init(opts ...Option) error {
// skip config as the struct not passed
continue
}
if err := cfg.Init(config.Context(s.opts.Context)); err != nil {
if err = cfg.Init(config.Context(s.opts.Context)); err != nil {
return err
}
if err := cfg.Load(s.opts.Context); err != nil {
if err = cfg.Load(s.opts.Context); err != nil {
return err
}
}
if s.opts.Logger != nil {
if err := s.opts.Logger.Init(
logger.WithContext(s.opts.Context),
); err != nil {
for _, log := range s.opts.Loggers {
if err = log.Init(logger.WithContext(s.opts.Context)); err != nil {
return err
}
}
if s.opts.Registry != nil {
if err := s.opts.Registry.Init(
registry.Context(s.opts.Context),
); err != nil {
for _, reg := range s.opts.Registers {
if err = reg.Init(register.Context(s.opts.Context)); err != nil {
return err
}
}
if s.opts.Broker != nil {
if err := s.opts.Broker.Init(
broker.Context(s.opts.Context),
); err != nil {
for _, brk := range s.opts.Brokers {
if err = brk.Init(broker.Context(s.opts.Context)); err != nil {
return err
}
}
if s.opts.Store != nil {
if err := s.opts.Store.Init(
store.Context(s.opts.Context),
); err != nil {
for _, str := range s.opts.Stores {
if err = str.Init(store.Context(s.opts.Context)); err != nil {
return err
}
}
if s.opts.Server != nil {
if err := s.opts.Server.Init(
server.Context(s.opts.Context),
); err != nil {
for _, srv := range s.opts.Servers {
if err = srv.Init(server.Context(s.opts.Context)); err != nil {
return err
}
}
if s.opts.Client != nil {
if err := s.opts.Client.Init(
client.Context(s.opts.Context),
); err != nil {
for _, cli := range s.opts.Clients {
if err = cli.Init(client.Context(s.opts.Context)); err != nil {
return err
}
}
@@ -110,36 +153,93 @@ func (s *service) Options() Options {
return s.opts
}
func (s *service) Broker() broker.Broker {
return s.opts.Broker
func (s *service) Broker(names ...string) broker.Broker {
idx := 0
if len(names) == 1 {
idx = getNameIndex(names[0], s.opts.Brokers)
}
return s.opts.Brokers[idx]
}
func (s *service) Client() client.Client {
return s.opts.Client
func (s *service) Tracer(names ...string) tracer.Tracer {
idx := 0
if len(names) == 1 {
idx = getNameIndex(names[0], s.opts.Tracers)
}
return s.opts.Tracers[idx]
}
func (s *service) Server() server.Server {
return s.opts.Server
func (s *service) Config(names ...string) config.Config {
idx := 0
if len(names) == 1 {
idx = getNameIndex(names[0], s.opts.Configs)
}
return s.opts.Configs[idx]
}
func (s *service) Store() store.Store {
return s.opts.Store
func (s *service) Client(names ...string) client.Client {
idx := 0
if len(names) == 1 {
idx = getNameIndex(names[0], s.opts.Clients)
}
return s.opts.Clients[idx]
}
func (s *service) Registry() registry.Registry {
return s.opts.Registry
func (s *service) Server(names ...string) server.Server {
idx := 0
if len(names) == 1 {
idx = getNameIndex(names[0], s.opts.Servers)
}
return s.opts.Servers[idx]
}
func (s *service) Logger() logger.Logger {
return s.opts.Logger
func (s *service) Store(names ...string) store.Store {
idx := 0
if len(names) == 1 {
idx = getNameIndex(names[0], s.opts.Stores)
}
return s.opts.Stores[idx]
}
func (s *service) Auth() auth.Auth {
return s.opts.Auth
func (s *service) Register(names ...string) register.Register {
idx := 0
if len(names) == 1 {
idx = getNameIndex(names[0], s.opts.Registers)
}
return s.opts.Registers[idx]
}
func (s *service) Router() router.Router {
return s.opts.Router
func (s *service) Logger(names ...string) logger.Logger {
idx := 0
if len(names) == 1 {
idx = getNameIndex(names[0], s.opts.Loggers)
}
return s.opts.Loggers[idx]
}
func (s *service) Auth(names ...string) auth.Auth {
idx := 0
if len(names) == 1 {
idx = getNameIndex(names[0], s.opts.Auths)
}
return s.opts.Auths[idx]
}
func (s *service) Router(names ...string) router.Router {
idx := 0
if len(names) == 1 {
idx = getNameIndex(names[0], s.opts.Routers)
}
return s.opts.Routers[idx]
}
func (s *service) Meter(names ...string) meter.Meter {
idx := 0
if len(names) == 1 {
idx = getNameIndex(names[0], s.opts.Meters)
}
return s.opts.Meters[idx]
}
func (s *service) String() string {
@@ -153,8 +253,8 @@ func (s *service) Start() error {
config := s.opts
s.RUnlock()
if config.Logger.V(logger.InfoLevel) {
config.Logger.Infof(s.opts.Context, "starting [service] %s", s.Name())
if config.Loggers[0].V(logger.InfoLevel) {
config.Loggers[0].Infof(s.opts.Context, "starting [service] %s", s.Name())
}
for _, fn := range s.opts.BeforeStart {
@@ -169,35 +269,37 @@ func (s *service) Start() error {
continue
}
if err := cfg.Load(s.opts.Context); err != nil {
if err = cfg.Load(s.opts.Context); err != nil {
return err
}
}
if s.opts.Server == nil {
if len(s.opts.Servers) == 0 {
return fmt.Errorf("cant start nil server")
}
if s.opts.Registry != nil {
if err := s.opts.Registry.Connect(s.opts.Context); err != nil {
for _, reg := range s.opts.Registers {
if err = reg.Connect(s.opts.Context); err != nil {
return err
}
}
if s.opts.Broker != nil {
if err := s.opts.Broker.Connect(s.opts.Context); err != nil {
for _, brk := range s.opts.Brokers {
if err = brk.Connect(s.opts.Context); err != nil {
return err
}
}
if s.opts.Store != nil {
if err := s.opts.Store.Connect(s.opts.Context); err != nil {
for _, str := range s.opts.Stores {
if err = str.Connect(s.opts.Context); err != nil {
return err
}
}
if err = s.opts.Server.Start(); err != nil {
return err
for _, srv := range s.opts.Servers {
if err = srv.Start(); err != nil {
return err
}
}
for _, fn := range s.opts.AfterStart {
@@ -214,8 +316,8 @@ func (s *service) Stop() error {
config := s.opts
s.RUnlock()
if config.Logger.V(logger.InfoLevel) {
config.Logger.Infof(s.opts.Context, "stoppping [service] %s", s.Name())
if config.Loggers[0].V(logger.InfoLevel) {
config.Loggers[0].Infof(s.opts.Context, "stoppping [service] %s", s.Name())
}
var err error
@@ -225,8 +327,10 @@ func (s *service) Stop() error {
}
}
if err = s.opts.Server.Stop(); err != nil {
return err
for _, srv := range s.opts.Servers {
if err = srv.Stop(); err != nil {
return err
}
}
for _, fn := range s.opts.AfterStop {
@@ -235,20 +339,20 @@ func (s *service) Stop() error {
}
}
if s.opts.Registry != nil {
if err := s.opts.Registry.Disconnect(s.opts.Context); err != nil {
for _, reg := range s.opts.Registers {
if err = reg.Disconnect(s.opts.Context); err != nil {
return err
}
}
if s.opts.Broker != nil {
if err := s.opts.Broker.Disconnect(s.opts.Context); err != nil {
for _, brk := range s.opts.Brokers {
if err = brk.Disconnect(s.opts.Context); err != nil {
return err
}
}
if s.opts.Store != nil {
if err := s.opts.Store.Disconnect(s.opts.Context); err != nil {
for _, str := range s.opts.Stores {
if err = str.Disconnect(s.opts.Context); err != nil {
return err
}
}
@@ -258,18 +362,19 @@ func (s *service) Stop() error {
func (s *service) Run() error {
// start the profiler
if s.opts.Profile != nil {
// to view mutex contention
rtime.SetMutexProfileFraction(5)
// to view blocking profile
rtime.SetBlockProfileRate(1)
/*
if s.opts.Profile != nil {
// to view mutex contention
rtime.SetMutexProfileFraction(5)
// to view blocking profile
rtime.SetBlockProfileRate(1)
if err := s.opts.Profile.Start(); err != nil {
return err
if err := s.opts.Profile.Start(); err != nil {
return err
}
defer s.opts.Profile.Stop()
}
defer s.opts.Profile.Stop()
}
*/
if err := s.Start(); err != nil {
return err
}
@@ -279,3 +384,16 @@ func (s *service) Run() error {
return s.Stop()
}
type nameIface interface {
Name() string
}
func getNameIndex(n string, ifaces ...interface{}) int {
for idx, iface := range ifaces {
if ifc, ok := iface.(nameIface); ok && ifc.Name() == n {
return idx
}
}
return 0
}

View File

@@ -32,3 +32,53 @@ func SetOption(k, v interface{}) Option {
o.Context = context.WithValue(o.Context, k, v)
}
}
// SetReadOption returns a function to setup a context with given value
func SetReadOption(k, v interface{}) ReadOption {
return func(o *ReadOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}
// SetWriteOption returns a function to setup a context with given value
func SetWriteOption(k, v interface{}) WriteOption {
return func(o *WriteOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}
// SetListOption returns a function to setup a context with given value
func SetListOption(k, v interface{}) ListOption {
return func(o *ListOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}
// SetDeleteOption returns a function to setup a context with given value
func SetDeleteOption(k, v interface{}) DeleteOption {
return func(o *DeleteOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}
// SetExistsOption returns a function to setup a context with given value
func SetExistsOption(k, v interface{}) ExistsOption {
return func(o *ExistsOptions) {
if o.Context == nil {
o.Context = context.Background()
}
o.Context = context.WithValue(o.Context, k, v)
}
}

View File

@@ -23,13 +23,18 @@ func (n *noopStore) Options() Options {
return n.opts
}
// Name
func (n *noopStore) Name() string {
return n.opts.Name
}
// String returns string representation
func (n *noopStore) String() string {
return "noop"
}
// Read reads store value by key
func (n *noopStore) Exists(ctx context.Context, key string) error {
func (n *noopStore) Exists(ctx context.Context, key string, opts ...ExistsOption) error {
return ErrNotFound
}

View File

@@ -2,15 +2,19 @@ package store
import (
"context"
"crypto/tls"
"time"
"github.com/unistack-org/micro/v3/codec"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/metadata"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/tracer"
)
// Options contains configuration for the Store
type Options struct {
Name string
// Nodes contains the addresses or other connection information of the backing storage.
// For example, an etcd implementation would contain the nodes of the cluster.
// A SQL implementation could contain one or more connection strings.
@@ -23,6 +27,13 @@ type Options struct {
Codec codec.Codec
// Logger the logger
Logger logger.Logger
// Meter the meter
Meter meter.Meter
// Tracer the tacer
Tracer tracer.Tracer
// TLSConfig specifies tls.Config for secure
TLSConfig *tls.Config
// Context should contain all implementation specific options, using context.WithValue.
Context context.Context
}
@@ -33,6 +44,8 @@ func NewOptions(opts ...Option) Options {
Logger: logger.DefaultLogger,
Context: context.Background(),
Codec: codec.DefaultCodec,
Tracer: tracer.DefaultTracer,
Meter: meter.DefaultMeter,
}
for _, o := range opts {
o(&options)
@@ -43,6 +56,13 @@ func NewOptions(opts ...Option) Options {
// Option sets values in Options
type Option func(o *Options)
// TLSConfig specifies a *tls.Config
func TLSConfig(t *tls.Config) Option {
return func(o *Options) {
o.TLSConfig = t
}
}
// Context pass context to store
func Context(ctx context.Context) Option {
return func(o *Options) {
@@ -64,6 +84,27 @@ func Logger(l logger.Logger) Option {
}
}
// Meter sets the meter
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// Name the name
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
// Tracer sets the tracer
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}
// Nodes contains the addresses or other connection information of the backing storage.
// For example, an etcd implementation would contain the nodes of the cluster.
// A SQL implementation could contain one or more connection strings.
@@ -98,8 +139,10 @@ func NewReadOptions(opts ...ReadOption) ReadOptions {
// ReadOptions configures an individual Read operation
type ReadOptions struct {
Database string
Table string
Database string
Table string
Namespace string
Context context.Context
}
// ReadOption sets values in ReadOptions
@@ -124,10 +167,12 @@ func NewWriteOptions(opts ...WriteOption) WriteOptions {
// WriteOptions configures an individual Write operation
type WriteOptions struct {
Database string
Table string
TTL time.Duration
Metadata metadata.Metadata
Database string
Table string
TTL time.Duration
Metadata metadata.Metadata
Namespace string
Context context.Context
}
// WriteOption sets values in WriteOptions
@@ -166,7 +211,10 @@ func NewDeleteOptions(opts ...DeleteOption) DeleteOptions {
// DeleteOptions configures an individual Delete operation
type DeleteOptions struct {
Database, Table string
Database string
Table string
Namespace string
Context context.Context
}
// DeleteOption sets values in DeleteOptions
@@ -200,7 +248,9 @@ type ListOptions struct {
// Limit limits the number of returned keys
Limit uint
// Offset when combined with Limit supports pagination
Offset uint
Offset uint
Namespace string
Context context.Context
}
// ListOption sets values in ListOptions
@@ -241,3 +291,20 @@ func ListOffset(o uint) ListOption {
l.Offset = o
}
}
type ExistsOption func(*ExistsOptions)
type ExistsOptions struct {
Namespace string
Context context.Context
}
func NewExistsOptions(opts ...ExistsOption) ExistsOptions {
options := ExistsOptions{
Context: context.Background(),
}
for _, o := range opts {
o(&options)
}
return options
}

View File

@@ -12,12 +12,15 @@ import (
var (
// ErrNotFound is returned when a key doesn't exist
ErrNotFound = errors.New("not found")
// ErrInvalidKey is returned when a key has empty or have invalid format
ErrInvalidKey = errors.New("invalid key")
// DefaultStore is the global default store
DefaultStore Store = NewStore()
)
// Store is a data storage interface
type Store interface {
Name() string
// Init initialises the store
Init(opts ...Option) error
// Connect is used when store needs to be connected
@@ -25,7 +28,7 @@ type Store interface {
// Options allows you to view the current options.
Options() Options
// Exists check that key exists in store
Exists(ctx context.Context, key string) error
Exists(ctx context.Context, key string, opts ...ExistsOption) error
// Read reads a single key name to provided value with optional ReadOptions
Read(ctx context.Context, key string, val interface{}, opts ...ReadOption) error
// Write writes a value to key name to the store with optional WriteOption

View File

@@ -4,16 +4,34 @@ import (
"time"
"github.com/unistack-org/micro/v3/logger"
"github.com/unistack-org/micro/v3/meter"
"github.com/unistack-org/micro/v3/tracer"
)
type Options struct {
Nodes []string
Prefix string
Logger logger.Logger
Tracer tracer.Tracer
Meter meter.Meter
}
type Option func(o *Options)
func NewOptions(opts ...Option) Options {
options := Options{
Logger: logger.DefaultLogger,
Meter: meter.DefaultMeter,
Tracer: tracer.DefaultTracer,
}
for _, o := range opts {
o(&options)
}
return options
}
type LeaderOptions struct{}
type LeaderOption func(o *LeaderOptions)
@@ -32,6 +50,20 @@ func Logger(l logger.Logger) Option {
}
}
// Meter sets the logger
func Meter(m meter.Meter) Option {
return func(o *Options) {
o.Meter = m
}
}
// Tracer sets the tracer
func Tracer(t tracer.Tracer) Option {
return func(o *Options) {
o.Tracer = t
}
}
// Nodes sets the addresses to use
func Nodes(a ...string) Option {
return func(o *Options) {

View File

@@ -14,11 +14,12 @@ const (
// FromContext returns a span from context
func FromContext(ctx context.Context) (traceID string, parentSpanID string, isFound bool) {
if ctx == nil {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return "", "", false
}
traceID, traceOk := metadata.Get(ctx, traceIDKey)
microID, microOk := metadata.Get(ctx, "Micro-Id")
traceID, traceOk := md.Get(traceIDKey)
microID, microOk := md.Get("Micro-Id")
if !traceOk && !microOk {
isFound = false
return
@@ -26,17 +27,17 @@ func FromContext(ctx context.Context) (traceID string, parentSpanID string, isFo
if !traceOk {
traceID = microID
}
parentSpanID, ok := metadata.Get(ctx, spanIDKey)
parentSpanID, ok = md.Get(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 {
if ctx == nil {
ctx = context.Background()
md, ok := metadata.FromContext(ctx)
if !ok {
md = metadata.New(2)
}
md := metadata.New(2)
md.Set(traceIDKey, traceID)
md.Set(spanIDKey, parentSpanID)
return metadata.MergeContext(ctx, md, true)
return metadata.NewContext(ctx, md)
}

View File

@@ -6,6 +6,10 @@ type noopTracer struct {
opts Options
}
func (n *noopTracer) Name() string {
return n.opts.Name
}
// Init initilize tracer
func (n *noopTracer) Init(opts ...Option) error {
for _, o := range opts {
@@ -19,6 +23,11 @@ func (n *noopTracer) Start(ctx context.Context, name string) (context.Context, *
return nil, nil
}
// Lookup get span from context
func (n *noopTracer) Lookup(ctx context.Context) (*Span, error) {
return nil, nil
}
// Finish finishes span
func (n *noopTracer) Finish(*Span) error {
return nil

View File

@@ -9,6 +9,7 @@ var (
// Options struct
type Options struct {
Name string
// Logger is the logger for messages
Logger logger.Logger
// Size is the size of ring buffer
@@ -52,3 +53,10 @@ func NewOptions(opts ...Option) Options {
}
return options
}
// Name sets the name
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}

View File

@@ -15,10 +15,14 @@ var (
// Tracer is an interface for distributed tracing
type Tracer interface {
Name() string
Init(...Option) error
// Start a trace
Start(ctx context.Context, name string) (context.Context, *Span)
// Finish the trace
Finish(*Span) error
// Lookup get span from context
Lookup(ctx context.Context) (*Span, error)
// Read the traces
Read(...ReadOption) ([]*Span, error)
}

View File

@@ -10,10 +10,9 @@ import (
func FromRequest(r *http.Request) context.Context {
ctx := r.Context()
md, ok := metadata.FromContext(ctx)
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
// create needed map with specific len
md = make(metadata.Metadata, len(r.Header)+2)
md = metadata.New(len(r.Header) + 2)
}
for key, val := range r.Header {
md.Set(key, strings.Join(val, ","))
@@ -22,5 +21,5 @@ func FromRequest(r *http.Request) context.Context {
md["Host"] = r.Host
// pass http method
md["Method"] = r.Method
return metadata.NewContext(ctx, md)
return metadata.NewIncomingContext(ctx, md)
}

View File

@@ -28,7 +28,7 @@ func TestRequestToContext(t *testing.T) {
for _, d := range testData {
ctx := FromRequest(d.request)
md, ok := metadata.FromContext(ctx)
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
t.Fatalf("Expected metadata for request %+v", d.request)
}

View File

@@ -8,15 +8,15 @@ import (
"net/http"
"testing"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/registry/memory"
"github.com/unistack-org/micro/v3/register"
"github.com/unistack-org/micro/v3/register/memory"
"github.com/unistack-org/micro/v3/router"
regRouter "github.com/unistack-org/micro/v3/router/registry"
regRouter "github.com/unistack-org/micro/v3/router/register"
)
func TestRoundTripper(t *testing.T) {
m := memory.NewRegistry()
r := regRouter.NewRouter(router.Registry(m))
m := memory.NewRegister()
r := regRouter.NewRouter(router.Register(m))
rt := NewRoundTripper(WithRouter(r))
@@ -32,9 +32,9 @@ func TestRoundTripper(t *testing.T) {
go http.Serve(l, nil)
m.Register(&registry.Service{
m.Register(&register.Service{
Name: "example.com",
Nodes: []*registry.Node{
Nodes: []*register.Node{
{
Id: "1",
Address: l.Addr().String(),

View File

@@ -2,24 +2,53 @@ package reflect
import (
"errors"
"fmt"
"net/url"
"reflect"
"regexp"
"strconv"
"strings"
"unicode"
)
var (
bracketSplitter = regexp.MustCompile(`\[|\]`)
ErrInvalidStruct = errors.New("invalid struct specified")
ErrInvalidParam = errors.New("invalid url query param provided")
)
func fieldName(name string) string {
newstr := make([]rune, 0)
upper := false
for idx, chr := range name {
if idx == 0 {
upper = true
} else if chr == '_' {
upper = true
continue
}
if upper {
newstr = append(newstr, unicode.ToUpper(chr))
} else {
newstr = append(newstr, chr)
}
upper = false
}
return string(newstr)
}
func IsEmpty(v reflect.Value) bool {
switch v.Kind() {
switch getKind(v) {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
return v.Len() == 0
case reflect.Bool:
return !v.Bool()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
case reflect.Int:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
case reflect.Uint:
return v.Uint() == 0
case reflect.Float32, reflect.Float64:
case reflect.Float32:
return v.Float() == 0
case reflect.Interface, reflect.Ptr:
if v.IsNil() {
@@ -119,3 +148,450 @@ func CopyFrom(a, b interface{}) {
}
}
}
func URLMap(query string) (map[string]interface{}, error) {
var (
mp interface{} = make(map[string]interface{})
)
params := strings.Split(query, "&")
for _, part := range params {
tm, err := queryToMap(part)
if err != nil {
return nil, err
}
mp = merge(mp, tm)
}
return mp.(map[string]interface{}), nil
}
func FlattenMap(a map[string]interface{}) map[string]interface{} {
// preprocess map
nb := make(map[string]interface{}, len(a))
for k, v := range a {
ps := strings.Split(k, ".")
if len(ps) == 1 {
nb[k] = v
continue
}
em := make(map[string]interface{})
em[ps[len(ps)-1]] = v
for i := len(ps) - 2; i > 0; i-- {
nm := make(map[string]interface{})
nm[ps[i]] = em
em = nm
}
if vm, ok := nb[ps[0]]; ok {
// nested map
nm := vm.(map[string]interface{})
for vk, vv := range em {
nm[vk] = vv
}
nb[ps[0]] = nm
} else {
nb[ps[0]] = em
}
}
return nb
}
func MergeMap(a interface{}, b map[string]interface{}) error {
var err error
ta := reflect.TypeOf(a)
if ta.Kind() == reflect.Ptr {
ta = ta.Elem()
}
va := reflect.ValueOf(a)
if va.Kind() == reflect.Ptr {
va = va.Elem()
}
for mk, mv := range b {
vmv := reflect.ValueOf(mv)
name := fieldName(mk)
fva := va.FieldByName(name)
fta, found := ta.FieldByName(name)
if !found || !fva.IsValid() || !fva.CanSet() || fta.PkgPath != "" {
continue
}
// fast path via direct assign
if vmv.Type().AssignableTo(fta.Type) && !IsEmpty(vmv) {
fva.Set(vmv)
continue
}
switch getKind(fva) {
case reflect.Bool:
err = mergeBool(fva, vmv)
case reflect.String:
err = mergeString(fva, vmv)
case reflect.Int:
err = mergeInt(fva, vmv)
case reflect.Uint:
err = mergeUint(fva, vmv)
case reflect.Float32:
err = mergeFloat(fva, vmv)
case reflect.Array:
//fmt.Printf("Array %#+v %#+v\n", fva, vmv)
case reflect.Slice:
err = mergeSlice(fva, vmv)
case reflect.Ptr:
if fva.IsNil() {
fva.Set(reflect.New(fva.Type().Elem()))
if fva.Elem().Type().Kind() == reflect.Struct {
for i := 0; i < fva.Elem().NumField(); i++ {
field := fva.Elem().Field(i)
if field.Type().Kind() == reflect.Ptr && field.IsNil() && fva.Elem().Type().Field(i).Anonymous == true {
field.Set(reflect.New(field.Type().Elem()))
}
}
}
}
if nmp, ok := vmv.Interface().(map[string]interface{}); ok {
err = MergeMap(fva.Interface(), nmp)
} else {
err = fmt.Errorf("cant fill")
}
case reflect.Struct:
if nmp, ok := vmv.Interface().(map[string]interface{}); ok {
err = MergeMap(fva.Interface(), nmp)
} else {
err = fmt.Errorf("cant fill")
}
case reflect.Map:
//fmt.Printf("Map %#+v %#+v\n", fva, vmv)
}
if err != nil {
return err
}
}
return nil
}
func mergeSlice(va, vb reflect.Value) error {
switch getKind(vb) {
/*
case reflect.Int:
if vb.Int() == 1 {
va.SetBool(true)
}
case reflect.Uint:
if vb.Uint() == 1 {
va.SetBool(true)
}
case reflect.Float64:
if vb.Float() == 1 {
va.SetBool(true)
}
*/
case reflect.String:
var err error
fn := func(c rune) bool { return c == ',' || c == ';' || c == ' ' }
slice := strings.FieldsFunc(vb.String(), fn)
va.Set(reflect.MakeSlice(va.Type(), len(slice), len(slice)))
for idx, sl := range slice {
vl := reflect.ValueOf(sl)
switch va.Type().Elem().Kind() {
case reflect.Bool:
err = mergeBool(va.Index(idx), vl)
case reflect.String:
err = mergeString(va.Index(idx), vl)
case reflect.Ptr:
if va.Index(idx).IsNil() {
va.Index(idx).Set(reflect.New(va.Index(idx).Type().Elem()))
}
switch va.Type().Elem().String() {
case "*wrapperspb.BoolValue":
if eva := reflect.Indirect(va.Index(idx)).FieldByName("Value"); eva.IsValid() {
err = mergeBool(eva, vl)
}
case "*wrapperspb.BytesValue":
if eva := va.Index(idx).FieldByName("Value"); eva.IsValid() {
err = mergeUint(eva, vl)
}
case "*wrapperspb.DoubleValue", "*wrapperspb.FloatValue":
if eva := reflect.Indirect(va.Index(idx)).FieldByName("Value"); eva.IsValid() {
err = mergeFloat(eva, vl)
}
case "*wrapperspb.Int32Value", "*wrapperspb.Int64Value":
if eva := reflect.Indirect(va.Index(idx)).FieldByName("Value"); eva.IsValid() {
err = mergeInt(eva, vl)
}
case "*wrapperspb.StringValue":
if eva := reflect.Indirect(va.Index(idx)).FieldByName("Value"); eva.IsValid() {
err = mergeString(eva, vl)
}
case "*wrapperspb.UInt32Value", "*wrapperspb.UInt64Value":
if eva := reflect.Indirect(va.Index(idx)).FieldByName("Value"); eva.IsValid() {
err = mergeUint(eva, vl)
}
}
}
if err != nil {
return err
}
}
default:
return fmt.Errorf("cant merge %v %s with %v %s", va, va.Kind(), vb, vb.Kind())
}
return nil
}
func mergeBool(va, vb reflect.Value) error {
switch getKind(vb) {
case reflect.Int:
if vb.Int() == 1 {
va.SetBool(true)
}
case reflect.Uint:
if vb.Uint() == 1 {
va.SetBool(true)
}
case reflect.Float32:
if vb.Float() == 1 {
va.SetBool(true)
}
case reflect.String:
if b, err := strconv.ParseBool(vb.String()); err != nil {
return err
} else {
va.SetBool(b)
}
default:
return fmt.Errorf("cant merge %v %s with %v %s", va, va.Kind(), vb, vb.Kind())
}
return nil
}
func mergeString(va, vb reflect.Value) error {
switch getKind(vb) {
case reflect.Int:
va.SetString(fmt.Sprintf("%d", vb.Int()))
case reflect.Uint:
va.SetString(fmt.Sprintf("%d", vb.Uint()))
case reflect.Float32:
va.SetString(fmt.Sprintf("%f", vb.Float()))
case reflect.String:
va.Set(vb)
default:
return fmt.Errorf("cant merge %v %s with %v %s", va, va.Kind(), vb, vb.Kind())
}
return nil
}
func mergeInt(va, vb reflect.Value) error {
switch getKind(vb) {
case reflect.Int:
va.Set(vb)
case reflect.Uint:
va.SetInt(int64(vb.Uint()))
case reflect.Float32:
va.SetInt(int64(vb.Float()))
case reflect.String:
if f, err := strconv.ParseInt(vb.String(), 10, va.Type().Bits()); err != nil {
return err
} else {
va.SetInt(f)
}
default:
return fmt.Errorf("cant merge %v %s with %v %s", va, va.Kind(), vb, vb.Kind())
}
return nil
}
func mergeUint(va, vb reflect.Value) error {
switch getKind(vb) {
case reflect.Int:
va.SetUint(uint64(vb.Int()))
case reflect.Uint:
va.Set(vb)
case reflect.Float32:
va.SetUint(uint64(vb.Float()))
case reflect.String:
if f, err := strconv.ParseUint(vb.String(), 10, va.Type().Bits()); err != nil {
return err
} else {
va.SetUint(f)
}
default:
return fmt.Errorf("cant merge %v %s with %v %s", va, va.Kind(), vb, vb.Kind())
}
return nil
}
func mergeFloat(va, vb reflect.Value) error {
switch getKind(vb) {
case reflect.Int:
va.SetFloat(float64(vb.Int()))
case reflect.Uint:
va.SetFloat(float64(vb.Uint()))
case reflect.Float32:
va.Set(vb)
case reflect.String:
if f, err := strconv.ParseFloat(vb.String(), va.Type().Bits()); err != nil {
return err
} else {
va.SetFloat(f)
}
default:
return fmt.Errorf("cant merge %v %s with %v %s", va, va.Kind(), vb, vb.Kind())
}
return nil
}
func getKind(val reflect.Value) reflect.Kind {
kind := val.Kind()
switch {
case kind >= reflect.Int && kind <= reflect.Int64:
return reflect.Int
case kind >= reflect.Uint && kind <= reflect.Uint64:
return reflect.Uint
case kind >= reflect.Float32 && kind <= reflect.Float64:
return reflect.Float32
}
return kind
}
func btSplitter(str string) []string {
r := bracketSplitter.Split(str, -1)
for idx, s := range r {
if len(s) == 0 {
if len(r) > idx+1 {
copy(r[idx:], r[idx+1:])
r = r[:len(r)-1]
}
}
}
return r
}
// queryToMap turns something like a[b][c]=4 into
// map[string]interface{}{
// "a": map[string]interface{}{
// "b": map[string]interface{}{
// "c": 4,
// },
// },
// }
func queryToMap(param string) (map[string]interface{}, error) {
rawKey, rawValue, err := splitKeyAndValue(param)
if err != nil {
return nil, err
}
rawValue, err = url.QueryUnescape(rawValue)
if err != nil {
return nil, err
}
rawKey, err = url.QueryUnescape(rawKey)
if err != nil {
return nil, err
}
pieces := btSplitter(rawKey)
key := pieces[0]
// If len==1 then rawKey has no [] chars and we can just
// decode this as key=value into {key: value}
if len(pieces) == 1 {
return map[string]interface{}{
key: rawValue,
}, nil
}
// If len > 1 then we have something like a[b][c]=2
// so we need to turn this into {"a": {"b": {"c": 2}}}
// To do this we break our key into two pieces:
// a and b[c]
// and then we set {"a": queryToMap("b[c]", value)}
ret := make(map[string]interface{})
ret[key], err = queryToMap(buildNewKey(rawKey) + "=" + rawValue)
if err != nil {
return nil, err
}
// When URL params have a set of empty brackets (eg a[]=1)
// it is assumed to be an array. This will get us the
// correct value for the array item and return it as an
// []interface{} so that it can be merged properly.
if pieces[1] == "" {
temp := ret[key].(map[string]interface{})
ret[key] = []interface{}{temp[""]}
}
return ret, nil
}
// buildNewKey will take something like:
// origKey = "bar[one][two]"
// pieces = [bar one two ]
// and return "one[two]"
func buildNewKey(origKey string) string {
pieces := btSplitter(origKey)
ret := origKey[len(pieces[0])+1:]
ret = ret[:len(pieces[1])] + ret[len(pieces[1])+1:]
return ret
}
// splitKeyAndValue splits a URL param at the last equal
// sign and returns the two strings. If no equal sign is
// found, the ErrInvalidParam error is returned.
func splitKeyAndValue(param string) (string, string, error) {
li := strings.LastIndex(param, "=")
if li == -1 {
return "", "", ErrInvalidParam
}
return param[:li], param[li+1:], nil
}
// merge merges a with b if they are either both slices
// or map[string]interface{} types. Otherwise it returns b.
func merge(a interface{}, b interface{}) interface{} {
if av, aok := a.(map[string]interface{}); aok {
if bv, bok := b.(map[string]interface{}); bok {
return mergeMapIface(av, bv)
}
}
if av, aok := a.([]interface{}); aok {
if bv, bok := b.([]interface{}); bok {
return mergeSliceIface(av, bv)
}
}
va := reflect.ValueOf(a)
vb := reflect.ValueOf(b)
if (va.Type().Kind() == reflect.Slice) && (va.Type().Elem().Kind() == vb.Type().Kind() || vb.Type().ConvertibleTo(va.Type().Elem())) {
va = reflect.Append(va, vb.Convert(va.Type().Elem()))
return va.Interface()
}
return b
}
// mergeMap merges a with b, attempting to merge any nested
// values in nested maps but eventually overwriting anything
// in a that can't be merged with whatever is in b.
func mergeMapIface(a map[string]interface{}, b map[string]interface{}) map[string]interface{} {
for bK, bV := range b {
if aV, ok := a[bK]; ok {
if (reflect.ValueOf(aV).Type().Kind() == reflect.ValueOf(bV).Type().Kind()) ||
((reflect.ValueOf(aV).Type().Kind() == reflect.Slice) && reflect.ValueOf(aV).Type().Elem().Kind() == reflect.ValueOf(bV).Type().Kind()) {
nV := []interface{}{aV, bV}
a[bK] = nV
} else {
a[bK] = merge(a[bK], bV)
}
} else {
a[bK] = bV
}
}
return a
}
// mergeSlice merges a with b and returns the result.
func mergeSliceIface(a []interface{}, b []interface{}) []interface{} {
a = append(a, b...)
return a
}

View File

@@ -0,0 +1,45 @@
package reflect
import (
"net/url"
"testing"
)
func TestURLSliceVars(t *testing.T) {
u, err := url.Parse("http://localhost/v1/test/call/my_name?key=arg1&key=arg2&key=arg3")
if err != nil {
t.Fatal(err)
}
mp, err := URLMap(u.RawQuery)
if err != nil {
t.Fatal(err)
}
v, ok := mp["key"]
if !ok {
t.Fatalf("key not exists: %#+v", mp)
}
vm, ok := v.([]interface{})
if !ok {
t.Fatalf("invalid key value")
}
if len(vm) != 3 {
t.Fatalf("missing key value: %#+v", mp)
}
}
func TestURLVars(t *testing.T) {
u, err := url.Parse("http://localhost/v1/test/call/my_name?req=key&arg1=arg1&arg2=12345&nested.string_args=str1&nested.string_args=str2&arg2=54321")
if err != nil {
t.Fatal(err)
}
mp, err := URLMap(u.RawQuery)
if err != nil {
t.Fatal(err)
}
_ = mp
}

View File

@@ -1,11 +1,11 @@
package registry
package register
import (
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
)
func addNodes(old, neu []*registry.Node) []*registry.Node {
nodes := make([]*registry.Node, len(neu))
func addNodes(old, neu []*register.Node) []*register.Node {
nodes := make([]*register.Node, len(neu))
// add all new nodes
for i, n := range neu {
node := *n
@@ -35,8 +35,8 @@ func addNodes(old, neu []*registry.Node) []*registry.Node {
return nodes
}
func delNodes(old, del []*registry.Node) []*registry.Node {
var nodes []*registry.Node
func delNodes(old, del []*register.Node) []*register.Node {
var nodes []*register.Node
for _, o := range old {
var rem bool
for _, n := range del {
@@ -53,24 +53,24 @@ func delNodes(old, del []*registry.Node) []*registry.Node {
}
// CopyService make a copy of service
func CopyService(service *registry.Service) *registry.Service {
func CopyService(service *register.Service) *register.Service {
// copy service
s := &registry.Service{}
s := &register.Service{}
*s = *service
// copy nodes
nodes := make([]*registry.Node, len(service.Nodes))
nodes := make([]*register.Node, len(service.Nodes))
for j, node := range service.Nodes {
n := &registry.Node{}
n := &register.Node{}
*n = *node
nodes[j] = n
}
s.Nodes = nodes
// copy endpoints
eps := make([]*registry.Endpoint, len(service.Endpoints))
eps := make([]*register.Endpoint, len(service.Endpoints))
for j, ep := range service.Endpoints {
e := &registry.Endpoint{}
e := &register.Endpoint{}
*e = *ep
eps[j] = e
}
@@ -79,8 +79,8 @@ func CopyService(service *registry.Service) *registry.Service {
}
// Copy makes a copy of services
func Copy(current []*registry.Service) []*registry.Service {
services := make([]*registry.Service, len(current))
func Copy(current []*register.Service) []*register.Service {
services := make([]*register.Service, len(current))
for i, service := range current {
services[i] = CopyService(service)
}
@@ -88,14 +88,14 @@ func Copy(current []*registry.Service) []*registry.Service {
}
// Merge merges two lists of services and returns a new copy
func Merge(olist []*registry.Service, nlist []*registry.Service) []*registry.Service {
var srv []*registry.Service
func Merge(olist []*register.Service, nlist []*register.Service) []*register.Service {
var srv []*register.Service
for _, n := range nlist {
var seen bool
for _, o := range olist {
if o.Version == n.Version {
sp := &registry.Service{}
sp := &register.Service{}
// make copy
*sp = *o
// set nodes
@@ -106,25 +106,25 @@ func Merge(olist []*registry.Service, nlist []*registry.Service) []*registry.Ser
srv = append(srv, sp)
break
} else {
sp := &registry.Service{}
sp := &register.Service{}
// make copy
*sp = *o
srv = append(srv, sp)
}
}
if !seen {
srv = append(srv, Copy([]*registry.Service{n})...)
srv = append(srv, Copy([]*register.Service{n})...)
}
}
return srv
}
// Remove removes services and returns a new copy
func Remove(old, del []*registry.Service) []*registry.Service {
var services []*registry.Service
func Remove(old, del []*register.Service) []*register.Service {
var services []*register.Service
for _, o := range old {
srv := &registry.Service{}
srv := &register.Service{}
*srv = *o
var rem bool

View File

@@ -1,18 +1,18 @@
package registry
package register
import (
"os"
"testing"
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
)
func TestRemove(t *testing.T) {
services := []*registry.Service{
services := []*register.Service{
{
Name: "foo",
Version: "1.0.0",
Nodes: []*registry.Node{
Nodes: []*register.Node{
{
Id: "foo-123",
Address: "localhost:9999",
@@ -22,7 +22,7 @@ func TestRemove(t *testing.T) {
{
Name: "foo",
Version: "1.0.0",
Nodes: []*registry.Node{
Nodes: []*register.Node{
{
Id: "foo-123",
Address: "localhost:6666",
@@ -31,7 +31,7 @@ func TestRemove(t *testing.T) {
},
}
servs := Remove([]*registry.Service{services[0]}, []*registry.Service{services[1]})
servs := Remove([]*register.Service{services[0]}, []*register.Service{services[1]})
if i := len(servs); i > 0 {
t.Errorf("Expected 0 nodes, got %d: %+v", i, servs)
}
@@ -41,11 +41,11 @@ func TestRemove(t *testing.T) {
}
func TestRemoveNodes(t *testing.T) {
services := []*registry.Service{
services := []*register.Service{
{
Name: "foo",
Version: "1.0.0",
Nodes: []*registry.Node{
Nodes: []*register.Node{
{
Id: "foo-123",
Address: "localhost:9999",
@@ -59,7 +59,7 @@ func TestRemoveNodes(t *testing.T) {
{
Name: "foo",
Version: "1.0.0",
Nodes: []*registry.Node{
Nodes: []*register.Node{
{
Id: "foo-123",
Address: "localhost:6666",

View File

@@ -1,7 +1,7 @@
package router
import (
"github.com/unistack-org/micro/v3/registry"
"github.com/unistack-org/micro/v3/register"
"github.com/unistack-org/micro/v3/router"
)
@@ -19,7 +19,7 @@ func (r *apiRouter) String() string {
}
// New router is a hack for API routing
func New(srvs []*registry.Service) router.Router {
func New(srvs []*register.Service) router.Router {
var routes []router.Route
for _, srv := range srvs {

View File

@@ -78,6 +78,11 @@ func (c *syncStore) Init(opts ...store.Option) error {
return nil
}
// Name returns the store name
func (c *syncStore) Name() string {
return c.storeOpts.Name
}
// Options returns the sync's store options
func (c *syncStore) Options() store.Options {
return c.storeOpts
@@ -96,7 +101,7 @@ func (c *syncStore) List(ctx context.Context, opts ...store.ListOption) ([]strin
return c.syncOpts.Stores[0].List(ctx, opts...)
}
func (c *syncStore) Exists(ctx context.Context, key string) error {
func (c *syncStore) Exists(ctx context.Context, key string, opts ...store.ExistsOption) error {
return c.syncOpts.Stores[0].Exists(ctx, key)
}