159
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										159
									
								
								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 { | ||||
| service Greeter { | ||||
| 	rpc Hello(HelloRequest) returns (HelloResponse) {} | ||||
| } | ||||
|  | ||||
| message HelloRequest { | ||||
| 	string name = 1; | ||||
| } | ||||
|  | ||||
| message Response { | ||||
|         string msg = 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 | ||||
| ``` | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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) | ||||
| } | ||||
|   | ||||
							
								
								
									
										257
									
								
								cmd/cmd.go
									
									
									
									
									
								
							
							
						
						
									
										257
									
								
								cmd/cmd.go
									
									
									
									
									
								
							| @@ -7,11 +7,9 @@ import ( | ||||
| 	"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 +18,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) error | ||||
| 	// 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", | ||||
| @@ -42,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{ | ||||
| @@ -59,7 +73,6 @@ var ( | ||||
| 		cli.StringFlag{ | ||||
| 			Name:   "broker", | ||||
| 			EnvVar: "MICRO_BROKER", | ||||
| 			Value:  "http", | ||||
| 			Usage:  "Broker for pub/sub. http, nats, rabbitmq", | ||||
| 		}, | ||||
| 		cli.StringFlag{ | ||||
| @@ -70,7 +83,6 @@ var ( | ||||
| 		cli.StringFlag{ | ||||
| 			Name:   "registry", | ||||
| 			EnvVar: "MICRO_REGISTRY", | ||||
| 			Value:  "consul", | ||||
| 			Usage:  "Registry for discovery. memory, consul, etcd, kubernetes", | ||||
| 		}, | ||||
| 		cli.StringFlag{ | ||||
| @@ -81,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{ | ||||
| @@ -127,58 +137,151 @@ 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, | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| 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 Setup(c *cli.Context) error { | ||||
| 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, | ||||
| 		Transports: DefaultTransports, | ||||
| 	} | ||||
|  | ||||
| 	for _, o := range opts { | ||||
| 		o(&options) | ||||
| 	} | ||||
|  | ||||
| 	if len(options.Description) == 0 { | ||||
| 		options.Description = "a go-micro service" | ||||
| 	} | ||||
|  | ||||
| 	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) {} | ||||
|  | ||||
| 	if len(options.Version) == 0 { | ||||
| 		cmd.app.HideVersion = true | ||||
| 	} | ||||
|  | ||||
| 	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 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")) | ||||
| 		} | ||||
|  | ||||
| 	if r, ok := Registries[c.String("registry")]; ok { | ||||
| 		registry.DefaultRegistry = r(strings.Split(c.String("registry_address"), ",")) | ||||
| 		serverOpts = append(serverOpts, server.Broker(*c.opts.Broker)) | ||||
| 		clientOpts = append(clientOpts, client.Broker(*c.opts.Broker)) | ||||
| 	} | ||||
|  | ||||
| 	if s, ok := Selectors[c.String("selector")]; ok { | ||||
| 		selector.DefaultSelector = s(selector.Registry(registry.DefaultRegistry)) | ||||
| 	// 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")) | ||||
| 		} | ||||
|  | ||||
| 	if t, ok := Transports[c.String("transport")]; ok { | ||||
| 		transport.DefaultTransport = t(strings.Split(c.String("transport_address"), ",")) | ||||
| 		serverOpts = append(serverOpts, server.Registry(*c.opts.Registry)) | ||||
| 		clientOpts = append(clientOpts, client.Registry(*c.opts.Registry)) | ||||
| 	} | ||||
|  | ||||
| 	// 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)) | ||||
| 	} | ||||
|  | ||||
| 	// 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 c.StringSlice("server_metadata") { | ||||
| 	for _, d := range ctx.StringSlice("server_metadata") { | ||||
| 		var key, val string | ||||
| 		parts := strings.Split(d, "=") | ||||
| 		key = parts[0] | ||||
| @@ -188,47 +291,59 @@ func Setup(c *cli.Context) error { | ||||
| 		metadata[key] = val | ||||
| 	} | ||||
|  | ||||
| 	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.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).Init(clientOpts...) | ||||
| 	} | ||||
|  | ||||
| 	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) | ||||
| func (c *cmd) Init(opts ...Option) error { | ||||
| 	for _, o := range opts { | ||||
| 		o(&c.opts) | ||||
| 	} | ||||
| 		w.Flush() | ||||
| 		os.Exit(2) | ||||
| 	} | ||||
|  | ||||
| 	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() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func Init(opts ...Option) error { | ||||
| 	return DefaultCmd.Init(opts...) | ||||
| } | ||||
|  | ||||
| func NewCmd(opts ...Option) Cmd { | ||||
| 	return newCmd(opts...) | ||||
| } | ||||
|   | ||||
							
								
								
									
										115
									
								
								cmd/options.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								cmd/options.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| 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(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 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // New registry func | ||||
| func NewRegistry(name string, r func([]string, ...registry.Option) registry.Registry) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Registries[name] = r | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // New selector func | ||||
| func NewSelector(name string, s func(...selector.Option) selector.Selector) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Selectors[name] = s | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // New transport func | ||||
| func NewTransport(name string, t func([]string, ...transport.Option) transport.Transport) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Transports[name] = t | ||||
| 	} | ||||
| } | ||||
| @@ -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"), | ||||
|   | ||||
							
								
								
									
										78
									
								
								examples/server/wrapper/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								examples/server/wrapper/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										38
									
								
								examples/service/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								examples/service/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -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. | ||||
							
								
								
									
										85
									
								
								examples/service/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								examples/service/main.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| 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) | ||||
| } | ||||
|  | ||||
| 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 | ||||
|  | ||||
| 		// 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( | ||||
| 		// 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 | ||||
|  | ||||
| 	// Setup the server | ||||
|  | ||||
| 	// Register handler | ||||
| 	proto.RegisterGreeterHandler(service.Server(), new(Greeter)) | ||||
|  | ||||
| 	// Run the server | ||||
| 	if err := service.Run(); err != nil { | ||||
| 		fmt.Println(err) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										124
									
								
								examples/service/proto/greeter.pb.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										124
									
								
								examples/service/proto/greeter.pb.go
									
									
									
									
									
										Normal file
									
								
							| @@ -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, | ||||
| } | ||||
							
								
								
									
										13
									
								
								examples/service/proto/greeter.proto
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								examples/service/proto/greeter.proto
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| syntax = "proto3"; | ||||
|  | ||||
| service Greeter { | ||||
| 	rpc Hello(HelloRequest) returns (HelloResponse) {} | ||||
| } | ||||
|  | ||||
| message HelloRequest { | ||||
| 	string name = 1; | ||||
| } | ||||
|  | ||||
| message HelloResponse { | ||||
| 	string greeting = 2; | ||||
| } | ||||
							
								
								
									
										47
									
								
								go-micro.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								go-micro.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| /* | ||||
| Go micro provides a pluggable library to build microservices. | ||||
|  | ||||
| 	import ( | ||||
| 		micro "github.com/micro/go-micro" | ||||
| 	) | ||||
|  | ||||
| 	service := micro.NewService() | ||||
| 	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 micro | ||||
|  | ||||
| import ( | ||||
| 	"github.com/micro/go-micro/client" | ||||
| 	"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 { | ||||
| 	Init(...Option) | ||||
| 	Options() Options | ||||
| 	Client() client.Client | ||||
| 	Server() server.Server | ||||
| 	Run() error | ||||
| 	String() string | ||||
| } | ||||
|  | ||||
| type Option func(*Options) | ||||
|  | ||||
| var ( | ||||
| 	HeaderPrefix = "X-Micro-" | ||||
| ) | ||||
|  | ||||
| func NewService(opts ...Option) Service { | ||||
| 	return newService(opts...) | ||||
| } | ||||
							
								
								
									
										130
									
								
								options.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								options.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| package micro | ||||
|  | ||||
| import ( | ||||
| 	"github.com/micro/cli" | ||||
| 	"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" | ||||
| ) | ||||
|  | ||||
| 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 { | ||||
| 	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) | ||||
| 	} | ||||
|  | ||||
| 	return opt | ||||
| } | ||||
|  | ||||
| func Broker(b broker.Broker) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Broker = b | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 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)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // 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) | ||||
| 	} | ||||
| } | ||||
| @@ -27,7 +27,7 @@ type blackListSelector struct { | ||||
| } | ||||
|  | ||||
| func init() { | ||||
| 	cmd.Selectors["blacklist"] = NewSelector | ||||
| 	cmd.DefaultSelectors["blacklist"] = NewSelector | ||||
| 	rand.Seed(time.Now().Unix()) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -6,7 +6,7 @@ import ( | ||||
| ) | ||||
|  | ||||
| func init() { | ||||
| 	cmd.Selectors["random"] = NewSelector | ||||
| 	cmd.DefaultSelectors["random"] = NewSelector | ||||
| } | ||||
|  | ||||
| func NewSelector(opts ...selector.Option) selector.Selector { | ||||
|   | ||||
| @@ -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) { | ||||
|   | ||||
| @@ -105,15 +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) | ||||
| 	} | ||||
| 	if len(s.opts.Id) == 0 { | ||||
| 		s.opts.Id = s.opts.Name + "-" + DefaultId | ||||
| 	} | ||||
| 	s.Unlock() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (s *rpcServer) NewHandler(h interface{}) Handler { | ||||
| @@ -187,7 +185,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 +258,7 @@ func (s *rpcServer) Deregister() error { | ||||
| 	} | ||||
|  | ||||
| 	node := ®istry.Node{ | ||||
| 		Id:      config.Id, | ||||
| 		Id:      config.Name + "-" + config.Id, | ||||
| 		Address: addr, | ||||
| 		Port:    port, | ||||
| 	} | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
							
								
								
									
										120
									
								
								service.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								service.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| package micro | ||||
|  | ||||
| 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 = &clientWrapper{ | ||||
| 		options.Client, | ||||
| 		context.Metadata{ | ||||
| 			HeaderPrefix + "From-Service": options.Server.Options().Name, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	return &service{ | ||||
| 		opts: options, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 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() | ||||
|  | ||||
| 	// Update any options to override command flags | ||||
| 	for _, o := range opts { | ||||
| 		o(&s.opts) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (s *service) Options() Options { | ||||
| 	return s.opts | ||||
| } | ||||
|  | ||||
| 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 { | ||||
| 	for _, fn := range s.opts.BeforeStart { | ||||
| 		if err := fn(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	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 | ||||
| 	} | ||||
|  | ||||
| 	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 { | ||||
| 	if err := s.Start(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	ch := make(chan os.Signal, 1) | ||||
| 	signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL) | ||||
| 	<-ch | ||||
|  | ||||
| 	if err := s.Stop(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										28
									
								
								wrapper.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								wrapper.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| package micro | ||||
|  | ||||
| import ( | ||||
| 	"github.com/micro/go-micro/client" | ||||
| 	cx "github.com/micro/go-micro/context" | ||||
|  | ||||
| 	"golang.org/x/net/context" | ||||
| ) | ||||
|  | ||||
| type clientWrapper struct { | ||||
| 	client.Client | ||||
| 	headers cx.Metadata | ||||
| } | ||||
|  | ||||
| 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 *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 *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...) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user