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