Kratos 微服務(wù)框架入門與實(shí)踐

一. Kratos 簡介

Kratos 是 B 站基于 Golang 實(shí)現(xiàn)的一個(gè)開源的面向微服務(wù)的框架. 使用 Kratos 可以很方便地構(gòu)建一個(gè)規(guī)范的服務(wù).

開源 GitHub 地址: https://github.com/go-kratos/kratos

文檔地址: https://go-kratos.dev/docs/

二. Kratos 特點(diǎn)

  • 簡單易用
  • 高效
  • 穩(wěn)定
  • 高性能
  • 可擴(kuò)展
  • 容錯(cuò)性好
  • 豐富的工具鏈

以上特點(diǎn), 還都是從 Readme 文檔翻譯出來的, 后面將逐一驗(yàn)證和體驗(yàn).

三. Kratos 實(shí)現(xiàn)一個(gè) Helloworld

安裝

go get github.com/go-kratos/kratos/cmd/kratos/v2@latest

根據(jù)文檔

# create project template
kratos new helloworld

cd helloworld
# download modules
go mod download

# generate Proto template
kratos proto add api/helloworld/helloworld.proto
# generate Proto source code
kratos proto client api/helloworld/helloworld.proto
# generate server template
kratos proto server api/helloworld/helloworld.proto -t internal/service

# generate all proto source code, wire, etc.
go generate ./...

編譯并執(zhí)行

mkdir bin
# compile
go build -o ./bin/ ./...
# run
./bin/helloworld -conf ./configs

就以上這么一頓操作, 就一起完成一個(gè)服務(wù)的構(gòu)建.

四. 目錄結(jié)構(gòu)分析

tree -d 查看目錄結(jié)構(gòu)

.
├── api
│   └── helloworld
│       └── v1
├── cmd
│   └── helloworld
├── configs
└── internal
    ├── biz
    ├── conf
    ├── data
    ├── server
    └── service

1. /api

這個(gè)文件夾下面, 包含 proto 文件和編譯 proto 文件生成的 go 文件.

之所以把目錄命名為 api, 是因?yàn)榭梢园?proto 當(dāng)作 api 文檔, 生成的 go 文件就相當(dāng)于 api.

2. /cmd

這個(gè)文件夾下的代碼, 主要負(fù)責(zé)程序的啟動记罚、關(guān)閉钩杰、配置初始化等.

cmd 大概率是 command 的縮寫, 下面的文件夾是一個(gè) helloworld 應(yīng)用并且包含 main.go文件.

在執(zhí)行 go build 的時(shí)候, 就會以文件夾的名稱來命名應(yīng)用.

一般不建議在 /cmd 這個(gè)文件夾下放置過多的代碼和文件夾.

如果代碼不是可重用的, 或者你不希望其他人重用它, 請將該代碼放到 /internal 目錄中.

3. /configs

放置配置文件模板, 或默認(rèn)配置, 一般是 yaml 文件.

比如注冊的服務(wù)的地址, 各種數(shù)據(jù)庫的連接配置等.

4. /internal

這個(gè)文件夾下面, 一般都放置不希望作為第三包給他人使用的, 即是當(dāng)作私有庫.

  • /biz 目錄: 業(yè)務(wù)邏輯的組裝層, 類似于 DDD (領(lǐng)域驅(qū)動設(shè)計(jì)) 的 domain 層.
  • /conf 目錄: 這里會有 proto 文件和編譯 proto 得到的 go 代碼, 而且 proto 會與上面的 /configs 下的 yaml 文件對應(yīng).
  • /data 目錄: 業(yè)務(wù)數(shù)據(jù)訪問, 包含 cache傻唾、db 等封裝, 實(shí)現(xiàn)了 biz 的 repo 接口. 類似于 DDD 的 repo.
  • /server 目錄: gRPC 和 HTTP 服務(wù)的注冊.
  • /service 目錄: 這里是實(shí)現(xiàn)上層的 /api 的 interface. 類似于 DDD 的 application 層.

更多參考: https://github.com/golang-standards/project-layout/blob/master/README_zh.md

五. 關(guān)鍵代碼分析

