From 2c983ef0213a5eb2305dd884a950cd3ec70ecbd0 Mon Sep 17 00:00:00 2001 From: Asim Date: Sun, 20 Dec 2015 23:50:16 +0000 Subject: [PATCH 01/13] Experimental top level init --- go-micro.go | 47 +++++++++++++++++++++++++++++++ options.go | 46 ++++++++++++++++++++++++++++++ service.go | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++ wrap.go | 28 ++++++++++++++++++ 4 files changed, 202 insertions(+) create mode 100644 go-micro.go create mode 100644 options.go create mode 100644 service.go create mode 100644 wrap.go diff --git a/go-micro.go b/go-micro.go new file mode 100644 index 00000000..36dedb0f --- /dev/null +++ b/go-micro.go @@ -0,0 +1,47 @@ +/* +Go micro provides a pluggable library to build microservices. + + import ( + micro "github.com/micro/go-micro" + ) + + service := micro.New( + micro.Registry(r), + micro.Broker(b), + ) + + h := service.Server().NewHandler(&Greeter{}) + service.Server().Handle(h) + service.Run() + + + req := service.Client.NewRequest(service, method, request) + rsp := response{} + err := service.Client.Call(req, rsp) + +*/ + +package gomicro + +import ( + "github.com/micro/go-micro/client" + "github.com/micro/go-micro/server" +) + +type Service interface { + Client() client.Client + Server() server.Server + Run() error + String() string +} + +type Option func(*Options) + +var ( + HeaderPrefix = "X-Micro-" + DefaultService = newService() +) + +func NewService(opts ...Option) Service { + return newService(opts...) +} diff --git a/options.go b/options.go new file mode 100644 index 00000000..42864480 --- /dev/null +++ b/options.go @@ -0,0 +1,46 @@ +package gomicro + +import ( + "github.com/micro/go-micro/broker" + "github.com/micro/go-micro/client" + "github.com/micro/go-micro/registry" + "github.com/micro/go-micro/server" + "github.com/micro/go-micro/transport" +) + +type Options struct { + Broker broker.Broker + Client client.Client + Server server.Server + Registry registry.Registry + Transport transport.Transport +} + +func newOptions(opts ...Option) Options { + var opt Options + for _, o := range opts { + o(&opt) + } + + if opt.Broker == nil { + opt.Broker = broker.DefaultBroker + } + + if opt.Client == nil { + opt.Client = client.DefaultClient + } + + if opt.Server == nil { + opt.Server = server.DefaultServer + } + + if opt.Registry == nil { + opt.Registry = registry.DefaultRegistry + } + + if opt.Transport == nil { + opt.Transport = transport.DefaultTransport + } + + return opt +} diff --git a/service.go b/service.go new file mode 100644 index 00000000..f0cc6d49 --- /dev/null +++ b/service.go @@ -0,0 +1,81 @@ +package gomicro + +import ( + "os" + "os/signal" + "syscall" + + "github.com/micro/go-micro/client" + "github.com/micro/go-micro/context" + "github.com/micro/go-micro/server" +) + +type service struct { + opts Options +} + +func newService(opts ...Option) Service { + options := newOptions(opts...) + + options.Client = &clientWrap{ + options.Client, + context.Metadata{ + HeaderPrefix + "From-Service": options.Server.Config().Name(), + }, + } + + return &service{ + opts: options, + } +} + +func (s *service) Client() client.Client { + return s.opts.Client +} + +func (s *service) Server() server.Server { + return s.opts.Server +} + +func (s *service) String() string { + return "go-micro" +} + +func (s *service) Start() error { + if err := s.opts.Server.Start(); err != nil { + return err + } + + if err := s.opts.Server.Register(); err != nil { + return err + } + + return nil +} + +func (s *service) Stop() error { + if err := s.opts.Server.Deregister(); err != nil { + return err + } + + if err := s.opts.Server.Stop(); err != nil { + return err + } + + return nil +} + +func (s *service) Run() error { + if err := s.Start(); err != nil { + return err + } + + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL) + + if err := s.Stop(); err != nil { + return err + } + + return nil +} diff --git a/wrap.go b/wrap.go new file mode 100644 index 00000000..671719d7 --- /dev/null +++ b/wrap.go @@ -0,0 +1,28 @@ +package gomicro + +import ( + "github.com/micro/go-micro/client" + cx "github.com/micro/go-micro/context" + + "golang.org/x/net/context" +) + +type clientWrap struct { + client.Client + headers cx.Metadata +} + +func (c *clientWrap) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { + ctx = cx.WithMetadata(ctx, c.headers) + return c.Client.Call(ctx, req, rsp, opts...) +} + +func (c *clientWrap) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Streamer, error) { + ctx = cx.WithMetadata(ctx, c.headers) + return c.Client.Stream(ctx, req, opts...) +} + +func (c *clientWrap) Publish(ctx context.Context, p client.Publication, opts ...client.PublishOption) error { + ctx = cx.WithMetadata(ctx, c.headers) + return c.Client.Publish(ctx, p, opts...) +} From cc720dd125cb7218aaf3e38f76fc2fda44ac20a0 Mon Sep 17 00:00:00 2001 From: Asim Date: Sun, 20 Dec 2015 23:52:58 +0000 Subject: [PATCH 02/13] update the comment --- go-micro.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/go-micro.go b/go-micro.go index 36dedb0f..fe05c251 100644 --- a/go-micro.go +++ b/go-micro.go @@ -5,19 +5,15 @@ Go micro provides a pluggable library to build microservices. micro "github.com/micro/go-micro" ) - service := micro.New( - micro.Registry(r), - micro.Broker(b), - ) - + service := micro.NewService() h := service.Server().NewHandler(&Greeter{}) service.Server().Handle(h) service.Run() - req := service.Client.NewRequest(service, method, request) + req := service.Client().NewRequest(service, method, request) rsp := response{} - err := service.Client.Call(req, rsp) + err := service.Client().Call(req, rsp) */ From 45dcffcaaf20b39fd063beb8da4f1f38c135e309 Mon Sep 17 00:00:00 2001 From: Asim Date: Mon, 21 Dec 2015 01:12:29 +0000 Subject: [PATCH 03/13] Remove default service --- go-micro.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/go-micro.go b/go-micro.go index fe05c251..e8a091f0 100644 --- a/go-micro.go +++ b/go-micro.go @@ -34,8 +34,7 @@ type Service interface { type Option func(*Options) var ( - HeaderPrefix = "X-Micro-" - DefaultService = newService() + HeaderPrefix = "X-Micro-" ) func NewService(opts ...Option) Service { From 4f1a1c678380691c1c9d68182a4692612fc2ea1b Mon Sep 17 00:00:00 2001 From: Asim Date: Mon, 21 Dec 2015 01:13:29 +0000 Subject: [PATCH 04/13] add ability to set the options --- options.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/options.go b/options.go index 42864480..ccb3ee07 100644 --- a/options.go +++ b/options.go @@ -44,3 +44,33 @@ func newOptions(opts ...Option) Options { return opt } + +func Broker(b broker.Broker) Option { + return func(o *Options) { + o.Broker = b + } +} + +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 + } +} + +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 + } +} From 0072b6e8f2d2b27756887d488c8a76d8d359a2a2 Mon Sep 17 00:00:00 2001 From: Asim Date: Mon, 21 Dec 2015 01:41:36 +0000 Subject: [PATCH 05/13] actually wait on the signal --- service.go | 1 + 1 file changed, 1 insertion(+) diff --git a/service.go b/service.go index f0cc6d49..770e8b2c 100644 --- a/service.go +++ b/service.go @@ -72,6 +72,7 @@ func (s *service) Run() error { ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL) + <-ch if err := s.Stop(); err != nil { return err From 651e9f8836f5285eb32cfc35aa3909a40e020249 Mon Sep 17 00:00:00 2001 From: Asim Date: Wed, 23 Dec 2015 00:02:42 +0000 Subject: [PATCH 06/13] rename wrap --- go-micro.go | 3 +++ service.go | 2 +- wrap.go => wrapper.go | 8 ++++---- 3 files changed, 8 insertions(+), 5 deletions(-) rename wrap.go => wrapper.go (52%) diff --git a/go-micro.go b/go-micro.go index e8a091f0..384f862b 100644 --- a/go-micro.go +++ b/go-micro.go @@ -24,6 +24,9 @@ import ( "github.com/micro/go-micro/server" ) +// Service is an interface that wraps the lower level libraries +// within go-micro. Its a convenience method for building +// and initialising services. type Service interface { Client() client.Client Server() server.Server diff --git a/service.go b/service.go index 770e8b2c..4b2d8d91 100644 --- a/service.go +++ b/service.go @@ -17,7 +17,7 @@ type service struct { func newService(opts ...Option) Service { options := newOptions(opts...) - options.Client = &clientWrap{ + options.Client = &clientWrapper{ options.Client, context.Metadata{ HeaderPrefix + "From-Service": options.Server.Config().Name(), diff --git a/wrap.go b/wrapper.go similarity index 52% rename from wrap.go rename to wrapper.go index 671719d7..7757a27d 100644 --- a/wrap.go +++ b/wrapper.go @@ -7,22 +7,22 @@ import ( "golang.org/x/net/context" ) -type clientWrap struct { +type clientWrapper struct { client.Client headers cx.Metadata } -func (c *clientWrap) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { +func (c *clientWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { ctx = cx.WithMetadata(ctx, c.headers) return c.Client.Call(ctx, req, rsp, opts...) } -func (c *clientWrap) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Streamer, error) { +func (c *clientWrapper) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Streamer, error) { ctx = cx.WithMetadata(ctx, c.headers) return c.Client.Stream(ctx, req, opts...) } -func (c *clientWrap) Publish(ctx context.Context, p client.Publication, opts ...client.PublishOption) error { +func (c *clientWrapper) Publish(ctx context.Context, p client.Publication, opts ...client.PublishOption) error { ctx = cx.WithMetadata(ctx, c.headers) return c.Client.Publish(ctx, p, opts...) } From 0d50b2eb3214c2d85836a396aad9ac2964fb4c88 Mon Sep 17 00:00:00 2001 From: Asim Date: Fri, 1 Jan 2016 01:16:21 +0000 Subject: [PATCH 07/13] Update top level init --- cmd/cmd.go | 156 ++++++++++++++++----------- cmd/options.go | 61 +++++++++++ examples/service/README.md | 38 +++++++ examples/service/main.go | 93 ++++++++++++++++ examples/service/proto/greeter.pb.go | 124 +++++++++++++++++++++ examples/service/proto/greeter.proto | 13 +++ go-micro.go | 3 + options.go | 83 ++++++++++---- service.go | 29 ++++- 9 files changed, 515 insertions(+), 85 deletions(-) create mode 100644 cmd/options.go create mode 100644 examples/service/README.md create mode 100644 examples/service/main.go create mode 100644 examples/service/proto/greeter.pb.go create mode 100644 examples/service/proto/greeter.proto diff --git a/cmd/cmd.go b/cmd/cmd.go index 776eb6a3..30fd85c7 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -3,15 +3,12 @@ package cmd import ( "flag" "fmt" - "io" "math/rand" "os" "strings" - "text/tabwriter" - "text/template" "time" - "github.com/codegangsta/cli" + "github.com/micro/cli" "github.com/micro/go-micro/broker" "github.com/micro/go-micro/client" "github.com/micro/go-micro/registry" @@ -20,10 +17,27 @@ import ( "github.com/micro/go-micro/transport" ) -var ( - Actions = []func(*cli.Context){} +type Cmd interface { + // The cli app within this cmd + App() *cli.App + // Adds options, parses flags and initialise + // exits on error + Init(opts ...Option) + // Options set within this command + Options() Options +} - Flags = []cli.Flag{ +type cmd struct { + opts Options + app *cli.App +} + +type Option func(o *Options) + +var ( + DefaultCmd = newCmd() + + DefaultFlags = []cli.Flag{ cli.StringFlag{ Name: "server_name", EnvVar: "MICRO_SERVER_NAME", @@ -127,19 +141,19 @@ var ( }, } - Brokers = map[string]func([]string, ...broker.Option) broker.Broker{ + DefaultBrokers = map[string]func([]string, ...broker.Option) broker.Broker{ "http": broker.NewBroker, } - Registries = map[string]func([]string, ...registry.Option) registry.Registry{ + DefaultRegistries = map[string]func([]string, ...registry.Option) registry.Registry{ "consul": registry.NewRegistry, } - Selectors = map[string]func(...selector.Option) selector.Selector{ + DefaultSelectors = map[string]func(...selector.Option) selector.Selector{ "random": selector.NewSelector, } - Transports = map[string]func([]string, ...transport.Option) transport.Transport{ + DefaultTransports = map[string]func([]string, ...transport.Option) transport.Transport{ "http": transport.NewTransport, } ) @@ -148,37 +162,68 @@ func init() { rand.Seed(time.Now().Unix()) } -func Setup(c *cli.Context) error { +func newCmd(opts ...Option) Cmd { + options := Options{ + Brokers: DefaultBrokers, + Registries: DefaultRegistries, + Selectors: DefaultSelectors, + Transports: DefaultTransports, + } + + for _, o := range opts { + o(&options) + } + + cmd := new(cmd) + cmd.opts = options + cmd.app = cli.NewApp() + cmd.app.Name = cmd.opts.Name + cmd.app.Version = cmd.opts.Version + cmd.app.Usage = cmd.opts.Description + cmd.app.Before = cmd.Before + cmd.app.Flags = DefaultFlags + cmd.app.Action = func(c *cli.Context) {} + return cmd +} + +func (c *cmd) App() *cli.App { + return c.app +} + +func (c *cmd) Options() Options { + return c.opts +} + +func (c *cmd) Before(ctx *cli.Context) error { + // Due to logger issues with glog, we need to do this os.Args = os.Args[:1] - - flag.Set("logtostderr", fmt.Sprintf("%v", c.Bool("logtostderr"))) - flag.Set("alsologtostderr", fmt.Sprintf("%v", c.Bool("alsologtostderr"))) - flag.Set("stderrthreshold", c.String("stderrthreshold")) - flag.Set("log_backtrace_at", c.String("log_backtrace_at")) - flag.Set("log_dir", c.String("log_dir")) - flag.Set("vmodule", c.String("vmodule")) - flag.Set("v", c.String("v")) - + flag.Set("logtostderr", fmt.Sprintf("%v", ctx.Bool("logtostderr"))) + flag.Set("alsologtostderr", fmt.Sprintf("%v", ctx.Bool("alsologtostderr"))) + flag.Set("stderrthreshold", ctx.String("stderrthreshold")) + flag.Set("log_backtrace_at", ctx.String("log_backtrace_at")) + flag.Set("log_dir", ctx.String("log_dir")) + flag.Set("vmodule", ctx.String("vmodule")) + flag.Set("v", ctx.String("v")) flag.Parse() - if b, ok := Brokers[c.String("broker")]; ok { - broker.DefaultBroker = b(strings.Split(c.String("broker_address"), ",")) + if b, ok := c.opts.Brokers[ctx.String("broker")]; ok { + broker.DefaultBroker = b(strings.Split(ctx.String("broker_address"), ",")) } - if r, ok := Registries[c.String("registry")]; ok { - registry.DefaultRegistry = r(strings.Split(c.String("registry_address"), ",")) + if r, ok := c.opts.Registries[ctx.String("registry")]; ok { + registry.DefaultRegistry = r(strings.Split(ctx.String("registry_address"), ",")) } - if s, ok := Selectors[c.String("selector")]; ok { + if s, ok := c.opts.Selectors[ctx.String("selector")]; ok { selector.DefaultSelector = s(selector.Registry(registry.DefaultRegistry)) } - if t, ok := Transports[c.String("transport")]; ok { - transport.DefaultTransport = t(strings.Split(c.String("transport_address"), ",")) + if t, ok := c.opts.Transports[ctx.String("transport")]; ok { + transport.DefaultTransport = t(strings.Split(ctx.String("transport_address"), ",")) } metadata := make(map[string]string) - for _, d := range c.StringSlice("server_metadata") { + for _, d := range ctx.StringSlice("server_metadata") { var key, val string parts := strings.Split(d, "=") key = parts[0] @@ -189,11 +234,11 @@ func Setup(c *cli.Context) error { } server.DefaultServer = server.NewServer( - server.Name(c.String("server_name")), - server.Version(c.String("server_version")), - server.Id(c.String("server_id")), - server.Address(c.String("server_address")), - server.Advertise(c.String("server_advertise")), + 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), ) @@ -202,33 +247,20 @@ func Setup(c *cli.Context) error { return nil } -func Init() { - cli.AppHelpTemplate = ` -GLOBAL OPTIONS: - {{range .Flags}}{{.}} - {{end}} -` - - cli.HelpPrinter = func(writer io.Writer, templ string, data interface{}) { - w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0) - t := template.Must(template.New("help").Parse(templ)) - err := t.Execute(w, data) - if err != nil { - panic(err) - } - w.Flush() - os.Exit(2) +func (c *cmd) Init(opts ...Option) { + for _, o := range opts { + o(&c.opts) } - - app := cli.NewApp() - app.HideVersion = true - app.Usage = "a go micro app" - app.Action = func(c *cli.Context) { - for _, action := range Actions { - action(c) - } - } - app.Before = Setup - app.Flags = Flags - app.RunAndExitOnError() + c.app.Name = c.opts.Name + c.app.Version = c.opts.Version + c.app.Usage = c.opts.Description + c.app.RunAndExitOnError() +} + +func Init(opts ...Option) { + DefaultCmd.Init(opts...) +} + +func NewCmd(opts ...Option) Cmd { + return newCmd(opts...) } diff --git a/cmd/options.go b/cmd/options.go new file mode 100644 index 00000000..70879daf --- /dev/null +++ b/cmd/options.go @@ -0,0 +1,61 @@ +package cmd + +import ( + "github.com/micro/go-micro/broker" + "github.com/micro/go-micro/registry" + "github.com/micro/go-micro/selector" + "github.com/micro/go-micro/transport" +) + +type Options struct { + Name string + Description string + Version string + + 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 +} + +func Name(n string) Option { + return func(o *Options) { + o.Name = n + } +} + +func Description(d string) Option { + return func(o *Options) { + o.Description = d + } +} + +func Version(v string) Option { + return func(o *Options) { + o.Version = v + } +} + +func Broker(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 { + return func(o *Options) { + o.Registries[name] = r + } +} + +func Selector(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 { + return func(o *Options) { + o.Transports[name] = t + } +} diff --git a/examples/service/README.md b/examples/service/README.md new file mode 100644 index 00000000..f474d65f --- /dev/null +++ b/examples/service/README.md @@ -0,0 +1,38 @@ +# Service + +This is an example of creating a micro service using the top level interface. + +## Prereqs + +Micro services need a discovery system so they can find each other. Micro uses consul by default but +its easily swapped out with etcd, kubernetes, or various other systems. We'll run consul for convenience. + +1. Follow the install instructions - [https://www.consul.io/intro/getting-started/install.html](https://www.consul.io/intro/getting-started/install.html) + +2. Run Consul + +```shell +$ consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul +``` + +## Run the example + +1. Get the service + +```shell +go get github.com/micro/go-micro/examples/service +``` + +2. Run the server + +```shell +$GOPATH/bin/service +``` + +3. Run the client + +```shell +$GOPATH/bin/service --client +``` + +And that's all there is to it. diff --git a/examples/service/main.go b/examples/service/main.go new file mode 100644 index 00000000..7ae76519 --- /dev/null +++ b/examples/service/main.go @@ -0,0 +1,93 @@ +package main + +import ( + "fmt" + "os" + + "github.com/micro/cli" + micro "github.com/micro/go-micro" + proto "github.com/micro/go-micro/examples/service/proto" + "golang.org/x/net/context" +) + +/* + +Example usage of top level service initialisation + +*/ + +type Greeter struct{} + +func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error { + rsp.Greeting = "Hello " + req.Name + return nil +} + +// Setup and the client +func client(service micro.Service) { + // 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) +} + +// Setup some command line flags +func flags(service micro.Service) { + app := service.Cmd().App() + app.Flags = append(app.Flags, + &cli.BoolFlag{ + Name: "server", + Usage: "Launch the server", + }, + &cli.BoolFlag{ + Name: "client", + Usage: "Launch the client", + }, + ) + + // Let's launch the server or the client + app.Action = func(c *cli.Context) { + if c.Bool("client") { + client(service) + os.Exit(0) + } + } +} + +func main() { + // 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", + }), + ) + + // Setup some flags. Specify --client to run the client + flags(service) + + // 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() + + // By default we'll run the server unless the flags catch us + + // Setup the server + + // Register handler + proto.RegisterGreeterHandler(service.Server(), new(Greeter)) + + // Run the server + if err := service.Run(); err != nil { + fmt.Println(err) + } +} diff --git a/examples/service/proto/greeter.pb.go b/examples/service/proto/greeter.pb.go new file mode 100644 index 00000000..20fd7cf1 --- /dev/null +++ b/examples/service/proto/greeter.pb.go @@ -0,0 +1,124 @@ +// Code generated by protoc-gen-go. +// source: go-micro/examples/service/proto/greeter.proto +// DO NOT EDIT! + +/* +Package greeter is a generated protocol buffer package. + +It is generated from these files: + go-micro/examples/service/proto/greeter.proto + +It has these top-level messages: + HelloRequest + HelloResponse +*/ +package greeter + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import ( + client "github.com/micro/go-micro/client" + server "github.com/micro/go-micro/server" + context "golang.org/x/net/context" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +type HelloRequest struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` +} + +func (m *HelloRequest) Reset() { *m = HelloRequest{} } +func (m *HelloRequest) String() string { return proto.CompactTextString(m) } +func (*HelloRequest) ProtoMessage() {} +func (*HelloRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +type HelloResponse struct { + Greeting string `protobuf:"bytes,2,opt,name=greeting" json:"greeting,omitempty"` +} + +func (m *HelloResponse) Reset() { *m = HelloResponse{} } +func (m *HelloResponse) String() string { return proto.CompactTextString(m) } +func (*HelloResponse) ProtoMessage() {} +func (*HelloResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func init() { + proto.RegisterType((*HelloRequest)(nil), "HelloRequest") + proto.RegisterType((*HelloResponse)(nil), "HelloResponse") +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ client.Option +var _ server.Option + +// Client API for Greeter service + +type GreeterClient interface { + Hello(ctx context.Context, in *HelloRequest, opts ...client.CallOption) (*HelloResponse, error) +} + +type greeterClient struct { + c client.Client + serviceName string +} + +func NewGreeterClient(serviceName string, c client.Client) GreeterClient { + if c == nil { + c = client.NewClient() + } + if len(serviceName) == 0 { + serviceName = "greeter" + } + return &greeterClient{ + c: c, + serviceName: serviceName, + } +} + +func (c *greeterClient) Hello(ctx context.Context, in *HelloRequest, opts ...client.CallOption) (*HelloResponse, error) { + req := c.c.NewRequest(c.serviceName, "Greeter.Hello", in) + out := new(HelloResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for Greeter service + +type GreeterHandler interface { + Hello(context.Context, *HelloRequest, *HelloResponse) error +} + +func RegisterGreeterHandler(s server.Server, hdlr GreeterHandler) { + s.Handle(s.NewHandler(&Greeter{hdlr})) +} + +type Greeter struct { + GreeterHandler +} + +func (h *Greeter) Hello(ctx context.Context, in *HelloRequest, out *HelloResponse) error { + return h.GreeterHandler.Hello(ctx, in, out) +} + +var fileDescriptor0 = []byte{ + // 153 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xd2, 0x4d, 0xcf, 0xd7, 0xcd, + 0xcd, 0x4c, 0x2e, 0xca, 0xd7, 0x4f, 0xad, 0x48, 0xcc, 0x2d, 0xc8, 0x49, 0x2d, 0xd6, 0x2f, 0x4e, + 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0xd7, 0x4f, 0x2f, 0x4a, 0x4d, + 0x2d, 0x49, 0x2d, 0xd2, 0x03, 0xf3, 0x94, 0x64, 0xb8, 0x78, 0x3c, 0x52, 0x73, 0x72, 0xf2, 0x83, + 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x78, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, 0x25, 0x18, + 0x15, 0x18, 0x35, 0x38, 0x95, 0x14, 0xb9, 0x78, 0xa1, 0xb2, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, + 0x42, 0x02, 0x5c, 0x1c, 0x60, 0xfd, 0x99, 0x79, 0xe9, 0x12, 0x4c, 0x20, 0x25, 0x46, 0xc6, 0x5c, + 0xec, 0xee, 0x10, 0x13, 0x85, 0x34, 0xb8, 0x58, 0xc1, 0xaa, 0x85, 0x78, 0xf5, 0x90, 0xcd, 0x94, + 0xe2, 0xd3, 0x43, 0x31, 0x44, 0x89, 0x21, 0x89, 0x0d, 0x6c, 0xb9, 0x31, 0x20, 0x00, 0x00, 0xff, + 0xff, 0x0f, 0xa9, 0x59, 0xb3, 0xad, 0x00, 0x00, 0x00, +} diff --git a/examples/service/proto/greeter.proto b/examples/service/proto/greeter.proto new file mode 100644 index 00000000..4ff347af --- /dev/null +++ b/examples/service/proto/greeter.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +service Greeter { + rpc Hello(HelloRequest) returns (HelloResponse) {} +} + +message HelloRequest { + string name = 1; +} + +message HelloResponse { + string greeting = 2; +} diff --git a/go-micro.go b/go-micro.go index 384f862b..94425bd6 100644 --- a/go-micro.go +++ b/go-micro.go @@ -21,6 +21,7 @@ package gomicro import ( "github.com/micro/go-micro/client" + "github.com/micro/go-micro/cmd" "github.com/micro/go-micro/server" ) @@ -28,6 +29,8 @@ import ( // within go-micro. Its a convenience method for building // and initialising services. type Service interface { + Init(...Option) + Cmd() cmd.Cmd Client() client.Client Server() server.Server Run() error diff --git a/options.go b/options.go index ccb3ee07..159873b4 100644 --- a/options.go +++ b/options.go @@ -3,6 +3,7 @@ package gomicro import ( "github.com/micro/go-micro/broker" "github.com/micro/go-micro/client" + "github.com/micro/go-micro/cmd" "github.com/micro/go-micro/registry" "github.com/micro/go-micro/server" "github.com/micro/go-micro/transport" @@ -10,38 +11,35 @@ import ( type Options struct { Broker broker.Broker + Cmd cmd.Cmd Client client.Client Server server.Server Registry registry.Registry Transport transport.Transport + + // Before and After funcs + BeforeStart []func() error + AfterStop []func() error + + // Alternative options for those implementing the interface + Options map[string]string } func newOptions(opts ...Option) Options { - var opt Options + opt := Options{ + Broker: broker.DefaultBroker, + Cmd: cmd.DefaultCmd, + Client: client.DefaultClient, + Server: server.DefaultServer, + Registry: registry.DefaultRegistry, + Transport: transport.DefaultTransport, + Options: map[string]string{}, + } + for _, o := range opts { o(&opt) } - if opt.Broker == nil { - opt.Broker = broker.DefaultBroker - } - - if opt.Client == nil { - opt.Client = client.DefaultClient - } - - if opt.Server == nil { - opt.Server = server.DefaultServer - } - - if opt.Registry == nil { - opt.Registry = registry.DefaultRegistry - } - - if opt.Transport == nil { - opt.Transport = transport.DefaultTransport - } - return opt } @@ -51,6 +49,12 @@ func Broker(b broker.Broker) Option { } } +func Cmd(c cmd.Cmd) Option { + return func(o *Options) { + o.Cmd = c + } +} + func Client(c client.Client) Option { return func(o *Options) { o.Client = c @@ -74,3 +78,40 @@ func Transport(t transport.Transport) Option { o.Transport = t } } + +// Convenience options + +// 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)) + } +} + +// Metadata associated with the service +func Metadata(md map[string]string) Option { + return func(o *Options) { + o.Server.Init(server.Metadata(md)) + } +} + +// Before and Afters + +func BeforeStart(fn func() error) Option { + return func(o *Options) { + o.BeforeStart = append(o.BeforeStart, fn) + } +} + +func AfterStop(fn func() error) Option { + return func(o *Options) { + o.AfterStop = append(o.AfterStop, fn) + } +} diff --git a/service.go b/service.go index 4b2d8d91..151f056d 100644 --- a/service.go +++ b/service.go @@ -6,6 +6,7 @@ import ( "syscall" "github.com/micro/go-micro/client" + "github.com/micro/go-micro/cmd" "github.com/micro/go-micro/context" "github.com/micro/go-micro/server" ) @@ -20,7 +21,7 @@ func newService(opts ...Option) Service { options.Client = &clientWrapper{ options.Client, context.Metadata{ - HeaderPrefix + "From-Service": options.Server.Config().Name(), + HeaderPrefix + "From-Service": options.Server.Options().Name, }, } @@ -29,6 +30,15 @@ func newService(opts ...Option) Service { } } +func (s *service) Init(opts ...Option) { + s.opts.Cmd.Init() + s = newService(opts...).(*service) +} + +func (s *service) Cmd() cmd.Cmd { + return s.opts.Cmd +} + func (s *service) Client() client.Client { return s.opts.Client } @@ -42,6 +52,12 @@ func (s *service) String() string { } func (s *service) Start() error { + for _, fn := range s.opts.BeforeStart { + if err := fn(); err != nil { + return err + } + } + if err := s.opts.Server.Start(); err != nil { return err } @@ -62,7 +78,16 @@ func (s *service) Stop() error { return err } - return nil + var gerr error + for _, fn := range s.opts.AfterStop { + if err := fn(); err != nil { + // should we bail if it fails? + // other funcs will not be executed + // seems wrong + gerr = err + } + } + return gerr } func (s *service) Run() error { From 210b81009607c7787da29151d378ccb032fded88 Mon Sep 17 00:00:00 2001 From: Asim Date: Fri, 1 Jan 2016 01:29:40 +0000 Subject: [PATCH 08/13] Fix the selectors --- selector/blacklist/black_list_selector.go | 2 +- selector/random/random.go | 2 +- selector/roundrobin/round_robin_selector.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/selector/blacklist/black_list_selector.go b/selector/blacklist/black_list_selector.go index 3769c0a4..689710f6 100644 --- a/selector/blacklist/black_list_selector.go +++ b/selector/blacklist/black_list_selector.go @@ -27,7 +27,7 @@ type blackListSelector struct { } func init() { - cmd.Selectors["blacklist"] = NewSelector + cmd.DefaultSelectors["blacklist"] = NewSelector rand.Seed(time.Now().Unix()) } diff --git a/selector/random/random.go b/selector/random/random.go index 57e325cc..a0fcefbd 100644 --- a/selector/random/random.go +++ b/selector/random/random.go @@ -6,7 +6,7 @@ import ( ) func init() { - cmd.Selectors["random"] = NewSelector + cmd.DefaultSelectors["random"] = NewSelector } func NewSelector(opts ...selector.Option) selector.Selector { diff --git a/selector/roundrobin/round_robin_selector.go b/selector/roundrobin/round_robin_selector.go index 48a89874..7ee019dd 100644 --- a/selector/roundrobin/round_robin_selector.go +++ b/selector/roundrobin/round_robin_selector.go @@ -13,7 +13,7 @@ type roundRobinSelector struct { } func init() { - cmd.Selectors["roundrobin"] = NewSelector + cmd.DefaultSelectors["roundrobin"] = NewSelector } func (r *roundRobinSelector) Select(service string, opts ...selector.SelectOption) (selector.Next, error) { From 9e7e8742b412232c295cdd0c18c6685e0a482a39 Mon Sep 17 00:00:00 2001 From: Asim Date: Fri, 1 Jan 2016 02:36:47 +0000 Subject: [PATCH 09/13] Don't need server flag --- examples/service/main.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/service/main.go b/examples/service/main.go index 7ae76519..fa96ad54 100644 --- a/examples/service/main.go +++ b/examples/service/main.go @@ -42,10 +42,6 @@ func client(service micro.Service) { func flags(service micro.Service) { app := service.Cmd().App() app.Flags = append(app.Flags, - &cli.BoolFlag{ - Name: "server", - Usage: "Launch the server", - }, &cli.BoolFlag{ Name: "client", Usage: "Launch the client", From 59a667130ca89984a8883d5b5a2a0f8312b621a2 Mon Sep 17 00:00:00 2001 From: Asim Date: Fri, 1 Jan 2016 02:45:15 +0000 Subject: [PATCH 10/13] Update help printer --- cmd/cmd.go | 15 +++++++++++++++ options.go | 9 ++++++++- service.go | 5 +++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index 30fd85c7..4c08970f 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -3,6 +3,7 @@ package cmd import ( "flag" "fmt" + "io" "math/rand" "os" "strings" @@ -160,6 +161,11 @@ var ( func init() { rand.Seed(time.Now().Unix()) + help := cli.HelpPrinter + cli.HelpPrinter = func(writer io.Writer, templ string, data interface{}) { + help(writer, templ, data) + os.Exit(0) + } } func newCmd(opts ...Option) Cmd { @@ -174,6 +180,10 @@ func newCmd(opts ...Option) Cmd { o(&options) } + if len(options.Description) == 0 { + options.Description = "a go-micro service" + } + cmd := new(cmd) cmd.opts = options cmd.app = cli.NewApp() @@ -183,6 +193,11 @@ func newCmd(opts ...Option) Cmd { cmd.app.Before = cmd.Before cmd.app.Flags = DefaultFlags cmd.app.Action = func(c *cli.Context) {} + + if len(options.Version) == 0 { + cmd.app.HideVersion = true + } + return cmd } diff --git a/options.go b/options.go index 159873b4..9667d91b 100644 --- a/options.go +++ b/options.go @@ -28,7 +28,6 @@ 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, @@ -40,6 +39,14 @@ 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/service.go b/service.go index 151f056d..6a82d8d0 100644 --- a/service.go +++ b/service.go @@ -32,6 +32,11 @@ func newService(opts ...Option) Service { func (s *service) Init(opts ...Option) { s.opts.Cmd.Init() + + for _, o := range opts { + o(&s.opts) + } + s = newService(opts...).(*service) } From 1f1bc274218495726ce5afcb133c3caec9155dff Mon Sep 17 00:00:00 2001 From: Asim Date: Sat, 2 Jan 2016 00:38:57 +0000 Subject: [PATCH 11/13] 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 { From 3b56a625891d0de664681b1b66f8b570d5a33ba6 Mon Sep 17 00:00:00 2001 From: Asim Date: Sat, 2 Jan 2016 19:12:17 +0000 Subject: [PATCH 12/13] Update the way flags are used --- client/client.go | 2 ++ client/rpc_client.go | 11 +++++++++++ cmd/cmd.go | 11 ++++++----- examples/service/main.go | 42 ++++++++++++++++++---------------------- go-micro.go | 3 +-- options.go | 13 +++++++++++++ server/rpc_server.go | 3 ++- server/server.go | 2 +- service.go | 14 +++++++++++--- 9 files changed, 66 insertions(+), 35 deletions(-) diff --git a/client/client.go b/client/client.go index 906e399f..7c1c940e 100644 --- a/client/client.go +++ b/client/client.go @@ -26,6 +26,8 @@ import ( ) type Client interface { + Init(...Option) error + Options() Options NewPublication(topic string, msg interface{}) Publication NewRequest(service, method string, req interface{}, reqOpts ...RequestOption) Request NewProtoRequest(service, method string, req interface{}, reqOpts ...RequestOption) Request diff --git a/client/rpc_client.go b/client/rpc_client.go index 8fcd573f..77622c35 100644 --- a/client/rpc_client.go +++ b/client/rpc_client.go @@ -149,6 +149,17 @@ func (r *rpcClient) stream(ctx context.Context, address string, req Request) (St return stream, err } +func (r *rpcClient) Init(opts ...Option) error { + for _, o := range opts { + o(&r.opts) + } + return nil +} + +func (r *rpcClient) Options() Options { + return r.opts +} + func (r *rpcClient) CallRemote(ctx context.Context, address string, request Request, response interface{}, opts ...CallOption) error { return r.call(ctx, address, request, response) } diff --git a/cmd/cmd.go b/cmd/cmd.go index 7d7ed024..824f13fb 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -23,7 +23,7 @@ type Cmd interface { App() *cli.App // Adds options, parses flags and initialise // exits on error - Init(opts ...Option) + Init(opts ...Option) error // Options set within this command Options() Options } @@ -323,13 +323,13 @@ func (c *cmd) Before(ctx *cli.Context) error { // Use an init option? if len(clientOpts) > 0 { - *c.opts.Client = client.NewClient(clientOpts...) + (*c.opts.Client).Init(clientOpts...) } return nil } -func (c *cmd) Init(opts ...Option) { +func (c *cmd) Init(opts ...Option) error { for _, o := range opts { o(&c.opts) } @@ -337,10 +337,11 @@ func (c *cmd) Init(opts ...Option) { c.app.Version = c.opts.Version c.app.Usage = c.opts.Description c.app.RunAndExitOnError() + return nil } -func Init(opts ...Option) { - DefaultCmd.Init(opts...) +func Init(opts ...Option) error { + return DefaultCmd.Init(opts...) } func NewCmd(opts ...Option) Cmd { diff --git a/examples/service/main.go b/examples/service/main.go index fa96ad54..d8f54e7c 100644 --- a/examples/service/main.go +++ b/examples/service/main.go @@ -38,25 +38,6 @@ func client(service micro.Service) { fmt.Println(rsp.Greeting) } -// Setup some command line flags -func flags(service micro.Service) { - app := service.Cmd().App() - app.Flags = append(app.Flags, - &cli.BoolFlag{ - Name: "client", - Usage: "Launch the client", - }, - ) - - // Let's launch the server or the client - app.Action = func(c *cli.Context) { - if c.Bool("client") { - client(service) - os.Exit(0) - } - } -} - func main() { // Create a new service. Optionally include some options here. service := micro.NewService( @@ -65,15 +46,30 @@ func main() { micro.Metadata(map[string]string{ "type": "helloworld", }), - ) - // Setup some flags. Specify --client to run the client - flags(service) + // Setup some flags. Specify --client to run the client + + // Add runtime flags + // We could do this below too + micro.Flags(cli.BoolFlag{ + Name: "client", + Usage: "Launch the client", + }), + ) // 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() + service.Init( + // Add runtime action + // We could actually do this above + micro.Action(func(c *cli.Context) { + if c.Bool("client") { + client(service) + os.Exit(0) + } + }), + ) // By default we'll run the server unless the flags catch us diff --git a/go-micro.go b/go-micro.go index 94425bd6..66be0c52 100644 --- a/go-micro.go +++ b/go-micro.go @@ -21,7 +21,6 @@ package gomicro import ( "github.com/micro/go-micro/client" - "github.com/micro/go-micro/cmd" "github.com/micro/go-micro/server" ) @@ -30,7 +29,7 @@ import ( // and initialising services. type Service interface { Init(...Option) - Cmd() cmd.Cmd + Options() Options Client() client.Client Server() server.Server Run() error diff --git a/options.go b/options.go index 159873b4..27a4f850 100644 --- a/options.go +++ b/options.go @@ -1,6 +1,7 @@ package gomicro import ( + "github.com/micro/cli" "github.com/micro/go-micro/broker" "github.com/micro/go-micro/client" "github.com/micro/go-micro/cmd" @@ -95,6 +96,18 @@ func Version(v string) Option { } } +func Flags(flags ...cli.Flag) Option { + return func(o *Options) { + o.Cmd.App().Flags = append(o.Cmd.App().Flags, flags...) + } +} + +func Action(a func(*cli.Context)) Option { + return func(o *Options) { + o.Cmd.App().Action = a + } +} + // Metadata associated with the service func Metadata(md map[string]string) Option { return func(o *Options) { diff --git a/server/rpc_server.go b/server/rpc_server.go index 1cd63ae9..14e035d7 100644 --- a/server/rpc_server.go +++ b/server/rpc_server.go @@ -105,12 +105,13 @@ func (s *rpcServer) Options() Options { return opts } -func (s *rpcServer) Init(opts ...Option) { +func (s *rpcServer) Init(opts ...Option) error { s.Lock() for _, opt := range opts { opt(&s.opts) } s.Unlock() + return nil } func (s *rpcServer) NewHandler(h interface{}) Handler { diff --git a/server/server.go b/server/server.go index 65449b18..0fff3afb 100644 --- a/server/server.go +++ b/server/server.go @@ -40,7 +40,7 @@ import ( type Server interface { Options() Options - Init(...Option) + Init(...Option) error Handle(Handler) error NewHandler(interface{}) Handler NewSubscriber(string, interface{}) Subscriber diff --git a/service.go b/service.go index 7b7fac58..3459ad42 100644 --- a/service.go +++ b/service.go @@ -6,7 +6,6 @@ import ( "syscall" "github.com/micro/go-micro/client" - "github.com/micro/go-micro/cmd" "github.com/micro/go-micro/context" "github.com/micro/go-micro/server" ) @@ -31,6 +30,15 @@ func newService(opts ...Option) Service { } func (s *service) Init(opts ...Option) { + // We might get more command flags or the action here + // This is pretty ugly, find a better way + options := newOptions() + options.Cmd = s.opts.Cmd + for _, o := range opts { + o(&options) + } + s.opts.Cmd = options.Cmd + // Initialise the command flags, overriding new service s.opts.Cmd.Init() @@ -40,8 +48,8 @@ func (s *service) Init(opts ...Option) { } } -func (s *service) Cmd() cmd.Cmd { - return s.opts.Cmd +func (s *service) Options() Options { + return s.opts } func (s *service) Client() client.Client { From 7a02bda06b429a4e68abccd6a8fec93a5e73fd76 Mon Sep 17 00:00:00 2001 From: Asim Date: Sat, 2 Jan 2016 19:14:56 +0000 Subject: [PATCH 13/13] Set package to micro --- go-micro.go | 2 +- options.go | 16 ++++++++-------- service.go | 2 +- wrapper.go | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/go-micro.go b/go-micro.go index 66be0c52..b8ac7fe9 100644 --- a/go-micro.go +++ b/go-micro.go @@ -17,7 +17,7 @@ Go micro provides a pluggable library to build microservices. */ -package gomicro +package micro import ( "github.com/micro/go-micro/client" diff --git a/options.go b/options.go index 27a4f850..8c347def 100644 --- a/options.go +++ b/options.go @@ -1,4 +1,4 @@ -package gomicro +package micro import ( "github.com/micro/cli" @@ -96,6 +96,13 @@ func Version(v string) Option { } } +// Metadata associated with the service +func Metadata(md map[string]string) Option { + return func(o *Options) { + o.Server.Init(server.Metadata(md)) + } +} + func Flags(flags ...cli.Flag) Option { return func(o *Options) { o.Cmd.App().Flags = append(o.Cmd.App().Flags, flags...) @@ -108,13 +115,6 @@ func Action(a func(*cli.Context)) Option { } } -// Metadata associated with the service -func Metadata(md map[string]string) Option { - return func(o *Options) { - o.Server.Init(server.Metadata(md)) - } -} - // Before and Afters func BeforeStart(fn func() error) Option { diff --git a/service.go b/service.go index 3459ad42..118614af 100644 --- a/service.go +++ b/service.go @@ -1,4 +1,4 @@ -package gomicro +package micro import ( "os" diff --git a/wrapper.go b/wrapper.go index 7757a27d..c75dbbc5 100644 --- a/wrapper.go +++ b/wrapper.go @@ -1,4 +1,4 @@ -package gomicro +package micro import ( "github.com/micro/go-micro/client"