Update top level init

This commit is contained in:
Asim 2016-01-01 01:16:21 +00:00
parent d705d3fe50
commit 0d50b2eb32
9 changed files with 515 additions and 85 deletions

View File

@ -3,15 +3,12 @@ package cmd
import (
"flag"
"fmt"
"io"
"math/rand"
"os"
"strings"
"text/tabwriter"
"text/template"
"time"
"github.com/codegangsta/cli"
"github.com/micro/cli"
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/registry"
@ -20,10 +17,27 @@ import (
"github.com/micro/go-micro/transport"
)
var (
Actions = []func(*cli.Context){}
type Cmd interface {
// The cli app within this cmd
App() *cli.App
// Adds options, parses flags and initialise
// exits on error
Init(opts ...Option)
// Options set within this command
Options() Options
}
Flags = []cli.Flag{
type cmd struct {
opts Options
app *cli.App
}
type Option func(o *Options)
var (
DefaultCmd = newCmd()
DefaultFlags = []cli.Flag{
cli.StringFlag{
Name: "server_name",
EnvVar: "MICRO_SERVER_NAME",
@ -127,19 +141,19 @@ var (
},
}
Brokers = map[string]func([]string, ...broker.Option) broker.Broker{
DefaultBrokers = map[string]func([]string, ...broker.Option) broker.Broker{
"http": broker.NewBroker,
}
Registries = map[string]func([]string, ...registry.Option) registry.Registry{
DefaultRegistries = map[string]func([]string, ...registry.Option) registry.Registry{
"consul": registry.NewRegistry,
}
Selectors = map[string]func(...selector.Option) selector.Selector{
DefaultSelectors = map[string]func(...selector.Option) selector.Selector{
"random": selector.NewSelector,
}
Transports = map[string]func([]string, ...transport.Option) transport.Transport{
DefaultTransports = map[string]func([]string, ...transport.Option) transport.Transport{
"http": transport.NewTransport,
}
)
@ -148,37 +162,68 @@ func init() {
rand.Seed(time.Now().Unix())
}
func Setup(c *cli.Context) error {
func newCmd(opts ...Option) Cmd {
options := Options{
Brokers: DefaultBrokers,
Registries: DefaultRegistries,
Selectors: DefaultSelectors,
Transports: DefaultTransports,
}
for _, o := range opts {
o(&options)
}
cmd := new(cmd)
cmd.opts = options
cmd.app = cli.NewApp()
cmd.app.Name = cmd.opts.Name
cmd.app.Version = cmd.opts.Version
cmd.app.Usage = cmd.opts.Description
cmd.app.Before = cmd.Before
cmd.app.Flags = DefaultFlags
cmd.app.Action = func(c *cli.Context) {}
return cmd
}
func (c *cmd) App() *cli.App {
return c.app
}
func (c *cmd) Options() Options {
return c.opts
}
func (c *cmd) Before(ctx *cli.Context) error {
// Due to logger issues with glog, we need to do this
os.Args = os.Args[:1]
flag.Set("logtostderr", fmt.Sprintf("%v", c.Bool("logtostderr")))
flag.Set("alsologtostderr", fmt.Sprintf("%v", c.Bool("alsologtostderr")))
flag.Set("stderrthreshold", c.String("stderrthreshold"))
flag.Set("log_backtrace_at", c.String("log_backtrace_at"))
flag.Set("log_dir", c.String("log_dir"))
flag.Set("vmodule", c.String("vmodule"))
flag.Set("v", c.String("v"))
flag.Set("logtostderr", fmt.Sprintf("%v", ctx.Bool("logtostderr")))
flag.Set("alsologtostderr", fmt.Sprintf("%v", ctx.Bool("alsologtostderr")))
flag.Set("stderrthreshold", ctx.String("stderrthreshold"))
flag.Set("log_backtrace_at", ctx.String("log_backtrace_at"))
flag.Set("log_dir", ctx.String("log_dir"))
flag.Set("vmodule", ctx.String("vmodule"))
flag.Set("v", ctx.String("v"))
flag.Parse()
if b, ok := Brokers[c.String("broker")]; ok {
broker.DefaultBroker = b(strings.Split(c.String("broker_address"), ","))
if b, ok := c.opts.Brokers[ctx.String("broker")]; ok {
broker.DefaultBroker = b(strings.Split(ctx.String("broker_address"), ","))
}
if r, ok := Registries[c.String("registry")]; ok {
registry.DefaultRegistry = r(strings.Split(c.String("registry_address"), ","))
if r, ok := c.opts.Registries[ctx.String("registry")]; ok {
registry.DefaultRegistry = r(strings.Split(ctx.String("registry_address"), ","))
}
if s, ok := Selectors[c.String("selector")]; ok {
if s, ok := c.opts.Selectors[ctx.String("selector")]; ok {
selector.DefaultSelector = s(selector.Registry(registry.DefaultRegistry))
}
if t, ok := Transports[c.String("transport")]; ok {
transport.DefaultTransport = t(strings.Split(c.String("transport_address"), ","))
if t, ok := c.opts.Transports[ctx.String("transport")]; ok {
transport.DefaultTransport = t(strings.Split(ctx.String("transport_address"), ","))
}
metadata := make(map[string]string)
for _, d := range c.StringSlice("server_metadata") {
for _, d := range ctx.StringSlice("server_metadata") {
var key, val string
parts := strings.Split(d, "=")
key = parts[0]
@ -189,11 +234,11 @@ func Setup(c *cli.Context) error {
}
server.DefaultServer = server.NewServer(
server.Name(c.String("server_name")),
server.Version(c.String("server_version")),
server.Id(c.String("server_id")),
server.Address(c.String("server_address")),
server.Advertise(c.String("server_advertise")),
server.Name(ctx.String("server_name")),
server.Version(ctx.String("server_version")),
server.Id(ctx.String("server_id")),
server.Address(ctx.String("server_address")),
server.Advertise(ctx.String("server_advertise")),
server.Metadata(metadata),
)
@ -202,33 +247,20 @@ func Setup(c *cli.Context) error {
return nil
}
func Init() {
cli.AppHelpTemplate = `
GLOBAL OPTIONS:
{{range .Flags}}{{.}}
{{end}}
`
cli.HelpPrinter = func(writer io.Writer, templ string, data interface{}) {
w := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', 0)
t := template.Must(template.New("help").Parse(templ))
err := t.Execute(w, data)
if err != nil {
panic(err)
}
w.Flush()
os.Exit(2)
func (c *cmd) Init(opts ...Option) {
for _, o := range opts {
o(&c.opts)
}
app := cli.NewApp()
app.HideVersion = true
app.Usage = "a go micro app"
app.Action = func(c *cli.Context) {
for _, action := range Actions {
action(c)
}
}
app.Before = Setup
app.Flags = Flags
app.RunAndExitOnError()
c.app.Name = c.opts.Name
c.app.Version = c.opts.Version
c.app.Usage = c.opts.Description
c.app.RunAndExitOnError()
}
func Init(opts ...Option) {
DefaultCmd.Init(opts...)
}
func NewCmd(opts ...Option) Cmd {
return newCmd(opts...)
}

61
cmd/options.go Normal file
View File

@ -0,0 +1,61 @@
package cmd
import (
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/selector"
"github.com/micro/go-micro/transport"
)
type Options struct {
Name string
Description string
Version string
Brokers map[string]func([]string, ...broker.Option) broker.Broker
Registries map[string]func([]string, ...registry.Option) registry.Registry
Selectors map[string]func(...selector.Option) selector.Selector
Transports map[string]func([]string, ...transport.Option) transport.Transport
}
func Name(n string) Option {
return func(o *Options) {
o.Name = n
}
}
func Description(d string) Option {
return func(o *Options) {
o.Description = d
}
}
func Version(v string) Option {
return func(o *Options) {
o.Version = v
}
}
func Broker(name string, b func([]string, ...broker.Option) broker.Broker) Option {
return func(o *Options) {
o.Brokers[name] = b
}
}
func Registry(name string, r func([]string, ...registry.Option) registry.Registry) Option {
return func(o *Options) {
o.Registries[name] = r
}
}
func Selector(name string, s func(...selector.Option) selector.Selector) Option {
return func(o *Options) {
o.Selectors[name] = s
}
}
func Transport(name string, t func([]string, ...transport.Option) transport.Transport) Option {
return func(o *Options) {
o.Transports[name] = t
}
}

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.

93
examples/service/main.go Normal file
View File

@ -0,0 +1,93 @@
package main
import (
"fmt"
"os"
"github.com/micro/cli"
micro "github.com/micro/go-micro"
proto "github.com/micro/go-micro/examples/service/proto"
"golang.org/x/net/context"
)
/*
Example usage of top level service initialisation
*/
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error {
rsp.Greeting = "Hello " + req.Name
return nil
}
// Setup and the client
func client(service micro.Service) {
// Create new greeter client
greeter := proto.NewGreeterClient("greeter", service.Client())
// Call the greeter
rsp, err := greeter.Hello(context.TODO(), &proto.HelloRequest{Name: "John"})
if err != nil {
fmt.Println(err)
}
// Print response
fmt.Println(rsp.Greeting)
}
// Setup some command line flags
func flags(service micro.Service) {
app := service.Cmd().App()
app.Flags = append(app.Flags,
&cli.BoolFlag{
Name: "server",
Usage: "Launch the server",
},
&cli.BoolFlag{
Name: "client",
Usage: "Launch the client",
},
)
// Let's launch the server or the client
app.Action = func(c *cli.Context) {
if c.Bool("client") {
client(service)
os.Exit(0)
}
}
}
func main() {
// Create a new service. Optionally include some options here.
service := micro.NewService(
micro.Name("greeter"),
micro.Version("latest"),
micro.Metadata(map[string]string{
"type": "helloworld",
}),
)
// Setup some flags. Specify --client to run the client
flags(service)
// Init will parse the command line flags. Any flags set will
// override the above settings. Options defined here will
// override anything set on the command line.
service.Init()
// By default we'll run the server unless the flags catch us
// Setup the server
// Register handler
proto.RegisterGreeterHandler(service.Server(), new(Greeter))
// Run the server
if err := service.Run(); err != nil {
fmt.Println(err)
}
}

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;
}

View File

@ -21,6 +21,7 @@ package gomicro
import (
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/cmd"
"github.com/micro/go-micro/server"
)
@ -28,6 +29,8 @@ import (
// within go-micro. Its a convenience method for building
// and initialising services.
type Service interface {
Init(...Option)
Cmd() cmd.Cmd
Client() client.Client
Server() server.Server
Run() error

View File

@ -3,6 +3,7 @@ package gomicro
import (
"github.com/micro/go-micro/broker"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/cmd"
"github.com/micro/go-micro/registry"
"github.com/micro/go-micro/server"
"github.com/micro/go-micro/transport"
@ -10,38 +11,35 @@ import (
type Options struct {
Broker broker.Broker
Cmd cmd.Cmd
Client client.Client
Server server.Server
Registry registry.Registry
Transport transport.Transport
// Before and After funcs
BeforeStart []func() error
AfterStop []func() error
// Alternative options for those implementing the interface
Options map[string]string
}
func newOptions(opts ...Option) Options {
var opt Options
opt := Options{
Broker: broker.DefaultBroker,
Cmd: cmd.DefaultCmd,
Client: client.DefaultClient,
Server: server.DefaultServer,
Registry: registry.DefaultRegistry,
Transport: transport.DefaultTransport,
Options: map[string]string{},
}
for _, o := range opts {
o(&opt)
}
if opt.Broker == nil {
opt.Broker = broker.DefaultBroker
}
if opt.Client == nil {
opt.Client = client.DefaultClient
}
if opt.Server == nil {
opt.Server = server.DefaultServer
}
if opt.Registry == nil {
opt.Registry = registry.DefaultRegistry
}
if opt.Transport == nil {
opt.Transport = transport.DefaultTransport
}
return opt
}
@ -51,6 +49,12 @@ func Broker(b broker.Broker) Option {
}
}
func Cmd(c cmd.Cmd) Option {
return func(o *Options) {
o.Cmd = c
}
}
func Client(c client.Client) Option {
return func(o *Options) {
o.Client = c
@ -74,3 +78,40 @@ func Transport(t transport.Transport) Option {
o.Transport = t
}
}
// Convenience options
// Name of the service
func Name(n string) Option {
return func(o *Options) {
o.Server.Init(server.Name(n))
}
}
// Version of the service
func Version(v string) Option {
return func(o *Options) {
o.Server.Init(server.Version(v))
}
}
// Metadata associated with the service
func Metadata(md map[string]string) Option {
return func(o *Options) {
o.Server.Init(server.Metadata(md))
}
}
// Before and Afters
func BeforeStart(fn func() error) Option {
return func(o *Options) {
o.BeforeStart = append(o.BeforeStart, fn)
}
}
func AfterStop(fn func() error) Option {
return func(o *Options) {
o.AfterStop = append(o.AfterStop, fn)
}
}

View File

@ -6,6 +6,7 @@ import (
"syscall"
"github.com/micro/go-micro/client"
"github.com/micro/go-micro/cmd"
"github.com/micro/go-micro/context"
"github.com/micro/go-micro/server"
)
@ -20,7 +21,7 @@ func newService(opts ...Option) Service {
options.Client = &clientWrapper{
options.Client,
context.Metadata{
HeaderPrefix + "From-Service": options.Server.Config().Name(),
HeaderPrefix + "From-Service": options.Server.Options().Name,
},
}
@ -29,6 +30,15 @@ func newService(opts ...Option) Service {
}
}
func (s *service) Init(opts ...Option) {
s.opts.Cmd.Init()
s = newService(opts...).(*service)
}
func (s *service) Cmd() cmd.Cmd {
return s.opts.Cmd
}
func (s *service) Client() client.Client {
return s.opts.Client
}
@ -42,6 +52,12 @@ func (s *service) String() string {
}
func (s *service) Start() error {
for _, fn := range s.opts.BeforeStart {
if err := fn(); err != nil {
return err
}
}
if err := s.opts.Server.Start(); err != nil {
return err
}
@ -62,7 +78,16 @@ func (s *service) Stop() error {
return err
}
return nil
var gerr error
for _, fn := range s.opts.AfterStop {
if err := fn(); err != nil {
// should we bail if it fails?
// other funcs will not be executed
// seems wrong
gerr = err
}
}
return gerr
}
func (s *service) Run() error {