# 編寫一個Go函數
這是開始使用go-micro函數的指南。函數是一次執行服務。
如果您首先喜歡更高級別的工具包概述,請查看介紹的博客文章 https://micro.mu/blog/2016/03/20/micro.html
## 編寫一個函數
頂層[函數接口](https://godoc.org/github.com/micro/go-micro#Function)是go-micro中函數編程模型的主要組件。它封裝了Service接口,同時提供一次執行。
```
// Function is a one time executing Service
type Function interface {
// Inherits Service interface
Service
// Done signals to complete execution
Done() error
// Handle registers an RPC handler
Handle(v interface{}) error
// Subscribe registers a subscriber
Subscribe(topic string, v interface{}) error
}
```
### 1.初始化
一個函數就像使用`micro.NewFunction`一樣創建。
```
import "github.com/micro/go-micro"
function := micro.NewFunction()
```
選項可以在創建過程中傳入。
```
function := micro.NewFunction(
micro.Name("greeter"),
micro.Version("latest"),
)
```
所有可用的選項可以在[這里](https://godoc.org/github.com/micro/go-micro#Option)找到。
Go Micro還提供了使用`micro.Flags`設置命令行標志的方法。
```
import (
"github.com/micro/cli"
"github.com/micro/go-micro"
)
function := micro.NewFunction(
micro.Flags(
cli.StringFlag{
Name: "environment",
Usage: "The environment",
},
)
)
```
解析標志使用`function.Init`。另外訪問標志使用`micro.Action`選項。
```
function.Init(
micro.Action(func(c *cli.Context) {
env := c.StringFlag("environment")
if len(env) > 0 {
fmt.Println("Environment set to", env)
}
}),
)
```
Go Micro提供了預定義的標志,如果調用了`function.Init`,它將被設置和解析。看到[這里](https://godoc.org/github.com/micro/go-micro/cmd#pkg-variables)的所有標志。
### 2.定義API
我們使用protobuf文件來定義API接口。這是嚴格定義API并提供服務端和客戶端的具體類型的一種非常方便的方式。
這是一個示例定義。
greeter.proto
```
syntax = "proto3";
service Greeter {
rpc Hello(HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string name = 1;
}
message HelloResponse {
string greeting = 2;
}
```
在這里我們定義了一個名為Greeter的函數處理程序,其中的方法Hello使用參數`HelloRequest`類型并返回`HelloResponse`。
### 3.生成API接口
我們使用protoc和protoc-gen-go為這個定義生成具體的實現。
Go-micro使用代碼生成來提供客戶端樁方法來減少代碼編寫,就像gRPC一樣。這是通過一個protobuf插件完成的,它需要一個[golang/protobuf](https://github.com/golang/protobuf)分支,可以在這里找到[github.com/micro/protobuf](https://micro.mu/docs/github.com/micro/protobuf)。
```
go get github.com/micro/protobuf/{proto,protoc-gen-go}
protoc --go_out=plugins=micro:. greeter.proto
```
生成的類型現在可以在請求時在服務端或客戶端的處理程序中導入和使用。
這是生成的代碼的一部分。
```
type HelloRequest struct {
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
}
type HelloResponse 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 *HelloRequest, opts ...client.CallOption) (*HelloResponse, 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 *HelloRequest, opts ...client.CallOption) (*HelloResponse, error) {
req := c.c.NewRequest(c.serviceName, "Greeter.Hello", in)
out := new(HelloResponse)
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, *HelloRequest, *HelloResponse) error
}
func RegisterGreeterHandler(s server.Server, hdlr GreeterHandler) {
s.Handle(s.NewHandler(&Greeter{hdlr}))
}
```
### 4.實現處理程序
服務器要求注冊處理程序來處理請求。處理程序是一種公共方法,符合簽名
```
func(ctx context.Context, req interface{}, rsp interface{}) error
```
正如你上面看到的,Greeter接口的處理器簽名看起來像這樣。
```
type GreeterHandler interface {
Hello(context.Context, *HelloRequest, *HelloResponse) error
}
```
這是一個Greeter處理程序的實現。
```
import proto "github.com/micro/examples/service/proto"
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error {
rsp.Greeting = "Hello " + req.Name
return nil
}
```
處理程序的注冊很像一個`http.Handler`。
```
function := micro.NewFunction(
micro.Name("greeter"),
)
proto.RegisterGreeterHandler(service.Server(), new(Greeter))
```
或者,函數接口提供了一個更簡單的注冊模式。
```
function := micro.NewFunction(
micro.Name("greeter"),
)
function.Handle(new(Greeter))
```
您也可以使用Subscribe方法注冊一個異步訂閱方法。
### 5.運行功能
該函數可以通過調用function.Run來運行。這將導致它綁定到配置中的地址(默認是第一個RFC1918接口和隨機端口)并監聽請求。
這將另外注冊功能與注冊表啟動和注銷時發出一個kill信號。
```
if err := function.Run(); err != nil {
log.Fatal(err)
}
```
一旦發出請求,函數將退出。您可以使用[micro run](https://micro.mu/docs/run.html)管理功能的生命周期。一個完整的例子可以在[examples/function](https://github.com/micro/examples/tree/master/function)中找到。
### 6.完整的功能
greeter.go
```
package main
import (
"log"
"github.com/micro/go-micro"
proto "github.com/micro/examples/function/proto"
"golang.org/x/net/context"
)
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error {
rsp.Greeting = "Hello " + req.Name
return nil
}
func main() {
function := micro.NewFunction(
micro.Name("greeter"),
micro.Version("latest"),
)
function.Init()
function.Handle(new(Greeter))
if err := function.Run(); err != nil {
log.Fatal(err)
}
}
```
注意:服務發現機制將需要運行,以便函數可以注冊以供希望查詢的人發現。快速入門就在[這里](https://github.com/micro/go-micro#getting-started)。
## 寫一個客戶端
[客戶端](https://godoc.org/github.com/micro/go-micro/client)軟件包用于查詢功能和服務。當您創建一個函數時,將包含一個與服務器使用的初始化包相匹配的客戶端。
查詢上述功能就像下面這樣簡單。
```
// create the greeter client using the service name and client
greeter := proto.NewGreeterClient("greeter", function.Client())
// request the Hello method on the Greeter handler
rsp, err := greeter.Hello(context.TODO(), &proto.HelloRequest{
Name: "John",
})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(rsp.Greeter)
```
`proto.NewGreeterClient`接受函數名稱和用于發出請求的客戶端。
完整的例子可以在[go-micro/examples/function](https://github.com/micro/examples/tree/master/function)中找到。