


























文档站点已发布,欢迎访问:https://go-sail.keepchen.com
go-sail是一个轻量的渐进式web框架,使用golang语言实现。它并不是重复造轮子的产物,而是站在巨人的肩膀上,整合现有的优秀组件,旨在帮助使用者以最简单的方式构建稳定可靠的服务。
正如它的名字一般,你可以把它视作自己在golang生态的一个开始。go-sail将助力你从轻出发,扬帆起航。
go-sail is a lightweight progressive web framework implemented using golang language. It is not the product of reinventing the wheel, but stands on the shoulders of giants and integrates existing excellent components to help users build stable and reliable services in the simplest way. As its name suggests, you can regard it as the beginning of your own journey in the golang ecosystem. go-sail will help you start lightly and set sail.
go version >= 1.19
go get -u github.com/keepchen/go-sail/v3
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/keepchen/go-sail/v3/constants"
"github.com/keepchen/go-sail/v3/http/api"
"github.com/keepchen/go-sail/v3/lib/logger"
"github.com/keepchen/go-sail/v3/sail"
"github.com/keepchen/go-sail/v3/sail/config"
)
var (
conf = &config.Config{
LoggerConf: logger.Conf{
Filename: "examples/logs/running.log",
},
HttpServer: config.HttpServerConf{
Debug: true,
Addr: ":8000",
Swagger: config.SwaggerConf{
Enable: true,
RedocUIPath: "examples/pkg/app/user/http/docs/docs.html",
JsonPath: "examples/pkg/app/user/http/docs/swagger.json",
},
Prometheus: config.PrometheusConf{
Enable: true,
Addr: ":19100",
AccessPath: "/metrics",
},
},
}
apiOption = &api.Option{
EmptyDataStruct: api.DefaultEmptyDataStructObject,
ErrNoneCode: constants.CodeType(200),
ErrNoneCodeMsg: "SUCCEED",
ForceHttpCode200: true,
}
registerRoutes = func(ginEngine *gin.Engine) {
ginEngine.GET("/hello", func(c *gin.Context){
c.String(http.StatusOK, "%s", "hello, world!")
})
}
before = func() {
fmt.Println("call user function [before] to do something...")
}
after = func() {
fmt.Println("call user function [after] to do something...")
}
)
func main() {
sail.WakeupHttp("go-sail", conf).
SetupApiOption(apiOption).
Hook(registerRoutes, before, after).
Launch()
}
当你看到终端如下图所示内容就表示服务启动成功了:
Console screenshot after launched like this:

