mirror of
https://github.com/go-micro/go-micro.git
synced 2025-08-10 21:52:01 +02:00
Plugins and profiles (#2764)
* feat: more plugins * chore(ci): split out benchmarks Attempt to resolve too many open files in ci * chore(ci): split out benchmarks * fix(ci): Attempt to resolve too many open files in ci * fix: set DefaultX for cli flag and service option * fix: restore http broker * fix: default http broker * feat: full nats profile * chore: still ugly, not ready * fix: better initialization for profiles * fix(tests): comment out flaky listen tests * fix: disable benchmarks on gha * chore: cleanup, comments * chore: add nats config source
This commit is contained in:
56
config/source/nats/README.md
Normal file
56
config/source/nats/README.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Nats Source
|
||||
|
||||
The nats source reads config from nats key/values
|
||||
|
||||
## Nats Format
|
||||
|
||||
The nats source expects keys under the default bucket `default` default key `micro_config`
|
||||
|
||||
Values are expected to be json
|
||||
|
||||
```
|
||||
nats kv put default micro_config '{"nats": {"address": "10.0.0.1", "port": 8488}}'
|
||||
```
|
||||
|
||||
```
|
||||
conf.Get("nats")
|
||||
```
|
||||
|
||||
## New Source
|
||||
|
||||
Specify source with data
|
||||
|
||||
```go
|
||||
natsSource := nats.NewSource(
|
||||
nats.WithUrl("127.0.0.1:4222"),
|
||||
nats.WithBucket("my_bucket"),
|
||||
nats.WithKey("my_key"),
|
||||
)
|
||||
```
|
||||
|
||||
## Load Source
|
||||
|
||||
Load the source into config
|
||||
|
||||
```go
|
||||
// Create new config
|
||||
conf := config.NewConfig()
|
||||
|
||||
// Load nats source
|
||||
conf.Load(natsSource)
|
||||
```
|
||||
|
||||
## Watch
|
||||
|
||||
```go
|
||||
wh, _ := natsSource.Watch()
|
||||
|
||||
for {
|
||||
v, err := watcher.Next()
|
||||
if err != nil {
|
||||
log.Fatalf("err %v", err)
|
||||
}
|
||||
|
||||
log.Infof("data %v", string(v.Data))
|
||||
}
|
||||
```
|
134
config/source/nats/nats.go
Normal file
134
config/source/nats/nats.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package nats
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
natsgo "github.com/nats-io/nats.go"
|
||||
"go-micro.dev/v5/config/source"
|
||||
log "go-micro.dev/v5/logger"
|
||||
)
|
||||
|
||||
type nats struct {
|
||||
url string
|
||||
bucket string
|
||||
key string
|
||||
kv natsgo.KeyValue
|
||||
opts source.Options
|
||||
}
|
||||
|
||||
// DefaultBucket is the bucket that nats keys will be assumed to have if you
|
||||
// haven't specified one.
|
||||
var (
|
||||
DefaultBucket = "default"
|
||||
DefaultKey = "micro_config"
|
||||
)
|
||||
|
||||
func (n *nats) Read() (*source.ChangeSet, error) {
|
||||
e, err := n.kv.Get(n.key)
|
||||
if err != nil {
|
||||
if err == natsgo.ErrKeyNotFound {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if e.Value() == nil || len(e.Value()) == 0 {
|
||||
return nil, fmt.Errorf("source not found: %s", n.key)
|
||||
}
|
||||
|
||||
cs := &source.ChangeSet{
|
||||
Data: e.Value(),
|
||||
Format: n.opts.Encoder.String(),
|
||||
Source: n.String(),
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
cs.Checksum = cs.Sum()
|
||||
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
func (n *nats) Write(cs *source.ChangeSet) error {
|
||||
_, err := n.kv.Put(n.key, cs.Data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *nats) String() string {
|
||||
return "nats"
|
||||
}
|
||||
|
||||
func (n *nats) Watch() (source.Watcher, error) {
|
||||
return newWatcher(n.kv, n.bucket, n.key, n.String(), n.opts.Encoder)
|
||||
}
|
||||
|
||||
func NewSource(opts ...source.Option) source.Source {
|
||||
options := source.NewOptions(opts...)
|
||||
|
||||
config := natsgo.GetDefaultOptions()
|
||||
|
||||
urls, ok := options.Context.Value(urlKey{}).([]string)
|
||||
endpoints := []string{}
|
||||
if ok {
|
||||
for _, u := range urls {
|
||||
addr, port, err := net.SplitHostPort(u)
|
||||
if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
|
||||
port = "4222"
|
||||
addr = u
|
||||
endpoints = append(endpoints, fmt.Sprintf("%s:%s", addr, port))
|
||||
} else if err == nil {
|
||||
endpoints = append(endpoints, fmt.Sprintf("%s:%s", addr, port))
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(endpoints) == 0 {
|
||||
endpoints = append(endpoints, "127.0.0.1:4222")
|
||||
}
|
||||
|
||||
bucket, ok := options.Context.Value(bucketKey{}).(string)
|
||||
if !ok {
|
||||
bucket = DefaultBucket
|
||||
}
|
||||
|
||||
key, ok := options.Context.Value(keyKey{}).(string)
|
||||
if !ok {
|
||||
key = DefaultKey
|
||||
}
|
||||
|
||||
config.Url = strings.Join(endpoints, ",")
|
||||
|
||||
nc, err := natsgo.Connect(config.Url)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
js, err := nc.JetStream(natsgo.MaxWait(10 * time.Second))
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
kv, err := js.KeyValue(bucket)
|
||||
if err == natsgo.ErrBucketNotFound || err == natsgo.ErrKeyNotFound {
|
||||
kv, err = js.CreateKeyValue(&natsgo.KeyValueConfig{Bucket: bucket})
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
|
||||
return &nats{
|
||||
url: config.Url,
|
||||
bucket: bucket,
|
||||
key: key,
|
||||
kv: kv,
|
||||
opts: options,
|
||||
}
|
||||
}
|
54
config/source/nats/options.go
Normal file
54
config/source/nats/options.go
Normal file
@@ -0,0 +1,54 @@
|
||||
package nats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
natsgo "github.com/nats-io/nats.go"
|
||||
"go-micro.dev/v5/config/source"
|
||||
)
|
||||
|
||||
type (
|
||||
urlKey struct{}
|
||||
bucketKey struct{}
|
||||
keyKey struct{}
|
||||
)
|
||||
|
||||
// WithUrl sets the nats url.
|
||||
func WithUrl(a ...string) source.Option {
|
||||
return func(o *source.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, urlKey{}, a)
|
||||
}
|
||||
}
|
||||
|
||||
// WithBucket sets the nats key.
|
||||
func WithBucket(a string) source.Option {
|
||||
return func(o *source.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, bucketKey{}, a)
|
||||
}
|
||||
}
|
||||
|
||||
// WithKey sets the nats key.
|
||||
func WithKey(a string) source.Option {
|
||||
return func(o *source.Options) {
|
||||
if o.Context == nil {
|
||||
o.Context = context.Background()
|
||||
}
|
||||
o.Context = context.WithValue(o.Context, keyKey{}, a)
|
||||
}
|
||||
}
|
||||
|
||||
func Client(url string) (natsgo.JetStreamContext, error) {
|
||||
nc, err := natsgo.Connect(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nc.JetStream(natsgo.MaxWait(10 * time.Second))
|
||||
}
|
79
config/source/nats/watcher.go
Normal file
79
config/source/nats/watcher.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package nats
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
natsgo "github.com/nats-io/nats.go"
|
||||
"go-micro.dev/v5/config/encoder"
|
||||
"go-micro.dev/v5/config/source"
|
||||
)
|
||||
|
||||
type watcher struct {
|
||||
e encoder.Encoder
|
||||
name string
|
||||
bucket string
|
||||
key string
|
||||
|
||||
ch chan *source.ChangeSet
|
||||
exit chan bool
|
||||
}
|
||||
|
||||
func newWatcher(kv natsgo.KeyValue, bucket, key, name string, e encoder.Encoder) (source.Watcher, error) {
|
||||
w := &watcher{
|
||||
e: e,
|
||||
name: name,
|
||||
bucket: bucket,
|
||||
key: key,
|
||||
ch: make(chan *source.ChangeSet),
|
||||
exit: make(chan bool),
|
||||
}
|
||||
|
||||
wh, _ := kv.Watch(key)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case v := <-wh.Updates():
|
||||
if v != nil {
|
||||
w.handle(v.Value())
|
||||
}
|
||||
case <-w.exit:
|
||||
_ = wh.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func (w *watcher) handle(data []byte) {
|
||||
cs := &source.ChangeSet{
|
||||
Timestamp: time.Now(),
|
||||
Format: w.e.String(),
|
||||
Source: w.name,
|
||||
Data: data,
|
||||
}
|
||||
cs.Checksum = cs.Sum()
|
||||
|
||||
w.ch <- cs
|
||||
}
|
||||
|
||||
func (w *watcher) Next() (*source.ChangeSet, error) {
|
||||
select {
|
||||
case cs := <-w.ch:
|
||||
return cs, nil
|
||||
case <-w.exit:
|
||||
return nil, source.ErrWatcherStopped
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) Stop() error {
|
||||
select {
|
||||
case <-w.exit:
|
||||
return nil
|
||||
default:
|
||||
close(w.exit)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user