Merge pull request #30 from micro/micro

Experimental top level init
This commit is contained in:
Asim 2016-01-02 19:22:56 +00:00
commit c4dabe2652
20 changed files with 1084 additions and 171 deletions

159
README.md
View File

@ -27,7 +27,7 @@ By default go-micro only provides a single implementation of each interface. Plu
## Prerequisites
Consul is the default discovery mechanism provided in go-micro. Discovery is however pluggable.
Consul is the default discovery mechanism provided in go-micro. Discovery is however pluggable so you can used etcd, kubernetes, zookeeper, etc.
### Install Consul
[https://www.consul.io/intro/getting-started/install.html](https://www.consul.io/intro/getting-started/install.html)
@ -41,104 +41,141 @@ $ consul agent -server -bootstrap-expect 1 -data-dir /tmp/consul
### Run Service
```
$ go run examples/server/main.go --logtostderr
I1108 11:08:19.926071 11358 server.go:96] Starting server go.micro.srv.example id go.micro.srv.example-04de4cf0-8609-11e5-bf3a-68a86d0d36b6
I1108 11:08:19.926407 11358 rpc_server.go:233] Listening on [::]:54080
I1108 11:08:19.926500 11358 http_broker.go:80] Broker Listening on [::]:54081
I1108 11:08:19.926632 11358 rpc_server.go:158] Registering node: go.micro.srv.example-04de4cf0-8609-11e5-bf3a-68a86d0d36b6
$ go run examples/service/main.go --logtostderr
I0102 00:22:26.413467 12018 rpc_server.go:297] Listening on [::]:62492
I0102 00:22:26.413803 12018 http_broker.go:115] Broker Listening on [::]:62493
I0102 00:22:26.414009 12018 rpc_server.go:212] Registering node: greeter-e6b2fc6f-b0e6-11e5-a42f-68a86d0d36b6
```
### Test Service
```
$ go run examples/client/main.go
go.micro.srv.example-59b6e0ab-0300-11e5-b696-68a86d0d36b6: Hello John
$ go run examples/service/main.go --client
Hello John
```
## Writing a service
### Create request/response proto
`go-micro/examples/server/proto/example/example.proto`:
`go-micro/examples/service/proto/greeter.proto`:
```
syntax = "proto3";
message Request {
service Greeter {
rpc Hello(HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string name = 1;
}
message Response {
string msg = 1;
message HelloResponse {
string greeting = 2;
}
```
Compile proto `protoc -I$GOPATH/src --go_out=$GOPATH/src $GOPATH/src/github.com/micro/go-micro/examples/server/proto/example/example.proto`
### Install protobuf for code generation
### Create request handler
`go-micro/examples/server/handler/example.go`:
We use a protobuf plugin for code generation. This is completely optional. Look at [examples/server](https://github.com/micro/go-micro/blob/master/examples/server/main.go)
and [examples/client](https://github.com/micro/go-micro/blob/master/examples/client/main.go) for examples without code generation.
```go
package handler
import (
log "github.com/golang/glog"
c "github.com/micro/go-micro/context"
example "github.com/micro/go-micro/examples/server/proto/example"
"github.com/micro/go-micro/server"
"golang.org/x/net/context"
)
type Example struct{}
func (e *Example) Call(ctx context.Context, req *example.Request, rsp *example.Response) error {
md, _ := c.GetMetadata(ctx)
log.Infof("Received Example.Call request with metadata: %v", md)
rsp.Msg = server.Options().Id + ": Hello " + req.Name
return nil
}
```shell
go get github.com/micro/protobuf
```
### Init server
`go-micro/examples/server/main.go`:
Compile proto `protoc -I$GOPATH/src --go_out=plugins=micro:$GOPATH/src $GOPATH/src/github.com/micro/go-micro/examples/service/proto/greeter.proto`
### Define the service
`go-micro/examples/service/main.go`:
```go
package main
import (
log "github.com/golang/glog"
"github.com/micro/go-micro/cmd"
"github.com/micro/go-micro/examples/server/handler"
"github.com/micro/go-micro/server"
"fmt"
micro "github.com/micro/go-micro"
proto "github.com/micro/go-micro/examples/service/proto"
"golang.org/x/net/context"
)
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error {
rsp.Greeting = "Hello " + req.Name
return nil
}
func main() {
// optionally setup command line usage
cmd.Init()
// Initialise Server
server.Init(
server.Name("go.micro.srv.example"),
// Create a new service. Optionally include some options here.
service := micro.NewService(
micro.Name("greeter"),
micro.Version("latest"),
micro.Metadata(map[string]string{
"type": "helloworld",
}),
)
// Register Handlers
server.Handle(
server.NewHandler(
new(handler.Example),
),
)
// Init will parse the command line flags. Any flags set will
// override the above settings. Options defined here will
// override anything set on the command line.
service.Init()
// Run server
if err := server.Run(); err != nil {
log.Fatal(err)
// Register handler
proto.RegisterGreeterHandler(service.Server(), new(Greeter))
// Run the server
if err := service.Run(); err != nil {
fmt.Println(err)
}
}
```
### Run service
```
$ go run examples/server/main.go --logtostderr
I1108 11:08:19.926071 11358 server.go:96] Starting server go.micro.srv.example id go.micro.srv.example-04de4cf0-8609-11e5-bf3a-68a86d0d36b6
I1108 11:08:19.926407 11358 rpc_server.go:233] Listening on [::]:54080
I1108 11:08:19.926500 11358 http_broker.go:80] Broker Listening on [::]:54081
I1108 11:08:19.926632 11358 rpc_server.go:158] Registering node: go.micro.srv.example-04de4cf0-8609-11e5-bf3a-68a86d0d36b6
go run examples/service/main.go --logtostderr
I0102 00:22:26.413467 12018 rpc_server.go:297] Listening on [::]:62492
I0102 00:22:26.413803 12018 http_broker.go:115] Broker Listening on [::]:62493
I0102 00:22:26.414009 12018 rpc_server.go:212] Registering node: greeter-e6b2fc6f-b0e6-11e5-a42f-68a86d0d36b6
```
### Define a client
`client.go`
```go
package main
import (
"fmt"
micro "github.com/micro/go-micro"
proto "github.com/micro/go-micro/examples/service/proto"
"golang.org/x/net/context"
)
func main() {
// Create a new service. Optionally include some options here.
service := micro.NewService(micro.Name("greeter.client"))
// Create new greeter client
greeter := proto.NewGreeterClient("greeter", service.Client())
// Call the greeter
rsp, err := greeter.Hello(context.TODO(), &proto.HelloRequest{Name: "John"})
if err != nil {
fmt.Println(err)
}
// Print response
fmt.Println(rsp.Greeting)
}
```
### Run the client
```shell
go run client.go
Hello John
```

View File

@ -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

View File

@ -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)
}

View File

@ -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: &registry.DefaultRegistry,
Server: &server.DefaultServer,
Selector: &selector.DefaultSelector,
Transport: &transport.DefaultTransport,
Brokers: DefaultBrokers,
Registries: DefaultRegistries,
Selectors: DefaultSelectors,
Transports: DefaultTransports,
}
for _, o := range opts {
o(&options)
}
if len(options.Description) == 0 {
options.Description = "a go-micro service"
}
cmd := new(cmd)
cmd.opts = options
cmd.app = cli.NewApp()
cmd.app.Name = cmd.opts.Name
cmd.app.Version = cmd.opts.Version
cmd.app.Usage = cmd.opts.Description
cmd.app.Before = cmd.Before
cmd.app.Flags = DefaultFlags
cmd.app.Action = func(c *cli.Context) {}
if len(options.Version) == 0 {
cmd.app.HideVersion = true
}
return cmd
}
func (c *cmd) App() *cli.App {
return c.app
}
func (c *cmd) Options() Options {
return c.opts
}
func (c *cmd) Before(ctx *cli.Context) error {
// Due to logger issues with glog, we need to do this
os.Args = os.Args[:1]
flag.Set("logtostderr", fmt.Sprintf("%v", c.Bool("logtostderr")))
flag.Set("alsologtostderr", fmt.Sprintf("%v", c.Bool("alsologtostderr")))
flag.Set("stderrthreshold", c.String("stderrthreshold"))
flag.Set("log_backtrace_at", c.String("log_backtrace_at"))
flag.Set("log_dir", c.String("log_dir"))
flag.Set("vmodule", c.String("vmodule"))
flag.Set("v", c.String("v"))
flag.Set("logtostderr", fmt.Sprintf("%v", ctx.Bool("logtostderr")))
flag.Set("alsologtostderr", fmt.Sprintf("%v", ctx.Bool("alsologtostderr")))
flag.Set("stderrthreshold", ctx.String("stderrthreshold"))
flag.Set("log_backtrace_at", ctx.String("log_backtrace_at"))
flag.Set("log_dir", ctx.String("log_dir"))
flag.Set("vmodule", ctx.String("vmodule"))
flag.Set("v", ctx.String("v"))
flag.Parse()
if b, ok := Brokers[c.String("broker")]; ok {
broker.DefaultBroker = b(strings.Split(c.String("broker_address"), ","))
// If flags are set then use them otherwise do nothing
var serverOpts []server.Option
var clientOpts []client.Option
// Set the broker
if len(ctx.String("broker")) > 0 {
if b, ok := c.opts.Brokers[ctx.String("broker")]; ok {
n := b(strings.Split(ctx.String("broker_address"), ","))
c.opts.Broker = &n
} else {
return fmt.Errorf("Broker %s not found", ctx.String("broker"))
}
if r, ok := Registries[c.String("registry")]; ok {
registry.DefaultRegistry = r(strings.Split(c.String("registry_address"), ","))
serverOpts = append(serverOpts, server.Broker(*c.opts.Broker))
clientOpts = append(clientOpts, client.Broker(*c.opts.Broker))
}
if s, ok := Selectors[c.String("selector")]; ok {
selector.DefaultSelector = s(selector.Registry(registry.DefaultRegistry))
// Set the registry
if len(ctx.String("registry")) > 0 {
if r, ok := c.opts.Registries[ctx.String("registry")]; ok {
n := r(strings.Split(ctx.String("registry_address"), ","))
c.opts.Registry = &n
} else {
return fmt.Errorf("Registry %s not found", ctx.String("registry"))
}
if t, ok := Transports[c.String("transport")]; ok {
transport.DefaultTransport = t(strings.Split(c.String("transport_address"), ","))
serverOpts = append(serverOpts, server.Registry(*c.opts.Registry))
clientOpts = append(clientOpts, client.Registry(*c.opts.Registry))
}
// Set the selector
if len(ctx.String("selector")) > 0 {
if s, ok := c.opts.Selectors[ctx.String("selector")]; ok {
n := s(selector.Registry(*c.opts.Registry))
c.opts.Selector = &n
} else {
return fmt.Errorf("Selector %s not found", ctx.String("selector"))
}
// No server option here. Should there be?
clientOpts = append(clientOpts, client.Selector(*c.opts.Selector))
}
// Set the transport
if len(ctx.String("transport")) > 0 {
if t, ok := c.opts.Transports[ctx.String("transport")]; ok {
n := t(strings.Split(ctx.String("transport_address"), ","))
c.opts.Transport = &n
} else {
return fmt.Errorf("Transport %s not found", ctx.String("transport"))
}
serverOpts = append(serverOpts, server.Transport(*c.opts.Transport))
clientOpts = append(clientOpts, client.Transport(*c.opts.Transport))
}
// Parse the server options
metadata := make(map[string]string)
for _, d := range c.StringSlice("server_metadata") {
for _, d := range ctx.StringSlice("server_metadata") {
var key, val string
parts := strings.Split(d, "=")
key = parts[0]
@ -188,47 +291,59 @@ func Setup(c *cli.Context) error {
metadata[key] = val
}
server.DefaultServer = server.NewServer(
server.Name(c.String("server_name")),
server.Version(c.String("server_version")),
server.Id(c.String("server_id")),
server.Address(c.String("server_address")),
server.Advertise(c.String("server_advertise")),
server.Metadata(metadata),
)
if len(metadata) > 0 {
serverOpts = append(serverOpts, server.Metadata(metadata))
}
client.DefaultClient = client.NewClient()
if len(ctx.String("server_name")) > 0 {
serverOpts = append(serverOpts, server.Name(ctx.String("server_name")))
}
if len(ctx.String("server_version")) > 0 {
serverOpts = append(serverOpts, server.Version(ctx.String("server_version")))
}
if len(ctx.String("server_id")) > 0 {
serverOpts = append(serverOpts, server.Id(ctx.String("server_id")))
}
if len(ctx.String("server_address")) > 0 {
serverOpts = append(serverOpts, server.Address(ctx.String("server_address")))
}
if len(ctx.String("server_advertise")) > 0 {
serverOpts = append(serverOpts, server.Advertise(ctx.String("server_advertise")))
}
// We have some command line opts for the server.
// Lets set it up
if len(serverOpts) > 0 {
(*c.opts.Server).Init(serverOpts...)
}
// Use an init option?
if len(clientOpts) > 0 {
(*c.opts.Client).Init(clientOpts...)
}
return nil
}
func Init() {
cli.AppHelpTemplate = `
GLOBAL OPTIONS:
{{range .Flags}}{{.}}
{{end}}
`
cli.HelpPrinter = func(writer io.Writer, templ string, data interface{}) {
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
t := template.Must(template.New("help").Parse(templ))
err := t.Execute(w, data)
if err != nil {
panic(err)
func (c *cmd) Init(opts ...Option) error {
for _, o := range opts {
o(&c.opts)
}
w.Flush()
os.Exit(2)
}
app := cli.NewApp()
app.HideVersion = true
app.Usage = "a go micro app"
app.Action = func(c *cli.Context) {
for _, action := range Actions {
action(c)
}
}
app.Before = Setup
app.Flags = Flags
app.RunAndExitOnError()
c.app.Name = c.opts.Name
c.app.Version = c.opts.Version
c.app.Usage = c.opts.Description
c.app.RunAndExitOnError()
return nil
}
func Init(opts ...Option) error {
return DefaultCmd.Init(opts...)
}
func NewCmd(opts ...Option) Cmd {
return newCmd(opts...)
}

115
cmd/options.go Normal file
View 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
}
}

View File

@ -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"),

View 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)
}
}

View 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
View 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)
}
}

View 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,
}

View 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
View 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
View 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)
}
}

View File

@ -27,7 +27,7 @@ type blackListSelector struct {
}
func init() {
cmd.Selectors["blacklist"] = NewSelector
cmd.DefaultSelectors["blacklist"] = NewSelector
rand.Seed(time.Now().Unix())
}

View File

@ -6,7 +6,7 @@ import (
)
func init() {
cmd.Selectors["random"] = NewSelector
cmd.DefaultSelectors["random"] = NewSelector
}
func NewSelector(opts ...selector.Option) selector.Selector {

View File

@ -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) {

View File

@ -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 := &registry.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 := &registry.Node{
Id: config.Id,
Id: config.Name + "-" + config.Id,
Address: addr,
Port: port,
}

View File

@ -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
View 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
View 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...)
}