helloworld/cmd/helloworld/main.go

func main() {
    flag.Parse()
    logger := log.NewStdLogger(os.Stdout)

    c := config.New(
        config.WithSource(
            file.NewSource(flagconf),
        ),
        config.WithDecoder(func(kv *config.KeyValue, v map[string]interface{}) error {
            return yaml.Unmarshal(kv.Value, v)
        }),
    )
    if err := c.Load(); err != nil {
        panic(err)
    }

    var bc conf.Bootstrap
    if err := c.Scan(&bc); err != nil {
        panic(err)
    }

    app, cleanup, err := initApp(bc.Server, bc.Data, logger)
    if err != nil {
        panic(err)
    }
    defer cleanup()

    // start and wait for stop signal
    if err := app.Run(); err != nil {
        panic(err)
    }
}

這里使用 "github.com/go-kratos/kratos/v2/config" 這個(gè)包, 它的作用是將解析 /configs/config.yaml 文件解析為 protobuf, 內(nèi)部隱藏的操作是, 將 yaml 文件轉(zhuǎn)為 json, 然后再轉(zhuǎn)為 protobuf.

六. 結(jié)合 Kratos 實(shí)現(xiàn) gRPC 客戶端

想法: 前面學(xué)習(xí)了 gRPC, 這里學(xué)習(xí)了 Kratos, 而 Kratos 里也有 gRPC, 已經(jīng)實(shí)現(xiàn)了 gRPC 的服務(wù)端. 所以, 不如結(jié)合 Kratos 實(shí)現(xiàn)一個(gè) gRPC 客戶端, 以作 Kratos 實(shí)戰(zhàn).

根據(jù)上面的目錄結(jié)構(gòu)分析, 客戶端的實(shí)現(xiàn)邏輯代碼, 應(yīng)當(dāng)放在 /internal/client 文件夾下.

另外 main.go 文件, 應(yīng)該放在 /cmd/helloworld_client 文件夾下.

如果先前沒有的文件夾則創(chuàng)建.

以下主要結(jié)合了 Kratos 的 log 和 config 兩個(gè)工具, 以及 wire (github.com/google/wire).

1./internal/client/grpc.go

為了使用 wire , 在 NewGrpcClient 函數(shù)的返回值的第二個(gè)參數(shù), 必須是 func() 類型, 用于 cleanup.

NewGrpcClient 函數(shù)主要有兩步, 第一步是建立 gRPC 服務(wù)連接并連接 greeter RPC, 第二步是 cleanup 處理.

GrpcClient 結(jié)構(gòu)體主要是實(shí)現(xiàn) RPC 的通信.

package client

import (
    "context"
    "time"

    "helloworld/internal/conf"

    "google.golang.org/grpc"

    "github.com/go-kratos/kratos/v2/log"

    v1 "helloworld/api/helloworld/v1"

    "github.com/pkg/errors"
)

type GrpcClient struct {
    logger *log.Helper
    cli    v1.GreeterClient
}

func (gc *GrpcClient) DoSay(name string) error {
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
    defer cancel()
    reply, err := gc.cli.SayHello(ctx, &v1.HelloRequest{Name: name})
    if err != nil {
        return errors.Wrap(err, "could not greet")
    }
    gc.logger.Infof("Greeting: %s", reply.GetMessage())
    return nil
}

func NewGrpcClient(confServer *conf.Server, logger log.Logger) (*GrpcClient, func(), error) {
    addr := confServer.Grpc.Addr
    conn, err := grpc.Dial(addr, grpc.WithInsecure(), grpc.WithBlock())
    if err != nil {
        return nil, nil, errors.Wrap(err, "did not dail gRPC server")
    }
    gc := &GrpcClient{
        logger: log.NewHelper("client/greeter", logger),
        cli:    v1.NewGreeterClient(conn),
    }
    cleanup := func() {
        if err := conn.Close(); err != nil {
            gc.logger.Errorf("hanppend a little wrongs when close gRPC server:\n %v", err)
        }
    }
    return gc, cleanup, nil
}

2./internal/client/client.go

