title | keywords | tags | sidebar | permalink | summary | |
---|---|---|---|---|---|---|
Writing a Service |
development |
|
home_sidebar |
/docs/writing-a-go-service.html |
This is a more detailed guide to writing a service and the internals related to it. The top level Service interface is the main component for building a service. It wraps all the underlying packages of Go Micro into a single convenient interface.
type Service interface {
Init(...Option)
Options() Options
Client() client.Client
Server() server.Server
Run() error
String() string
}
A service is created like so using micro.NewService
.
import "github.com/micro/go-micro/v2"
service := micro.NewService()
Options can be passed in during creation.
service := micro.NewService(
micro.Name("greeter"),
micro.Version("latest"),
)
All the available options can be found here.
Go Micro also provides a way to set command line flags using micro.Flags
.
import (
"github.com/micro/cli"
"github.com/micro/go-micro/v2"
)
service := micro.NewService(
micro.Flags(
cli.StringFlag{
Name: "environment",
Usage: "The environment",
},
)
)
To parse flags use service.Init
. Additionally access flags use the micro.Action
option.
service.Init(
micro.Action(func(c *cli.Context) {
env := c.StringFlag("environment")
if len(env) > 0 {
fmt.Println("Environment set to", env)
}
}),
)
Go Micro provides predefined flags which are set and parsed if service.Init
is called. See all the flags
here.
We use protobuf files to define the service API interface. This is a very convenient way to strictly define the API and provide concrete types for both the server and a client.
Here's an example definition.
greeter.proto
syntax = "proto3";
service Greeter {
rpc Hello(Request) returns (Response) {}
}
message Request {
string name = 1;
}
message Response {
string greeting = 2;
}
Here we're defining a service handler called Greeter with the method Hello which takes the parameter Request type and returns Response.
You'll need the following to generate protobuf code
We use protoc, protoc-gen-go and protoc-gen-micro to generate the concrete go implementation for this definition.
go get github.com/golang/protobuf/{proto,protoc-gen-go}
go get github.com/micro/micro/v2/cmd/protoc-gen-micro@master
protoc --proto_path=$GOPATH/src:. --micro_out=. --go_out=. greeter.proto
The types generated can now be imported and used within a handler for a server or the client when making a request.
Here's part of the generated code.
type Request struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
}
type Response struct {
Greeting string `protobuf:"bytes,2,opt,name=greeting" json:"greeting,omitempty"`
}
// Client API for Greeter service
type GreeterClient interface {
Hello(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, 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 *Request, opts ...client.CallOption) (*Response, error) {
req := c.c.NewRequest(c.serviceName, "Greeter.Hello", in)
out := new(Response)
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, *Request, *Response) error
}
func RegisterGreeterHandler(s server.Server, hdlr GreeterHandler) {
s.Handle(s.NewHandler(&Greeter{hdlr}))
}
The server requires handlers to be registered to serve requests. A handler is an public type with public methods
which conform to the signature func(ctx context.Context, req interface{}, rsp interface{}) error
.
As you can see above, a handler signature for the Greeter interface looks like so.
type GreeterHandler interface {
Hello(context.Context, *Request, *Response) error
}
Here's an implementation of the Greeter handler.
import proto "github.com/micro/examples/service/proto"
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
rsp.Greeting = "Hello " + req.Name
return nil
}
The handler is registered with your service much like a http.Handler.
service := micro.NewService(
micro.Name("greeter"),
)
pb.RegisterGreeterHandler(service.Server(), new(Greeter))
You can also create a bidirectional streaming handler but we'll leave that for another day.
The service can be run by calling server.Run
. This causes the service to bind to the address in the config
(which defaults to the first RFC1918 interface found and a random port) and listen for requests.
This will additionally Register the service with the registry on start and Deregister when issued a kill signal.
if err := service.Run(); err != nil {
log.Fatal(err)
}
greeter.go
package main
import (
"log"
"github.com/micro/go-micro/v2"
pb "github.com/micro/examples/service/proto"
"golang.org/x/net/context"
)
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
rsp.Greeting = "Hello " + req.Name
return nil
}
func main() {
service := micro.NewService(
micro.Name("greeter"),
)
service.Init()
pb.RegisterGreeterHandler(service.Server(), new(Greeter))
if err := service.Run(); err != nil {
log.Fatal(err)
}
}
Note. The service discovery mechanism will need to be running so the service can register to be discovered by clients and other services. A quick getting started for that is here.
The client package is used to query services. When you create a Service, a Client is included which matches the initialised packages used by the server.
Querying the above service is as simple as the following.
// create the greeter client using the service name and client
greeter := pb.NewGreeterService("greeter", service.Client())
// request the Hello method on the Greeter handler
rsp, err := greeter.Hello(context.TODO(), &pb.Request{
Name: "John",
})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(rsp.Greeting)
pb.NewGreeterClient
takes the service name and the client used for making requests.
The full example is can be found at go-micro/examples/service.