Golang微服务开发常用工具包 - Gooseberry

Golang微服务开发常用工具包 - Gooseberry https://github.com/voicera/gooseberry

Gooseberry 是 Voicera 在微服务中使用的一组通用 Go 包集合。 这是一个尚不完整的库,以一种看起来像未成熟小柑橘的水果命名。 我们希望将 Gooseberry 打造成类似 Java 中 Guava 的存在。

功能特性

  • REST 客户端,具有日志记录、基础认证支持等的 Web 客户端
  • 容器结构体,如不可变映射、优先级队列、集合等
  • 错误聚合(将多个错误合并为一个并附带头部消息)
  • 带前缀的层级日志记录器,以及 zap 的封装器
  • 采用指数退避和伯努利试验进行重置的轮询机制
  • 实现了 RFC8141 的统一资源名称结构体

快速开始

获取最新版本:go get -u github.com/voicera/gooseberry

示例

下面的示例创建了一个 RESTful 的 Twilio 客户端来拨打电话并轮询通话记录。该客户端使用记录器记录请求和响应,并使用指数退避轮询器持续轮询已拨打的电话。

package main

import (
	"net/http"
	"time"

	"github.com/voicera/gooseberry"
	"github.com/voicera/gooseberry/log"
	"github.com/voicera/gooseberry/log/zap"
	"github.com/voicera/gooseberry/polling"
	"github.com/voicera/gooseberry/web"
	"github.com/voicera/gooseberry/web/rest"
)

const (
	baseURL    = "https://api.twilio.com/2010-04-01/Accounts/"
	accountSid = "AC072dcbab90350495b2c0fabf9a7817bb"
	authToken  = "883XXXXXXXXXXXXXXXXXXXXXXXXX1985"
)

type call struct {
	SID    string `json:"sid"`
	Status string `json:"status"`
}

type receiver struct {
	restClient rest.Client
}

func main() {
	gooseberry.Logger = zap.DefaultLogger
	gooseberry.Logger.Info("starting example")
	transport := web.NewBasicAuthRoundTripper(
		web.NewLeveledLoggerRoundTripper(
			http.DefaultTransport,
			log.NewPrefixedLeveledLogger(gooseberry.Logger, "TWL:")),
		accountSid, authToken)
	httpClient := &http.Client{Transport: transport}
	twilioClient := rest.NewURLEncodedRequestJSONResponseClient(httpClient).
		WithBaseURL(baseURL + accountSid)
	makeCall(twilioClient)
	poll(&receiver{twilioClient})
	time.Sleep(2 * time.Second)
	gooseberry.Logger.Sync()
}

func makeCall(twilioClient rest.Client) {
	parameters := map[string]string{
		"From": "+15005550006",
		"To":   "+14108675310",
		"Url":  "http://demo.twilio.com/docs/voice.xml",
	}
	call := &call{}
	if _, err := twilioClient.Post("Calls.json", parameters, &call); err != nil {
		gooseberry.Logger.Error("error making a call", "err", err)
	} else {
		gooseberry.Logger.Debug("made a call", "sid", call.SID)
	}
}

func poll(receiver *receiver) {
	poller, err := polling.NewBernoulliExponentialBackoffPoller(
		receiver, "twilio", 0.95, time.Second, time.Minute)
	if err != nil {
		gooseberry.Logger.Error("error creating a poller", "err", err)
	}
	go poller.Start()
}

func (r *receiver) Receive() (interface{}, bool, error) {
	calls := []*call{}
	_, err := r.restClient.Get("Calls", nil, &calls)
	return calls, len(calls) > 0, err
}

运行上述示例会产生类似以下的输出(为简洁起见已大幅编辑):

