Go - Micro微服務(wù)框架實踐 - 編寫Golang服務(wù)(十)

編寫Golang Function

編寫Golang Function

本篇是指導(dǎo)大家使用go-micro的Function功能,F(xiàn)unction是執(zhí)行一次的服務(wù)。(譯者按:這里Function并不等同與平常我們編寫的函數(shù),而是只執(zhí)行一次的服務(wù)所以我沒有直接翻譯,以免引起誤解)

如果想先從更高的角度了解相關(guān)的工具集,可以查看博客https://micro.mu/blog/2016/03/20/micro.html。

先寫一個Function

Function作為頂級的接口,它是go-micro中函數(shù)式編程模型主要組件。它封裝服務(wù)接口,并提供執(zhí)行一次函數(shù)的能力。

// Function 是只執(zhí)行一次的函數(shù)
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. 初始化

Function使用micro.NewFunction構(gòu)建。

import "github.com/micro/go-micro"

function := micro.NewFunction() 

構(gòu)建時也可以傳入選項參數(shù)。

function := micro.NewFunction(
        micro.Name("greeter"),
        micro.Version("latest"),
)

可選參數(shù)參考。

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",
                },
        )
)

命令行標(biāo)記參數(shù)可以使用function.Init解析。增加參數(shù)可以使用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提供了一些預(yù)定義的參數(shù)標(biāo)記,這些標(biāo)記在執(zhí)行function.Init時解析。所有預(yù)定義的標(biāo)記參數(shù)可以參考

2. 定義API

我們使用protobuf文件來定義服務(wù)的API接口。使用protobuf可以非常方便去嚴(yán)格定義API,提供服務(wù)端與客戶端雙邊具體一致的類型。

greeter.proto

syntax = "proto3";

service Greeter {
    rpc Hello(HelloRequest) returns (HelloResponse) {}
}

message HelloRequest {
    string name = 1;
}

message HelloResponse {
    string greeting = 2;
}

我們定義了一個服務(wù)叫做Greeter的Function處理器,它有一個接收HelloRequest并返回HelloResponse的Hello方法。

3. 生成API接口

我們需要protocprotoc-gen-go來生成protobuf代碼文件,它們負(fù)責(zé)生成定義的go代碼實現(xiàn)。

Go-micro使用代碼生成器生成客戶端存根方法,這樣可以像gRPC減少模板方法。這一步需要golang/protobuffork出來的插件github.com/micro/protobuf.

go get github.com/micro/protobuf/{proto,protoc-gen-go}
protoc --go_out=plugins=micro:. greeter.proto

生成的類現(xiàn)在可以引入handler中,在服務(wù)或客戶端來創(chuàng)建請求了。

下面是代碼生成器生成的一部分代碼。

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"`
}

// 定義Greeter客戶端的接口

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
}

// Greeter的服務(wù)端API

type GreeterHandler interface {
    Hello(context.Context, *HelloRequest, *HelloResponse) error
}

func RegisterGreeterHandler(s server.Server, hdlr GreeterHandler) {
    s.Handle(s.NewHandler(&Greeter{hdlr}))
}

4. 實現(xiàn)處理器

服務(wù)端需要注冊handlers,這樣才能提供服務(wù)并接收請求。處理器相當(dāng)于是一個擁有公共方法的公共類,它需要符合簽名func(ctx context.Context, req interface{}, rsp interface{}) error。

通過上面的內(nèi)容,我們看到,Greeter interface的簽名的看上去就是這樣:

type GreeterHandler interface {
        Hello(context.Context, *HelloRequest, *HelloResponse) error
}

Greeter處理器實現(xiàn)。

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))

另外,F(xiàn)unction接口也提供更簡單的注冊方式。

function := micro.NewFunction(
        micro.Name("greeter"),
)

function.Handle(new(Greeter))

也可以使用Subscribe方法注冊成異步的訂閱者。

5. 運行Function

運行Function可以通過function.Run。這樣它會綁定到配置中指定的地址(默認(rèn)使用RFC1918規(guī)則來分配并生成隨機(jī)端口),然后開始偵聽端口。

另外,這一步會在服務(wù)啟動時向注冊中心注冊,并在服務(wù)接收到關(guān)閉信號時卸載。

if err := function.Run(); err != nil {
    log.Fatal(err)
}

有接受服務(wù)請求后,這人Function就會退出??梢允褂?a target="_blank" rel="nofollow">micro run 來管理Funtion的生命周期。完整的例子查看:examples/function.

6. 完整的函數(shù)

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)
        }
}

需要注意的是,要保證服務(wù)發(fā)現(xiàn)機(jī)制運行起來,這樣服務(wù)才能注冊,其它服務(wù)或客戶端才能發(fā)現(xiàn)它。快速啟動可參考。

編寫客戶端

客戶端包用于查詢服務(wù),當(dāng)創(chuàng)建服務(wù)時,也包含了一個客戶端,這個客戶端匹配服務(wù)所使用的初始化包。

查詢上面的服務(wù)很簡單:

// 創(chuàng)建greate客戶端,這需要傳入服務(wù)名與服務(wù)的客戶端方法構(gòu)建的客戶端對象
greeter := proto.NewGreeterClient("greeter", function.Client())

// 在Greeter handler上請求調(diào)用Hello方法
rsp, err := greeter.Hello(context.TODO(), &proto.HelloRequest{
    Name: "John",
})
if err != nil {
    fmt.Println(err)
    return
}

fmt.Println(rsp.Greeter)

proto.NewGreeterClient 需要Function名與客戶端來請求服務(wù)。

完整例子可查看go-micro/examples/function.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容