go-kit入門指南

go-kit 入門

1. microservice

Go-Kit

 

go kit 是一個分佈式的開發工具集,在大型的組織(業務)中可以用來構建微服務。其解決了分佈式系統中的大多數常見問題,因此,使用者可以將精力集中在業務邏輯上。

2. go-kit 組件介紹

2.1 Endpoint(端點)

Go kit首先解決了RPC消息模式。其使用了一個抽象的 endpoint 來爲每一個RPC建立模型。

endpoint通過被一個server進行實現(implement),或是被一個client調用。這是很多 Go kit組件的基本構建代碼塊。

2.2 Circuit breaker(迴路斷路器)

Circuitbreaker(迴路斷路器) 模塊提供了很多流行的迴路斷路lib的端點(endpoint)適配器。迴路斷路器可以避免雪崩,並且提高了針對間歇性錯誤的彈性。每一個client的端點都應該封裝(wrapped)在迴路斷路器中。

2.3 Rate limiter(限流器)

ratelimit模塊提供了到限流器代碼包的端點適配器。限流器對服務端(server-client)和客戶端(client-side)同等生效。使用限流器可以強制進、出請求量在閾值上限以下。

2.4 Transport(傳輸層)

transport 模塊提供了將特定的序列化算法綁定到端點的輔助方法。當前,Go kit只針對JSON和HTTP提供了輔助方法。如果你的組織使用完整功能的傳輸層,典型的方案是使用Go在傳輸層提供的函數庫,Go kit並不需要來做太多的事情。這些情況,可以查閱代碼例子來理解如何爲你的端點寫一個適配器。目前,可以查看 addsvc的代碼來理解Transport綁定是如何工作的。我們還提供了針對Thirft,gRPC,net/rpc,和http json的特殊例子。對JSON/RPC和Swagger的支持在計劃中。

2.5 Logging(日誌)

服務產生的日誌是會被延遲消費(使用)的,或者是人或者是機器(來使用)。人可能會對調試錯誤、跟蹤特殊的請求感興趣。機器可能會對統計那些有趣的事件,或是對離線處理的結果進行聚合。這兩種情況,日誌消息的結構化和可操作性是很重要的。Go kit的 log 模塊針對這些實踐提供了最好的設計。

2.6 Metrics(Instrumentation)度量/儀表盤

直到服務經過了跟蹤計數、延遲、健康狀況和其他的週期性的或針對每個請求信息的儀表盤化,才能被認爲是“生產環境”完備的。Go kit 的 metric 模塊爲你的服務提供了通用並健壯的接口集合。可以綁定到常用的後端服務,比如 expvar 、statsdPrometheus

2.7 Request tracing(請求跟蹤)

