啓動程序說明
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個參數來控制服務。
- 1 micro.Name,指定服務名稱。默認是go.micro.server。
// Name of the service
func Name(n string) Option {
return func(o *Options) {
o.Server.Init(server.Name(n))
}
}
- 2 micro.Version指定服務版本,默認是啓動時間的格式化字符串。使用正確的版本號,結合正確的選擇器,我們可以實現優雅的輪換升級,灰度測試,A/B測試及其他操作
// Version of the service
func Version(v string) Option {
return func(o *Options) {
o.Server.Init(server.Version(v))
}
}
- 3 micro.Address 指定grpc地址,默認是本地並且隨機端口。因爲使用Registry進行服務發現,所以隨機端口並不影響。然而,實踐中通常會固定一個端口,有利於管控與安全
func Address(addr string) Option {
return func(o *Options) {
o.Server.Init(server.Address(addr))
}
}
- 4 micro.RegisterTTL 指定服務註冊超時時間,默認1分鐘
// 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))
}
}
- 5 micro.RegisterInterval 指定服務向registry報告狀態的時間間隔。避免服務停機時註冊信息出現錯誤
// 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))
}
}
- 6 WrapHandler 從概念上講與中間件類似,集中控制方法的行爲。可以有多層,執行順序從外向內
// 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
- 7 micro.WrapSubscriber 與 WrapHandler 類似,除了它是用於異步消息傳遞
// 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
}
- 8 micro.WrapCall 包裝來自客戶端的每個方法調用
// 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
- 9 micro.WrapClient 包裝服務鏈接,可以應用於多個層次,執行順序由內到外
// 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
- 10 micro.BeforeStart 在服務啓動前設置多個回調函數
// BeforeStart run funcs before service starts
func BeforeStart(fn func() error) Option {
return func(o *Options) {
o.BeforeStart = append(o.BeforeStart, fn)
}
- 11 micro.BeforeStop 在服務停止前設置多個回調函數
// BeforeStop run funcs before service stops
func BeforeStop(fn func() error) Option {
return func(o *Options) {
o.BeforeStop = append(o.BeforeStop, fn)
}
}
- 12 micro.AfterStart 在服務啓動後設置多個回調函數
// AfterStart run funcs after service starts
func AfterStart(fn func() error) Option {
return func(o *Options) {
o.AfterStart = append(o.AfterStart, fn)
}
}
- 13 micro.AfterStop 在服務停止後設置多個回調函數
// AfterStop run funcs after service stops
func AfterStop(fn func() error) Option {
return func(o *Options) {
o.AfterStop = append(o.AfterStop, fn)
}
}
- 14 micro.Action 處理命令行參數,支持子命令與標誌。更多參考micro/cli
// 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
}
- 15 micro.Flags 處理命令行標誌。更多參考micro/cli
// 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...)
}
}
- 16 micro.Cmd 指定命令行處理對象
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
}
- 17 micro.Metadata 指定服務元信息,元信息通常用來給服務打標籤或分組,實現客戶端負載均衡策略等
// Metadata associated with the service
func Metadata(md map[string]string) Option {
return func(o *Options) {
o.Server.Init(server.Metadata(md))
}
}
- 18 micro.Transport 指定傳輸協議,默認HTTP
// 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
}
- 19 micro.Selector 指定節點選擇來實現不同的負載均衡,默認隨機選擇
// 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
}
- 20 micro.Registry指定服務發現,默認使用mdns
// 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
}
- 21 micro.Server 指定使用的服務,默認rpcServer
// 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
}
- 22 micro.HandleSignal 指定是否默認捕獲TERM, INT, QUIT信號。 默認true
// 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
}
}
- 23 micro.Context 指定服務使用的上下文信息。默認context.BackGround()。可以用來控制服務的聲明週期
// 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
}
}
- 24 micro.Client 指定服務鏈接。默認rpcClient
// 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
}
- 25 micro.Broker 指定消息代理。默認HTTP broker
// 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
}
- 26 micro.Profile 用於調試的配置文件
// 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
}
- 27 micro.Tracer 指定服務的鏈路追蹤程序
// 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)
}
- 28 micro.Auth 設置服務用戶
// 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
}
- 29 micro.Config 服務配置信息
// 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))
只有在處理程序註冊後,我們的業務代碼才能真正的對外提供服務,有兩個典型的註冊:
- 1 註冊grcp處理程序,創建一個handler.Micro對象,然後註冊到服務中。可以註冊多個Handler來提供不同的業務方法
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
}
- 2 註冊消息處理對象,第一個參數是message topic, 第二個是server 第三個是消息處理對象
// 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
我們可以檢查服務的各種信息:
- 服務名稱
- 節點列表,這裏開啓了兩個服務
- 端點:服務定義,groc服務名,方法名, 參數信息,數據類別等
./micro_hello 與 micro web 是不能相互訪問的,web是如何獲取服務信息的呢?,我使用etcd註冊,可以在etcd中查看服務註冊信息
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"}}]}