package main
import (
"github.com/micro/go-micro/v2"
log "github.com/micro/go-micro/v2/logger"
"micro-hello/handler"
hello "micro-hello/proto/hello"
"micro-hello/subscriber"
)
func main() {
// New Service
service := micro.NewService(
micro.Name("com.foo.service.micro"),
micro.Version("latest"),
)
// Initialise service
service.Init()
// Register Handler
hello.RegisterMicroHandler(service.Server(), new(handler.Micro))
// Register Struct as Subscriber
micro.RegisterSubscriber("go.micro.service.micro", service.Server(), new(subscriber.Micro))
// Run service
if err := service.Run(); err != nil {
log.Fatal(err)
}
}
代码可分为4部分:
特别注意这行代码
hello "micro-hello/proto/hello"
我们给micro-hello/proto/hello 起了别名hello, 这也是micro的惯例:给所有接口包设置别名。
// New Service
service := micro.NewService(
micro.Name("com.foo.service.micro"),
micro.Version("latest"),
)
我们使用func NewService(opts …Option) Service {}方法去创建爱你一个服务,这个方法可以接收多个micro.Option作为参数。共有29个参数来控制服务。
// Name of the service
func Name(n string) Option {
return func(o *Options) {
o.Server.Init(server.Name(n))
}
}
// Version of the service
func Version(v string) Option {
return func(o *Options) {
o.Server.Init(server.Version(v))
}
}
func Address(addr string) Option {
return func(o *Options) {
o.Server.Init(server.Address(addr))
}
}
// RegisterTTL specifies the TTL to use when registering the service
func RegisterTTL(t time.Duration) Option {
return func(o *Options) {
o.Server.Init(server.RegisterTTL(t))
}
}
// RegisterInterval specifies the interval on which to re-register
func RegisterInterval(t time.Duration) Option {
return func(o *Options) {
o.Server.Init(server.RegisterInterval(t))
}
}
// 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))
}
// Init once
o.Server.Init(wrappers...)
}
}
// HandlerWrapper wraps the HandlerFunc and returns the equivalent
type HandlerWrapper func(HandlerFunc) HandlerFunc
// HandlerFunc represents a single method of a handler. It's used primarily
// for the wrappers. What's handed to the actual method is the concrete
// request and response types.
type HandlerFunc func(ctx context.Context, req Request, rsp interface{
}) error
// 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...)
}
}
// SubscriberWrapper wraps the SubscriberFunc and returns the equivalent
type SubscriberWrapper func(SubscriberFunc) SubscriberFunc
// SubscriberFunc represents a single method of a subscriber. It's used primarily
// for the wrappers. What's handed to the actual method is the concrete
// publication message.
type SubscriberFunc func(ctx context.Context, msg Message) error
// Message is an async message interface
type Message interface {
// Topic of the message
Topic() string
// The decoded payload value
Payload() interface{
}
// The content type of the payload
ContentType() string
// The raw headers of the message
Header() map[string]string
// The raw body of the message
Body() []byte
// Codec used to decode the message
Codec() codec.Reader
}
// 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...))
}
}
// CallFunc represents the individual call func
type CallFunc func(ctx context.Context, node *registry.Node, req Request, rsp interface{
}, opts CallOptions) error
// CallWrapper is a low level wrapper for the CallFunc
type CallWrapper func(CallFunc) CallFunc
// 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)
}
}
}
// Wrapper wraps a client and returns a client
type Wrapper func(Client) Client
// Client is the interface used to make requests to services.
// It supports Request/Response via Transport and Publishing via the Broker.
// It also supports bidirectional streaming of requests.
type Client interface {
Init(...Option) error
Options() Options
NewMessage(topic string, msg interface{
}, opts ...MessageOption) Message
NewRequest(service, endpoint string, req interface{
}, reqOpts ...RequestOption) Request
Call(ctx context.Context, req Request, rsp interface{
}, opts ...CallOption) error
Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error)
Publish(ctx context.Context, msg Message, opts ...PublishOption) error
String() string
// BeforeStart run funcs before service starts
func BeforeStart(fn func() error) Option {
return func(o *Options) {
o.BeforeStart = append(o.BeforeStart, fn)
}
// BeforeStop run funcs before service stops
func BeforeStop(fn func() error) Option {
return func(o *Options) {
o.BeforeStop = append(o.BeforeStop, fn)
}
}
// AfterStart run funcs after service starts
func AfterStart(fn func() error) Option {
return func(o *Options) {
o.AfterStart = append(o.AfterStart, fn)
}
}
// AfterStop run funcs after service stops
func AfterStop(fn func() error) Option {
return func(o *Options) {
o.AfterStop = append(o.AfterStop, fn)
}
}
// Action can be used to parse user provided cli options
func Action(a func(*cli.Context) error) Option {
return func(o *Options) {
o.Cmd.App().Action = a
}
}
// Context is a type that is passed through to
// each Handler action in a cli application. Context
// can be used to retrieve context-specific args and
// parsed command-line options.
type Context struct {
context.Context
App *App
Command *Command
shellComplete bool
flagSet *flag.FlagSet
parentContext *Context
}
// Flags that can be passed to service
func Flags(flags ...cli.Flag) Option {
return func(o *Options) {
o.Cmd.App().Flags = append(o.Cmd.App().Flags, flags...)
}
}
func Cmd(c cmd.Cmd) Option {
return func(o *Options) {
o.Cmd = c
}
}
type Cmd interface {
// The cli app within this cmd
App() *cli.App
// Adds options, parses flags and initialise
// exits on error
Init(opts ...Option) error
// Options set within this command
Options() Options
}
// Metadata associated with the service
func Metadata(md map[string]string) Option {
return func(o *Options) {
o.Server.Init(server.Metadata(md))
}
}
// Transport sets the transport for the service
// and the underlying components
func Transport(t transport.Transport) Option {
return func(o *Options) {
o.Transport = t
// Update Client and Server
o.Client.Init(client.Transport(t))
o.Server.Init(server.Transport(t))
}
}
// Transport is an interface which is used for communication between
// services. It uses connection based socket send/recv semantics and
// has various implementations; http, grpc, quic.
type Transport interface {
Init(...Option) error
Options() Options
Dial(addr string, opts ...DialOption) (Client, error)
Listen(addr string, opts ...ListenOption) (Listener, error)
String() string
}
// Selector sets the selector for the service client
func Selector(s selector.Selector) Option {
return func(o *Options) {
o.Client.Init(client.Selector(s))
}
}
// Selector builds on the registry as a mechanism to pick nodes
// and mark their status. This allows host pools and other things
// to be built using various algorithms.
type Selector interface {
Init(opts ...Option) error
Options() Options
// Select returns a function which should return the next node
Select(service string, opts ...SelectOption) (Next, error)
// Mark sets the success/error against a node
Mark(service string, node *registry.Node, err error)
// Reset returns state back to zero for a service
Reset(service string)
// Close renders the selector unusable
Close() error
// Name of the selector
String() string
}
// Registry sets the registry for the service
// and the underlying components
func Registry(r registry.Registry) Option {
return func(o *Options) {
o.Registry = r
// Update Client and Server
o.Client.Init(client.Registry(r))
o.Server.Init(server.Registry(r))
// Update Broker
o.Broker.Init(broker.Registry(r))
}
}
// The registry provides an interface for service discovery
// and an abstraction over varying implementations
// {consul, etcd, zookeeper, ...}
type Registry interface {
Init(...Option) error
Options() Options
Register(*Service, ...RegisterOption) error
Deregister(*Service, ...DeregisterOption) error
GetService(string, ...GetOption) ([]*Service, error)
ListServices(...ListOption) ([]*Service, error)
Watch(...WatchOption) (Watcher, error)
String() string
}
// Server to be used for service
func Server(s server.Server) Option {
return func(o *Options) {
o.Server = s
}
}
// Server is a simple micro server abstraction
type Server interface {
// Initialise options
Init(...Option) error
// Retrieve the options
Options() Options
// Register a handler
Handle(Handler) error
// Create a new handler
NewHandler(interface{
}, ...HandlerOption) Handler
// Create a new subscriber
NewSubscriber(string, interface{
}, ...SubscriberOption) Subscriber
// Register a subscriber
Subscribe(Subscriber) error
// Start the server
Start() error
// Stop the server
Stop() error
// Server implementation
String() string
}
// HandleSignal toggles automatic installation of the signal handler that
// traps TERM, INT, and QUIT. Users of this feature to disable the signal
// handler, should control liveness of the service through the context.
func HandleSignal(b bool) Option {
return func(o *Options) {
o.Signal = b
}
}
// 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) {
o.Context = ctx
}
}
// Client to be used for service
func Client(c client.Client) Option {
return func(o *Options) {
o.Client = c
}
}
// Client is the interface used to make requests to services.
// It supports Request/Response via Transport and Publishing via the Broker.
// It also supports bidirectional streaming of requests.
type Client interface {
Init(...Option) error
Options() Options
NewMessage(topic string, msg interface{
}, opts ...MessageOption) Message
NewRequest(service, endpoint string, req interface{
}, reqOpts ...RequestOption) Request
Call(ctx context.Context, req Request, rsp interface{
}, opts ...CallOption) error
Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error)
Publish(ctx context.Context, msg Message, opts ...PublishOption) error
String() string
}
// Broker to be used for service
func Broker(b broker.Broker) Option {
return func(o *Options) {
o.Broker = b
// Update Client and Server
o.Client.Init(client.Broker(b))
o.Server.Init(server.Broker(b))
}
}
// Broker is an interface used for asynchronous messaging.
type Broker interface {
Init(...Option) error
Options() Options
Address() string
Connect() error
Disconnect() error
Publish(topic string, m *Message, opts ...PublishOption) error
Subscribe(topic string, h Handler, opts ...SubscribeOption) (Subscriber, error)
String() string
}
// Profile to be used for debug profile
func Profile(p profile.Profile) Option {
return func(o *Options) {
o.Profile = p
}
}
type Profile interface {
// Start the profiler
Start() error
// Stop the profiler
Stop() error
// Name of the profiler
String() string
}
// Tracer sets the tracer for the service
func Tracer(t trace.Tracer) Option {
return func(o *Options) {
o.Server.Init(server.Tracer(t))
}
}
// Tracer is an interface for distributed tracing
type Tracer interface {
// Start a trace
Start(ctx context.Context, name string) (context.Context, *Span)
// Finish the trace
Finish(*Span) error
// Read the traces
Read(...ReadOption) ([]*Span, error)
}
// Auth sets the auth for the service
func Auth(a auth.Auth) Option {
return func(o *Options) {
o.Auth = a
o.Server.Init(server.Auth(a))
}
}
// Auth providers authentication and authorization
type Auth interface {
// Init the auth
Init(opts ...Option)
// Options set for auth
Options() Options
// Generate a new account
Generate(id string, opts ...GenerateOption) (*Account, error)
// Grant access to a resource
Grant(role string, res *Resource) error
// Revoke access to a resource
Revoke(role string, res *Resource) error
// Verify an account has access to a resource
Verify(acc *Account, res *Resource) error
// Inspect a token
Inspect(token string) (*Account, error)
// Token generated using refresh token
Token(opts ...TokenOption) (*Token, error)
// String returns the name of the implementation
String() string
}
// Config sets the config for the service
func Config(c config.Config) Option {
return func(o *Options) {
o.Config = c
}
}
// Config is an interface abstraction for dynamic configuration
type Config interface {
// provide the reader.Values interface
reader.Values
// Init the config
Init(opts ...Option) error
// Options in the config
Options() Options
// Stop the config loader/watcher
Close() error
// Load config sources
Load(source ...source.Source) error
// Force a source changeset sync
Sync() error
// Watch a value for changes
Watch(path ...string) (Watcher, error)
}
Micro 推荐用户通过环境变量指定设置,这种方法更加灵活。运行 ./micro_hello -h 查看所有可以指定的参数
luslin@local:~/go/workspace/tools/micro-hello$ ./micro_hello -h
NAME:
com.foo.service.micro - a go-micro service
USAGE:
micro_hello [global options] command [command options] [arguments...]
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--client value Client for go-micro; rpc [$MICRO_CLIENT]
--client_request_timeout value Sets the client request timeout. e.g 500ms, 5s, 1m. Default: 5s [$MICRO_CLIENT_REQUEST_TIMEOUT]
--client_retries value Sets the client retries. Default: 1 (default: 1) [$MICRO_CLIENT_RETRIES]
--client_pool_size value Sets the client connection pool size. Default: 1 (default: 0) [$MICRO_CLIENT_POOL_SIZE]
--client_pool_ttl value Sets the client connection pool ttl. e.g 500ms, 5s, 1m. Default: 1m [$MICRO_CLIENT_POOL_TTL]
--register_ttl value Register TTL in seconds (default: 60) [$MICRO_REGISTER_TTL]
--register_interval value Register interval in seconds (default: 30) [$MICRO_REGISTER_INTERVAL]
--server value Server for go-micro; rpc [$MICRO_SERVER]
--server_name value Name of the server. go.micro.srv.example [$MICRO_SERVER_NAME]
--server_version value Version of the server. 1.1.0 [$MICRO_SERVER_VERSION]
--server_id value Id of the server. Auto-generated if not specified [$MICRO_SERVER_ID]
--server_address value Bind address for the server. 127.0.0.1:8080 [$MICRO_SERVER_ADDRESS]
--server_advertise value Used instead of the server_address when registering with discovery. 127.0.0.1:8080 [$MICRO_SERVER_ADVERTISE]
--server_metadata value A list of key-value pairs defining metadata. version=1.0.0 [$MICRO_SERVER_METADATA]
--broker value Broker for pub/sub. http, nats, rabbitmq [$MICRO_BROKER]
--broker_address value Comma-separated list of broker addresses [$MICRO_BROKER_ADDRESS]
--profile value Debug profiler for cpu and memory stats [$MICRO_DEBUG_PROFILE]
--registry value Registry for discovery. etcd, mdns [$MICRO_REGISTRY]
--registry_address value Comma-separated list of registry addresses [$MICRO_REGISTRY_ADDRESS]
--runtime value Runtime for building and running services e.g local, kubernetes (default: "local") [$MICRO_RUNTIME]
--runtime_source value Runtime source for building and running services e.g github.com/micro/service (default: "github.com/micro/services") [$MICRO_RUNTIME_SOURCE]
--selector value Selector used to pick nodes for querying [$MICRO_SELECTOR]
--store value Store used for key-value storage [$MICRO_STORE]
--store_address value Comma-separated list of store addresses [$MICRO_STORE_ADDRESS]
--store_database value Database option for the underlying store [$MICRO_STORE_DATABASE]
--store_table value Table option for the underlying store [$MICRO_STORE_TABLE]
--transport value Transport mechanism used; http [$MICRO_TRANSPORT]
--transport_address value Comma-separated list of transport addresses [$MICRO_TRANSPORT_ADDRESS]
--tracer value Tracer for distributed tracing, e.g. memory, jaeger [$MICRO_TRACER]
--tracer_address value Comma-separated list of tracer addresses [$MICRO_TRACER_ADDRESS]
--auth value Auth for role based access control, e.g. service [$MICRO_AUTH]
--auth_id value Account ID used for client authentication [$MICRO_AUTH_ID]
--auth_secret value Account secret used for client authentication [$MICRO_AUTH_SECRET]
--auth_namespace value Namespace for the services auth account [$MICRO_AUTH_NAMESPACE]
--auth_public_key value Public key for JWT auth (base64 encoded PEM) [$MICRO_AUTH_PUBLIC_KEY]
--auth_private_key value Private key for JWT auth (base64 encoded PEM) [$MICRO_AUTH_PRIVATE_KEY]
--auth_provider value Auth provider used to login user [$MICRO_AUTH_PROVIDER]
--auth_provider_client_id value The client id to be used for oauth [$MICRO_AUTH_PROVIDER_CLIENT_ID]
--auth_provider_client_secret value The client secret to be used for oauth [$MICRO_AUTH_PROVIDER_CLIENT_SECRET]
--auth_provider_endpoint value The enpoint to be used for oauth [$MICRO_AUTH_PROVIDER_ENDPOINT]
--auth_provider_redirect value The redirect to be used for oauth [$MICRO_AUTH_PROVIDER_REDIRECT]
--auth_provider_scope value The scope to be used for oauth [$MICRO_AUTH_PROVIDER_SCOPE]
--config value The source of the config to be used to get configuration [$MICRO_CONFIG]
--help, -h show help (default: false)
初始化服务:
// Initialise service
service.Init()
// Init initialises options
Init(...Option)
Init()也可以接收Option,所以上面的29个option也可以在这里使用。它们有相同的作用,但执行时期不同
由于此时服务已经创建,我们可以从实例中获取信息,比如获取随机使用的端口
// Initialise service
service.Init(
micro.AfterStart(func() error {
log.Infof("service listening on %s\n", service.Options().Server.Options().Address)
return nil
}))
结果:
2020-05-23 13:54:21 file=main.go:21 level=info service listening on [::]:37125
// Register Handler
hello.RegisterMicroHandler(service.Server(), new(handler.Micro))
// Register Struct as Subscriber
micro.RegisterSubscriber("go.micro.service.micro", service.Server(), new(subscriber.Micro))
只有在处理程序注册后,我们的业务代码才能真正的对外提供服务,有两个典型的注册:
func RegisterMicroHandler(s server.Server, hdlr MicroHandler, opts ...server.HandlerOption) error {
type micro interface {
Call(ctx context.Context, in *Request, out *Response) error
Stream(ctx context.Context, stream server.Stream) error
PingPong(ctx context.Context, stream server.Stream) error
}
type Micro struct {
micro
}
h := µHandler{
hdlr}
return s.Handle(s.NewHandler(&Micro{
h}, opts...))
}
type MicroHandler interface {
Call(context.Context, *Request, *Response) error
Stream(context.Context, *StreamingRequest, Micro_StreamStream) error
PingPong(context.Context, Micro_PingPongStream) error
}
// 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...))
}
// Run service
if err := service.Run(); err != nil {
log.Fatal(err)
}
在命令行中运行 micro web
micro web
2020-05-23 14:11:21 file=http.go:90 level=info service=web HTTP API Listening on [::]:8082
2020-05-23 14:11:21 file=auth.go:31 level=info service=web Auth [noop] Authenticated as go.micro.web-f65783c6-0ffc-4d88-bc58-f2a5de158845 in the go.micro namespace
2020-05-23 14:11:21 file=service.go:205 level=info service=web Starting [service] go.micro.web
2020-05-23 14:11:21 file=grpc.go:845 level=info service=web Server [grpc] Listening on [::]:38703
2020-05-23 14:11:21 file=grpc.go:676 level=info service=web Registry [mdns] Registering node: go.micro.web-f65783c6-0ffc-4d88-bc58-f2a5de158845
默认http 端口是8082 ,使用 micro web -h 获取更多信息
luslin@local:~/software/zipkin$ micro web -h
NAME:
micro web - Run the web dashboard
USAGE:
micro web [command options] [arguments...]
OPTIONS:
--address value Set the web UI address e.g 0.0.0.0:8082 [$MICRO_WEB_ADDRESS]
--namespace value Set the namespace used by the Web proxy e.g. com.example.web [$MICRO_WEB_NAMESPACE]
--resolver value Set the resolver to route to services e.g path, domain [$MICRO_WEB_RESOLVER]
--auth_login_url value The relative URL where a user can login [$MICRO_AUTH_LOGIN_URL]
--help, -h show help (default: false)
由于我的etcd 搭在本地了, 所以指定了register
MICRO_REGISTRY=etcd micro web
然后访问: http://127.0.0.1:8082/service/com.foo.service.micro
我们可以检查服务的各种信息:
luslin@local:~/software/zipkin$ etcdctl get / --prefix
/micro/registry/com.foo.service.micro/com.foo.service.micro-7a74aca6-2270-4cf3-9a92-208474becde7
{
"name":"com.foo.service.micro","version":"latest","metadata":null,"endpoints":[{
"name":"Micro.Call","request":{
"name":"Request","type":"Request","values":[{
"name":"name","type":"string","values":null}]},"response":{
"name":"Response","type":"Response","values":[{
"name":"msg","type":"string","values":null}]},"metadata":{
}},{
"name":"Micro.PingPong","request":{
"name":"Context","type":"Context","values":null},"response":{
"name":"Stream","type":"Stream","values":null},"metadata":{
"stream":"true"}},{
"name":"Micro.Stream","request":{
"name":"Context","type":"Context","values":null},"response":{
"name":"Stream","type":"Stream","values":null},"metadata":{
"stream":"true"}},{
"name":"Micro.Handle","request":{
"name":"Message","type":"Message","values":[{
"name":"say","type":"string","values":null}]},"response":null,"metadata":{
"subscriber":"true","topic":"go.micro.service.micro"}}],"nodes":[{
"id":"com.foo.service.micro-7a74aca6-2270-4cf3-9a92-208474becde7","address":"192.168.1.88:34551","metadata":{
"broker":"http","protocol":"grpc","registry":"etcd","server":"grpc","transport":"grpc"}}]}
/micro/registry/com.foo.service.micro/com.foo.service.micro-d3216e6d-28f0-4ba8-9d17-1a20e51a44ac
{
"name":"com.foo.service.micro","version":"latest","metadata":null,"endpoints":[{
"name":"Micro.Call","request":{
"name":"Request","type":"Request","values":[{
"name":"name","type":"string","values":null}]},"response":{
"name":"Response","type":"Response","values":[{
"name":"msg","type":"string","values":null}]},"metadata":{
}},{
"name":"Micro.PingPong","request":{
"name":"Context","type":"Context","values":null},"response":{
"name":"Stream","type":"Stream","values":null},"metadata":{
"stream":"true"}},{
"name":"Micro.Stream","request":{
"name":"Context","type":"Context","values":null},"response":{
"name":"Stream","type":"Stream","values":null},"metadata":{
"stream":"true"}},{
"name":"Micro.Handle","request":{
"name":"Message","type":"Message","values":[{
"name":"say","type":"string","values":null}]},"response":null,"metadata":{
"subscriber":"true","topic":"go.micro.service.micro"}}],"nodes":[{
"id":"com.foo.service.micro-d3216e6d-28f0-4ba8-9d17-1a20e51a44ac","address":"192.168.1.88:45125","metadata":{
"broker":"http","protocol":"grpc","registry":"etcd","server":"grpc","transport":"grpc"}}]}
/micro/registry/go.micro.web/go.micro.web-3f6c03dd-06a1-462c-84b2-c013055c7b29
{
"name":"go.micro.web","version":"latest","metadata":null,"endpoints":[],"nodes":[{
"id":"go.micro.web-3f6c03dd-06a1-462c-84b2-c013055c7b29","address":"192.168.1.88:40275","metadata":{
"broker":"http","protocol":"grpc","registry":"etcd","server":"grpc","transport":"grpc"}}]}
/micro/registry/micro.http.broker/go.micro.service.micro-3219d5be-29c2-4d49-ad8d-5c1029bf0e37
{
"name":"micro.http.broker","version":"ff.http.broadcast","metadata":null,"endpoints":null,"nodes":[{
"id":"go.micro.service.micro-3219d5be-29c2-4d49-ad8d-5c1029bf0e37","address":"127.0.0.1:42653","metadata":{
"broker":"http","secure":"false","topic":"go.micro.service.micro"}}]}
/micro/registry/micro.http.broker/go.micro.service.micro-a2c12e05-862b-4574-859c-be9056cb485b
{
"name":"micro.http.broker","version":"ff.http.broadcast","metadata":null,"endpoints":null,"nodes":[{
"id":"go.micro.service.micro-a2c12e05-862b-4574-859c-be9056cb485b","address":"127.0.0.1:36133","metadata":{
"broker":"http","secure":"false","topic":"go.micro.service.micro"}}]}