go kit 是一个分布式的开发工具集,在大型的组织(业务)中可以用来构建微服务。其解决了分布式系统中的大多数常见问题,因此,使用者可以将精力集中在业务逻辑上。
Go kit首先解决了RPC消息模式。其使用了一个抽象的 endpoint 来为每一个RPC建立模型。
endpoint通过被一个server进行实现(implement),或是被一个client调用。这是很多 Go kit组件的基本构建代码块。
Circuitbreaker(回路断路器) 模块提供了很多流行的回路断路lib的端点(endpoint)适配器。回路断路器可以避免雪崩,并且提高了针对间歇性错误的弹性。每一个client的端点都应该封装(wrapped)在回路断路器中。
ratelimit模块提供了到限流器代码包的端点适配器。限流器对服务端(server-client)和客户端(client-side)同等生效。使用限流器可以强制进、出请求量在阈值上限以下。
transport 模块提供了将特定的序列化算法绑定到端点的辅助方法。当前,Go kit只针对JSON和HTTP提供了辅助方法。如果你的组织使用完整功能的传输层,典型的方案是使用Go在传输层提供的函数库,Go kit并不需要来做太多的事情。这些情况,可以查阅代码例子来理解如何为你的端点写一个适配器。目前,可以查看 addsvc的代码来理解Transport绑定是如何工作的。我们还提供了针对Thirft,gRPC,net/rpc,和http json的特殊例子。对JSON/RPC和Swagger的支持在计划中。
服务产生的日志是会被延迟消费(使用)的,或者是人或者是机器(来使用)。人可能会对调试错误、跟踪特殊的请求感兴趣。机器可能会对统计那些有趣的事件,或是对离线处理的结果进行聚合。这两种情况,日志消息的结构化和可操作性是很重要的。Go kit的 log 模块针对这些实践提供了最好的设计。
直到服务经过了跟踪计数、延迟、健康状况和其他的周期性的或针对每个请求信息的仪表盘化,才能被认为是“生产环境”完备的。Go kit 的 metric 模块为你的服务提供了通用并健壮的接口集合。可以绑定到常用的后端服务,比如 expvar 、statsd、Prometheus。
随着你的基础设施的增长,能够跟踪一个请求变得越来越重要,因为它可以在多个服务中进行穿梭并回到用户。Go kit的 tracing 模块提供了为端点和传输的增强性的绑定功能,以捕捉关于请求的信息,并把它们发送到跟踪系统中。(当前支持 Zipkin,计划支持Appdash
如果你的服务调用了其他的服务,需要知道如何找到它(另一个服务),并且应该智能的将负载在这些发现的实例上铺开(即,让被发现的实例智能的分担服务压力)。Go kit的loadbalancer模块提供了客户端端点的中间件来解决这类问题,无论你是使用的静态的主机名还是IP地址,或是 DNS的 SRV 记录,Consul,etcd 或是 Zookeeper。并且,如果你使用定制的系统,也可以非常容易的编写你自己的 Publisher,以使用 Go kit 提供的负载均衡策略。(目前,支持静态主机名、etcd、Consul、Zookeeper)
Go kit 是一个函数库,设计的目标是引入到二进制文件中。对于二进制软件包的作者来讲,Vendoring是目前用来确保软件可靠、可重新构建的最好的机制。因此,我们强烈的建议我们的用户使用vendoring机制来管理他们软件的依赖,包括Go kit。
为了避免兼容性和可用性的问题,Go kit没有vendor它自己的依赖,并且并不推荐使用第三方的引用代理。
有一些工具可以让vendor机制更简单,包括 gb、glide、gvt、 govendor 和 vendetta。另外,Go kit使用了一系列的持续集成的机制来确保在尽快地修复那些复杂问题。
标注有 ★ 的项目对 Go kit 的设计有着特别的影响 (反之亦然)
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
Beego
Gin
Goji
Gorilla
Martini
Negroni
Revel (considered harmful)
###其他参考Architecting for the Cloud — Netflix
Dapper, a Large-Scale Distributed Systems Tracing Infrastructure — Google
Your Server as a Function (PDF) — Twitter
发表于2016 年 5 月 28 日由chunshengster@gmail.com
服务(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"`
}
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
}
}
现在我们需要将服务暴露给外界,这样它们才能被调用。对于服务如何与外界交互,你的组织可能已经有了定论。可能你会使用 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)
}
发表于2016 年 6 月 1 日由chunshengster@gmail.com
任何服务在日志和仪表化没有就绪的情况下,都不能说是生产环境就绪的。
任何需要日志记录的组件都需要将 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.
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
发表于2016 年 6 月 16 日由chunshengster@gmail.com
存在“真空”(即极其独立,与其他任何服务无互相调用的关系)中的服务是罕见的。而我们常见的是,我们需要调用其他的服务。这也是 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$ 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 是原来的一个例子。它公开 所有支持的传输方式 的系列操作。它完整地做了日志记录、仪表盘化,并且使用 Zipkin 来跟踪请求。同样,它也示范了如何创建和使用客户端package。它是一个非常棒的例子,为 Go kit 服务提供了完整的功能示例。
profilesvc 示范了如何使用 Go kit 来打造 REST 风格的微服务。
apigateway 示范了如何实现一个 API 网关模式,通过 Consul 作为服务发现系统。
shipping 是一个完整的,真实的应用,由多个微服务组成,基于领域驱动设计原则。
文章来源: http://blog.chunshengster.me/?s=go-kit