隨着你的基礎設施的增長,能夠跟蹤一個請求變得越來越重要,因爲它可以在多個服務中進行穿梭並回到用戶。Go kit的 tracing 模塊提供了爲端點和傳輸的增強性的綁定功能,以捕捉關於請求的信息,並把它們發送到跟蹤系統中。(當前支持 Zipkin,計劃支持Appdash

2.8 Service discovery and load balancing(服務發現和負載均衡)

如果你的服務調用了其他的服務,需要知道如何找到它(另一個服務),並且應該智能的將負載在這些發現的實例上鋪開(即,讓被發現的實例智能的分擔服務壓力)。Go kit的loadbalancer模塊提供了客戶端端點的中間件來解決這類問題,無論你是使用的靜態的主機名還是IP地址,或是 DNS的 SRV 記錄,Consul,etcd 或是 Zookeeper。並且,如果你使用定製的系統,也可以非常容易的編寫你自己的 Publisher,以使用 Go kit 提供的負載均衡策略。(目前,支持靜態主機名、etcd、Consul、Zookeeper)

3 目標

  • 在各種SOA架構中操作–預期會與各種非Go kit服務進行交互
  • 使用RPC作爲最主要的消息模式
  • 可插拔的序列化和傳輸–不僅僅只有JSON和HTTP
  • 簡單便可融入現有的架構–沒有任何特殊工具、技術的相關指令

4 目標之外(不考慮做的事情)

  • 支持除RPC之外的消息模式(至少目前是)–比如 MPI、pub/sub,CQRS,等
  • 除適配現有軟件外,重新實現一些功能
  • 在運維方面進行評論:部署、配置、進程管理、服務編排等

5 依賴管理

Go kit 是一個函數庫,設計的目標是引入到二進制文件中。對於二進制軟件包的作者來講,Vendoring是目前用來確保軟件可靠、可重新構建的最好的機制。因此,我們強烈的建議我們的用戶使用vendoring機制來管理他們軟件的依賴,包括Go kit。

爲了避免兼容性和可用性的問題,Go kit沒有vendor它自己的依賴,並且並不推薦使用第三方的引用代理。

有一些工具可以讓vendor機制更簡單,包括 gbglidegvt、 govendor 和 vendetta。另外,Go kit使用了一系列的持續集成的機制來確保在儘快地修復那些複雜問題。

5 相關項目

標註有 ★ 的項目對 Go kit 的設計有着特別的影響 (反之亦然)

  1. 服務框架

    • gizmo, a microservice toolkit from The New York Times ★
    • go-micro, a microservices client/server library ★
    • gocircuit, dynamic cloud orchestration
    • gotalk, async peer communication protocol & library
    • h2, a microservices framework ★
    • Kite, a micro-service framework
  2. 獨立組件

    afex/hystrix-go, client-side latency and fault tolerance library

    armon/go-metrics, library for exporting performance and runtime metrics to external metrics systems

    codahale/lunk, structured logging in the style of Google’s Dapper or Twitter’s Zipkin

    eapache/go-resiliency, resiliency patterns

    sasbury/logging, a tagged style of logging

    grpc/grpc-go, HTTP/2 based RPC

    inconshreveable/log15, simple, powerful logging for Go ★

    mailgun/vulcand, programmatic load balancer backed by etcd

    mattheath/phosphor, distributed system tracing

    pivotal-golang/lager, an opinionated logging library

    rubyist/circuitbreaker, circuit breaker library

    Sirupsen/logrus, structured, pluggable logging for Go ★

    sourcegraph/appdash, application tracing system based on Google’s Dapper

    spacemonkeygo/monitor, data collection, monitoring, instrumentation, and Zipkin client library

    streadway/handy, net/http handler filters

    vitess/rpcplus, package rpc + context.Context

    gdamore/mangos, nanomsg implementation in pure Go

  3. Web 框架

    Beego

    Gin

    Goji

    Gorilla

    Martini

    Negroni

    Revel (considered harmful)

  4. ###其他參考

    Architecting for the Cloud — Netflix

    Dapper, a Large-Scale Distributed Systems Tracing Infrastructure — Google

    Your Server as a Function (PDF) — Twitter

 

 

 

 

 

go-kit 入門 (二) 第一個 Go kit 程序

發表於2016 年 5 月 28 日[email protected]

下面讓我來們創建一個非常精簡的 Go kit 服務

業務邏輯邏輯

服務(Service)是從業務邏輯開始的,在 Go kit 中,我們將服務以 interface 作爲模型

 

1

2

3

4

5

// StringService provides operations on strings.

type StringService interface {

    Uppercase(string) (string, error)

    Count(string) int

}

這個 interface 需要有一個“實現”

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

type stringService struct{}

 

func (stringService) Uppercase(s string) (string, error) {

    if s == "" {

        return "", ErrEmpty

    }

    return strings.ToUpper(s), nil

}

 

func (stringService) Count(s string) int {

    return len(s)

}

 

// ErrEmpty is returned when input string is empty

var ErrEmpty = errors.New("Empty string")

請求和響應

在 Go kit 中,主要的消息模式是 RPC。因此,接口( interface )的每一個方法都會被模型化爲遠程過程調用(RPC)。對於每一個方法,我們都定義了請求和響應的結構體,捕獲輸入、輸出各自的所有參數。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

type uppercaseRequest struct {

    S string `json:"s"`

}

 

type uppercaseResponse struct {

    V   string `json:"v"`

    Err string `json:"err,omitempty"` // errors don't define JSON marshaling

}

 

type countRequest struct {

    S string `json:"s"`

}

 

type countResponse struct {

    V int `json:"v"`

}

端點 (endpoint)

Go kit 通過 endpoint 提供了非常豐富的功能。

一個端點代表一個RPC,也就是我們服務接口中的一個函數。我們將編寫簡單的適配器,將我們的服務的每一個方法轉換成端點。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

import (

    "golang.org/x/net/context"

    "github.com/go-kit/kit/endpoint"

)

 

 

func makeUppercaseEndpoint(svc StringService) endpoint.Endpoint {

    return func(ctx context.Context, request interface{}) (interface{}, error) {

        req := request.(uppercaseRequest)

        v, err := svc.Uppercase(req.S)

        if err != nil {

            return uppercaseResponse{v, err.Error()}, nil

        }

        return uppercaseResponse{v, ""}, nil

    }

}

 

func makeCountEndpoint(svc StringService) endpoint.Endpoint {

    return func(ctx context.Context, request interface{}) (interface{}, error) {

        req := request.(countRequest)

        v := svc.Count(req.S)

        return countResponse{v}, nil

    }

}

傳輸(Transports)

現在我們需要將服務暴露給外界,這樣它們才能被調用。對於服務如何與外界交互,你的組織可能已經有了定論。可能你會使用 Thrift、基於 HTTP 的自定義 JSON。Go kit支持多種開箱即用的 傳輸 方式。(Adding support for new ones is easy—just 對新方式的支持是非常簡單的。參見 這裏

針對我們現在的這個微型的服務例子,我們使用基於 HTTP 的 JSON。Go kit 中提供了一個輔助結構體,在 transport/http 中。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

import (

    "encoding/json"

    "log"

    "net/http"

 

    "golang.org/x/net/context"

 

    httptransport "github.com/go-kit/kit/transport/http"

)

 

func main() {

    ctx := context.Background()

    svc := stringService{}

 

    uppercaseHandler := httptransport.NewServer(

        ctx,

        makeUppercaseEndpoint(svc),

        decodeUppercaseRequest,

        encodeResponse,

    )

 

    countHandler := httptransport.NewServer(

        ctx,

        makeCountEndpoint(svc),

        decodeCountRequest,

        encodeResponse,

    )

 

    http.Handle("/uppercase", uppercaseHandler)

    http.Handle("/count", countHandler)

    log.Fatal(http.ListenAndServe(":8080", nil))

}

 

func decodeUppercaseRequest(_ context.Context, r *http.Request) (interface{}, error) {

    var request uppercaseRequest

    if err := json.NewDecoder(r.Body).Decode(&request); err != nil {

        return nil, err

    }

    return request, nil

}

 

func decodeCountRequest(_ context.Context, r *http.Request) (interface{}, error) {

    var request countRequest

    if err := json.NewDecoder(r.Body).Decode(&request); err != nil {

        return nil, err

    }

    return request, nil

}

 

func encodeResponse(_ context.Context, w http.ResponseWriter, response interface{}) error {

    return json.NewEncoder(w).Encode(response)

}

 

 

 

 

 

go-kit 入門(三)日誌和儀表化

發表於2016 年 6 月 1 日[email protected]

日誌和儀表化

任何服務在日誌和儀表化沒有就緒的情況下,都不能說是生產環境就緒的。

傳輸日誌

任何需要日誌記錄的組件都需要將 logger 作爲依賴,就像數據庫連接一樣。因此,我們在 main 函數中構造 logger 對象,然後將其傳入需要使用它的組件中。我們始終不去使用一個全局的 logger 對象。

我們可以直接將 logger 傳入到 stringService 的實現代碼中,但是,還有一個更好的方式。我們可以使用 中間件 (middleware) ,也常常被稱爲 裝飾者。

middleware 是一個函數,它接收一個 endpoint 作爲參數,並且返回一個 endpoint。

 

1

type Middleware func(Endpoint) Endpoint

在函數中,它可以做任何事情。下面就讓我們來創建一個基本的日誌中間件。

 

1

2

3

4

5

6

7

8

9

func loggingMiddleware(logger log.Logger) Middleware {

    return func(next endpoint.Endpoint) endpoint.Endpoint {

        return func(ctx context.Context, request interface{}) (interface{}, error) {

            logger.Log("msg", "calling endpoint")

            defer logger.Log("msg", "called endpoint")

            return next(ctx, request)

        }

    }

}

然後,我們將它加入到每一個處理函數中。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

logger := log.NewLogfmtLogger(os.Stderr)

 

svc := stringService{}

 

var uppercase endpoint.Endpoint

uppercase = makeUppercaseEndpoint(svc)

uppercase = loggingMiddleware(log.NewContext(logger).With("method", "uppercase"))(uppercase)

 

var count endpoint.Endpoint

count = makeCountEndpoint(svc)

count = loggingMiddleware(log.NewContext(logger).With("method", "count"))(count)

 

uppercaseHandler := httptransport.Server(

    // ...

    uppercase,

    // ...

)

 

countHandler := httptransport.Server(

    // ...

    count,

    // ...

)

事實證明,這項技術是非常有價值的,遠遠不止於記錄日誌,Go kit 的很多模塊都被實現爲端點中間件。

應用日誌

那麼,在我們的應用中,應該如何記錄日誌呢?比如那些需要被傳入的參數等。事實上,我們能夠爲我們的服務定義一箇中間件,從而獲得同樣好的組合效果。由於我們的 StringService被定義爲一個接口,我們只需要作出一個新的類型,來包裝先有的 StringService,讓它來執行擴充的記錄日誌的任務。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

type loggingMiddleware struct{

    logger log.Logger

    StringService

}

 

func (mw loggingMiddleware) Uppercase(s string) (output string, err error) {

    defer func(begin time.Time) {

        mw.logger.Log(

            "method", "uppercase",

            "input", s,

            "output", output,

            "err", err,

            "took", time.Since(begin),

        )

    }(time.Now())

 

    output, err = mw.StringService.Uppercase(s)

    return

}

 

func (mw loggingMiddleware) Count(s string) (n int) {

    defer func(begin time.Time) {

        mw.logger.Log(

            "method", "count",

            "input", s,

            "n", n,

            "took", time.Since(begin),

        )

    }(time.Now())

 

    n = mw.StringService.Count(s)

    return

}

然後,將新的類型引入到下面的代碼中:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

import (

    "os"

 

    "github.com/go-kit/kit/log"

    httptransport "github.com/go-kit/kit/transport/http"

)

 

func main() {

    logger := log.NewLogfmtLogger(os.Stderr)

 

    svc := stringService{}

    svc = loggingMiddleware{logger, svc}

 

    uppercaseHandler := httptransport.NewServer(

        // ...

        makeUppercaseEndpoint(svc),

        // ...

    )

 

    countHandler := httptransport.NewServer(

        // ...

        makeCountEndpoint(svc),

        // ...

    )

}

在傳輸環節使用端點中間件,比如迴路斷路器和速率限制。在業務環節使用服務中間件,比如日誌和儀表化。

儀表化

在 Go kit 中,儀表化意味着使用 包指標 來記錄關於服務運行行爲的狀態。統計執行的任務的數量,在請求完成後記錄消耗的時間,以及跟蹤所有正在執行的操作的數量,都被認爲是 儀表化。

我們可以使用同樣的中間件模式,在記錄日誌的環節我們曾經用過。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

type instrumentingMiddleware struct {

    requestCount   metrics.Counter

    requestLatency metrics.TimeHistogram

    countResult    metrics.Histogram

    StringService

}

 

func (mw instrumentingMiddleware) Uppercase(s string) (output string, err error) {

    defer func(begin time.Time) {

        methodField := metrics.Field{Key: "method", Value: "uppercase"}

        errorField := metrics.Field{Key: "error", Value: fmt.Sprintf("%v", err)}

        mw.requestCount.With(methodField).With(errorField).Add(1)

        mw.requestLatency.With(methodField).With(errorField).Observe(time.Since(begin))

    }(time.Now())

 

    output, err = mw.StringService.Uppercase(s)

    return

}

 

func (mw instrumentingMiddleware) Count(s string) (n int) {

    defer func(begin time.Time) {

        methodField := metrics.Field{Key: "method", Value: "count"}

        errorField := metrics.Field{Key: "error", Value: fmt.Sprintf("%v", error(nil))}

        mw.requestCount.With(methodField).With(errorField).Add(1)

        mw.requestLatency.With(methodField).With(errorField).Observe(time.Since(begin))

        mw.countResult.Observe(int64(n))

    }(time.Now())

 

    n = mw.StringService.Count(s)

    return

}

然後將其引入到服務中:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

import (

    stdprometheus "github.com/prometheus/client_golang/prometheus"

    kitprometheus "github.com/go-kit/kit/metrics/prometheus"

    "github.com/go-kit/kit/metrics"

)

 

func main() {

    logger := log.NewLogfmtLogger(os.Stderr)

 

    fieldKeys := []string{"method", "error"}

    requestCount := kitprometheus.NewCounter(stdprometheus.CounterOpts{

        // ...

    }, fieldKeys)

    requestLatency := metrics.NewTimeHistogram(time.Microsecond, kitprometheus.NewSummary(stdprometheus.SummaryOpts{

        // ...

    }, fieldKeys))

    countResult := kitprometheus.NewSummary(stdprometheus.SummaryOpts{

        // ...

    }, []string{}))

 

    svc := stringService{}

    svc = loggingMiddleware{logger, svc}

    svc = instrumentingMiddleware{requestCount, requestLatency, countResult, svc}

 

    uppercaseHandler := httptransport.NewServer(

        // ...

        makeUppercaseEndpoint(svc),

        // ...

    )

 

    countHandler := httptransport.NewServer(

        // ...

        makeCountEndpoint(svc),

        // ...

    )

 

    http.Handle("/metrics", stdprometheus.Handler())

}

 

stringsvc2

目前位置,完整的服務是 stringsvc2.

 

1

2

3

$ go get github.com/go-kit/kit/examples/stringsvc2

$ stringsvc2

msg=HTTP addr=:8080

 

 

1

2

3

4

$ curl -XPOST -d'{"s":"hello, world"}' localhost:8080/uppercase

{"v":"HELLO, WORLD","err":null}

$ curl -XPOST -d'{"s":"hello, world"}' localhost:8080/count

{"v":12}

 

 

1

2

method=uppercase input="hello, world" output="HELLO, WORLD" err=null took=2.455µs

method=count input="hello, world" n=12 took=743ns

 

 

 

 

 

Go-kit 入門(四)服務調用

發表於2016 年 6 月 16 日[email protected]

調用服務

存在“真空”(即極其獨立,與其他任何服務無互相調用的關係)中的服務是罕見的。而我們常見的是,我們需要調用其他的服務。這也是 Go kit 的閃光點 ,我們提供了 傳輸中間件機制來解決可能出現的很多問題。

下面我們將實現一個代理功能的中間件,作爲一個服務中間件。在這裏我們只代理一個方法,Uppercase。

 

1

2

3

4

5

6

7

8

// proxymw implements StringService, forwarding Uppercase requests to the

// provided endpoint, and serving all other (i.e. Count) requests via the

// embedded StringService.

type proxymw struct {

   context.Context

   StringService                       // Serve most requests via this embedded service...

   UppercaseEndpoint endpoint.Endpoint // ...except Uppercase, which gets served by this endpoint

}

客戶端端點

我們已經有了一個跟我們所知道的完全相同的端點,但是我們將使用它來調用一個請求,而不是提供服務。按照這種方式來使用它的時候,我們稱它爲客戶端端點。爲了調用客戶端端點,我們需要做一些簡單的轉換。

 

1

2

3

4

5

6

7

8

9

10

11

func (mw proxymw) Uppercase(s string) (string, error) {

    response, err := mw.UppercaseEndpoint(mw.Context, uppercaseRequest{S: s})

    if err != nil {

        return "", err

    }

    resp := response.(uppercaseResponse)

    if resp.Err != "" {

        return resp.V, errors.New(resp.Err)

    }

    return resp.V, nil

}

現在,我們爲了構造一個代理中間件,我們將一個代理URL字符串轉換爲一個端點。加入我們使用 HTTP 協議之上的 JSON,我們可以使用 transport/http 包中的一個輔助(helper)函數。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

import (

   httptransport "github.com/go-kit/kit/transport/http"

)

 

func proxyingMiddleware(proxyURL string, ctx context.Context) ServiceMiddleware {

   return func(next StringService) StringService {

      return proxymw{ctx, next, makeUppercaseEndpoint(ctx, proxyURL)}

   }

}

 

func makeUppercaseEndpoint(ctx context.Context, proxyURL string) endpoint.Endpoint {

   return httptransport.NewClient(

      "GET",

      mustParseURL(proxyURL),

      encodeUppercaseRequest,

      decodeUppercaseResponse,

   ).Endpoint()

}

服務發現和負載均衡

如果我們只使用一個遠程的服務就好了。但是實際上,我們往往 會有很多個服務實例。我們希望通過一些服務發現算法來發現它們,然後將我們的負載分散到它們上面,並且如果這些實例中的任何一個變得糟糕,我們能夠處理它,並且不影響我們服務的可用性。

Go kit 爲不同的服務發現系統提供了適配器,爲了獲取最新的服務實例集合,暴露端點個體。這些適配器被稱爲 發佈器(publishers)。

 

1

2

3

type Publisher interface {

    Endpoints() ([]endpoint.Endpoint, error)

}

在發佈器內部,它使用一個私有的工廠函數,將被發現的每一個 host:port 字符串 轉換成一個可用的端點。

 

1

type Factory func(instance string) (endpoint.Endpoint, error)

目前,我們的工程方法,makeUppercaseEndpoint,只是直接請求 URL。但是,在工廠函數中加入一些安全的中間件方法是很重要的,比如 迴路斷路器 和 限流器。

 

1

2

3

4

5

6

7

8

9

func factory(ctx context.Context, maxQPS int) loadbalancer.Factory {

    return func(instance string) (endpoint.Endpoint, error) {

        var e endpoint.Endpoint

        e = makeUppercaseProxy(ctx, instance)

        e = circuitbreaker.Gobreaker(gobreaker.NewCircuitBreaker(gobreaker.Settings{}))(e)

        e = kitratelimit.NewTokenBucketLimiter(jujuratelimit.NewBucketWithRate(float64(maxQPS), int64(maxQPS)))(e)

        return e, nil

    }

}

現在,我們已經有了一系列的端點,我們需要在其中選擇一個。負載均衡器包裝了 發佈器,並且從端點集合中選擇其中的某一個。Go kit 提供了一組基本的負載均衡器,並且,如果你希望更多的高級的算法,也可以很容易的自己來寫一個。

 

1

2

3

type LoadBalancer interface {

    Endpoint() (endpoint.Endpoint, error)

}

現在,我們可以根據一下算法來選擇端點。我們能夠使用它爲消費器提供一個單一的、邏輯的可靠的端點。通過重試的策略包裝負載均衡器,並且返回一個可用的端點。重試的策略可以將一個失敗的請求進行重試,直到達到最大的可重試次數或是達到超時時間。

 

1

func Retry(max int, timeout time.Duration, lb LoadBalancer) endpoint.Endpoint

現在,讓我們將最後的代理中間件加入到代碼中。爲了簡潔,我們假設用戶會爲逗號(,)分隔的多個實例端點指定一個標記。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

func proxyingMiddleware(proxyList string, ctx context.Context, logger log.Logger) ServiceMiddleware {

    return func(next StringService) StringService {

        var (

            qps         = 100 // max to each instance

            publisher   = static.NewPublisher(split(proxyList), factory(ctx, qps), logger)

            lb          = loadbalancer.NewRoundRobin(publisher)

            maxAttempts = 3

            maxTime     = 100 * time.Millisecond

            endpoint    = loadbalancer.Retry(maxAttempts, maxTime, lb)

        )

        return proxymw{ctx, endpoint, next}

    }

}

stringsvc3

截止目前,這個完整的服務是 stringsvc3$ go get github.com/go-kit/kit/examples/stringsvc3 $ stringsvc3 -listen=:8001 & listen=:8001 caller=proxying.go:25 proxy_to=none listen=:8001 caller=main.go:72 msg=HTTP addr=:8001 $ stringsvc3 -listen=:8002 & listen=:8002 caller=proxying.go:25 proxy_to=none listen=:8002 caller=main.go:72 msg=HTTP addr=:8002 $ stringsvc3 -listen=:8003 & listen=:8003 caller=proxying.go:25 proxy_to=none listen=:8003 caller=main.go:72 msg=HTTP addr=:8003 $ stringsvc3 -listen=:8080 -proxy=localhost:8001,localhost:8002,localhost:8003 listen=:8080 caller=proxying.go:29 proxy_to="[localhost:8001 localhost:8002 localhost:8003]" listen=:8080 caller=main.go:72 msg=HTTP addr=:8080

 

1

2

3

4

$ for s in foo bar baz ; do curl -d"{\"s\":\"$s\"}" localhost:8080/uppercase ; done

{"v":"FOO","err":null}

{"v":"BAR","err":null}

{"v":"BAZ","err":null}

 

1

2

3

4

5

6

listen=:8001 caller=logging.go:28 method=uppercase input=foo output=FOO err=null took=5.168µs

listen=:8080 caller=logging.go:28 method=uppercase input=foo output=FOO err=null took=4.39012ms

listen=:8002 caller=logging.go:28 method=uppercase input=bar output=BAR err=null took=5.445µs

listen=:8080 caller=logging.go:28 method=uppercase input=bar output=BAR err=null took=2.04831ms

listen=:8003 caller=logging.go:28 method=uppercase input=baz output=BAZ err=null took=3.285µs

listen=:8080 caller=logging.go:28 method=uppercase input=baz output=BAZ err=null took=1.388155ms

高級話題

線程上下文

上下文對象用來在單個請求中攜帶那些需要跨越概念性邊界的信息。在我們的例子中,我們還沒有在業務邏輯中使用線程上下文。但是,這種方式幾乎會永遠都是一個好方案。它允許我們在業務邏輯和中間件中傳遞請求範圍內的信息,並且對於複雜的任務(比如,分佈式系統的細粒度追蹤信息)也是很必要的。

具體點來講,這也意味着你的業務邏輯接口會這樣:

 

1

2

3

4

5

type MyService interface {

    Foo(context.Context, string, int) (string, error)

    Bar(context.Context, string) error

    Baz(context.Context) (int, error)

}

 

請求跟蹤

一旦你的基礎設施增長超過一定的規模,在多個服務中跟蹤請求是非常必要的,這樣,你就能夠分析並解決故障熱點。參見 package tracing 獲取更多信息。

創建一個客戶端軟件包

使用 Go kit 爲你的服務創建一個客戶端軟件包是很可能的事情,讓你的服務能夠很容易對其他的 Go 程序進行調用。實際上,你的客戶端package會提供你的服務接口,這個接口會使用指定的傳輸方式來調用遠程服務。參見 package addsvc/client 作爲參考例子.

其他例子

addsvc

addsvc 是原來的一個例子。它公開 所有支持的傳輸方式 的系列操作。它完整地做了日誌記錄、儀表盤化,並且使用 Zipkin 來跟蹤請求。同樣,它也示範瞭如何創建和使用客戶端package。它是一個非常棒的例子,爲 Go kit 服務提供了完整的功能示例。

profilesvc

profilesvc 示範瞭如何使用 Go kit 來打造 REST 風格的微服務。

apigateway

apigateway 示範瞭如何實現一個 API 網關模式,通過 Consul 作爲服務發現系統。

shipping

shipping 是一個完整的,真實的應用,由多個微服務組成,基於領域驅動設計原則。

文章來源: http://blog.chunshengster.me/?s=go-kit

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章