From 1f1bc274218495726ce5afcb133c3caec9155dff Mon Sep 17 00:00:00 2001 From: Asim Date: Sat, 2 Jan 2016 00:38:57 +0000 Subject: [PATCH] Make command internal --- README.md | 161 ++++++++++++++++++++------------ cmd/cmd.go | 111 +++++++++++++++++----- cmd/options.go | 62 +++++++++++- examples/server/main.go | 28 ------ examples/server/wrapper/main.go | 78 ++++++++++++++++ options.go | 9 +- server/rpc_server.go | 7 +- service.go | 4 +- 8 files changed, 329 insertions(+), 131 deletions(-) create mode 100644 examples/server/wrapper/main.go diff --git a/README.md b/README.md index 22f68aec..3dbce10a 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ By default go-micro only provides a single implementation of each interface. Plu ## Prerequisites -Consul is the default discovery mechanism provided in go-micro. Discovery is however pluggable. +Consul is the default discovery mechanism provided in go-micro. Discovery is however pluggable so you can used etcd, kubernetes, zookeeper, etc. ### Install Consul [https://www.consul.io/intro/getting-started/install.html](https://www.consul.io/intro/getting-started/install.html) @@ -41,104 +41,141 @@ $ consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul ### Run Service ``` -$ go run examples/server/main.go --logtostderr -I1108 11:08:19.926071 11358 server.go:96] Starting server go.micro.srv.example id go.micro.srv.example-04de4cf0-8609-11e5-bf3a-68a86d0d36b6 -I1108 11:08:19.926407 11358 rpc_server.go:233] Listening on [::]:54080 -I1108 11:08:19.926500 11358 http_broker.go:80] Broker Listening on [::]:54081 -I1108 11:08:19.926632 11358 rpc_server.go:158] Registering node: go.micro.srv.example-04de4cf0-8609-11e5-bf3a-68a86d0d36b6 +$ go run examples/service/main.go --logtostderr +I0102 00:22:26.413467 12018 rpc_server.go:297] Listening on [::]:62492 +I0102 00:22:26.413803 12018 http_broker.go:115] Broker Listening on [::]:62493 +I0102 00:22:26.414009 12018 rpc_server.go:212] Registering node: greeter-e6b2fc6f-b0e6-11e5-a42f-68a86d0d36b6 ``` ### Test Service ``` -$ go run examples/client/main.go -go.micro.srv.example-59b6e0ab-0300-11e5-b696-68a86d0d36b6: Hello John +$ go run examples/service/main.go --client +Hello John ``` ## Writing a service ### Create request/response proto -`go-micro/examples/server/proto/example/example.proto`: +`go-micro/examples/service/proto/greeter.proto`: ``` syntax = "proto3"; -message Request { - string name = 1; +service Greeter { + rpc Hello(HelloRequest) returns (HelloResponse) {} } -message Response { - string msg = 1; +message HelloRequest { + string name = 1; +} + +message HelloResponse { + string greeting = 2; } ``` -Compile proto `protoc -I$GOPATH/src --go_out=$GOPATH/src $GOPATH/src/github.com/micro/go-micro/examples/server/proto/example/example.proto` +### Install protobuf for code generation -### Create request handler -`go-micro/examples/server/handler/example.go`: +We use a protobuf plugin for code generation. This is completely optional. Look at [examples/server](https://github.com/micro/go-micro/blob/master/examples/server/main.go) +and [examples/client](https://github.com/micro/go-micro/blob/master/examples/client/main.go) for examples without code generation. -```go -package handler - -import ( - log "github.com/golang/glog" - c "github.com/micro/go-micro/context" - example "github.com/micro/go-micro/examples/server/proto/example" - "github.com/micro/go-micro/server" - - "golang.org/x/net/context" -) - -type Example struct{} - -func (e *Example) Call(ctx context.Context, req *example.Request, rsp *example.Response) error { - md, _ := c.GetMetadata(ctx) - log.Infof("Received Example.Call request with metadata: %v", md) - rsp.Msg = server.Options().Id + ": Hello " + req.Name - return nil -} +```shell +go get github.com/micro/protobuf ``` -### Init server -`go-micro/examples/server/main.go`: +Compile proto `protoc -I$GOPATH/src --go_out=plugins=micro:$GOPATH/src $GOPATH/src/github.com/micro/go-micro/examples/service/proto/greeter.proto` + +### Define the service +`go-micro/examples/service/main.go`: ```go package main import ( - log "github.com/golang/glog" - "github.com/micro/go-micro/cmd" - "github.com/micro/go-micro/examples/server/handler" - "github.com/micro/go-micro/server" + "fmt" + + micro "github.com/micro/go-micro" + proto "github.com/micro/go-micro/examples/service/proto" + "golang.org/x/net/context" ) +type Greeter struct{} + +func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error { + rsp.Greeting = "Hello " + req.Name + return nil +} + func main() { - // optionally setup command line usage - cmd.Init() - - // Initialise Server - server.Init( - server.Name("go.micro.srv.example"), + // Create a new service. Optionally include some options here. + service := micro.NewService( + micro.Name("greeter"), + micro.Version("latest"), + micro.Metadata(map[string]string{ + "type": "helloworld", + }), ) - // Register Handlers - server.Handle( - server.NewHandler( - new(handler.Example), - ), - ) + // Init will parse the command line flags. Any flags set will + // override the above settings. Options defined here will + // override anything set on the command line. + service.Init() - // Run server - if err := server.Run(); err != nil { - log.Fatal(err) + // Register handler + proto.RegisterGreeterHandler(service.Server(), new(Greeter)) + + // Run the server + if err := service.Run(); err != nil { + fmt.Println(err) } } ``` ### Run service ``` -$ go run examples/server/main.go --logtostderr -I1108 11:08:19.926071 11358 server.go:96] Starting server go.micro.srv.example id go.micro.srv.example-04de4cf0-8609-11e5-bf3a-68a86d0d36b6 -I1108 11:08:19.926407 11358 rpc_server.go:233] Listening on [::]:54080 -I1108 11:08:19.926500 11358 http_broker.go:80] Broker Listening on [::]:54081 -I1108 11:08:19.926632 11358 rpc_server.go:158] Registering node: go.micro.srv.example-04de4cf0-8609-11e5-bf3a-68a86d0d36b6 +go run examples/service/main.go --logtostderr +I0102 00:22:26.413467 12018 rpc_server.go:297] Listening on [::]:62492 +I0102 00:22:26.413803 12018 http_broker.go:115] Broker Listening on [::]:62493 +I0102 00:22:26.414009 12018 rpc_server.go:212] Registering node: greeter-e6b2fc6f-b0e6-11e5-a42f-68a86d0d36b6 +``` + +### Define a client + +`client.go` + +```go +package main + +import ( + "fmt" + + micro "github.com/micro/go-micro" + proto "github.com/micro/go-micro/examples/service/proto" + "golang.org/x/net/context" +) + + +func main() { + // Create a new service. Optionally include some options here. + service := micro.NewService(micro.Name("greeter.client")) + + // Create new greeter client + greeter := proto.NewGreeterClient("greeter", service.Client()) + + // Call the greeter + rsp, err := greeter.Hello(context.TODO(), &proto.HelloRequest{Name: "John"}) + if err != nil { + fmt.Println(err) + } + + // Print response + fmt.Println(rsp.Greeting) +} +``` + +### Run the client + +```shell +go run client.go +Hello John ``` diff --git a/cmd/cmd.go b/cmd/cmd.go index 4c08970f..7d7ed024 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -57,7 +57,6 @@ var ( cli.StringFlag{ Name: "server_address", EnvVar: "MICRO_SERVER_ADDRESS", - Value: ":0", Usage: "Bind address for the server. 127.0.0.1:8080", }, cli.StringFlag{ @@ -74,7 +73,6 @@ var ( cli.StringFlag{ Name: "broker", EnvVar: "MICRO_BROKER", - Value: "http", Usage: "Broker for pub/sub. http, nats, rabbitmq", }, cli.StringFlag{ @@ -85,7 +83,6 @@ var ( cli.StringFlag{ Name: "registry", EnvVar: "MICRO_REGISTRY", - Value: "consul", Usage: "Registry for discovery. memory, consul, etcd, kubernetes", }, cli.StringFlag{ @@ -96,13 +93,11 @@ var ( cli.StringFlag{ Name: "selector", EnvVar: "MICRO_SELECTOR", - Value: "selector", Usage: "Selector used to pick nodes for querying. random, roundrobin, blacklist", }, cli.StringFlag{ Name: "transport", EnvVar: "MICRO_TRANSPORT", - Value: "http", Usage: "Transport mechanism used; http, rabbitmq, nats", }, cli.StringFlag{ @@ -170,6 +165,13 @@ func init() { func newCmd(opts ...Option) Cmd { options := Options{ + Broker: &broker.DefaultBroker, + Client: &client.DefaultClient, + Registry: ®istry.DefaultRegistry, + Server: &server.DefaultServer, + Selector: &selector.DefaultSelector, + Transport: &transport.DefaultTransport, + Brokers: DefaultBrokers, Registries: DefaultRegistries, Selectors: DefaultSelectors, @@ -221,22 +223,63 @@ func (c *cmd) Before(ctx *cli.Context) error { flag.Set("v", ctx.String("v")) flag.Parse() - if b, ok := c.opts.Brokers[ctx.String("broker")]; ok { - broker.DefaultBroker = b(strings.Split(ctx.String("broker_address"), ",")) + // If flags are set then use them otherwise do nothing + var serverOpts []server.Option + var clientOpts []client.Option + + // Set the broker + if len(ctx.String("broker")) > 0 { + if b, ok := c.opts.Brokers[ctx.String("broker")]; ok { + n := b(strings.Split(ctx.String("broker_address"), ",")) + c.opts.Broker = &n + } else { + return fmt.Errorf("Broker %s not found", ctx.String("broker")) + } + + serverOpts = append(serverOpts, server.Broker(*c.opts.Broker)) + clientOpts = append(clientOpts, client.Broker(*c.opts.Broker)) } - if r, ok := c.opts.Registries[ctx.String("registry")]; ok { - registry.DefaultRegistry = r(strings.Split(ctx.String("registry_address"), ",")) + // Set the registry + if len(ctx.String("registry")) > 0 { + if r, ok := c.opts.Registries[ctx.String("registry")]; ok { + n := r(strings.Split(ctx.String("registry_address"), ",")) + c.opts.Registry = &n + } else { + return fmt.Errorf("Registry %s not found", ctx.String("registry")) + } + + serverOpts = append(serverOpts, server.Registry(*c.opts.Registry)) + clientOpts = append(clientOpts, client.Registry(*c.opts.Registry)) } - if s, ok := c.opts.Selectors[ctx.String("selector")]; ok { - selector.DefaultSelector = s(selector.Registry(registry.DefaultRegistry)) + // Set the selector + if len(ctx.String("selector")) > 0 { + if s, ok := c.opts.Selectors[ctx.String("selector")]; ok { + n := s(selector.Registry(*c.opts.Registry)) + c.opts.Selector = &n + } else { + return fmt.Errorf("Selector %s not found", ctx.String("selector")) + } + + // No server option here. Should there be? + clientOpts = append(clientOpts, client.Selector(*c.opts.Selector)) } - if t, ok := c.opts.Transports[ctx.String("transport")]; ok { - transport.DefaultTransport = t(strings.Split(ctx.String("transport_address"), ",")) + // Set the transport + if len(ctx.String("transport")) > 0 { + if t, ok := c.opts.Transports[ctx.String("transport")]; ok { + n := t(strings.Split(ctx.String("transport_address"), ",")) + c.opts.Transport = &n + } else { + return fmt.Errorf("Transport %s not found", ctx.String("transport")) + } + + serverOpts = append(serverOpts, server.Transport(*c.opts.Transport)) + clientOpts = append(clientOpts, client.Transport(*c.opts.Transport)) } + // Parse the server options metadata := make(map[string]string) for _, d := range ctx.StringSlice("server_metadata") { var key, val string @@ -248,16 +291,40 @@ func (c *cmd) Before(ctx *cli.Context) error { metadata[key] = val } - server.DefaultServer = server.NewServer( - server.Name(ctx.String("server_name")), - server.Version(ctx.String("server_version")), - server.Id(ctx.String("server_id")), - server.Address(ctx.String("server_address")), - server.Advertise(ctx.String("server_advertise")), - server.Metadata(metadata), - ) + if len(metadata) > 0 { + serverOpts = append(serverOpts, server.Metadata(metadata)) + } - client.DefaultClient = client.NewClient() + if len(ctx.String("server_name")) > 0 { + serverOpts = append(serverOpts, server.Name(ctx.String("server_name"))) + } + + if len(ctx.String("server_version")) > 0 { + serverOpts = append(serverOpts, server.Version(ctx.String("server_version"))) + } + + if len(ctx.String("server_id")) > 0 { + serverOpts = append(serverOpts, server.Id(ctx.String("server_id"))) + } + + if len(ctx.String("server_address")) > 0 { + serverOpts = append(serverOpts, server.Address(ctx.String("server_address"))) + } + + if len(ctx.String("server_advertise")) > 0 { + serverOpts = append(serverOpts, server.Advertise(ctx.String("server_advertise"))) + } + + // We have some command line opts for the server. + // Lets set it up + if len(serverOpts) > 0 { + (*c.opts.Server).Init(serverOpts...) + } + + // Use an init option? + if len(clientOpts) > 0 { + *c.opts.Client = client.NewClient(clientOpts...) + } return nil } diff --git a/cmd/options.go b/cmd/options.go index 70879daf..cd237afe 100644 --- a/cmd/options.go +++ b/cmd/options.go @@ -2,59 +2,113 @@ package cmd import ( "github.com/micro/go-micro/broker" + "github.com/micro/go-micro/client" "github.com/micro/go-micro/registry" "github.com/micro/go-micro/selector" + "github.com/micro/go-micro/server" "github.com/micro/go-micro/transport" ) type Options struct { + // For the Command Line itself Name string Description string Version string + // We need pointers to things so we can swap them out if needed. + Broker *broker.Broker + Registry *registry.Registry + Selector *selector.Selector + Transport *transport.Transport + Client *client.Client + Server *server.Server + Brokers map[string]func([]string, ...broker.Option) broker.Broker Registries map[string]func([]string, ...registry.Option) registry.Registry Selectors map[string]func(...selector.Option) selector.Selector Transports map[string]func([]string, ...transport.Option) transport.Transport } +// Command line Name func Name(n string) Option { return func(o *Options) { o.Name = n } } +// Command line Description func Description(d string) Option { return func(o *Options) { o.Description = d } } +// Command line Version func Version(v string) Option { return func(o *Options) { o.Version = v } } -func Broker(name string, b func([]string, ...broker.Option) broker.Broker) Option { +func Broker(b *broker.Broker) Option { + return func(o *Options) { + o.Broker = b + } +} + +func Selector(s *selector.Selector) Option { + return func(o *Options) { + o.Selector = s + } +} + +func Registry(r *registry.Registry) Option { + return func(o *Options) { + o.Registry = r + } +} + +func Transport(t *transport.Transport) Option { + return func(o *Options) { + o.Transport = t + } +} + +func Client(c *client.Client) Option { + return func(o *Options) { + o.Client = c + } +} + +func Server(s *server.Server) Option { + return func(o *Options) { + o.Server = s + } +} + +// New broker func +func NewBroker(name string, b func([]string, ...broker.Option) broker.Broker) Option { return func(o *Options) { o.Brokers[name] = b } } -func Registry(name string, r func([]string, ...registry.Option) registry.Registry) Option { +// New registry func +func NewRegistry(name string, r func([]string, ...registry.Option) registry.Registry) Option { return func(o *Options) { o.Registries[name] = r } } -func Selector(name string, s func(...selector.Option) selector.Selector) Option { +// New selector func +func NewSelector(name string, s func(...selector.Option) selector.Selector) Option { return func(o *Options) { o.Selectors[name] = s } } -func Transport(name string, t func([]string, ...transport.Option) transport.Transport) Option { +// New transport func +func NewTransport(name string, t func([]string, ...transport.Option) transport.Transport) Option { return func(o *Options) { o.Transports[name] = t } diff --git a/examples/server/main.go b/examples/server/main.go index ad9485fa..bb45e759 100644 --- a/examples/server/main.go +++ b/examples/server/main.go @@ -6,40 +6,12 @@ import ( "github.com/micro/go-micro/examples/server/handler" "github.com/micro/go-micro/examples/server/subscriber" "github.com/micro/go-micro/server" - "golang.org/x/net/context" ) -func logWrapper(fn server.HandlerFunc) server.HandlerFunc { - return func(ctx context.Context, req server.Request, rsp interface{}) error { - log.Infof("[Log Wrapper] Before serving request method: %v", req.Method()) - err := fn(ctx, req, rsp) - log.Infof("[Log Wrapper] After serving request") - return err - } -} - -func logSubWrapper(fn server.SubscriberFunc) server.SubscriberFunc { - return func(ctx context.Context, req server.Publication) error { - log.Infof("[Log Sub Wrapper] Before serving publication topic: %v", req.Topic()) - err := fn(ctx, req) - log.Infof("[Log Sub Wrapper] After serving publication") - return err - } -} - func main() { // optionally setup command line usage cmd.Init() - md := server.DefaultOptions().Metadata - md["datacenter"] = "local" - - server.DefaultServer = server.NewServer( - server.WrapHandler(logWrapper), - server.WrapSubscriber(logSubWrapper), - server.Metadata(md), - ) - // Initialise Server server.Init( server.Name("go.micro.srv.example"), diff --git a/examples/server/wrapper/main.go b/examples/server/wrapper/main.go new file mode 100644 index 00000000..ad9485fa --- /dev/null +++ b/examples/server/wrapper/main.go @@ -0,0 +1,78 @@ +package main + +import ( + log "github.com/golang/glog" + "github.com/micro/go-micro/cmd" + "github.com/micro/go-micro/examples/server/handler" + "github.com/micro/go-micro/examples/server/subscriber" + "github.com/micro/go-micro/server" + "golang.org/x/net/context" +) + +func logWrapper(fn server.HandlerFunc) server.HandlerFunc { + return func(ctx context.Context, req server.Request, rsp interface{}) error { + log.Infof("[Log Wrapper] Before serving request method: %v", req.Method()) + err := fn(ctx, req, rsp) + log.Infof("[Log Wrapper] After serving request") + return err + } +} + +func logSubWrapper(fn server.SubscriberFunc) server.SubscriberFunc { + return func(ctx context.Context, req server.Publication) error { + log.Infof("[Log Sub Wrapper] Before serving publication topic: %v", req.Topic()) + err := fn(ctx, req) + log.Infof("[Log Sub Wrapper] After serving publication") + return err + } +} + +func main() { + // optionally setup command line usage + cmd.Init() + + md := server.DefaultOptions().Metadata + md["datacenter"] = "local" + + server.DefaultServer = server.NewServer( + server.WrapHandler(logWrapper), + server.WrapSubscriber(logSubWrapper), + server.Metadata(md), + ) + + // Initialise Server + server.Init( + server.Name("go.micro.srv.example"), + ) + + // Register Handlers + server.Handle( + server.NewHandler( + new(handler.Example), + ), + ) + + // Register Subscribers + if err := server.Subscribe( + server.NewSubscriber( + "topic.go.micro.srv.example", + new(subscriber.Example), + ), + ); err != nil { + log.Fatal(err) + } + + if err := server.Subscribe( + server.NewSubscriber( + "topic.go.micro.srv.example", + subscriber.Handler, + ), + ); err != nil { + log.Fatal(err) + } + + // Run server + if err := server.Run(); err != nil { + log.Fatal(err) + } +} diff --git a/options.go b/options.go index 9667d91b..159873b4 100644 --- a/options.go +++ b/options.go @@ -28,6 +28,7 @@ type Options struct { func newOptions(opts ...Option) Options { opt := Options{ Broker: broker.DefaultBroker, + Cmd: cmd.DefaultCmd, Client: client.DefaultClient, Server: server.DefaultServer, Registry: registry.DefaultRegistry, @@ -39,14 +40,6 @@ func newOptions(opts ...Option) Options { o(&opt) } - // New Command - if opt.Cmd == nil { - opt.Cmd = cmd.NewCmd( - cmd.Name(opt.Server.Options().Name), - cmd.Version(opt.Server.Options().Version), - ) - } - return opt } diff --git a/server/rpc_server.go b/server/rpc_server.go index 6b65b0ed..1cd63ae9 100644 --- a/server/rpc_server.go +++ b/server/rpc_server.go @@ -110,9 +110,6 @@ func (s *rpcServer) Init(opts ...Option) { for _, opt := range opts { opt(&s.opts) } - if len(s.opts.Id) == 0 { - s.opts.Id = s.opts.Name + "-" + DefaultId - } s.Unlock() } @@ -187,7 +184,7 @@ func (s *rpcServer) Register() error { // register service node := ®istry.Node{ - Id: config.Id, + Id: config.Name + "-" + config.Id, Address: addr, Port: port, Metadata: config.Metadata, @@ -260,7 +257,7 @@ func (s *rpcServer) Deregister() error { } node := ®istry.Node{ - Id: config.Id, + Id: config.Name + "-" + config.Id, Address: addr, Port: port, } diff --git a/service.go b/service.go index 6a82d8d0..7b7fac58 100644 --- a/service.go +++ b/service.go @@ -31,13 +31,13 @@ func newService(opts ...Option) Service { } func (s *service) Init(opts ...Option) { + // Initialise the command flags, overriding new service s.opts.Cmd.Init() + // Update any options to override command flags for _, o := range opts { o(&s.opts) } - - s = newService(opts...).(*service) } func (s *service) Cmd() cmd.Cmd {