commit
c4dabe2652
161
README.md
161
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
|
||||
```
|
||||
|
@ -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"))
|
||||
}
|
||||
|
||||
serverOpts = append(serverOpts, server.Broker(*c.opts.Broker))
|
||||
clientOpts = append(clientOpts, client.Broker(*c.opts.Broker))
|
||||
}
|
||||
|
||||
if r, ok := Registries[c.String("registry")]; ok {
|
||||
registry.DefaultRegistry = r(strings.Split(c.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 := Selectors[c.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 := Transports[c.String("transport")]; ok {
|
||||
transport.DefaultTransport = t(strings.Split(c.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 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)
|
||||
}
|
||||
w.Flush()
|
||||
os.Exit(2)
|
||||
func (c *cmd) Init(opts ...Option) error {
|
||||
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()
|
||||
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...)
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user