github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/docs/v2/guides/writing-a-go-function.md (about) 1 --- 2 title: Writing a Go Function 3 keywords: development 4 tags: [development] 5 sidebar: home_sidebar 6 permalink: /writing-a-go-function 7 summary: 8 --- 9 10 This is a guide to getting started with go-micro functions. Functions are one time executing Services. 11 12 If you prefer a higher level overview of the toolkit first, checkout the introductory blog post [https://micro.dev/blog/2016/03/20/micro.html](https://micro.dev/blog/2016/03/20/micro.html). 13 14 ## Writing a Function 15 16 The top level [Function](https://pkg.go.dev/github.com/micro/go-micro/v2#Function) interface is the main component for the 17 function programming model in go-micro. It encapsulates the Service interface while providing one time execution. 18 19 ```go 20 // Function is a one time executing Service 21 type Function interface { 22 // Inherits Service interface 23 Service 24 // Done signals to complete execution 25 Done() error 26 // Handle registers an RPC handler 27 Handle(v interface{}) error 28 // Subscribe registers a subscriber 29 Subscribe(topic string, v interface{}) error 30 } 31 ``` 32 33 ### 1. Initialisation 34 35 A function is created like so using `micro.NewFunction`. 36 37 ```go 38 import "github.com/micro/go-micro/v2" 39 40 function := micro.NewFunction() 41 ``` 42 43 Options can be passed in during creation. 44 45 ```go 46 function := micro.NewFunction( 47 micro.Name("greeter"), 48 micro.Version("latest"), 49 ) 50 ``` 51 52 All the available options can be found [here](https://pkg.go.dev/github.com/micro/go-micro/v2#Option). 53 54 Go Micro also provides a way to set command line flags using `micro.Flags`. 55 56 ```go 57 import ( 58 "github.com/micro/cli" 59 "github.com/micro/go-micro/v2" 60 ) 61 62 function := micro.NewFunction( 63 micro.Flags( 64 cli.StringFlag{ 65 Name: "environment", 66 Usage: "The environment", 67 }, 68 ) 69 ) 70 ``` 71 72 To parse flags use `function.Init`. Additionally access flags use the `micro.Action` option. 73 74 ```go 75 function.Init( 76 micro.Action(func(c *cli.Context) { 77 env := c.StringFlag("environment") 78 if len(env) > 0 { 79 fmt.Println("Environment set to", env) 80 } 81 }), 82 ) 83 ``` 84 85 Go Micro provides predefined flags which are set and parsed if `function.Init` is called. See all the flags 86 [here](https://pkg.go.dev/github.com/micro/go-micro/v2/cmd#pkg-variables). 87 88 ### 2. Defining the API 89 90 We use protobuf files to define the API interface. This is a very convenient way to strictly define the API and 91 provide concrete types for both the server and a client. 92 93 Here's an example definition. 94 95 greeter.proto 96 97 ```proto 98 syntax = "proto3"; 99 100 service Greeter { 101 rpc Hello(HelloRequest) returns (HelloResponse) {} 102 } 103 104 message HelloRequest { 105 string name = 1; 106 } 107 108 message HelloResponse { 109 string greeting = 2; 110 } 111 ``` 112 113 Here we're defining a function handler called Greeter with the method Hello which takes the parameter HelloRequest type and returns HelloResponse. 114 115 ### 3. Generate the API interface 116 117 We use protoc and protoc-gen-go to generate the concrete go implementation for this definition. 118 119 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 120 which requires a fork of [golang/protobuf](https://github.com/golang/protobuf) that can be found at 121 [github.com/micro/protobuf](https://github.com/micro/protobuf). 122 123 ```shell 124 go get github.com/micro/protobuf/{proto,protoc-gen-go} 125 protoc --go_out=plugins=micro:. 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 HelloRequest struct { 134 Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` 135 } 136 137 type HelloResponse 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 *HelloRequest, opts ...client.CallOption) (*HelloResponse, 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 *HelloRequest, opts ...client.CallOption) (*HelloResponse, error) { 166 req := c.c.NewRequest(c.serviceName, "Greeter.Hello", in) 167 out := new(HelloResponse) 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, *HelloRequest, *HelloResponse) 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, *HelloRequest, *HelloResponse) 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 *proto.HelloRequest, rsp *proto.HelloResponse) error { 207 rsp.Greeting = "Hello " + req.Name 208 return nil 209 } 210 ``` 211 212 The handler is registered much like a http.Handler. 213 214 ``` 215 function := micro.NewFunction( 216 micro.Name("greeter"), 217 ) 218 219 proto.RegisterGreeterHandler(service.Server(), new(Greeter)) 220 ``` 221 222 Alternatively the Function interface provides a simpler registration pattern. 223 224 ``` 225 function := micro.NewFunction( 226 micro.Name("greeter"), 227 ) 228 229 function.Handle(new(Greeter)) 230 ``` 231 232 You can also register an async subscriber using the Subscribe method. 233 234 ### 5. Running the function 235 236 The function can be run by calling `function.Run`. This causes it to bind to the address in the config 237 (which defaults to the first RFC1918 interface found and a random port) and listen for requests. 238 239 This will additionally Register the function with the registry on start and Deregister when issued a kill signal. 240 241 ```go 242 if err := function.Run(); err != nil { 243 log.Fatal(err) 244 } 245 ``` 246 247 Upon serving a request the function will exit. You can use [micro run](https://micro.dev/docs/run.html) to manage the lifecycle 248 of functions. A complete example can be found at [examples/function](https://github.com/micro/examples/tree/master/function). 249 250 ### 6. The complete function 251 <br> 252 greeter.go 253 254 ```go 255 package main 256 257 import ( 258 "log" 259 260 "github.com/micro/go-micro/v2" 261 proto "github.com/micro/examples/function/proto" 262 263 "golang.org/x/net/context" 264 ) 265 266 type Greeter struct{} 267 268 func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error { 269 rsp.Greeting = "Hello " + req.Name 270 return nil 271 } 272 273 func main() { 274 function := micro.NewFunction( 275 micro.Name("greeter"), 276 micro.Version("latest"), 277 ) 278 279 function.Init() 280 281 function.Handle(new(Greeter)) 282 283 if err := function.Run(); err != nil { 284 log.Fatal(err) 285 } 286 } 287 ``` 288 289 Note. The service discovery mechanism will need to be running so the function can register to be discovered by those wishing 290 to query it. A quick getting started for that is [here](https://github.com/micro/go-micro#getting-started). 291 292 ## Writing a Client 293 294 The [client](https://pkg.go.dev/github.com/micro/go-micro/v2/client) package is used to query functions and services. When you create a 295 Function, a Client is included which matches the initialised packages used by the server. 296 297 Querying the above function is as simple as the following. 298 299 ```go 300 // create the greeter client using the service name and client 301 greeter := proto.NewGreeterClient("greeter", function.Client()) 302 303 // request the Hello method on the Greeter handler 304 rsp, err := greeter.Hello(context.TODO(), &proto.HelloRequest{ 305 Name: "John", 306 }) 307 if err != nil { 308 fmt.Println(err) 309 return 310 } 311 312 fmt.Println(rsp.Greeter) 313 ``` 314 315 `proto.NewGreeterClient` takes the function name and the client used for making requests. 316 317 The full example is can be found at [go-micro/examples/function](https://github.com/micro/examples/tree/master/function). 318