go-sail启动时,会根据配置文件启动相应的应用组件,可使用
sail关键字统一获取
import (
"github.com/keepchen/go-sail/v3/sail"
)
//日志组件
sail.GetLogger()
//数据库连接(读、写实例)
sail.GetDB()
//redis连接(standalone模式)
sail.GetRedis()
//redis连接(cluster模式)
sail.GetRedisCluster()
//nats连接
sail.GetNats()
//获取kafka完整连接实例
sail.GetKafkaInstance()
//获取etcd连接实例
sail.GetEtcdInstance()
更多组件持续开发中,也欢迎大家提PR👏🏻👏🏻 PR is welcome👏🏻👏🏻
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/keepchen/go-sail/v3/constants"
"github.com/keepchen/go-sail/v3/sail"
)
//handler
func SayHello(c *gin.Context) {
sail.Response(c).Builder(constants.ErrNone, nil, "OK").Send()
}
返回结果如下:
{
"requestId": "8d33237f-ce1d-4f61-a9a0-fd6e4fd5211b",
"code": 200,
"success": true,
"message": "OK",
"ts": 1703758125020,
"data": "hello, go-sail"
}
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/keepchen/go-sail/v3/constants"
"github.com/keepchen/go-sail/v3/http/pojo/dto"
"github.com/keepchen/go-sail/v3/sail"
)
type UserInfo struct {
dto.Base
Data struct {
Nickname string `json:"nickname" validate:"required" format:"string"` //nickname
Age int `json:"int" validate:"required" format:"number"` //age
} `json:"data" validate:"required"` //body data
}
//implement dto.IResponse interface
func (v UserInfo) GetData() interface{} {
return v.Data
}
//handler
func GetUserInfo(c *gin.Context) {
var resp UserInfo
resp.Data.Nickname = "go-sail"
resp.Data.Age = 18
sail.Response(c).Builder(constants.ErrNone, resp).Send()
}
返回结果如下:
{
"requestId": "2ea1c7db-cd13-4bcb-9c2d-cb129107998e",
"code": 200,
"success": true,
"message": "SUCCEED",
"ts": 1703758452250,
"data": {
"nickname": "go-sail",
"age": 18
}
}
import "github.com/keepchen/go-sail/v3/constants"
const (
ErrNone = CodeType(200) //没有错误,占位
ErrStatusGatewayTimeoutTimeOut = CodeType(504) //超时
ErrInternalSeverError = CodeType(999999) //服务器内部错误
ErrRequestParamsInvalid = CodeType(100000) //请求参数有误
ErrAuthorizationTokenInvalid = CodeType(100001) //令牌已失效
ErrTypos = CodeType(777777) //拼写错误
)
// 错误码信息表
//
// READONLY for concurrency safety
var codeMsgMap = map[CodeType]string{
ErrNone: "SUCCESS",
ErrStatusGatewayTimeoutTimeOut: "Timeout",
ErrInternalSeverError: "Internal server error",
ErrRequestParamsInvalid: "Bad request parameters",
ErrAuthorizationTokenInvalid: "Token invalid",
ErrTypos: "Oops! Typos",
}
for code, msg := range codeMsgMap {
constants.RegisterCode(code, msg)
}
使用自定义错误码
sail.Response(c).Builder(constants.ErrTypos, resp).Send()
apiOption = &api.Option{
EmptyDataStruct: api.DefaultEmptyDataStructObject,
ErrNoneCode: constants.CodeType(200),
ErrNoneCodeMsg: "SUCCEED",
ForceHttpCode200: true,
}
其中:
ErrNoneCode为设定无错误的错误码,框架默认为0。ErrNoneCodeMsg为设定无错误的消息提示,框架默认为SUCCESS。ForceHttpCode200为设定是否所有错误码均返回200的http状态码,框架默认为凡是错误则http状态码为非200。EmptyDataStruct为设定响应字段data为空时的值,框架默认为null。日志组件封装的是Uber提供的zap日志库,go-sail封装好的logger提供了以下功能:
使用示例
//简单信息打印
sail.GetLogger().Info("print something...")
//带字段的信息打印
sail.GetLogger().Error("something went wrong...", zap.Errors("errors", []error{err1, err2}))
//json格式化后的信息打印
sail.GetLogger().Error("something went wrong...",
zap.String("value", logger.MarshalInterfaceValue(SomethingStruct)),
zap.Errors("errors", []error{err1, err2}))
//指定打印到某个模块文件
sail.GetLogger("order-services").Info("order info...")
数据库组件是对gorm的简单封装,实现了简单的读写实例分离(在调用时显式指定),并将其默认的logger整合到go-sail的logger组件中。
使用示例
// 调用读写实例
reader, writer := sail.GetDB()
reader.Where("id = ?", 1).First(&order)
writer.Where("id = ?", 1).Updates(&Order{Status: 1})
// 直接调用读实例
sail.GetDBR().Where("id = ?", 1).First(&order)
// 直接调用写实例
sail.GetDBW().Where("id = ?", 1).Updates(&Order{Status: 1})
redis组件是对go-redis的简单封装,可以根据配置自行决定使用standalone模式还是cluster模式的连接。go-sail的redis导出器使用到的就是这里的组件。
使用示例
// 调用redis单实例连接
sail.GetRedis().Set(ctx, "order-info", "1", time.Minute * 10)
// 调用redis集群实例连接
sail.GetRedisCluster().Set(ctx, "order-info", "1", time.Minute * 10)
由go程序自行管理
// 设置key-value
cache.Set("key", "value", time.Seconds* 5)
// 删除key
cache.Forget("key")
// 设置过期时间
cache.Expire("key", time.Seconds* 5)
// 初始化list
cache.InitList()
// 新建list
cache.NewList("new-list-key")
// 向左追加
cache.GetListInstance().LPush("a")
cache.GetListInstance().LPush(1)
cache.GetListInstance().LPush(map[string]string{"name":"go-sail"})
// 向右追加
cache.GetListInstance().RPush("a")
cache.GetListInstance().RPush(1)
cache.GetListInstance().RPush(map[string]string{"name":"go-sail"})
// 从左侧取出
cache.GetListInstance().LPop()
// 从右侧取出
cache.GetListInstance().RPop()
// 清空并销毁list
cache.GetListInstance().FlushDB()
消息队列组件目前封装了两个库支持:
1.简单封装的nats连接客户端。go-sail日志组件的nats导出器使用到的就是这里的组件。
2.简单封装的kafka连接客户端。
配置中心组件目前封装了两个库支持:
1.简单封装的nacos客户端,由于自身业务的发展历史,配置中心使用的java生态的nacos而不是etcd,因此go-sail封装的是nacos的连接客户端。实现了一些常用功能的语法糖:监听配置、注册/下线服务、获取健康实例。
2.简单封装的etcd客户端。
使用示例
注意:需要先调用nacos.InitClient()方法进行初始化
// 使用nacos监听配置文件
nacos.ListenConfig("order", "order.yaml", orderConfig, "yaml")
localIP, _ := utils.GetLocalIP()
// 使用nacos注册服务
ok, err := nacos.RegisterService("order", "order-service", localIP, 8080, nil)
// 使用nacos下线服务
ok, err := nacos.UnregisterService("order", "order-service", localIP, 8080)
// 使用etcd监听key
fn := func(k, v []byte) {
config.XX = v
}
etcd.Watch("order-key", fn)
go-sail封装了一些常用的路由中间件,包括:
使用示例
ginEngine.Use(
middleware.RequestEntry(), // <- 前置注入上下文
middleware.WithCors(), // <- 开启跨域
middleware.PrometheusExporter()) // <- 配置Prometheus收集器
计划任务分为两种,一种是常规时间间隔型,这一类使用golang原生计时器(time.Ticker)实现;一种是Linux Crontab风格的表达式型,这一类封装的robfig/cron库来实现的。
使用示例
fn := func() {
fmt.Println("to do something...")
}
// 启动任务
schedule.Job("notify-order-status", fn).EveryFiveSeconds()
// 启动任务(单态运行)
schedule.Job("notify-order-status", fn).WithoutOverlapping().EveryFiveSeconds()
// 启动任务(Linux crontab风格的时间配置)
schedule.Job("notify-order-status", fn).WithoutOverlapping().RunAt(schedule.FirstDayOfMonth)
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。