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
|
## 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
|
### Install Consul
|
||||||
[https://www.consul.io/intro/getting-started/install.html](https://www.consul.io/intro/getting-started/install.html)
|
[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
|
### Run Service
|
||||||
```
|
```
|
||||||
$ go run examples/server/main.go --logtostderr
|
$ go run examples/service/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
|
I0102 00:22:26.413467 12018 rpc_server.go:297] Listening on [::]:62492
|
||||||
I1108 11:08:19.926407 11358 rpc_server.go:233] Listening on [::]:54080
|
I0102 00:22:26.413803 12018 http_broker.go:115] Broker Listening on [::]:62493
|
||||||
I1108 11:08:19.926500 11358 http_broker.go:80] Broker Listening on [::]:54081
|
I0102 00:22:26.414009 12018 rpc_server.go:212] Registering node: greeter-e6b2fc6f-b0e6-11e5-a42f-68a86d0d36b6
|
||||||
I1108 11:08:19.926632 11358 rpc_server.go:158] Registering node: go.micro.srv.example-04de4cf0-8609-11e5-bf3a-68a86d0d36b6
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Test Service
|
### Test Service
|
||||||
```
|
```
|
||||||
$ go run examples/client/main.go
|
$ go run examples/service/main.go --client
|
||||||
go.micro.srv.example-59b6e0ab-0300-11e5-b696-68a86d0d36b6: Hello John
|
Hello John
|
||||||
```
|
```
|
||||||
|
|
||||||
## Writing a service
|
## Writing a service
|
||||||
|
|
||||||
### Create request/response proto
|
### Create request/response proto
|
||||||
`go-micro/examples/server/proto/example/example.proto`:
|
`go-micro/examples/service/proto/greeter.proto`:
|
||||||
|
|
||||||
```
|
```
|
||||||
syntax = "proto3";
|
syntax = "proto3";
|
||||||
|
|
||||||
message Request {
|
service Greeter {
|
||||||
string name = 1;
|
rpc Hello(HelloRequest) returns (HelloResponse) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
message Response {
|
message HelloRequest {
|
||||||
string msg = 1;
|
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
|
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)
|
||||||
`go-micro/examples/server/handler/example.go`:
|
and [examples/client](https://github.com/micro/go-micro/blob/master/examples/client/main.go) for examples without code generation.
|
||||||
|
|
||||||
```go
|
```shell
|
||||||
package handler
|
go get github.com/micro/protobuf
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Init server
|
Compile proto `protoc -I$GOPATH/src --go_out=plugins=micro:$GOPATH/src $GOPATH/src/github.com/micro/go-micro/examples/service/proto/greeter.proto`
|
||||||
`go-micro/examples/server/main.go`:
|
|
||||||
|
### Define the service
|
||||||
|
`go-micro/examples/service/main.go`:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
log "github.com/golang/glog"
|
"fmt"
|
||||||
"github.com/micro/go-micro/cmd"
|
|
||||||
"github.com/micro/go-micro/examples/server/handler"
|
micro "github.com/micro/go-micro"
|
||||||
"github.com/micro/go-micro/server"
|
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() {
|
func main() {
|
||||||
// optionally setup command line usage
|
// Create a new service. Optionally include some options here.
|
||||||
cmd.Init()
|
service := micro.NewService(
|
||||||
|
micro.Name("greeter"),
|
||||||
// Initialise Server
|
micro.Version("latest"),
|
||||||
server.Init(
|
micro.Metadata(map[string]string{
|
||||||
server.Name("go.micro.srv.example"),
|
"type": "helloworld",
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Register Handlers
|
// Init will parse the command line flags. Any flags set will
|
||||||
server.Handle(
|
// override the above settings. Options defined here will
|
||||||
server.NewHandler(
|
// override anything set on the command line.
|
||||||
new(handler.Example),
|
service.Init()
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Run server
|
// Register handler
|
||||||
if err := server.Run(); err != nil {
|
proto.RegisterGreeterHandler(service.Server(), new(Greeter))
|
||||||
log.Fatal(err)
|
|
||||||
|
// Run the server
|
||||||
|
if err := service.Run(); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run service
|
### Run service
|
||||||
```
|
```
|
||||||
$ go run examples/server/main.go --logtostderr
|
go run examples/service/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
|
I0102 00:22:26.413467 12018 rpc_server.go:297] Listening on [::]:62492
|
||||||
I1108 11:08:19.926407 11358 rpc_server.go:233] Listening on [::]:54080
|
I0102 00:22:26.413803 12018 http_broker.go:115] Broker Listening on [::]:62493
|
||||||
I1108 11:08:19.926500 11358 http_broker.go:80] Broker Listening on [::]:54081
|
I0102 00:22:26.414009 12018 rpc_server.go:212] Registering node: greeter-e6b2fc6f-b0e6-11e5-a42f-68a86d0d36b6
|
||||||
I1108 11:08:19.926632 11358 rpc_server.go:158] Registering node: go.micro.srv.example-04de4cf0-8609-11e5-bf3a-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 {
|
type Client interface {
|
||||||
|
Init(...Option) error
|
||||||
|
Options() Options
|
||||||
NewPublication(topic string, msg interface{}) Publication
|
NewPublication(topic string, msg interface{}) Publication
|
||||||
NewRequest(service, method string, req interface{}, reqOpts ...RequestOption) Request
|
NewRequest(service, method string, req interface{}, reqOpts ...RequestOption) Request
|
||||||
NewProtoRequest(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
|
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 {
|
func (r *rpcClient) CallRemote(ctx context.Context, address string, request Request, response interface{}, opts ...CallOption) error {
|
||||||
return r.call(ctx, address, request, response)
|
return r.call(ctx, address, request, response)
|
||||||
}
|
}
|
||||||
|
257
cmd/cmd.go
257
cmd/cmd.go
@ -7,11 +7,9 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"text/tabwriter"
|
|
||||||
"text/template"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/micro/cli"
|
||||||
"github.com/micro/go-micro/broker"
|
"github.com/micro/go-micro/broker"
|
||||||
"github.com/micro/go-micro/client"
|
"github.com/micro/go-micro/client"
|
||||||
"github.com/micro/go-micro/registry"
|
"github.com/micro/go-micro/registry"
|
||||||
@ -20,10 +18,27 @@ import (
|
|||||||
"github.com/micro/go-micro/transport"
|
"github.com/micro/go-micro/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type Cmd interface {
|
||||||
Actions = []func(*cli.Context){}
|
// 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{
|
cli.StringFlag{
|
||||||
Name: "server_name",
|
Name: "server_name",
|
||||||
EnvVar: "MICRO_SERVER_NAME",
|
EnvVar: "MICRO_SERVER_NAME",
|
||||||
@ -42,7 +57,6 @@ var (
|
|||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "server_address",
|
Name: "server_address",
|
||||||
EnvVar: "MICRO_SERVER_ADDRESS",
|
EnvVar: "MICRO_SERVER_ADDRESS",
|
||||||
Value: ":0",
|
|
||||||
Usage: "Bind address for the server. 127.0.0.1:8080",
|
Usage: "Bind address for the server. 127.0.0.1:8080",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
@ -59,7 +73,6 @@ var (
|
|||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "broker",
|
Name: "broker",
|
||||||
EnvVar: "MICRO_BROKER",
|
EnvVar: "MICRO_BROKER",
|
||||||
Value: "http",
|
|
||||||
Usage: "Broker for pub/sub. http, nats, rabbitmq",
|
Usage: "Broker for pub/sub. http, nats, rabbitmq",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
@ -70,7 +83,6 @@ var (
|
|||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "registry",
|
Name: "registry",
|
||||||
EnvVar: "MICRO_REGISTRY",
|
EnvVar: "MICRO_REGISTRY",
|
||||||
Value: "consul",
|
|
||||||
Usage: "Registry for discovery. memory, consul, etcd, kubernetes",
|
Usage: "Registry for discovery. memory, consul, etcd, kubernetes",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
@ -81,13 +93,11 @@ var (
|
|||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "selector",
|
Name: "selector",
|
||||||
EnvVar: "MICRO_SELECTOR",
|
EnvVar: "MICRO_SELECTOR",
|
||||||
Value: "selector",
|
|
||||||
Usage: "Selector used to pick nodes for querying. random, roundrobin, blacklist",
|
Usage: "Selector used to pick nodes for querying. random, roundrobin, blacklist",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "transport",
|
Name: "transport",
|
||||||
EnvVar: "MICRO_TRANSPORT",
|
EnvVar: "MICRO_TRANSPORT",
|
||||||
Value: "http",
|
|
||||||
Usage: "Transport mechanism used; http, rabbitmq, nats",
|
Usage: "Transport mechanism used; http, rabbitmq, nats",
|
||||||
},
|
},
|
||||||
cli.StringFlag{
|
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,
|
"http": broker.NewBroker,
|
||||||
}
|
}
|
||||||
|
|
||||||
Registries = map[string]func([]string, ...registry.Option) registry.Registry{
|
DefaultRegistries = map[string]func([]string, ...registry.Option) registry.Registry{
|
||||||
"consul": registry.NewRegistry,
|
"consul": registry.NewRegistry,
|
||||||
}
|
}
|
||||||
|
|
||||||
Selectors = map[string]func(...selector.Option) selector.Selector{
|
DefaultSelectors = map[string]func(...selector.Option) selector.Selector{
|
||||||
"random": selector.NewSelector,
|
"random": selector.NewSelector,
|
||||||
}
|
}
|
||||||
|
|
||||||
Transports = map[string]func([]string, ...transport.Option) transport.Transport{
|
DefaultTransports = map[string]func([]string, ...transport.Option) transport.Transport{
|
||||||
"http": transport.NewTransport,
|
"http": transport.NewTransport,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rand.Seed(time.Now().Unix())
|
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]
|
os.Args = os.Args[:1]
|
||||||
|
flag.Set("logtostderr", fmt.Sprintf("%v", ctx.Bool("logtostderr")))
|
||||||
flag.Set("logtostderr", fmt.Sprintf("%v", c.Bool("logtostderr")))
|
flag.Set("alsologtostderr", fmt.Sprintf("%v", ctx.Bool("alsologtostderr")))
|
||||||
flag.Set("alsologtostderr", fmt.Sprintf("%v", c.Bool("alsologtostderr")))
|
flag.Set("stderrthreshold", ctx.String("stderrthreshold"))
|
||||||
flag.Set("stderrthreshold", c.String("stderrthreshold"))
|
flag.Set("log_backtrace_at", ctx.String("log_backtrace_at"))
|
||||||
flag.Set("log_backtrace_at", c.String("log_backtrace_at"))
|
flag.Set("log_dir", ctx.String("log_dir"))
|
||||||
flag.Set("log_dir", c.String("log_dir"))
|
flag.Set("vmodule", ctx.String("vmodule"))
|
||||||
flag.Set("vmodule", c.String("vmodule"))
|
flag.Set("v", ctx.String("v"))
|
||||||
flag.Set("v", c.String("v"))
|
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if b, ok := Brokers[c.String("broker")]; ok {
|
// If flags are set then use them otherwise do nothing
|
||||||
broker.DefaultBroker = b(strings.Split(c.String("broker_address"), ","))
|
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 {
|
// Set the registry
|
||||||
registry.DefaultRegistry = r(strings.Split(c.String("registry_address"), ","))
|
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 {
|
// Set the selector
|
||||||
selector.DefaultSelector = s(selector.Registry(registry.DefaultRegistry))
|
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 {
|
// Set the transport
|
||||||
transport.DefaultTransport = t(strings.Split(c.String("transport_address"), ","))
|
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)
|
metadata := make(map[string]string)
|
||||||
for _, d := range c.StringSlice("server_metadata") {
|
for _, d := range ctx.StringSlice("server_metadata") {
|
||||||
var key, val string
|
var key, val string
|
||||||
parts := strings.Split(d, "=")
|
parts := strings.Split(d, "=")
|
||||||
key = parts[0]
|
key = parts[0]
|
||||||
@ -188,47 +291,59 @@ func Setup(c *cli.Context) error {
|
|||||||
metadata[key] = val
|
metadata[key] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
server.DefaultServer = server.NewServer(
|
if len(metadata) > 0 {
|
||||||
server.Name(c.String("server_name")),
|
serverOpts = append(serverOpts, server.Metadata(metadata))
|
||||||
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),
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Init() {
|
func (c *cmd) Init(opts ...Option) error {
|
||||||
cli.AppHelpTemplate = `
|
for _, o := range opts {
|
||||||
GLOBAL OPTIONS:
|
o(&c.opts)
|
||||||
{{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)
|
|
||||||
}
|
}
|
||||||
|
c.app.Name = c.opts.Name
|
||||||
app := cli.NewApp()
|
c.app.Version = c.opts.Version
|
||||||
app.HideVersion = true
|
c.app.Usage = c.opts.Description
|
||||||
app.Usage = "a go micro app"
|
c.app.RunAndExitOnError()
|
||||||
app.Action = func(c *cli.Context) {
|
return nil
|
||||||
for _, action := range Actions {
|
}
|
||||||
action(c)
|
|
||||||
}
|
func Init(opts ...Option) error {
|
||||||
}
|
return DefaultCmd.Init(opts...)
|
||||||
app.Before = Setup
|
}
|
||||||
app.Flags = Flags
|
|
||||||
app.RunAndExitOnError()
|
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/handler"
|
||||||
"github.com/micro/go-micro/examples/server/subscriber"
|
"github.com/micro/go-micro/examples/server/subscriber"
|
||||||
"github.com/micro/go-micro/server"
|
"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() {
|
func main() {
|
||||||
// optionally setup command line usage
|
// optionally setup command line usage
|
||||||
cmd.Init()
|
cmd.Init()
|
||||||
|
|
||||||
md := server.DefaultOptions().Metadata
|
|
||||||
md["datacenter"] = "local"
|
|
||||||
|
|
||||||
server.DefaultServer = server.NewServer(
|
|
||||||
server.WrapHandler(logWrapper),
|
|
||||||
server.WrapSubscriber(logSubWrapper),
|
|
||||||
server.Metadata(md),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Initialise Server
|
// Initialise Server
|
||||||
server.Init(
|
server.Init(
|
||||||
server.Name("go.micro.srv.example"),
|
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() {
|
func init() {
|
||||||
cmd.Selectors["blacklist"] = NewSelector
|
cmd.DefaultSelectors["blacklist"] = NewSelector
|
||||||
rand.Seed(time.Now().Unix())
|
rand.Seed(time.Now().Unix())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmd.Selectors["random"] = NewSelector
|
cmd.DefaultSelectors["random"] = NewSelector
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSelector(opts ...selector.Option) selector.Selector {
|
func NewSelector(opts ...selector.Option) selector.Selector {
|
||||||
|
@ -13,7 +13,7 @@ type roundRobinSelector struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmd.Selectors["roundrobin"] = NewSelector
|
cmd.DefaultSelectors["roundrobin"] = NewSelector
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *roundRobinSelector) Select(service string, opts ...selector.SelectOption) (selector.Next, error) {
|
func (r *roundRobinSelector) Select(service string, opts ...selector.SelectOption) (selector.Next, error) {
|
||||||
|
@ -105,15 +105,13 @@ func (s *rpcServer) Options() Options {
|
|||||||
return opts
|
return opts
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *rpcServer) Init(opts ...Option) {
|
func (s *rpcServer) Init(opts ...Option) error {
|
||||||
s.Lock()
|
s.Lock()
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(&s.opts)
|
opt(&s.opts)
|
||||||
}
|
}
|
||||||
if len(s.opts.Id) == 0 {
|
|
||||||
s.opts.Id = s.opts.Name + "-" + DefaultId
|
|
||||||
}
|
|
||||||
s.Unlock()
|
s.Unlock()
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *rpcServer) NewHandler(h interface{}) Handler {
|
func (s *rpcServer) NewHandler(h interface{}) Handler {
|
||||||
@ -187,7 +185,7 @@ func (s *rpcServer) Register() error {
|
|||||||
|
|
||||||
// register service
|
// register service
|
||||||
node := ®istry.Node{
|
node := ®istry.Node{
|
||||||
Id: config.Id,
|
Id: config.Name + "-" + config.Id,
|
||||||
Address: addr,
|
Address: addr,
|
||||||
Port: port,
|
Port: port,
|
||||||
Metadata: config.Metadata,
|
Metadata: config.Metadata,
|
||||||
@ -260,7 +258,7 @@ func (s *rpcServer) Deregister() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
node := ®istry.Node{
|
node := ®istry.Node{
|
||||||
Id: config.Id,
|
Id: config.Name + "-" + config.Id,
|
||||||
Address: addr,
|
Address: addr,
|
||||||
Port: port,
|
Port: port,
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ import (
|
|||||||
|
|
||||||
type Server interface {
|
type Server interface {
|
||||||
Options() Options
|
Options() Options
|
||||||
Init(...Option)
|
Init(...Option) error
|
||||||
Handle(Handler) error
|
Handle(Handler) error
|
||||||
NewHandler(interface{}) Handler
|
NewHandler(interface{}) Handler
|
||||||
NewSubscriber(string, interface{}) Subscriber
|
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…
Reference in New Issue
Block a user