惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Azure Blog
Microsoft Azure Blog
Google Online Security Blog
Google Online Security Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Simon Willison's Weblog
Simon Willison's Weblog
T
Threat Research - Cisco Blogs
C
CXSECURITY Database RSS Feed - CXSecurity.com
L
Lohrmann on Cybersecurity
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Forbes - Security
Forbes - Security
P
Palo Alto Networks Blog
Schneier on Security
Schneier on Security
S
Schneier on Security
T
Tor Project blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
WordPress大学
WordPress大学
The Hacker News
The Hacker News
Hacker News - Newest:
Hacker News - Newest: "LLM"
罗磊的独立博客
Application and Cybersecurity Blog
Application and Cybersecurity Blog
F
Fortinet All Blogs
博客园 - 三生石上(FineUI控件)
小众软件
小众软件
C
Check Point Blog
Stack Overflow Blog
Stack Overflow Blog
Blog — PlanetScale
Blog — PlanetScale
雷峰网
雷峰网
S
Security @ Cisco Blogs
PCI Perspectives
PCI Perspectives
Spread Privacy
Spread Privacy
W
WeLiveSecurity
SecWiki News
SecWiki News
A
About on SuperTechFans
H
Help Net Security
博客园 - 司徒正美
Recent Commits to openclaw:main
Recent Commits to openclaw:main
爱范儿
爱范儿
S
Securelist
M
MIT News - Artificial intelligence
云风的 BLOG
云风的 BLOG
月光博客
月光博客
Jina AI
Jina AI
博客园 - 叶小钗
Vercel News
Vercel News
阮一峰的网络日志
阮一峰的网络日志
Recent Announcements
Recent Announcements
S
Secure Thoughts
The Cloudflare Blog
美团技术团队
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

希仁之拥

领克900半年使用体验 | 希仁之拥的博客 Ubuntu 26.04 Desktop使用体验 | 希仁之拥的博客 【转载】谈谈不受欢迎的博客技术特征 | 希仁之拥的博客 【转载】ClaudeCode 你想知道的所有秘密,源码深度研究报告 | 希仁之拥的博客 2025年年终总结 | 希仁之拥的博客 集成和使用Openclaw后的思考 | 希仁之拥的博客 我买了领克900 | 希仁之拥的博客 服务器性能优化之io拷贝 | 希仁之拥的博客 Go-Sail导航站上线啦 | 希仁之拥的博客 今年国庆的一些感受 [2025] | 希仁之拥的博客 在Deepin 25上配置forticlient | 希仁之拥的博客 分享一些酷酷的站点 [20250908] | 希仁之拥的博客 Go-Sail发布v3.0.6版本了 | 希仁之拥的博客 我对V2EX发布$V2EX讨论的一些感受 | 希仁之拥的博客 如何让Stripe支持支付宝和微信支付 | 希仁之拥的博客 2025上半年里程碑 | 希仁之拥的博客 GitLab+Drone使用体验 | 希仁之拥的博客 四姑娘山之旅 | 希仁之拥的博客 近来帮同事做性能优化的过程回顾 | 希仁之拥的博客 聊聊接口的返回数据结构 | 希仁之拥的博客 由GORM的Updates语法糖 我把 Go-Sail 的文档站更新了 | 希仁之拥的博客 这就是我为什么讨厌拼多多 | 希仁之拥的博客 元旦快乐~ | 希仁之拥的博客 致敬还在写博客的我们 | 希仁之拥的博客 逐步的把图片资源迁移到星光图床上 | 希仁之拥的博客 帮弟弟配了一台mini主机 | 希仁之拥的博客 国庆的一些碎碎念 | 希仁之拥的博客 就这一刻而言,我觉得科技冷冰冰的。 | 希仁之拥的博客 如何使用acme.sh自动续签证书 | 希仁之拥的博客 Go-Sail的文档站点搭建好了 | 希仁之拥的博客 我的第一个谷歌浏览器插件发布成功啦! | 希仁之拥的博客 流芳 | 希仁之拥的博客 第一次开发微信小程序 | 希仁之拥的博客 春节快乐 | 希仁之拥的博客 我把博客站点重写了 | 希仁之拥的博客 阆中古城 | 希仁之拥的博客 入了把Keydous NJ80 | 希仁之拥的博客 Mac上有用的知识 | 希仁之拥的博客 记一次服务器被攻击 | 希仁之拥的博客 Ubuntu server不停机扩容系统盘 | 希仁之拥的博客 端午安康 | 希仁之拥的博客 搭建hadoop伪分布式集群总结 | 希仁之拥的博客 在goa框架中,如何访问原始请求数据 | 希仁之拥的博客 postgresql相关操作笔记 | 希仁之拥的博客 golang解析带命名空间的xml数据(带冒号的节点或属性名) | 希仁之拥的博客 【转载】解决deepin环境下wine QQ无法接收图片问题 | 希仁之拥的博客 杂项笔记 | 希仁之拥的博客 Mac如何修改通知横幅消失时间 | 希仁之拥的博客 Golang校验结构体是否实现了接口 | 希仁之拥的博客 关于deepin os的大半年使用体验 | 希仁之拥的博客 有态度的翻唱 | 希仁之拥的博客 【转载】Golang 在 Mac、Linux、Windows 下如何交叉编译 | 希仁之拥的博客 和幺幺一起成长的半年时光 | 希仁之拥的博客 Element UI中message消息提示实现原理 | 希仁之拥的博客 从predis组件开始了解如何与redis服务交互 | 希仁之拥的博客 【转载】 尤雨溪谈Vue.js :缔造自由与真我 | 希仁之拥的博客 【转载】百万 Go TCP 连接的思考: epoll方式减少资源占用 | 希仁之拥的博客 【分享】移除git中的commit历史(保留文件,仅移除提交历史信息) | 希仁之拥的博客 shell学习笔记 | 希仁之拥的博客 【分享】Ruby 之父松本行弘:程序员的乐趣、存在感与平衡 | 希仁之拥的博客 安利一下我用过的一些sublime text 3工具、插件及配置 | 希仁之拥的博客 PHP处理苹果内购数据验证 | 希仁之拥的博客 PHP获取XML所有节点名称 | 希仁之拥的博客 Sentinel::check() 在Laravel5.3版本之后总是返回false的解决方案 | 希仁之拥的博客 吃货的美食集,持续更新~ | 希仁之拥的博客 第二十一届成都汽车博览会 | 希仁之拥的博客 马云宣布1年后卸任集团董事局主席 由CEO张勇接任 | 希仁之拥的博客 nginx配置非443端口的https连接 | 希仁之拥的博客 PHP7 异常类型列表 | 希仁之拥的博客 【分享】《燃点》:不被嘲笑的愿望是不值得去实现的|一部属于创业者的电影 | 希仁之拥的博客 【分享】前端资源加载优先级[译] | 希仁之拥的博客 【小技巧】Laravel动态配置多个数据库连接 | 希仁之拥的博客 php使用ftp上传、下载文件 | 希仁之拥的博客 php使用mysqlnd扩展进行mysql异步并行查询 | 希仁之拥的博客 基于以太坊区块链,使用web3.js在geth服务器上执行合约方法以及事件监听 | 希仁之拥的博客 Smartisan OS | 希仁之拥的博客 母校 | 希仁之拥的博客 php后期静态绑定 | 希仁之拥的博客 [mysql优化]子查询与连接查询 | 希仁之拥的博客 PHP常见运行模式及原理 | 希仁之拥的博客 mysql用户与授权 | 希仁之拥的博客 如何在ubuntu14.04上安装composer | 希仁之拥的博客 关于mews/captcha for laravel存在method [validateCaptcha] doesn't exists解决方法 | 希仁之拥的博客
我开源了一个golang的web框架 | 希仁之拥的博客
希仁之拥 · 2023-12-19 · via 希仁之拥

