github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/docs/blog/_posts/2016-03-28-go-micro.md (about) 1 --- 2 layout: post 3 title: Writing microservices with Go Micro 4 date: 2016-03-28 09:00:00 5 --- 6 <br> 7 This is a high level guide to writing microservices with [**go-micro**](https://github.com/micro/go-micro). 8 9 If you want to learn more about microservices check out the introductory blog post [here]({{ site.baseurl }}/2016/03/17/introduction.html) 10 and if you want to know more about [**Micro**](https://github.com/tickoalcantara12/micro), the microservice toolkit, look [here]({{ site.baseurl }}/2016/03/20/micro.html). 11 12 Let's get to it. 13 14 ##### What is Go Micro? 15 16 [**Go Micro**](https://github.com/micro/go-micro) is a pluggable RPC based library which provides the fundamental building blocks 17 for writing microservices in Go. The Micro philosophy is "batteries included" with a pluggable architecture. Out of the box, it implements 18 service discovery using consul, communication via http and encoding using proto-rpc or json-rpc. 19 20 That's a bit of a mouthful so let's break it down. 21 22 Go Micro is: 23 24 1. A library written in Go 25 2. A set of pluggable interfaces 26 3. RPC based 27 28 Go Micro provides interfaces for: 29 30 1. Service Discovery 31 2. Encoding 32 3. Client/Server 33 4. Pub/Sub 34 35 A more detailed breakdown can be found [here]({{ site.baseurl }}/2016/03/20/micro.html#go-micro). 36 37 ##### Why Go Micro? 38 39 Go Micro started more than a year ago, initially serving a personal need. It was clear very soon after that it would 40 be valuable to a broader audience also looking to write microservices. It's based on experiences at various 41 technology companies that operate microservice platforms at scale like Google and Hailo. 42 43 As mentioned before Go Micro is a pluggable architecture that focuses on providing Go based interfaces which when used 44 together providing the building blocks for writing microservices. These interfaces can be satisfied by concrete 45 implementations. For example the [Registry](https://godoc.org/github.com/micro/go-micro/registry) interface for 46 service discovery has a default implementation for Consul but can be swapped out 47 for etcd, zookeeper, or anything else that is able to satify the interface. 48 49 The pluggable architecture means zero code rewriting if you want to swap out the underlying technologies. 50 51 Let's get into writing a service. 52 53 ##### Writing a service 54 55 If you want to get straight into reading the code, look at [examples/service](https://github.com/micro/examples/tree/master/service). 56 57 The top level [Service](https://godoc.org/github.com/micro/go-micro#Service) interface is the main component for 58 building a service. It wraps all the underlying packages of Go Micro into a single convenient interface. 59 60 ```go 61 type Service interface { 62 Init(...Option) 63 Options() Options 64 Client() client.Client 65 Server() server.Server 66 Run() error 67 String() string 68 } 69 ``` 70 71 ##### 1. Initialisation 72 73 A service is created like so using `micro.NewService`. 74 75 ```go 76 import "github.com/micro/go-micro" 77 78 service := micro.NewService() 79 ``` 80 81 Options can be passed in during creation. 82 83 ```go 84 service := micro.NewService( 85 micro.Name("greeter"), 86 micro.Version("latest"), 87 ) 88 ``` 89 90 All the available options can be found [here](https://godoc.org/github.com/micro/go-micro#Option). 91 92 Go Micro also provides a way to set command line flags using `micro.Flags`. 93 94 ```go 95 import ( 96 "github.com/micro/cli" 97 "github.com/micro/go-micro" 98 ) 99 100 service := micro.NewService( 101 micro.Flags( 102 cli.StringFlag{ 103 Name: "environment", 104 Usage: "The environment", 105 }, 106 ) 107 ) 108 ``` 109 110 To parse flags use `service.Init`. Additionally access flags use the `micro.Action` option. 111 112 ```go 113 service.Init( 114 micro.Action(func(c *cli.Context) { 115 env := c.StringFlag("environment") 116 if len(env) > 0 { 117 fmt.Println("Environment set to", env) 118 } 119 }), 120 ) 121 ``` 122 123 Go Micro provides predefined flags which are set and parsed if `service.Init` is called. See all the flags 124 [here](https://godoc.org/github.com/micro/go-micro/cmd#pkg-variables). 125 126 ###### 2. Defining the API 127 128 We use protobuf files to define the service API interface. This is a very convenient way to strictly define the API and 129 provide concrete types for both the server and a client. 130 131 Here's an example definition. 132 133 greeter.proto 134 135 ``` 136 syntax = "proto3"; 137 138 service Greeter { 139 rpc Hello(HelloRequest) returns (HelloResponse) {} 140 } 141 142 message HelloRequest { 143 string name = 1; 144 } 145 146 message HelloResponse { 147 string greeting = 2; 148 } 149 ``` 150 151 Here we're defining a service handler called Greeter with the method Hello which takes the parameter HelloRequest type and returns HelloResponse. 152 153 ###### 3. Generate the API interface 154 155 We use protoc and protoc-gen-go to generate the concrete go implementation for this definition. 156 157 Go-micro uses code generation to provide client stub methods to reduce boiler plate code much like gRPC. It's done via a protobuf plugin 158 which requires a fork of [golang/protobuf](https://github.com/golang/protobuf) that can be found here 159 [github.com/micro/protobuf](github.com/micro/protobuf). 160 161 ```shell 162 go get github.com/micro/protobuf/{proto,protoc-gen-go} 163 protoc --go_out=plugins=micro:. greeter.proto 164 ``` 165 166 The types generated can now be imported and used within a **handler** for a server or the client when making a request. 167 168 Here's part of the generated code. 169 170 ```go 171 type HelloRequest struct { 172 Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` 173 } 174 175 type HelloResponse struct { 176 Greeting string `protobuf:"bytes,2,opt,name=greeting" json:"greeting,omitempty"` 177 } 178 179 // Client API for Greeter service 180 181 type GreeterClient interface { 182 Hello(ctx context.Context, in *HelloRequest, opts ...client.CallOption) (*HelloResponse, error) 183 } 184 185 type greeterClient struct { 186 c client.Client 187 serviceName string 188 } 189 190 func NewGreeterClient(serviceName string, c client.Client) GreeterClient { 191 if c == nil { 192 c = client.NewClient() 193 } 194 if len(serviceName) == 0 { 195 serviceName = "greeter" 196 } 197 return &greeterClient{ 198 c: c, 199 serviceName: serviceName, 200 } 201 } 202 203 func (c *greeterClient) Hello(ctx context.Context, in *HelloRequest, opts ...client.CallOption) (*HelloResponse, error) { 204 req := c.c.NewRequest(c.serviceName, "Greeter.Hello", in) 205 out := new(HelloResponse) 206 err := c.c.Call(ctx, req, out, opts...) 207 if err != nil { 208 return nil, err 209 } 210 return out, nil 211 } 212 213 // Server API for Greeter service 214 215 type GreeterHandler interface { 216 Hello(context.Context, *HelloRequest, *HelloResponse) error 217 } 218 219 func RegisterGreeterHandler(s server.Server, hdlr GreeterHandler) { 220 s.Handle(s.NewHandler(&Greeter{hdlr})) 221 } 222 ``` 223 224 ###### 4. Implement the handler 225 226 The server requires **handlers** to be registered to serve requests. A handler is an public type with public methods 227 which conform to the signature `func(ctx context.Context, req interface{}, rsp interface{}) error`. 228 229 As you can see above, a handler signature for the Greeter interface looks like so. 230 231 ```go 232 type GreeterHandler interface { 233 Hello(context.Context, *HelloRequest, *HelloResponse) error 234 } 235 ``` 236 237 Here's an implementation of the Greeter handler. 238 239 ```go 240 import proto "github.com/micro/examples/service/proto" 241 242 type Greeter struct{} 243 244 func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error { 245 rsp.Greeting = "Hello " + req.Name 246 return nil 247 } 248 ``` 249 250 251 The handler is registered with your service much like a http.Handler. 252 253 ``` 254 service := micro.NewService( 255 micro.Name("greeter"), 256 ) 257 258 proto.RegisterGreeterHandler(service.Server(), new(Greeter)) 259 ``` 260 261 You can also create a bidirectional streaming handler but we'll leave that for another day. 262 263 ###### 5. Running the service 264 265 The service can be run by calling `server.Run`. This causes the service to bind to the address in the config 266 (which defaults to the first RFC1918 interface found and a random port) and listen for requests. 267 268 This will additionally Register the service with the registry on start and Deregister when issued a kill signal. 269 270 ```go 271 if err := service.Run(); err != nil { 272 log.Fatal(err) 273 } 274 ``` 275 276 ###### 6. The complete service 277 <br> 278 greeter.go 279 280 ```go 281 package main 282 283 import ( 284 "log" 285 286 "github.com/micro/go-micro" 287 proto "github.com/micro/examples/service/proto" 288 289 "golang.org/x/net/context" 290 ) 291 292 type Greeter struct{} 293 294 func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error { 295 rsp.Greeting = "Hello " + req.Name 296 return nil 297 } 298 299 func main() { 300 service := micro.NewService( 301 micro.Name("greeter"), 302 micro.Version("latest"), 303 ) 304 305 service.Init() 306 307 proto.RegisterGreeterHandler(service.Server(), new(Greeter)) 308 309 if err := service.Run(); err != nil { 310 log.Fatal(err) 311 } 312 } 313 ``` 314 315 Note. The service discovery mechanism will need to be running so the service can register to be discovered by clients and 316 other services. A quick getting started for that is [here](https://github.com/micro/go-micro#getting-started). 317 318 ###### Writing a Client 319 320 The [client](https://godoc.org/github.com/micro/go-micro/client) package is used to query services. When you create a 321 Service, a Client is included which matches the initialised packages used by the server. 322 323 Querying the above service is as simple as the following. 324 325 ```go 326 // create the greeter client using the service name and client 327 greeter := proto.NewGreeterClient("greeter", service.Client()) 328 329 // request the Hello method on the Greeter handler 330 rsp, err := greeter.Hello(context.TODO(), &proto.HelloRequest{ 331 Name: "John", 332 }) 333 if err != nil { 334 fmt.Println(err) 335 return 336 } 337 338 fmt.Println(rsp.Greeter) 339 ``` 340 341 `proto.NewGreeterClient` takes the service name and the client used for making requests. 342 343 The full example is can be found at [go-micro/examples/service](https://github.com/micro/examples/tree/master/service). 344 345 ###### Summary 346 347 Hopefully this blog post has been a helpful high level guide into writing microservices with [**Go Micro**](https://github.com/micro/go-micro). 348 You can find many more example services in the repo [github.com/micro](https://github.com/micro) to help you gain a further 349 understanding of more real world solutions. 350 351 If you want to learn more about the services we offer or microservices, checkout the website [micro.mu](https://m3o.com) or 352 the github [repo](https://github.com/tickoalcantara12/micro). 353 354 Follow us on Twitter at [@MicroHQ](https://twitter.com/m3ocloud) or join the [Slack](https://slack.m3o.com) 355 community [here](http://slack.m3o.com).