github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/docs/v2/guides/writing-a-go-service.md (about) 1 --- 2 title: Writing a Service 3 keywords: development 4 tags: [development] 5 sidebar: home_sidebar 6 permalink: /writing-a-go-service 7 summary: 8 --- 9 10 This is a more detailed guide to writing a service and the internals related to it. The top level [Service](https://pkg.go.dev/github.com/micro/go-micro/v2#Service) 11 interface is the main component for building a service. It wraps all the underlying packages of Go Micro into a single convenient interface. 12 13 ```go 14 type Service interface { 15 Init(...Option) 16 Options() Options 17 Client() client.Client 18 Server() server.Server 19 Run() error 20 String() string 21 } 22 ``` 23 24 ### 1. Initialisation 25 26 A service is created like so using `micro.NewService`. 27 28 ```go 29 import "github.com/micro/go-micro/v2" 30 31 service := micro.NewService() 32 ``` 33 34 Options can be passed in during creation. 35 36 ```go 37 service := micro.NewService( 38 micro.Name("greeter"), 39 micro.Version("latest"), 40 ) 41 ``` 42 43 All the available options can be found [here](https://pkg.go.dev/github.com/micro/go-micro/v2#Option). 44 45 Go Micro also provides a way to set command line flags using `micro.Flags`. 46 47 ```go 48 import ( 49 "github.com/micro/cli" 50 "github.com/micro/go-micro/v2" 51 ) 52 53 service := micro.NewService( 54 micro.Flags( 55 cli.StringFlag{ 56 Name: "environment", 57 Usage: "The environment", 58 }, 59 ) 60 ) 61 ``` 62 63 To parse flags use `service.Init`. Additionally access flags use the `micro.Action` option. 64 65 ```go 66 service.Init( 67 micro.Action(func(c *cli.Context) { 68 env := c.StringFlag("environment") 69 if len(env) > 0 { 70 fmt.Println("Environment set to", env) 71 } 72 }), 73 ) 74 ``` 75 76 Go Micro provides predefined flags which are set and parsed if `service.Init` is called. See all the flags 77 [here](https://pkg.go.dev/github.com/micro/go-micro/v2/config/cmd#pkg-variables). 78 79 ### 2. Defining the API 80 81 We use protobuf files to define the service API interface. This is a very convenient way to strictly define the API and 82 provide concrete types for both the server and a client. 83 84 Here's an example definition. 85 86 greeter.proto 87 88 ```proto 89 syntax = "proto3"; 90 91 service Greeter { 92 rpc Hello(Request) returns (Response) {} 93 } 94 95 message Request { 96 string name = 1; 97 } 98 99 message Response { 100 string greeting = 2; 101 } 102 ``` 103 104 Here we're defining a service handler called Greeter with the method Hello which takes the parameter Request type and returns Response. 105 106 ### 3. Generate the API interface 107 108 You'll need the following to generate protobuf code 109 110 - [protoc](https://github.com/google/protobuf) 111 - [protoc-gen-go](https://github.com/golang/protobuf) 112 - [protoc-gen-micro](https://github.com/tickoalcantara12/micro/tree/master/cmd/protoc-gen-micro) 113 114 We use protoc, protoc-gen-go and protoc-gen-micro to generate the concrete go implementation for this definition. 115 116 ```shell 117 go get github.com/golang/protobuf/{proto,protoc-gen-go} 118 ``` 119 120 ```shell 121 go get github.com/tickoalcantara12/micro/v2/cmd/protoc-gen-micro@master 122 ``` 123 124 ``` 125 protoc --proto_path=$GOPATH/src:. --micro_out=. --go_out=. greeter.proto 126 ``` 127 128 The types generated can now be imported and used within a **handler** for a server or the client when making a request. 129 130 Here's part of the generated code. 131 132 ```go 133 type Request struct { 134 Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` 135 } 136 137 type Response struct { 138 Greeting string `protobuf:"bytes,2,opt,name=greeting" json:"greeting,omitempty"` 139 } 140 141 // Client API for Greeter service 142 143 type GreeterClient interface { 144 Hello(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) 145 } 146 147 type greeterClient struct { 148 c client.Client 149 serviceName string 150 } 151 152 func NewGreeterClient(serviceName string, c client.Client) GreeterClient { 153 if c == nil { 154 c = client.NewClient() 155 } 156 if len(serviceName) == 0 { 157 serviceName = "greeter" 158 } 159 return &greeterClient{ 160 c: c, 161 serviceName: serviceName, 162 } 163 } 164 165 func (c *greeterClient) Hello(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) { 166 req := c.c.NewRequest(c.serviceName, "Greeter.Hello", in) 167 out := new(Response) 168 err := c.c.Call(ctx, req, out, opts...) 169 if err != nil { 170 return nil, err 171 } 172 return out, nil 173 } 174 175 // Server API for Greeter service 176 177 type GreeterHandler interface { 178 Hello(context.Context, *Request, *Response) error 179 } 180 181 func RegisterGreeterHandler(s server.Server, hdlr GreeterHandler) { 182 s.Handle(s.NewHandler(&Greeter{hdlr})) 183 } 184 ``` 185 186 ### 4. Implement the handler 187 188 The server requires **handlers** to be registered to serve requests. A handler is an public type with public methods 189 which conform to the signature `func(ctx context.Context, req interface{}, rsp interface{}) error`. 190 191 As you can see above, a handler signature for the Greeter interface looks like so. 192 193 ```go 194 type GreeterHandler interface { 195 Hello(context.Context, *Request, *Response) error 196 } 197 ``` 198 199 Here's an implementation of the Greeter handler. 200 201 ```go 202 import proto "github.com/micro/examples/service/proto" 203 204 type Greeter struct{} 205 206 func (g *Greeter) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error { 207 rsp.Greeting = "Hello " + req.Name 208 return nil 209 } 210 ``` 211 212 213 The handler is registered with your service much like a http.Handler. 214 215 ``` 216 service := micro.NewService( 217 micro.Name("greeter"), 218 ) 219 220 pb.RegisterGreeterHandler(service.Server(), new(Greeter)) 221 ``` 222 223 You can also create a bidirectional streaming handler but we'll leave that for another day. 224 225 ### 5. Running the service 226 227 The service can be run by calling `server.Run`. This causes the service to bind to the address in the config 228 (which defaults to the first RFC1918 interface found and a random port) and listen for requests. 229 230 This will additionally Register the service with the registry on start and Deregister when issued a kill signal. 231 232 ```go 233 if err := service.Run(); err != nil { 234 log.Fatal(err) 235 } 236 ``` 237 238 ### 6. The complete service 239 <br> 240 greeter.go 241 242 ```go 243 package main 244 245 import ( 246 "log" 247 248 "github.com/micro/go-micro/v2" 249 pb "github.com/micro/examples/service/proto" 250 251 "golang.org/x/net/context" 252 ) 253 254 type Greeter struct{} 255 256 func (g *Greeter) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error { 257 rsp.Greeting = "Hello " + req.Name 258 return nil 259 } 260 261 func main() { 262 service := micro.NewService( 263 micro.Name("greeter"), 264 ) 265 266 service.Init() 267 268 pb.RegisterGreeterHandler(service.Server(), new(Greeter)) 269 270 if err := service.Run(); err != nil { 271 log.Fatal(err) 272 } 273 } 274 ``` 275 276 Note. The service discovery mechanism will need to be running so the service can register to be discovered by clients and 277 other services. A quick getting started for that is [here](https://github.com/micro/go-micro#getting-started). 278 279 ## Writing a Client 280 281 The [client](https://pkg.go.dev/github.com/micro/go-micro/v2/client) package is used to query services. When you create a 282 Service, a Client is included which matches the initialised packages used by the server. 283 284 Querying the above service is as simple as the following. 285 286 ```go 287 // create the greeter client using the service name and client 288 greeter := pb.NewGreeterService("greeter", service.Client()) 289 290 // request the Hello method on the Greeter handler 291 rsp, err := greeter.Hello(context.TODO(), &pb.Request{ 292 Name: "John", 293 }) 294 if err != nil { 295 fmt.Println(err) 296 return 297 } 298 299 fmt.Println(rsp.Greeting) 300 ``` 301 302 `pb.NewGreeterClient` takes the service name and the client used for making requests. 303 304 The full example is can be found at [go-micro/examples/service](https://github.com/micro/examples/tree/master/service). 305