wire.NewSet 可以放多個(gè)函數(shù), 不分順序.

package client

import "github.com/google/wire"

var ProviderSet = wire.NewSet(NewGrpcClient)

3./cmd/helloworld_client/wire.go

這個(gè)文件主要是通過 wire 命令來生成 wire_gen.go, 關(guān)鍵函數(shù)是 wire.Build , 同時(shí)不要?jiǎng)h掉這行代碼 //+build wireinject .

//+build wireinject

package main

import (
    "helloworld/internal/client"
    "helloworld/internal/conf"

    "github.com/go-kratos/kratos/v2/log"
    "github.com/google/wire"
)

func initClient(confServer *conf.Server, logger log.Logger) (*client.GrpcClient, func(), error) {
    panic(wire.Build(client.ProviderSet))
}

4./cmd/helloworld_client/main.go

這里也主要學(xué)習(xí)了 helloworld 應(yīng)用中的 conf 的使用, 以及發(fā)了 100 次的 SayHello.

package main

import (
    "flag"
    "os"
    "strconv"

    "helloworld/internal/conf"

    "github.com/go-kratos/kratos/v2/config"
    "github.com/go-kratos/kratos/v2/config/file"
    "github.com/go-kratos/kratos/v2/log"
    "gopkg.in/yaml.v2"
)

var (
    flagconf string
    logger   log.Logger
)

func init() {
    flag.StringVar(&flagconf, "conf", "../../configs", "config path, eg: -conf config.yaml")
    logger = log.NewStdLogger(os.Stdout)
}

func main() {
    flag.Parse()

    // yaml 先轉(zhuǎn) json,
    // json 再轉(zhuǎn) protobuf
    cfg := config.New(
        config.WithSource(
            file.NewSource(flagconf),
        ),
        config.WithDecoder(func(kv *config.KeyValue, v map[string]interface{}) error {
            return yaml.Unmarshal(kv.Value, v)
        }),
    )
    if err := cfg.Load(); err != nil {
        panic(err)
    }

    var bc conf.Bootstrap
    if err := cfg.Scan(&bc); err != nil {
        panic(err)
    }

    gcli, cleanup, err := initClient(bc.Server, logger)
    if err != nil {
        panic(err)
    }
    defer cleanup()

    lg := log.NewHelper("client/main", logger)

    for i := 0; i < 100; i++ {
        if err := gcli.DoSay("fango" + strconv.Itoa(i+1)); err != nil {
            lg.Error(err)
        }
    }
    lg.Info("finished")
}

5.編譯 helloworld_client

go build -o ./bin/ ./cmd/helloworld_client

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末命锄,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子低斋,更是在濱河造成了極大的恐慌俐筋,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粮揉,死亡現(xiàn)場離奇詭異巡李,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)扶认,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門侨拦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人辐宾,你說我怎么就攤上這事狱从。” “怎么了叠纹?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵季研,是天一觀的道長。 經(jīng)常有香客問我吊洼,道長训貌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮递沪,結(jié)果婚禮上豺鼻,老公的妹妹穿的比我還像新娘。我一直安慰自己款慨,他們只是感情好儒飒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著檩奠,像睡著了一般桩了。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上埠戳,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天井誉,我揣著相機(jī)與錄音,去河邊找鬼整胃。 笑死颗圣,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的屁使。 我是一名探鬼主播在岂,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蛮寂!你這毒婦竟也來了蔽午?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤酬蹋,失蹤者是張志新(化名)和其女友劉穎及老,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體除嘹,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡写半,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了尉咕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片叠蝇。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖年缎,靈堂內(nèi)的尸體忽然破棺而出悔捶,到底是詐尸還是另有隱情,我是刑警寧澤单芜,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布蜕该,位于F島的核電站,受9級特大地震影響洲鸠,放射性物質(zhì)發(fā)生泄漏堂淡。R本人自食惡果不足惜馋缅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绢淀。 院中可真熱鬧萤悴,春花似錦、人聲如沸皆的。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽费薄。三九已至硝全,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間楞抡,已是汗流浹背伟众。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拌倍,地道東北人赂鲤。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像柱恤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子找爱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容