文档站点已发布,欢迎访问:https://go-sail.keepchen.com

go-sail是什么? Whats the go-sail?

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.

如何使用? How to use?

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 launch.png

特性 Features

  • 获取组件 Get components

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👏🏻👏🏻

  • 返回响应 Response
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"
}
  • 返回响应实体 Response (entity)
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()
  • 修改response默认行为
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

已集成的组件

日志 Logger

日志组件封装的是Uber提供的zap日志库,go-sail封装好的logger提供了以下功能:

  • 日志轮转
    支持按文件大小和文件数量上限进行留存文件,防止写满磁盘。
  • 日志导出器
    目前已内置了redis、nats-io、kafka三种方式的导出器,也支持在初始化的时候设置其他自定义的导出器。
  • 按模块写入不同的日志文件
    可以在初始化的时候,根据不同的逻辑或需求区分日志文件。

使用示例

//简单信息打印
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...")

数据库 Database

数据库组件是对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

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)

本地缓存 Local Cache

由go程序自行管理

  • key-value
// 设置key-value
cache.Set("key", "value", time.Seconds* 5)

// 删除key
cache.Forget("key")

// 设置过期时间
cache.Expire("key", time.Seconds* 5)
  • list
// 初始化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()

消息队列 Message Queue

消息队列组件目前封装了两个库支持:
1.简单封装的nats连接客户端。go-sail日志组件的nats导出器使用到的就是这里的组件。
2.简单封装的kafka连接客户端。

配置中心 Configuration Center

配置中心组件目前封装了两个库支持:
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)

路由中间件 Route Middleware

go-sail封装了一些常用的路由中间件,包括:

  • 跨域配置
  • Prometheus指标收集
  • 请求载荷打印
  • 解析客户端语言
  • 请求入口
    (注入请求到达时间、注入请求id(用于调用链日志查询调试)、注入带请求id的日志组件。)

使用示例

ginEngine.Use(
middleware.RequestEntry(), // <- 前置注入上下文
middleware.WithCors(),  // <- 开启跨域
middleware.PrometheusExporter()) // <- 配置Prometheus收集器

计划任务 Task Schedule

计划任务分为两种,一种是常规时间间隔型,这一类使用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)

工具类

  • MD5摘要计算
  • SM4国标加解密
  • AES加解密
  • Base64转义
  • File文件操作
    (保存、读取、逐行读取、存在检测、扩展名获取等)
  • IP地址获取
    (获取本地ip地址(全局单播地址))
  • Redis分布式锁
    (锁定、释放、自动续期)
  • RSA加签验签
  • Signal信号量监听
  • String字符串处理
    (随机字符串、包装公私钥)
  • Time时区封装
  • Webpush浏览器消息推送
  • Datetime时间解析、格式化
  • Swagger文档注释生成
  • Version软件版本打印

框架常量

  • 错误码
    内置错误码、注册自定义错误码,框架的响应组件使用的就是这里的错误码。
  • 时区
    从-11 ~ +12时区和默认时区。

其他插件 Other Plugins

README.md

源代码仓库地址 Source Code Repo

keepchen/go-sail

Go CodeQL Go Report Card
GitHub Repo stars GitHub watchers GitHub forks Latest release


980_120.png(via stardots.io)