{"level":"info","ts":"2018-04-02T00:58:05Z","caller":"runtime/proc.go:198","msg":"starting example"}
{"level":"debug","ts":"2018-04-02T00:58:05Z","caller":"web/web.go:90","msg":"TWL:Request","request":"POST /2010-04-01/Accounts/AC072dcbab90350495b2c0fabf9a7817bb/Calls.json HTTP/1.1\r\nHost: api.twilio.com\r\nUser-Agent: gooseberry\r\nContent-Length: 89\r\nAuthorization: *******STRIPPED OUT*******\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip\r\n\r\nFrom=%2B15005550006&To=%2B14108675310&Url=http%3A%2F%2Fdemo.twilio.com%2Fdocs%2Fvoice.xml"}
{"level":"debug","ts":"2018-04-02T00:58:06Z","caller":"web/web.go:108","msg":"TWL:Response","response":"HTTP/1.1 401 UNAUTHORIZED
...
\"Your AccountSid or AuthToken was incorrect.\", \"message\": \"Authenticate\", \"more_info\": \"https://www.twilio.com/docs/errors/20003\", \"status\": 401}"}
{"level":"error","ts":"2018-04-02T00:58:06Z","caller":"example/main.go:40","msg":"error making a call","err":"HTTP Status Code 401
...
runtime.main\n\t/usr/local/go/src/runtime/proc.go:198"}
{"level":"debug","ts":"2018-04-02T00:58:06Z","caller":"runtime/asm_amd64.s:2361","msg":"Started","poller":"twilio"}
{"level":"debug","ts":"2018-04-02T00:58:06Z","caller":"web/web.go:90","msg":"TWL:Request","request":"GET /2010-04-01/Accounts/AC072dcbab90350495b2c0fabf9a7817bb/Calls HTTP/1.1\r\nHost: api.twilio.com\r\nUser-Agent: gooseberry\r\nAuthorization: *******STRIPPED OUT*******\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip\r\n\r\n"}
{"level":"debug","ts":"2018-04-02T00:58:06Z","caller":"web/web.go:108","msg":"TWL:Response","response":"HTTP/1.1 401 UNAUTHORIZED\r\nContent-Length:
...
X-Shenanigans: none\r\n\r\n<?xml version='1.0' encoding='UTF-8'?>\n<TwilioResponse><RestException><Code>20003</Code><Detail>Your AccountSid or AuthToken was incorrect.</Detail><Message>Authenticate</Message><MoreInfo>https://www.twilio.com/docs/errors/20003</MoreInfo><Status>401</Status></RestException></TwilioResponse>"}
...
{"level":"debug","ts":"2018-04-02T00:58:06Z","caller":"polling/poller.go:98","msg":"Relaxing","poller":"twilio"}
{"level":"debug","ts":"2018-04-02T00:58:07Z","caller":"web/web.go:90","msg":"TWL:Request","request":"GET /2010-04-01/Accounts/AC072dcbab90350495b2c0fabf9a7817bb/Calls HTTP/1.1\r\nHost: api.twilio.com\r\nUser-Agent: gooseberry\r\nAuthorization: *******STRIPPED OUT*******\r\nContent-Type: application/x-www-form-urlencoded\r\nAccept-Encoding: gzip\r\n\r\n"}
...
{"level":"debug","ts":"2018-04-02T00:58:07Z","caller":"polling/poller.go:98","msg":"Relaxing","poller":"twilio"}

更多关于Golang微服务开发常用工具包 - Gooseberry的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang微服务开发常用工具包 - Gooseberry的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Gooseberry 是一个专注于微服务开发的 Go 工具包集合,提供了多个实用组件来简化常见任务。以下是对其核心功能的专业分析及代码示例:

1. REST 客户端

Gooseberry 的 webrest 包封装了 HTTP 客户端,支持日志记录和基础认证。示例中展示了如何创建带认证的 Twilio 客户端:

transport := web.NewBasicAuthRoundTripper(
    web.NewLeveledLoggerRoundTripper(http.DefaultTransport, log.NewPrefixedLeveledLogger(gooseberry.Logger, "TWL:")),
    accountSid, authToken)
httpClient := &http.Client{Transport: transport}
twilioClient := rest.NewURLEncodedRequestJSONResponseClient(httpClient).WithBaseURL(baseURL + accountSid)

此代码通过 NewBasicAuthRoundTripper 添加基础认证,并使用 LeveledLoggerRoundTripper 自动记录请求/响应日志。

2. 轮询机制

polling 包实现了指数退避和伯努利试验策略,适用于异步任务状态检查。示例中的轮询器配置:

poller, err := polling.NewBernoulliExponentialBackoffPoller(
    receiver, "twilio", 0.95, time.Second, time.Minute)

此轮询器以 95% 的概率(伯努利试验)在每次迭代后重置退避时间,初始间隔 1 秒,最大间隔 1 分钟。

3. 结构化日志

通过 log 包和 zap 封装器,Gooseberry 支持带前缀的层级日志:

gooseberry.Logger = zap.DefaultLogger
gooseberry.Logger.Info("starting example", "key", "value")

输出为 JSON 格式,包含时间戳、调用位置和自定义字段。

4. 错误聚合

errors 包允许合并多个错误:

errs := errors.NewAggregate("operation failed")
errs.Add(fmt.Errorf("network timeout"))
errs.Add(fmt.Errorf("invalid response"))
if errs.HasErrors() {
    gooseberry.Logger.Error("aggregated errors", "err", errs)
}

聚合错误会保留原始错误链,便于统一处理。

5. 容器结构体

Gooseberry 提供了不可变映射等线程安全容器:

immutableMap := containers.NewImmutableMap(map[string]int{"a": 1})
value, exists := immutableMap.Get("a")

不可变映射适用于并发读取场景,避免数据竞争。

总结

Gooseberry 通过模块化设计解决了微服务中的通用需求,如 HTTP 通信、异步轮询和日志管理。其 API 设计注重可组合性,例如将日志记录器与 HTTP 传输层解耦。虽然项目处于早期阶段,但代码结构清晰,适合作为微服务基础设施的补充组件。

回到顶部