Compare commits

...

64 Commits

Author SHA1 Message Date
李光春 9ffa6c2c05 - update
1 year ago
李光春 cd06d7d7e9 - update
1 year ago
李光春 7f12ec6a01 - add next_run_time
2 years ago
李光春 9469dcc3d6 - update config
2 years ago
李光春 78ed7bdfd8 - update time
2 years ago
李光春 3e404e4454 - update ip
2 years ago
李光春 bb6f68d444 - update system
2 years ago
李光春 60b50bf6f8 - update
2 years ago
李光春 11884afa4f - update
2 years ago
李光春 625deb44ca - update
2 years ago
李光春 d84a9656a7 - update ip
2 years ago
李光春 3d5d7c3a7d - update context
2 years ago
李光春 3a9b47cc7f - update db
2 years ago
李光春 5d910fb616 - update db
2 years ago
李光春 a2f2689d10 - update mongo
2 years ago
李光春 883fcec2cb - update mongo
2 years ago
李光春 2c6cf23f1b - update
2 years ago
李光春 e015e8f77d - update ip
2 years ago
李光春 d7a75aaac3 - update config
2 years ago
李光春 e4858bb504 - update lock
2 years ago
李光春 44fdf7efe0 - update lock
2 years ago
李光春 dcbcbbe079 - update fun
2 years ago
李光春 21c597f0f5 - update
2 years ago
李光春 19adea0395 - 移除mongo
2 years ago
李光春 882cb83ec8 - update mongo
2 years ago
李光春 1d5d63b0de - update mongo
2 years ago
李光春 4e1dc1e1b1 - update
2 years ago
李光春 b53a7490ce - update redis
2 years ago
李光春 917a716c90 - update
2 years ago
李光春 fbb4ccb197 - update
2 years ago
李光春 1bb38af4d7 - update gorm model
2 years ago
李光春 6b3b29d0c2 - update gorm model
2 years ago
李光春 1d1be8330b - update run_id
2 years ago
李光春 79b18ef741 - update error
2 years ago
李光春 049585a1fa - update lock
2 years ago
李光春 bab21c66bc - update lock
2 years ago
李光春 c66aa424b3 - update mongo model
2 years ago
李光春 649ace79cf - update mongo model
2 years ago
李光春 f7b6e6c8bc - update
2 years ago
李光春 d57ee8daaf - add vendor
2 years ago
李光春 7cf8704ec8 - update model
2 years ago
李光春 48494e8a9e - update model
2 years ago
李光春 a89dea2d47 - add mongo record
2 years ago
李光春 082767de39 - add task_issue_record
2 years ago
李光春 a20d2b6d80 - add task_issue_record
2 years ago
李光春 5014f8a92a - update log
2 years ago
李光春 44366e6c9a - update log
2 years ago
李光春 8a56a93570 - add mongo
2 years ago
李光春 7a14377892 - update lock
2 years ago
李光春 d5f6ecadda - add lock id
2 years ago
李光春 8e88606e91 - update model
2 years ago
李光春 08ee16063c - update model
2 years ago
李光春 a03babb8b6 - add StartTask func
2 years ago
李光春 5e80f3edf0 - update run
2 years ago
李光春 3705004c3d - update create wait
2 years ago
李光春 6a2a5447b9 - update get
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 7bfa68d812 - update GetSubscribeClientList
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 7b6ca6f957 - update redis get
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 663eab78a8 - update redis
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 84d516deff - update redis
continuous-integration/drone/push Build is passing Details
continuous-integration/drone/tag Build is passing Details
2 years ago
李光春 b76dd4fb57 - update ctx
2 years ago
李光春 590b7bf7b2 - update
continuous-integration/drone/tag Build is failing Details
continuous-integration/drone/push Build was killed Details
2 years ago
李光春 68cafc549f - update
continuous-integration/drone/push Build was killed Details
continuous-integration/drone/tag Build was killed Details
2 years ago
李光春 1ebc8cf708 - add log
continuous-integration/drone/push Build is failing Details
continuous-integration/drone/tag Build is failing Details
2 years ago

4
.gitignore vendored

@ -6,7 +6,5 @@
*.log
gomod.sh
grpc_build.sh
etcd_server_test.go
etcd_worker_test.go
grpc_server_test.go
*_test.go
/vendor/

@ -0,0 +1,130 @@
package gojobs
import (
"context"
"go.dtapp.net/dorm"
"go.dtapp.net/goip"
"go.dtapp.net/golog"
)
// 前缀
// lockKeyPrefix 锁Key前缀 xxx_lock
// lockKeySeparator 锁Key分隔符 :
// cornKeyPrefix 任务Key前缀 xxx_cron
// cornKeyCustom 任务Key自定义 xxx_cron_自定义 xxx_cron_自定义_*
type redisPrefixFun func() (lockKeyPrefix, lockKeySeparator, cornKeyPrefix, cornKeyCustom string)
// ClientConfig 实例配置
type ClientConfig struct {
GormClientFun dorm.GormClientFun // 数据库驱动
MongoClientFun dorm.MongoClientFun // 数据库驱动
RedisClientFun dorm.RedisClientFun // 数据库驱动
RedisPrefixFun redisPrefixFun // 前缀
ZapLog *golog.ZapLog // 日志服务
CurrentIp string // 当前ip
}
// Client 实例
type Client struct {
gormClient *dorm.GormClient // 数据库
mongoClient *dorm.MongoClient // 数据库
zapLog *golog.ZapLog // 日志服务
config struct {
systemHostname string // 主机名
systemOs string // 系统类型
systemVersion string // 系统版本
systemKernel string // 系统内核
systemKernelVersion string // 系统内核版本
systemBootTime uint64 // 系统开机时间
cpuCores int // CPU核数
cpuModelName string // CPU型号名称
cpuMhz float64 // CPU兆赫
systemInsideIp string // 内网ip
systemOutsideIp string // 外网ip
goVersion string // go版本
sdkVersion string // sdk版本
mongoVersion string // mongo版本
mongoSdkVersion string // mongo sdk版本
redisVersion string // redis版本
redisSdkVersion string // redis sdk版本
logVersion string // log版本
}
cache struct {
redisClient *dorm.RedisClient // 数据库
redisLockClient *dorm.RedisClientLock // 锁服务
lockKeyPrefix string // 锁Key前缀 xxx_lock
lockKeySeparator string // 锁Key分隔符 :
cornKeyPrefix string // 任务Key前缀 xxx_cron
cornKeyCustom string // 任务Key自定义
}
mongoConfig struct {
stats bool // 状态
databaseName string // 库名
}
}
// NewClient 创建实例
func NewClient(config *ClientConfig) (*Client, error) {
var ctx = context.Background()
c := &Client{}
c.zapLog = config.ZapLog
if config.CurrentIp != "" && config.CurrentIp != "0.0.0.0" {
c.config.systemOutsideIp = config.CurrentIp
}
c.config.systemOutsideIp = goip.IsIp(c.config.systemOutsideIp)
if c.config.systemOutsideIp == "" {
return nil, currentIpNoConfig
}
// 配置缓存
redisClient := config.RedisClientFun()
if redisClient != nil && redisClient.GetDb() != nil {
c.cache.redisClient = redisClient
c.cache.redisLockClient = c.cache.redisClient.NewLock()
} else {
return nil, redisPrefixFunNoConfig
}
// 配置缓存前缀
c.cache.lockKeyPrefix, c.cache.lockKeySeparator, c.cache.cornKeyPrefix, c.cache.cornKeyCustom = config.RedisPrefixFun()
if c.cache.lockKeyPrefix == "" || c.cache.lockKeySeparator == "" || c.cache.cornKeyPrefix == "" || c.cache.cornKeyCustom == "" {
return nil, redisPrefixFunNoConfig
}
// 配置信息
c.setConfig(ctx)
// 配置关系数据库
gormClient := config.GormClientFun()
if gormClient != nil && gormClient.GetDb() != nil {
c.gormClient = gormClient
c.autoMigrateTask(ctx)
c.autoMigrateTaskLog(ctx)
} else {
return nil, gormClientFunNoConfig
}
// 配置非关系数据库
mongoClient, databaseName := config.MongoClientFun()
if mongoClient != nil && mongoClient.GetDb() != nil {
c.mongoClient = mongoClient
if databaseName == "" {
return nil, mongoClientFunNoConfig
} else {
c.mongoConfig.databaseName = databaseName
}
TaskLog{}.createCollection(ctx, c.zapLog, c.mongoClient, c.mongoConfig.databaseName)
TaskLog{}.createIndexes(ctx, c.zapLog, c.mongoClient, c.mongoConfig.databaseName)
c.mongoConfig.stats = true
}
return c, nil
}

@ -0,0 +1,88 @@
package gojobs
import (
"context"
"github.com/go-redis/redis/v9"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/host"
"go.dtapp.net/goip"
"go.dtapp.net/golog"
"go.mongodb.org/mongo-driver/version"
"log"
"runtime"
)
type systemResult struct {
SystemHostname string // 主机名
SystemOs string // 系统类型
SystemVersion string // 系统版本
SystemKernel string // 系统内核
SystemKernelVersion string // 系统内核版本
SystemUpTime uint64 // 系统运行时间
SystemBootTime uint64 // 系统开机时间
CpuCores int // CPU核数
CpuModelName string // CPU型号名称
CpuMhz float64 // CPU兆赫
}
func getSystem() (result systemResult) {
hInfo, err := host.Info()
if err != nil {
log.Printf("getSystem.host.Info%s\n", err)
}
result.SystemHostname = hInfo.Hostname
result.SystemOs = hInfo.OS
result.SystemVersion = hInfo.PlatformVersion
result.SystemKernel = hInfo.KernelArch
result.SystemKernelVersion = hInfo.KernelVersion
result.SystemUpTime = hInfo.Uptime
if hInfo.BootTime != 0 {
result.SystemBootTime = hInfo.BootTime
}
hCpu, err := cpu.Times(true)
if err != nil {
log.Printf("getSystem.cpu.Times%s\n", err)
}
result.CpuCores = len(hCpu)
cInfo, err := cpu.Info()
if err != nil {
log.Printf("getSystem.cpu.Info%s\n", err)
}
if len(cInfo) > 0 {
result.CpuModelName = cInfo[0].ModelName
result.CpuMhz = cInfo[0].Mhz
}
return result
}
func (c *Client) setConfig(ctx context.Context) {
info := getSystem()
c.config.systemHostname = info.SystemHostname
c.config.systemOs = info.SystemOs
c.config.systemVersion = info.SystemVersion
c.config.systemKernel = info.SystemKernel
c.config.systemKernelVersion = info.SystemKernelVersion
c.config.systemBootTime = info.SystemBootTime
c.config.cpuCores = info.CpuCores
c.config.cpuModelName = info.CpuModelName
c.config.cpuMhz = info.CpuMhz
c.config.systemInsideIp = goip.GetInsideIp(ctx)
c.config.sdkVersion = Version
c.config.goVersion = runtime.Version()
c.config.mongoSdkVersion = version.Driver
c.config.redisSdkVersion = redis.Version()
c.config.logVersion = golog.Version
}

@ -1,3 +1,6 @@
package gojobs
const Version = "1.0.56"
const (
Version = "1.0.113"
SpecifyIpNull = "0.0.0.0"
)

@ -1,113 +1,15 @@
package gojobs
import (
"context"
"errors"
"fmt"
"go.dtapp.net/gojobs/jobs_gorm_model"
"go.dtapp.net/gostring"
"go.dtapp.net/gotime"
"gorm.io/gorm"
"log"
)
// Run 运行
func (j *JobsGorm) Run(info jobs_gorm_model.Task, status int, desc string) {
// 请求函数记录
err := j.gormClient.Db.Create(&jobs_gorm_model.TaskLog{
TaskId: info.Id,
StatusCode: status,
Desc: desc,
Version: j.config.runVersion,
}).Error
if err != nil {
log.Println("statusCreate", err.Error())
}
if status == 0 {
err = j.EditTask(j.gormClient.Db, info.Id).
Select("run_id").
Updates(jobs_gorm_model.Task{
RunId: gostring.GetUuId(),
}).Error
if err != nil {
log.Println("statusEdit", err.Error())
}
return
}
// 任务
if status == CodeSuccess {
// 执行成功
err = j.EditTask(j.gormClient.Db, info.Id).
Select("status_desc", "number", "run_id", "updated_ip", "result").
Updates(jobs_gorm_model.Task{
StatusDesc: "执行成功",
Number: info.Number + 1,
RunId: gostring.GetUuId(),
UpdatedIp: j.config.outsideIp,
Result: desc,
}).Error
if err != nil {
log.Println("statusEdit", err.Error())
}
}
if status == CodeEnd {
// 执行成功、提前结束
err = j.EditTask(j.gormClient.Db, info.Id).
Select("status", "status_desc", "number", "updated_ip", "result").
Updates(jobs_gorm_model.Task{
Status: TASK_SUCCESS,
StatusDesc: "结束执行",
Number: info.Number + 1,
UpdatedIp: j.config.outsideIp,
Result: desc,
}).Error
if err != nil {
log.Println("statusEdit", err.Error())
}
}
if status == CodeError {
// 执行失败
err = j.EditTask(j.gormClient.Db, info.Id).
Select("status_desc", "number", "run_id", "updated_ip", "result").
Updates(jobs_gorm_model.Task{
StatusDesc: "执行失败",
Number: info.Number + 1,
RunId: gostring.GetUuId(),
UpdatedIp: j.config.outsideIp,
Result: desc,
}).Error
if err != nil {
log.Println("statusEdit", err.Error())
}
}
if info.MaxNumber != 0 {
if info.Number+1 >= info.MaxNumber {
// 关闭执行
err = j.EditTask(j.gormClient.Db, info.Id).
Select("status").
Updates(jobs_gorm_model.Task{
Status: TASK_TIMEOUT,
}).Error
if err != nil {
log.Println("statusEdit", err.Error())
}
}
}
}
// RunAddLog 任务执行日志
func (j *JobsGorm) RunAddLog(id uint, runId string) error {
return j.gormClient.Db.Create(&jobs_gorm_model.TaskLogRun{
TaskId: id,
RunId: runId,
InsideIp: j.config.insideIp,
OutsideIp: j.config.outsideIp,
Os: j.config.os,
Arch: j.config.arch,
Gomaxprocs: j.config.maxProCs,
GoVersion: j.config.version,
MacAddrs: j.config.macAddrS,
}).Error
}
// ConfigCreateInCustomId 创建正在运行任务
type ConfigCreateInCustomId struct {
Tx *gorm.DB // 驱动
@ -118,13 +20,13 @@ type ConfigCreateInCustomId struct {
Type string // 类型
TypeName string // 类型名称
SpecifyIp string // 指定外网IP
CurrentIp string // 当前ip
CurrentIp string // 当前外网IP
}
// CreateInCustomId 创建正在运行任务
func (j *JobsGorm) CreateInCustomId(config *ConfigCreateInCustomId) error {
func (c *Client) CreateInCustomId(ctx context.Context, config *ConfigCreateInCustomId) error {
if config.CurrentIp == "" {
config.CurrentIp = j.config.outsideIp
config.CurrentIp = c.config.systemOutsideIp
}
err := config.Tx.Create(&jobs_gorm_model.Task{
Status: TASK_IN,
@ -139,6 +41,7 @@ func (j *JobsGorm) CreateInCustomId(config *ConfigCreateInCustomId) error {
CreatedIp: config.CurrentIp,
SpecifyIp: config.SpecifyIp,
UpdatedIp: config.CurrentIp,
NextRunTime: gotime.Current().AfterSeconds(config.Frequency).Time,
}).Error
if err != nil {
return errors.New(fmt.Sprintf("创建[%s@%s]任务失败:%s", config.CustomId, config.Type, err.Error()))
@ -156,17 +59,17 @@ type ConfigCreateInCustomIdOnly struct {
Type string // 类型
TypeName string // 类型名称
SpecifyIp string // 指定外网IP
CurrentIp string // 当前ip
CurrentIp string // 当前外网IP
}
// CreateInCustomIdOnly 创建正在运行唯一任务
func (j *JobsGorm) CreateInCustomIdOnly(config *ConfigCreateInCustomIdOnly) error {
query := j.TaskTypeTakeIn(config.Tx, config.CustomId, config.Type)
func (c *Client) CreateInCustomIdOnly(ctx context.Context, config *ConfigCreateInCustomIdOnly) error {
query := c.TaskTypeTakeIn(ctx, config.Tx, config.CustomId, config.Type)
if query.Id != 0 {
return errors.New(fmt.Sprintf("%d:[%s@%s]任务已存在", query.Id, config.CustomId, config.Type))
return TaskIsExist
}
if config.CurrentIp == "" {
config.CurrentIp = j.config.outsideIp
config.CurrentIp = c.config.systemOutsideIp
}
err := config.Tx.Create(&jobs_gorm_model.Task{
Status: TASK_IN,
@ -181,6 +84,7 @@ func (j *JobsGorm) CreateInCustomIdOnly(config *ConfigCreateInCustomIdOnly) erro
CreatedIp: config.CurrentIp,
SpecifyIp: config.SpecifyIp,
UpdatedIp: config.CurrentIp,
NextRunTime: gotime.Current().AfterSeconds(config.Frequency).Time,
}).Error
if err != nil {
return errors.New(fmt.Sprintf("创建[%s@%s]任务失败:%s", config.CustomId, config.Type, err.Error()))
@ -199,13 +103,13 @@ type ConfigCreateInCustomIdMaxNumber struct {
Type string // 类型
TypeName string // 类型名称
SpecifyIp string // 指定外网IP
CurrentIp string // 当前ip
CurrentIp string // 当前外网IP
}
// CreateInCustomIdMaxNumber 创建正在运行任务并限制数量
func (j *JobsGorm) CreateInCustomIdMaxNumber(config *ConfigCreateInCustomIdMaxNumber) error {
func (c *Client) CreateInCustomIdMaxNumber(ctx context.Context, config *ConfigCreateInCustomIdMaxNumber) error {
if config.CurrentIp == "" {
config.CurrentIp = j.config.outsideIp
config.CurrentIp = c.config.systemOutsideIp
}
err := config.Tx.Create(&jobs_gorm_model.Task{
Status: TASK_IN,
@ -221,6 +125,7 @@ func (j *JobsGorm) CreateInCustomIdMaxNumber(config *ConfigCreateInCustomIdMaxNu
CreatedIp: config.CurrentIp,
SpecifyIp: config.SpecifyIp,
UpdatedIp: config.CurrentIp,
NextRunTime: gotime.Current().AfterSeconds(config.Frequency).Time,
}).Error
if err != nil {
return errors.New(fmt.Sprintf("创建[%s@%s]任务失败:%s", config.CustomId, config.Type, err.Error()))
@ -239,17 +144,17 @@ type ConfigCreateInCustomIdMaxNumberOnly struct {
Type string // 类型
TypeName string // 类型名称
SpecifyIp string // 指定外网IP
CurrentIp string // 当前ip
CurrentIp string // 当前外网IP
}
// CreateInCustomIdMaxNumberOnly 创建正在运行唯一任务并限制数量
func (j *JobsGorm) CreateInCustomIdMaxNumberOnly(config *ConfigCreateInCustomIdMaxNumberOnly) error {
query := j.TaskTypeTakeIn(config.Tx, config.CustomId, config.Type)
func (c *Client) CreateInCustomIdMaxNumberOnly(ctx context.Context, config *ConfigCreateInCustomIdMaxNumberOnly) error {
query := c.TaskTypeTakeIn(ctx, config.Tx, config.CustomId, config.Type)
if query.Id != 0 {
return errors.New(fmt.Sprintf("%d:[%s@%s]任务已存在", query.Id, config.CustomId, config.Type))
return TaskIsExist
}
if config.CurrentIp == "" {
config.CurrentIp = j.config.outsideIp
config.CurrentIp = c.config.systemOutsideIp
}
err := config.Tx.Create(&jobs_gorm_model.Task{
Status: TASK_IN,
@ -265,6 +170,7 @@ func (j *JobsGorm) CreateInCustomIdMaxNumberOnly(config *ConfigCreateInCustomIdM
CreatedIp: config.CurrentIp,
SpecifyIp: config.SpecifyIp,
UpdatedIp: config.CurrentIp,
NextRunTime: gotime.Current().AfterSeconds(config.Frequency).Time,
}).Error
if err != nil {
return errors.New(fmt.Sprintf("创建[%s@%s]任务失败:%s", config.CustomId, config.Type, err.Error()))

@ -0,0 +1,50 @@
package gojobs
import (
"context"
"errors"
"fmt"
"go.dtapp.net/gojobs/jobs_gorm_model"
"go.dtapp.net/gostring"
"go.dtapp.net/gotime"
"gorm.io/gorm"
)
// ConfigCreateWaitCustomId 创建正在运行任务
type ConfigCreateWaitCustomId struct {
Tx *gorm.DB // 驱动
Params string // 参数
Frequency int64 // 频率(秒单位)
CustomId string // 自定义编号
CustomSequence int64 // 自定义顺序
Type string // 类型
TypeName string // 类型名称
SpecifyIp string // 指定外网IP
CurrentIp string // 当前外网IP
}
// CreateWaitCustomId 创建正在运行任务
func (c *Client) CreateWaitCustomId(ctx context.Context, config *ConfigCreateWaitCustomId) error {
if config.CurrentIp == "" {
config.CurrentIp = c.config.systemOutsideIp
}
err := config.Tx.Create(&jobs_gorm_model.Task{
Status: TASK_WAIT,
Params: config.Params,
StatusDesc: "首次添加等待任务",
Frequency: config.Frequency,
RunId: gostring.GetUuId(),
CustomId: config.CustomId,
CustomSequence: config.CustomSequence,
Type: config.Type,
TypeName: config.TypeName,
CreatedIp: config.CurrentIp,
SpecifyIp: config.SpecifyIp,
UpdatedIp: config.CurrentIp,
NextRunTime: gotime.Current().AfterSeconds(config.Frequency).Time,
}).Error
if err != nil {
return errors.New(fmt.Sprintf("创建[%s@%s]任务失败:%s", config.CustomId, config.Type, err.Error()))
}
return nil
}

@ -1,26 +0,0 @@
package gojobs
import "testing"
func TestSpec(t *testing.T) {
t.Log("每隔10秒执行一次", GetSeconds(10).Spec())
t.Log("每隔10秒执行一次", GetSeconds(10).Frequency())
t.Log("每隔60秒执行一次", GetSeconds(60).Spec())
t.Log("每隔60秒执行一次", GetSeconds(60).Frequency())
t.Log("每隔5分钟执行一次", GetMinutes(5).Spec())
t.Log("每隔5分钟执行一次", GetMinutes(5).Frequency())
t.Log("每隔10分钟执行一次", GetMinutes(10).Spec())
t.Log("每隔10分钟执行一次", GetMinutes(10).Frequency())
t.Log("每隔60分钟执行一次", GetMinutes(60).Spec())
t.Log("每隔60分钟执行一次", GetMinutes(60).Frequency())
t.Log("每天n点执行一次", GetHour(10).Spec())
t.Log("每天n点执行一次", GetHour(10).Frequency())
t.Log("每隔n小时执行一次", GetHourInterval(1).Spec())
t.Log("每隔n小时执行一次", GetHourInterval(1).Frequency())
}

@ -1,45 +0,0 @@
package gojobs
import (
"fmt"
"github.com/jasonlvhit/gocron"
"log"
"testing"
)
func TestCron1(t *testing.T) {
// 创建一个cron实例 精确到秒
crontab := NewCron()
log.Println(crontab)
err := crontab.AddJobByFunc("1", "*/1 * * * * *", func() {
log.Println("哈哈哈哈")
})
if err != nil {
fmt.Printf("添加任务时出错:%s", err)
return
}
err = crontab.AddJobByFunc("2", "*/2 * * * * *", func() {
log.Println("啊啊啊啊")
})
if err != nil {
fmt.Printf("添加任务时出错:%s", err)
return
}
crontab.Start()
select {}
}
func TestCron2(t *testing.T) {
i := 0
s := gocron.NewScheduler()
s.Every(5).Seconds().Do(func() {
i++
log.Println("execute per 5 seconds", i)
})
<-s.Start()
}

@ -0,0 +1,11 @@
package gojobs
import "errors"
var (
currentIpNoConfig = errors.New("请配置 CurrentIp")
redisPrefixFunNoConfig = errors.New("请配置 RedisPrefixFun")
gormClientFunNoConfig = errors.New("请配置 GormClientFun")
mongoClientFunNoConfig = errors.New("请配置 MongoClientFun")
TaskIsExist = errors.New("任务已存在")
)

@ -0,0 +1,32 @@
package gojobs
import (
"github.com/go-redis/redis/v9"
"go.mongodb.org/mongo-driver/mongo"
"gorm.io/gorm"
)
// GetDb 获取数据库驱动
func (c *Client) GetDb() *gorm.DB {
return c.gormClient.GetDb()
}
// GetMongoDb 获取数据库驱动
func (c *Client) GetMongoDb() *mongo.Client {
return c.mongoClient.GetDb()
}
// GetRedis 获取缓存数据库驱动
func (c *Client) GetRedis() *redis.Client {
return c.cache.redisClient.GetDb()
}
// GetCurrentIp 获取当前ip
func (c *Client) GetCurrentIp() string {
return c.config.systemOutsideIp
}
// GetSubscribeAddress 获取订阅地址
func (c *Client) GetSubscribeAddress() string {
return c.cache.cornKeyPrefix + "_" + c.cache.cornKeyCustom
}

@ -3,18 +3,18 @@ module go.dtapp.net/gojobs
go 1.19
require (
github.com/go-redis/redis/v9 v9.0.0-beta.2
github.com/go-redis/redis/v9 v9.0.0-rc.2
github.com/jasonlvhit/gocron v0.0.1
github.com/robfig/cron/v3 v3.0.1
go.dtapp.net/dorm v1.0.21
go.dtapp.net/goarray v1.0.1
go.dtapp.net/goip v1.0.25
go.dtapp.net/gostring v1.0.6
github.com/shirou/gopsutil v3.21.11+incompatible
go.dtapp.net/dorm v1.0.54
go.dtapp.net/goip v1.0.39
go.dtapp.net/golog v1.0.102
go.dtapp.net/gostring v1.0.10
go.dtapp.net/gotime v1.0.5
google.golang.org/grpc v1.48.0
google.golang.org/protobuf v1.28.1
gorm.io/gorm v1.23.8
xorm.io/xorm v1.3.1
go.dtapp.net/gotrace_id v1.0.6
go.mongodb.org/mongo-driver v1.11.0
gorm.io/gorm v1.24.2
)
require (
@ -24,12 +24,12 @@ require (
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.8.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.0 // indirect
github.com/go-playground/validator/v10 v10.11.1 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/goccy/go-json v0.9.10 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.13.0 // indirect
@ -38,54 +38,62 @@ require (
github.com/jackc/pgproto3/v2 v2.3.1 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.12.0 // indirect
github.com/jackc/pgx/v4 v4.17.0 // indirect
github.com/jackc/pgx/v4 v4.17.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/klauspost/compress v1.15.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/lib/pq v1.10.6 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/lib/pq v1.10.7 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.6.6 // indirect
github.com/pelletier/go-toml/v2 v2.0.2 // indirect
github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
github.com/oschwald/geoip2-golang v1.8.0 // indirect
github.com/oschwald/maxminddb-golang v1.10.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/saracen/go7z v0.0.0-20191010121135-9c09b6bd7fda // indirect
github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f // indirect
github.com/segmentio/fasthash v1.0.3 // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/tklauser/go-sysconf v0.3.11 // indirect
github.com/tklauser/numcpus v0.6.0 // indirect
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/upper/db/v4 v4.5.4 // indirect
github.com/uptrace/bun v1.1.7 // indirect
github.com/uptrace/bun/dialect/mysqldialect v1.1.7 // indirect
github.com/uptrace/bun/dialect/pgdialect v1.1.7 // indirect
github.com/uptrace/bun/driver/pgdriver v1.1.7 // indirect
github.com/upper/db/v4 v4.6.0 // indirect
github.com/uptrace/bun v1.1.9 // indirect
github.com/uptrace/bun/dialect/mysqldialect v1.1.9 // indirect
github.com/uptrace/bun/dialect/pgdialect v1.1.9 // indirect
github.com/uptrace/bun/driver/pgdriver v1.1.9 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.1 // indirect
github.com/xdg-go/stringprep v1.0.3 // indirect
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
go.dtapp.net/gorandom v1.0.1 // indirect
go.dtapp.net/gorequest v1.0.26 // indirect
go.dtapp.net/gotrace_id v1.0.2 // indirect
go.mongodb.org/mongo-driver v1.10.1 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20220812174116-3211cb980234 // indirect
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20220812140447-cec7f5303424 // indirect
go.dtapp.net/gorequest v1.0.36 // indirect
go.dtapp.net/gourl v1.0.0 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/crypto v0.3.0 // indirect
golang.org/x/mod v0.7.0 // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.2.0 // indirect
golang.org/x/text v0.4.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/driver/mysql v1.3.5 // indirect
gorm.io/driver/postgres v1.3.8 // indirect
mellium.im/sasl v0.2.1 // indirect
gorm.io/driver/mysql v1.4.4 // indirect
gorm.io/driver/postgres v1.4.5 // indirect
mellium.im/sasl v0.3.0 // indirect
modernc.org/ccgo/v3 v3.16.8 // indirect
modernc.org/libc v1.16.17 // indirect
xorm.io/builder v0.3.12 // indirect
xorm.io/xorm v1.3.2 // indirect
)

233
go.sum

@ -3,6 +3,7 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
@ -15,7 +16,6 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@ -27,6 +27,7 @@ github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/basgys/goxml2json v1.1.0 h1:4ln5i4rseYfXNd86lGEB+Vi652IsIXIvggKM/BhUKVw=
github.com/basgys/goxml2json v1.1.0/go.mod h1:wH7a5Np/Q4QoECFIU8zTQlZwZkrilY0itPfecMw41Dw=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
@ -40,12 +41,6 @@ github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cb
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
@ -73,11 +68,7 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
@ -97,24 +88,26 @@ github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vb
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-playground/validator/v10 v10.11.1 h1:prmOlTVv+YjZjmRmNSF3VmspqJIxJWXmqUsHwfTRRkQ=
github.com/go-playground/validator/v10 v10.11.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-redis/redis v6.15.5+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v9 v9.0.0-beta.2 h1:ZSr84TsnQyKMAg8gnV+oawuQezeJR11/09THcWCQzr4=
github.com/go-redis/redis/v9 v9.0.0-beta.2/go.mod h1:Bldcd/M/bm9HbnNPi/LUtYBSD8ttcZYBMupwMXhdU0o=
github.com/go-redis/redis/v9 v9.0.0-rc.2 h1:IN1eI8AvJJeWHjMW/hlFAv2sAfvTun2DVksDDJ3a6a0=
github.com/go-redis/redis/v9 v9.0.0-rc.2/go.mod h1:cgBknjwcBJa2prbnuHH/4k/Mlj4r0pWNV2HBanHujfY=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.9.10 h1:hCeNmprSNLB8B8vQKWl6DpuH0t60oEs+TAk9a7CScKc=
github.com/goccy/go-json v0.9.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@ -130,18 +123,7 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@ -152,18 +134,14 @@ github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@ -174,7 +152,6 @@ github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
@ -216,7 +193,6 @@ github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
github.com/jackc/pgconn v1.12.1/go.mod h1:ZkhRC59Llhrq3oSfrikvwQ5NaxYExr6twkdkMLaKono=
github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys=
github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
@ -236,7 +212,6 @@ github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwX
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y=
github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
@ -252,7 +227,6 @@ github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI
github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE=
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.11.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w=
github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
@ -265,15 +239,15 @@ github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CI
github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60=
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw=
github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ=
github.com/jackc/pgx/v4 v4.17.0 h1:Hsx+baY8/zU2WtPLQyZi8WbecgcsWEeyoK1jvg/WgIo=
github.com/jackc/pgx/v4 v4.17.0/go.mod h1:Gd6RmOhtFLTu8cp/Fhq4kP195KrshxYJH3oW8AWJ1pw=
github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E=
github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jasonlvhit/gocron v0.0.1 h1:qTt5qF3b3srDjeOIR4Le1LfeyvoYzJlYpqvG7tJX5YU=
github.com/jasonlvhit/gocron v0.0.1/go.mod h1:k9a3TV8VcU73XZxfVHCHWMWF9SOqgoku0/QlY2yvlA4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
@ -295,8 +269,8 @@ github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:C
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@ -317,8 +291,8 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
github.com/lib/pq v1.10.6/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
@ -333,18 +307,18 @@ github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@ -359,6 +333,8 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ
github.com/montanaflynn/stats v0.6.6 h1:Duep6KMIDpY4Yo11iFsvyqJDyfzLF9+sndUKT+v64GQ=
github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
@ -376,7 +352,7 @@ github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q=
github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
@ -386,11 +362,15 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml/v2 v2.0.2 h1:+jQXlF3scKIcSEKkdHzXhCTDLPFi5r1wnK6yPS+49Gw=
github.com/pelletier/go-toml/v2 v2.0.2/go.mod h1:MovirKjgVRESsAvNZlAjtFwV867yGuwRkXbG66OzopI=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
@ -426,7 +406,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
@ -444,6 +423,10 @@ github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f h1:1cJITU3JUI8q
github.com/saracen/solidblock v0.0.0-20190426153529-45df20abab6f/go.mod h1:LyBTue+RWeyIfN3ZJ4wVxvDuvlGJtDgCLgCb6HCPgps=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=
github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
@ -468,6 +451,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@ -475,13 +459,17 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tklauser/go-sysconf v0.3.11 h1:89WgdJhk5SNwJfu+GKyYveZ4IaJ7xAkecBo+KdJV0CM=
github.com/tklauser/go-sysconf v0.3.11/go.mod h1:GqXfhXY3kiPa0nAXPDIQIWzJbMCB7AmcWpGR8lSZfqI=
github.com/tklauser/numcpus v0.6.0 h1:kebhY2Qt+3U6RNK7UqpYNA+tJ23IBEGKkB7JQBfDYms=
github.com/tklauser/numcpus v0.6.0/go.mod h1:FEZLMke0lhOUG6w2JadTzp0a+Nl8PF/GFkQ5UVIcaL4=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
@ -490,16 +478,16 @@ github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/upper/db/v4 v4.5.4 h1:Hxho4jSx4E+3fxlFgdH4wQTRKygtL0YQPDLQPCUu9wg=
github.com/upper/db/v4 v4.5.4/go.mod h1:wyu5BM5Y2gowOt4i6C4LbxftH9QeUF338XVGH4uk+Eo=
github.com/uptrace/bun v1.1.7 h1:biOoh5dov69hQPBlaRsXSHoEOIEnCxFzQvUmbscSNJI=
github.com/uptrace/bun v1.1.7/go.mod h1:Z2Pd3cRvNKbrYuL6Gp1XGjA9QEYz+rDz5KkEi9MZLnQ=
github.com/uptrace/bun/dialect/mysqldialect v1.1.7 h1:eMDtsuu5BRuh0P2l0/j0Qv5UBmcqJE0u3F8Zy//klNM=
github.com/uptrace/bun/dialect/mysqldialect v1.1.7/go.mod h1:cCSZH3IULSGaG76Z96mAC7O74MeIYGfDX7CWGanGc0s=
github.com/uptrace/bun/dialect/pgdialect v1.1.7 h1:94GPc8RRC9AVoQ+4KCqRX2zScevsVfOttk13wm60/P8=
github.com/uptrace/bun/dialect/pgdialect v1.1.7/go.mod h1:kKHFmQIyBl0kvQDsoyrlXaKsceTH2TJnbCUFlK9QAmE=
github.com/uptrace/bun/driver/pgdriver v1.1.7 h1:WExzNbsMBWmkjcylV8kKzcbA7pKWZ5UhPflknzUP2PA=
github.com/uptrace/bun/driver/pgdriver v1.1.7/go.mod h1:ZswdiMQOKmY5OBR84YGzaNddbpDo/kkhCzQXm5E2ZqA=
github.com/upper/db/v4 v4.6.0 h1:0VmASnqrl/XN8Ehoq++HBgZ4zRD5j3GXygW8FhP0C5I=
github.com/upper/db/v4 v4.6.0/go.mod h1:2mnRcPf+RcCXmVcD+o04LYlyu3UuF7ubamJia7CkN6s=
github.com/uptrace/bun v1.1.9 h1:6zs+YJcgw8oj67c+YmI8edQokDFeyR4BE/ykNWjGYYs=
github.com/uptrace/bun v1.1.9/go.mod h1:fpYRCGyruLCyP7dNjMfqulYn4VBP/fH0enc0j0yW/Cs=
github.com/uptrace/bun/dialect/mysqldialect v1.1.9 h1:TPK14lATldiJiIpwAVl4RzgoNkY+wo+7RVbNmGdYVsc=
github.com/uptrace/bun/dialect/mysqldialect v1.1.9/go.mod h1:C7tZ3CSfWNsjr9ujTedyeI8Zy5iCcaqnCGYObY9MFz0=
github.com/uptrace/bun/dialect/pgdialect v1.1.9 h1:V23SU89WfjqtePLFPRXVXCwmSyYb0XKeg8Z6BMXgyHg=
github.com/uptrace/bun/dialect/pgdialect v1.1.9/go.mod h1:+ux7PjC4NYsNMdGE9b2ERxCi2jJai8Z8zniXFExq0Ns=
github.com/uptrace/bun/driver/pgdriver v1.1.9 h1:Dy9g/EpgOG15RP3mDAuJd/hnfXAUdBsfxpg9On27M+Y=
github.com/uptrace/bun/driver/pgdriver v1.1.9/go.mod h1:YnPHzR4fT24PXrBTcadclXfdtkR9dYouqk2HwOiKf2g=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
@ -517,45 +505,55 @@ github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7Jul
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.dtapp.net/dorm v1.0.21 h1:e5GccTOkFJ66+fTBu0Voo45Cpj+NKsVYLmq2IKQbH+w=
go.dtapp.net/dorm v1.0.21/go.mod h1:bHG7BmgeLaAlc56myYF63lwZAIuMeWRAqHBb/L84dLM=
go.dtapp.net/goarray v1.0.1 h1:cHNHaJ2MFcuJPA1WKU2PM1EUZShS1vQqEH7n6YXsQVU=
go.dtapp.net/goarray v1.0.1/go.mod h1:/MPhlFCAhQyeNV1M0v1PAOOX33Sg705fVjUgMO12IBQ=
go.dtapp.net/goip v1.0.25 h1:GSgQkR7dc28TX8E1mF3wAy+EEGZ++tgqc8Q85OKZPEY=
go.dtapp.net/goip v1.0.25/go.mod h1:dKeNuWJttH4AvvYVHV0FagB6BVvFM1HGDKFxLKr+hkQ=
go.dtapp.net/dorm v1.0.54 h1:yY3NqVSPKDsoLUrEkVzyM9hiZO95jLT1BALXErt+DAw=
go.dtapp.net/dorm v1.0.54/go.mod h1:9QRNnXLkHev7MllwpPq6OYusP8khujurXfMZv6p7CYo=
go.dtapp.net/goip v1.0.39 h1:6fEV0yrY3PNUK3lhEuMqxZJYEXzhbHannvsgZA7s7hE=
go.dtapp.net/goip v1.0.39/go.mod h1:9o6uaw0JCPvTwGFWRkznI8EidQ9bwWJ5dx02/7ZUnEM=
go.dtapp.net/golog v1.0.102 h1:WVfx+vxWODqPkYJNeLfxIENyQO3RCCWr5xLhhPauOpM=
go.dtapp.net/golog v1.0.102/go.mod h1:doXs2CQmQOMGhrZD7bRRzdUh+cYWfiugJEmpJWBeXDw=
go.dtapp.net/gorandom v1.0.1 h1:IWfMClh1ECPvyUjlqD7MwLq4mZdUusD1qAwAdsvEJBs=
go.dtapp.net/gorandom v1.0.1/go.mod h1:ZPdgalKpvFV/ATQqR0k4ns/F/IpITAZpx6WkWirr5Y8=
go.dtapp.net/gorequest v1.0.26 h1:t+rMW7liLHz1RgY60ztWIghSkTeQePKWM0EsDG1IqMI=
go.dtapp.net/gorequest v1.0.26/go.mod h1:sDhiEPwdZnUbyDuq/3OAzA1qvpU5yo49v1fY5sVgj18=
go.dtapp.net/gostring v1.0.6 h1:XqNaThEfHpweLofru5sBqm1UUzc9JWsXyB/M/rTk29w=
go.dtapp.net/gostring v1.0.6/go.mod h1:AMnnLjyNxH+cphxyASJGYCzWpVrkP5RncuVo8xL8s3E=
go.dtapp.net/gorequest v1.0.36 h1:c3DqlQ1bvTwCOJ3gRnDE5nsg8iqnjEYmVAFHn3d8rpY=
go.dtapp.net/gorequest v1.0.36/go.mod h1:IbhBN1ID6CqMitHjAYdEBmhK1P5FVDLUeMl1JqxE3qY=
go.dtapp.net/gostring v1.0.10 h1:eG+1kQehdJUitj9Hfwy79SndMHYOB7ABpWkTs7mDGeQ=
go.dtapp.net/gostring v1.0.10/go.mod h1:L4kREy89a9AraMHB5tUjjl+5rxP1gpXkDouRKKuzT50=
go.dtapp.net/gotime v1.0.5 h1:12aNgB2ULpP6QgQHEUkLilZ4ASvhpFxMFQkBwn0par8=
go.dtapp.net/gotime v1.0.5/go.mod h1:Gq7eNLr2iMLP18UNWONRq4V3Uhf/ADp4bIrS+Tc6ktY=
go.dtapp.net/gotrace_id v1.0.2 h1:wSc7dYzkAwSz3MbPu2Io4XqHe0l0dIaJe3vlpDAZrXY=
go.dtapp.net/gotrace_id v1.0.2/go.mod h1:476T5KMJw9c6DM/rJn7y6d28hcNDv4+wew4sd+powes=
go.dtapp.net/gotrace_id v1.0.6 h1:q6s8jy50vt1820b69JKQaFqbhGS5yJGMVtocwOGOPO0=
go.dtapp.net/gotrace_id v1.0.6/go.mod h1:o5kSzNK4s3GrrKpkRKXtAhArtBG1e5N5O5KGPlBlWG4=
go.dtapp.net/gourl v1.0.0 h1:Zbe0GiMFyyRy2+bjbVsYulakH5d58w3CDZkUPonlMoQ=
go.dtapp.net/gourl v1.0.0/go.mod h1:x9A/pJ3iKDTb6Gu2RtJy2iHg56IowXIcIGQdogqpGjs=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.mongodb.org/mongo-driver v1.10.1 h1:NujsPveKwHaWuKUer/ceo9DzEe7HIj1SlJ6uvXZG0S4=
go.mongodb.org/mongo-driver v1.10.1/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8=
go.mongodb.org/mongo-driver v1.11.0 h1:FZKhBSTydeuffHj9CBjXlR8vQLee1cQyTWYPA6/tqiE=
go.mongodb.org/mongo-driver v1.11.0/go.mod h1:s7p5vEtfbeR1gYi6pnj3c3/urpbLv2T5Sfd6Rp2HBB8=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180910181607-0e37d006457b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@ -572,12 +570,12 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220307211146-efcb8507fb70/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -588,8 +586,8 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -607,15 +605,13 @@ golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220812174116-3211cb980234 h1:RDqmgfe7SvlMWoqC3xwQ2blLO3fcWcxMa3eBLRdRW7E=
golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -624,8 +620,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -643,15 +639,14 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -661,8 +656,9 @@ golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -670,8 +666,9 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -691,7 +688,7 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.11 h1:loJ25fNOEhSXfHrpoGj91eCUThwdNX6u24rO1xnNteY=
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -707,10 +704,6 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20220812140447-cec7f5303424 h1:zZnTt15U44/Txe/9cN/tVbteBkPMiyXK48hPsKRmqj4=
google.golang.org/genproto v0.0.0-20220812140447-cec7f5303424/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
@ -719,25 +712,8 @@ google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
@ -752,6 +728,7 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@ -759,28 +736,28 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.3.5 h1:iWBTVW/8Ij5AG4e0G/zqzaJblYkBI1VIL1LG2HUGsvY=
gorm.io/driver/mysql v1.3.5/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
gorm.io/driver/postgres v1.3.8 h1:8bEphSAB69t3odsCR4NDzt581iZEWQuRM27Cg6KgfPY=
gorm.io/driver/postgres v1.3.8/go.mod h1:qB98Aj6AhRO/oyu/jmZsi/YM9g6UzVCjMxO/6frFvcA=
gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE=
gorm.io/driver/mysql v1.4.4 h1:MX0K9Qvy0Na4o7qSC/YI7XxqUw5KDw01umqgID+svdQ=
gorm.io/driver/mysql v1.4.4/go.mod h1:BCg8cKI+R0j/rZRQxeKis/forqRwRSYOR8OM3Wo6hOM=
gorm.io/driver/postgres v1.4.5 h1:mTeXTTtHAgnS9PgmhN2YeUbazYpLhUI1doLnw42XUZc=
gorm.io/driver/postgres v1.4.5/go.mod h1:GKNQYSJ14qvWkvPwXljMGehpKrhlDNsqYRr5HnYGncg=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
gorm.io/gorm v1.24.1-0.20221019064659-5dd2bb482755/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.24.2 h1:9wR6CFD+G8nOusLdvkZelOEhpJVwwHzpQOUM+REd6U0=
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
mellium.im/sasl v0.2.1 h1:nspKSRg7/SyO0cRGY71OkfHab8tf9kCts6a6oTDut0w=
mellium.im/sasl v0.2.1/go.mod h1:ROaEDLQNuf9vjKqE1SrAfnsobm2YKXT1gnN1uDp1PjQ=
mellium.im/sasl v0.3.0 h1:0qoaTCTo5Py7u/g0cBIQZcMOgG/5LM71nshbXwznBh8=
mellium.im/sasl v0.3.0/go.mod h1:xm59PUYpZHhgQ9ZqoJ5QaCqzWMi8IeS49dhp6plPCzw=
modernc.org/b v1.0.2/go.mod h1:fVGfCIzkZw5RsuF2A2WHbJmY7FiMIq30nP4s52uWsoY=
modernc.org/cc/v3 v3.33.6/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
modernc.org/cc/v3 v3.33.9/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
@ -887,8 +864,8 @@ modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A=
modernc.org/libc v1.16.1/go.mod h1:JjJE0eu4yeK7tab2n4S1w8tlWd9MxXLRzheaRnAKymU=
modernc.org/libc v1.16.17 h1:rXo8IZJvP+QSN1KrlV23dtkM3XfGYXjx3RbLLzBtndM=
modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU=
modernc.org/libc v1.16.19 h1:S8flPn5ZeXx6iw/8yNa986hwTQDrY8RXU7tObZuAozo=
modernc.org/lldb v1.0.2/go.mod h1:ovbKqyzA9H/iPwHkAOH0qJbIQVT9rlijecenxDwVUi0=
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
@ -905,7 +882,7 @@ modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/ql v1.4.0/go.mod h1:q4c29Bgdx+iAtxx47ODW5Xo2X0PDkjSCK9NdQl6KFxc=
modernc.org/sortutil v1.1.0/go.mod h1:ZyL98OQHJgH9IEfN71VsamvJgrtRX9Dj2gX+vH86L1k=
modernc.org/sqlite v1.14.2/go.mod h1:yqfn85u8wVOE6ub5UT8VI9JjhrwBUUCNyTACN0h6Sx8=
modernc.org/sqlite v1.17.3 h1:iE+coC5g17LtByDYDWKpR6m2Z9022YrSh3bumwOnIrI=
modernc.org/sqlite v1.18.0 h1:ef66qJSgKeyLyrF4kQ2RHw/Ue3V89fyFNbGL073aDjI=
modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
modernc.org/strutil v1.1.2 h1:iFBDH6j1Z0bN/Q9udJnnFoFpENA4252qe/7/5woE5MI=
modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY=
@ -918,5 +895,5 @@ sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/builder v0.3.12 h1:ASZYX7fQmy+o8UJdhlLHSW57JDOkM8DNhcAF5d0LiJM=
xorm.io/builder v0.3.12/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE=
xorm.io/xorm v1.3.1 h1:z5egKrDoOLqZFhMjcGF4FBHiTmE5/feQoHclfhNidfM=
xorm.io/xorm v1.3.1/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw=
xorm.io/xorm v1.3.2 h1:uTRRKF2jYzbZ5nsofXVUx6ncMaek+SHjWYtCXyZo1oM=
xorm.io/xorm v1.3.2/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw=

@ -0,0 +1,196 @@
package gojobs
import (
"context"
"go.dtapp.net/gojobs/jobs_gorm_model"
"go.dtapp.net/gotime"
"gorm.io/gorm"
)
// TaskTakeId 编号查询任务
func (c *Client) TaskTakeId(ctx context.Context, tx *gorm.DB, id uint) (result jobs_gorm_model.Task) {
err := tx.Where("id = ?", id).Take(&result).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("编号查询任务:%v", err)
}
return result
}
// TaskTake 自定义编号查询任务
func (c *Client) TaskTake(ctx context.Context, tx *gorm.DB, customId string) (result jobs_gorm_model.Task) {
err := tx.Where("custom_id = ?", customId).Take(&result).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("自定义编号查询任务:%v", err)
}
return result
}
// 自定义编号加状态查询任务
func (c *Client) taskTake(ctx context.Context, tx *gorm.DB, customId, status string) (result jobs_gorm_model.Task) {
err := tx.Where("custom_id = ?", customId).Where("status = ?", status).Take(&result).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("自定义编号加状态查询任务:%v", err)
}
return result
}
// TaskTakeIn 查询单任务 - 任务运行
func (c *Client) TaskTakeIn(ctx context.Context, tx *gorm.DB, customId string) jobs_gorm_model.Task {
return c.taskTake(ctx, tx, customId, TASK_IN)
}
// TaskTakeSuccess 查询单任务 - 任务完成
func (c *Client) TaskTakeSuccess(ctx context.Context, tx *gorm.DB, customId string) jobs_gorm_model.Task {
return c.taskTake(ctx, tx, customId, TASK_SUCCESS)
}
// TaskTakeError 查询单任务 - 任务异常
func (c *Client) TaskTakeError(ctx context.Context, tx *gorm.DB, customId string) jobs_gorm_model.Task {
return c.taskTake(ctx, tx, customId, TASK_ERROR)
}
// TaskTakeTimeout 查询单任务 - 任务超时
func (c *Client) TaskTakeTimeout(ctx context.Context, tx *gorm.DB, customId string) jobs_gorm_model.Task {
return c.taskTake(ctx, tx, customId, TASK_TIMEOUT)
}
// TaskTakeWait 查询单任务 - 任务等待
func (c *Client) TaskTakeWait(ctx context.Context, tx *gorm.DB, customId string) jobs_gorm_model.Task {
return c.taskTake(ctx, tx, customId, TASK_WAIT)
}
// TaskTypeTake 查询单任务
func (c *Client) TaskTypeTake(ctx context.Context, tx *gorm.DB, customId, Type string) (result jobs_gorm_model.Task) {
err := tx.Where("custom_id = ?", customId).Where("type = ?", Type).Take(&result).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("查询单任务:%v", err)
}
return result
}
// 查询单任务
func (c *Client) taskTypeTake(ctx context.Context, tx *gorm.DB, customId, Type, status string) (result jobs_gorm_model.Task) {
err := tx.Where("custom_id = ?", customId).Where("type = ?", Type).Where("status = ?", status).Take(&result).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("查询单任务:%v", err)
}
return result
}
// TaskTypeTakeIn 查询单任务 - 任务运行
func (c *Client) TaskTypeTakeIn(ctx context.Context, tx *gorm.DB, customId, Type string) jobs_gorm_model.Task {
return c.taskTypeTake(ctx, tx, customId, Type, TASK_IN)
}
// TaskTypeTakeSuccess 查询单任务 - 任务完成
func (c *Client) TaskTypeTakeSuccess(ctx context.Context, tx *gorm.DB, customId, Type string) jobs_gorm_model.Task {
return c.taskTypeTake(ctx, tx, customId, Type, TASK_SUCCESS)
}
// TaskTypeTakeError 查询单任务 - 任务异常
func (c *Client) TaskTypeTakeError(ctx context.Context, tx *gorm.DB, customId, Type string) jobs_gorm_model.Task {
return c.taskTypeTake(ctx, tx, customId, Type, TASK_ERROR)
}
// TaskTypeTakeTimeout 查询单任务 - 任务超时
func (c *Client) TaskTypeTakeTimeout(ctx context.Context, tx *gorm.DB, customId, Type string) jobs_gorm_model.Task {
return c.taskTypeTake(ctx, tx, customId, Type, TASK_TIMEOUT)
}
// TaskTypeTakeWait 查询单任务 - 任务等待
func (c *Client) TaskTypeTakeWait(ctx context.Context, tx *gorm.DB, customId, Type string) jobs_gorm_model.Task {
return c.taskTypeTake(ctx, tx, customId, Type, TASK_WAIT)
}
// TaskFindAll 查询多任务
func (c *Client) TaskFindAll(ctx context.Context, tx *gorm.DB, frequency int64) (results []jobs_gorm_model.Task) {
err := tx.Where("frequency = ?", frequency).Order("id asc").Find(&results).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("查询多任务:%v", err)
}
return results
}
// 查询多任务
func (c *Client) taskFindAll(ctx context.Context, tx *gorm.DB, frequency int64, status string) (results []jobs_gorm_model.Task) {
err := tx.Where("frequency = ?", frequency).Where("status = ?", status).Order("id asc").Find(&results).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("查询多任务:%v", err)
}
return results
}
// TaskFindAllIn 查询多任务 - 任务运行
func (c *Client) TaskFindAllIn(ctx context.Context, tx *gorm.DB, frequency int64) []jobs_gorm_model.Task {
return c.taskFindAll(ctx, tx, frequency, TASK_IN)
}
// TaskFindAllSuccess 查询多任务 - 任务完成
func (c *Client) TaskFindAllSuccess(ctx context.Context, tx *gorm.DB, frequency int64) []jobs_gorm_model.Task {
return c.taskFindAll(ctx, tx, frequency, TASK_SUCCESS)
}
// TaskFindAllError 查询多任务 - 任务异常
func (c *Client) TaskFindAllError(ctx context.Context, tx *gorm.DB, frequency int64) []jobs_gorm_model.Task {
return c.taskFindAll(ctx, tx, frequency, TASK_ERROR)
}
// TaskFindAllTimeout 查询多任务 - 任务超时
func (c *Client) TaskFindAllTimeout(ctx context.Context, tx *gorm.DB, frequency int64) []jobs_gorm_model.Task {
return c.taskFindAll(ctx, tx, frequency, TASK_TIMEOUT)
}
// TaskFindAllWait 查询多任务 - 任务等待
func (c *Client) TaskFindAllWait(ctx context.Context, tx *gorm.DB, frequency int64) []jobs_gorm_model.Task {
return c.taskFindAll(ctx, tx, frequency, TASK_WAIT)
}
// StartTask 任务启动
func (c *Client) StartTask(ctx context.Context, tx *gorm.DB, id uint) error {
err := c.EditTask(tx, id).
Select("status", "status_desc").
Updates(jobs_gorm_model.Task{
Status: TASK_IN,
StatusDesc: "启动任务",
}).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("任务启动失败:%v", err)
}
return err
}
// StartTaskCustom 任务启动自定义
func (c *Client) StartTaskCustom(ctx context.Context, tx *gorm.DB, customId string, customSequence int64) error {
err := tx.Model(&jobs_gorm_model.Task{}).
Where("custom_id = ?", customId).
Where("custom_sequence = ?", customSequence).
Where("status = ?", TASK_WAIT).
Select("status", "status_desc").
Updates(jobs_gorm_model.Task{
Status: TASK_IN,
StatusDesc: "启动任务",
}).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("任务启动自定义失败:%v", err)
}
return err
}
// EditTask 任务修改
func (c *Client) EditTask(tx *gorm.DB, id uint) *gorm.DB {
return tx.Model(&jobs_gorm_model.Task{}).Where("id = ?", id)
}
// UpdateFrequency 更新任务频率
func (c *Client) UpdateFrequency(ctx context.Context, tx *gorm.DB, id uint, frequency int64) error {
err := c.EditTask(tx, id).
Select("frequency", "next_run_time").
Updates(jobs_gorm_model.Task{
Frequency: frequency,
NextRunTime: gotime.Current().AfterSeconds(frequency).Time,
}).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("更新任务频率失败:%v", err)
}
return err
}

@ -1,21 +0,0 @@
package gojobs
import (
"context"
"fmt"
"go.dtapp.net/goip"
)
var ip string
func configIp() {
ip = goip.GetOutsideIp(context.Background())
}
const prefix = "cron:"
const prefixIp = "cron_%s:"
func prefixSprintf(str string) string {
return fmt.Sprintf(prefixIp, str)
}

@ -1,38 +0,0 @@
package gojobs
import (
"google.golang.org/grpc"
)
// ClientConfig 客户端配置
type ClientConfig struct {
Address string // 服务端口 127.0.0.1:8888
}
// Client 定时任务
type Client struct {
ClientConfig // 配置
Conn *grpc.ClientConn // 链接信息
}
// NewClient 创建客户端
func NewClient(config *ClientConfig) *Client {
if config.Address == "" {
panic("[客户端]请填写服务端口")
}
c := &Client{}
c.Address = config.Address
var err error
// 建立连接 获取client
c.Conn, err = grpc.Dial(c.Address, grpc.WithInsecure())
if err != nil {
panic("[客户端]{连接失败}" + err.Error())
}
return c
}

@ -1,56 +0,0 @@
package gojobs
import (
"context"
"go.dtapp.net/gojobs/pb"
"google.golang.org/grpc"
"log"
)
// CronConfig 定时任务配置
type CronConfig struct {
Address string // 服务端口 127.0.0.1:8888
}
// GrpcCron 定时任务
type GrpcCron struct {
CronConfig // 配置
Pub pb.PubSubClient // 订阅
Conn *grpc.ClientConn // 链接信息
}
// NewGrpcCron 创建定时任务
func NewGrpcCron(config *CronConfig) *GrpcCron {
if config.Address == "" {
panic("[定时任务]请填写服务端口")
}
c := &GrpcCron{}
c.Address = config.Address
var err error
// 建立连接 获取client
c.Conn, err = grpc.Dial(c.Address, grpc.WithInsecure())
if err != nil {
panic("[定时任务]{连接失败}" + err.Error())
}
// 新建一个客户端
c.Pub = pb.NewPubSubClient(c.Conn)
return c
}
// Send 发送
func (c *GrpcCron) Send(in *pb.PublishRequest) (*pb.PublishResponse, error) {
log.Printf("[定时任务]{广播开始}编号:%s 类型:%s ip%s\n", in.GetId(), in.GetValue(), in.GetIp())
stream, err := c.Pub.Publish(context.Background(), in)
if err != nil {
log.Printf("[定时任务]{广播失败}编号:%s %v\n", in.GetId(), err)
}
log.Printf("[定时任务]{广播成功}编号:%s 类型:%s ip%s\n", in.GetId(), in.GetValue(), in.GetIp())
return stream, err
}

@ -1,84 +0,0 @@
package gojobs
import (
"errors"
"go.dtapp.net/gojobs/pb"
"go.dtapp.net/gojobs/pubsub"
"google.golang.org/grpc"
"log"
"net"
"strings"
"time"
)
// ServerConfig 服务配置
type ServerConfig struct {
PublishTimeout time.Duration // 控制发布时最大阻塞时间
PubBuffer int // 缓冲区大小控制每个订阅者的chan缓冲区大小
Address string // 服务端口 0.0.0.0:8888
}
// Server 服务
type Server struct {
ServerConfig // 配置
Pub *pubsub.Publisher // 订阅
Conn *grpc.Server // 链接信息
}
// NewServer 创建服务和注册
func NewServer(config *ServerConfig) *Server {
if config.Address == "" {
panic("[服务中转]请填写服务端口")
}
s := &Server{}
s.PublishTimeout = config.PublishTimeout
s.PubBuffer = config.PubBuffer
s.Address = config.Address
s.Pub = pubsub.NewPublisher(config.PublishTimeout, config.PubBuffer)
// 创建gRPC服务器
s.Conn = grpc.NewServer()
// 注册
pb.RegisterPubSubServer(s.Conn, pb.NewPubSubServerService())
return s
}
// StartCron 启动定时任务
func (s *Server) StartCron() {
cron := s.Pub.SubscribeTopic(func(v interface{}) bool {
if key, ok := v.(string); ok {
if strings.HasPrefix(key, prefix) {
return true
}
}
return false
})
go func() {
log.Println("crontopic:", <-cron)
}()
}
// StartUp 启动服务
func (s *Server) StartUp() {
// 监听本地端口
lis, err := net.Listen("tcp", s.Address)
if err != nil {
panic(errors.New("[服务中转]{创建监听失败}" + err.Error()))
}
log.Println("[服务中转]{监听}", lis.Addr())
// 启动grpc
err = s.Conn.Serve(lis)
if err != nil {
panic(errors.New("[服务中转]{创建服务失败}" + err.Error()))
}
}

@ -1,76 +0,0 @@
package gojobs
import (
"context"
"go.dtapp.net/gojobs/pb"
"go.dtapp.net/gostring"
"google.golang.org/grpc"
)
// WorkerConfig 工作配置
type WorkerConfig struct {
Address string // 服务端口 127.0.0.1:8888
ClientIp string // 自己的ip地址
}
// Worker 工作
type Worker struct {
WorkerConfig // 配置
Pub pb.PubSubClient // 订阅
Conn *grpc.ClientConn // 链接信息
}
// NewWorker 创建工作
func NewWorker(config *WorkerConfig) *Worker {
if config.Address == "" {
panic("[工作线]请填写服务端口")
}
if config.ClientIp == "" {
panic("[定时任务]请填写ip地址")
}
w := &Worker{}
w.Address = config.Address
w.ClientIp = config.ClientIp
var err error
// 建立连接 获取client
w.Conn, err = grpc.Dial(w.Address, grpc.WithInsecure())
if err != nil {
panic("[工作线]{连接失败}" + err.Error())
}
// 新建一个客户端
w.Pub = pb.NewPubSubClient(w.Conn)
return w
}
// SubscribeCron 订阅服务
func (w *Worker) SubscribeCron() pb.PubSub_SubscribeClient {
stream, err := w.Pub.Subscribe(context.Background(), &pb.SubscribeRequest{
Id: gostring.GetUuId(),
Value: prefix,
Ip: w.ClientIp,
})
if err != nil {
panic("[工作线]{订阅服务失败}" + err.Error())
}
return stream
}
// StartCron 启动任务
func (w *Worker) StartCron() pb.PubSub_SubscribeClient {
stream, err := w.Pub.Subscribe(context.Background(), &pb.SubscribeRequest{
Id: gostring.GetUuId(),
Value: prefixSprintf(w.ClientIp),
Ip: w.ClientIp,
})
if err != nil {
panic("[工作线]{启动任务失败}" + err.Error())
}
return stream
}

@ -1,5 +1,10 @@
package gojobs
import (
"context"
"go.dtapp.net/gojobs/jobs_gorm_model"
)
const (
TASK_IN = "IN" // 任务运行
TASK_SUCCESS = "SUCCESS" // 任务完成
@ -11,15 +16,15 @@ const (
// Cron
type jobs interface {
// Run 运行
Run(info interface{}, status int, desc string)
// RunAddLog 任务执行日志
RunAddLog(id uint, runId string)
Run(ctx context.Context, info jobs_gorm_model.Task, status int, result string)
// CreateInCustomId 创建正在运行任务
CreateInCustomId()
CreateInCustomId(ctx context.Context, config *ConfigCreateInCustomId) error
// CreateInCustomIdOnly 创建正在运行唯一任务
CreateInCustomIdOnly()
CreateInCustomIdOnly(ctx context.Context, config *ConfigCreateInCustomIdOnly) error
// CreateInCustomIdMaxNumber 创建正在运行任务并限制数量
CreateInCustomIdMaxNumber()
CreateInCustomIdMaxNumber(ctx context.Context, config *ConfigCreateInCustomIdMaxNumber) error
// CreateInCustomIdMaxNumberOnly 创建正在运行唯一任务并限制数量
CreateInCustomIdMaxNumberOnly()
CreateInCustomIdMaxNumberOnly(ctx context.Context, config *ConfigCreateInCustomIdMaxNumberOnly) error
// CreateWaitCustomId 创建正在运行任务
CreateWaitCustomId(ctx context.Context, config *ConfigCreateWaitCustomId) error
}

@ -1,113 +0,0 @@
package gojobs
import (
"context"
"errors"
"fmt"
"go.dtapp.net/dorm"
"go.dtapp.net/goarray"
"go.dtapp.net/goip"
"go.dtapp.net/gojobs/jobs_gorm_model"
"log"
"runtime"
)
type JobsGormConfig struct {
GormClient *dorm.GormClient // 数据库驱动
RedisClient *dorm.RedisClient // 缓存数据库驱动
CurrentIp string // 当前ip
LockKeyPrefix string // 锁Key前缀 xxx_lock
LockKeySeparator string // 锁Key分隔符 :
CornKeyPrefix string // 任务Key前缀 xxx_cron
CornKeyCustom string // 任务Key自定义 xxx_cron_自定义 xxx_cron_自定义_*
Debug bool // 调试
}
// JobsGorm Gorm数据库驱动
type JobsGorm struct {
gormClient *dorm.GormClient // 数据库驱动
redisClient *dorm.RedisClient // 缓存驱动
lockClient *dorm.RedisClientLock // 锁驱动
config struct {
debug bool // 调试
runVersion string // 运行版本
os string // 系统类型
arch string // 系统架构
maxProCs int // CPU核数
version string // GO版本
macAddrS string // Mac地址
insideIp string // 内网ip
outsideIp string // 外网ip
lockKeyPrefix string // 锁Key前缀 xxx_lock
lockKeySeparator string // 锁Key分隔符 :
cornKeyPrefix string // 任务Key前缀 xxx_cron
cornKeyCustom string // 任务Key自定义
}
}
// NewJobsGorm 初始化
func NewJobsGorm(config *JobsGormConfig) (*JobsGorm, error) {
// 判断
if config.LockKeyPrefix == "" {
return nil, errors.New("需要配置锁Key前缀")
}
if config.LockKeySeparator == "" {
return nil, errors.New("需要配置锁Key分隔符")
}
if config.CornKeyPrefix == "" {
return nil, errors.New("需要配置任务Key前缀")
}
if config.CornKeyCustom == "" {
return nil, errors.New("需要配置任务Key自定义")
}
if config.CurrentIp == "" {
return nil, errors.New("需要配置当前的IP")
}
c := &JobsGorm{}
c.gormClient = config.GormClient
c.redisClient = config.RedisClient
c.config.outsideIp = config.CurrentIp
c.config.lockKeyPrefix = config.LockKeyPrefix
c.config.lockKeySeparator = config.LockKeySeparator
c.config.cornKeyPrefix = config.CornKeyPrefix
c.config.cornKeyCustom = config.CornKeyCustom
c.config.debug = config.Debug
if c.gormClient.Db == nil {
return nil, errors.New("需要配置数据库驱动")
}
if c.redisClient.Db == nil {
return nil, errors.New("需要配置缓存数据库驱动")
}
// 锁
c.lockClient = c.redisClient.NewLock()
// 配置信息
c.config.runVersion = Version
c.config.os = runtime.GOOS
c.config.arch = runtime.GOARCH
c.config.maxProCs = runtime.GOMAXPROCS(0)
c.config.version = runtime.Version()
c.config.macAddrS = goarray.TurnString(goip.GetMacAddr(context.Background()))
c.config.insideIp = goip.GetInsideIp(context.Background())
// 创建模型
err := c.gormClient.Db.AutoMigrate(
&jobs_gorm_model.Task{},
&jobs_gorm_model.TaskLog{},
&jobs_gorm_model.TaskLogRun{},
&jobs_gorm_model.TaskIp{},
)
if err != nil {
return nil, errors.New(fmt.Sprintf("创建任务模型失败:%v\n", err))
}
if c.config.debug == true {
log.Printf("JOBS配置%+v\n", c.config)
}
return c, nil
}

@ -1,40 +0,0 @@
package gojobs
import (
"go.dtapp.net/gojobs/jobs_gorm_model"
"go.dtapp.net/gotime"
"gorm.io/gorm"
"log"
)
// CheckManyTask 多任务检查
func (j *JobsGorm) CheckManyTask(tx *gorm.DB, vs []jobs_gorm_model.Task) {
if len(vs) > 0 {
for _, v := range vs {
diffInSecondWithAbs := gotime.Current().DiffInSecondWithAbs(gotime.SetCurrent(v.UpdatedAt).Time)
if diffInSecondWithAbs >= v.Frequency*3 {
if j.config.debug == true {
log.Printf("每隔%v秒任务%v相差%v秒\n", v.Frequency, v.Id, diffInSecondWithAbs)
}
err := tx.Where("task_id = ?", v.Id).Where("run_id = ?", v.RunId).Delete(&jobs_gorm_model.TaskLogRun{}).Error
if err != nil {
log.Println("删除失败", err.Error())
}
}
}
}
}
// CheckSingleTask 单任务检查
func (j *JobsGorm) CheckSingleTask(tx *gorm.DB, v jobs_gorm_model.Task) {
diffInSecondWithAbs := gotime.Current().DiffInSecondWithAbs(gotime.SetCurrent(v.UpdatedAt).Time)
if diffInSecondWithAbs >= v.Frequency*3 {
if j.config.debug == true {
log.Printf("每隔%v秒任务%v相差%v秒\n", v.Frequency, v.Id, diffInSecondWithAbs)
}
err := tx.Where("task_id = ?", v.Id).Where("run_id = ?", v.RunId).Delete(&jobs_gorm_model.TaskLogRun{}).Error
if err != nil {
log.Println("删除失败", err.Error())
}
}
}

@ -1,21 +0,0 @@
package gojobs
import (
"github.com/go-redis/redis/v9"
"gorm.io/gorm"
)
// GetDb 获取数据库驱动
func (j *JobsGorm) GetDb() *gorm.DB {
return j.gormClient.Db
}
// GetRedis 获取缓存数据库驱动
func (j *JobsGorm) GetRedis() *redis.Client {
return j.redisClient.Db
}
// GetCurrentIp 获取当前ip
func (j *JobsGorm) GetCurrentIp() string {
return j.config.outsideIp
}

@ -1,21 +0,0 @@
package gojobs
import (
"context"
"go.dtapp.net/goip"
"go.dtapp.net/gojobs/jobs_gorm_model"
"gorm.io/gorm"
)
// RefreshIp 刷新Ip
func (j *JobsGorm) RefreshIp(ctx context.Context, tx *gorm.DB) {
xip := goip.GetOutsideIp(ctx)
if j.config.outsideIp == "" || j.config.outsideIp == "0.0.0.0" {
return
}
if j.config.outsideIp == xip {
return
}
tx.Where("ips = ?", j.config.outsideIp).Delete(&jobs_gorm_model.TaskIp{}) // 删除
j.config.outsideIp = xip
}

@ -1,23 +0,0 @@
package gojobs
import (
"context"
"fmt"
"go.dtapp.net/gojobs/jobs_gorm_model"
"time"
)
// Lock 上锁
func (j *JobsGorm) Lock(ctx context.Context, info jobs_gorm_model.Task, id any) (string, error) {
return j.lockClient.Lock(ctx, fmt.Sprintf("%s%s%v%s%v", j.config.lockKeyPrefix, j.config.lockKeySeparator, info.Type, j.config.lockKeySeparator, id), fmt.Sprintf("已在%s@%s机器上锁成功", j.config.insideIp, j.config.outsideIp), time.Duration(info.Frequency)*3*time.Second)
}
// Unlock Lock 解锁
func (j *JobsGorm) Unlock(ctx context.Context, info jobs_gorm_model.Task, id any) error {
return j.lockClient.Unlock(ctx, fmt.Sprintf("%s%s%v%s%v", j.config.lockKeyPrefix, j.config.lockKeySeparator, info.Type, j.config.lockKeySeparator, id))
}
// LockForever 永远上锁
func (j *JobsGorm) LockForever(ctx context.Context, info jobs_gorm_model.Task, id any) (string, error) {
return j.lockClient.LockForever(ctx, fmt.Sprintf("%s%s%v%s%v", j.config.lockKeyPrefix, j.config.lockKeySeparator, info.Type, j.config.lockKeySeparator, id), fmt.Sprintf("已在%s@%s机器永远上锁成功", j.config.insideIp, j.config.outsideIp))
}

@ -1,196 +0,0 @@
package gojobs
import (
"go.dtapp.net/gojobs/jobs_gorm_model"
"gorm.io/gorm"
"log"
"strings"
)
// TaskTakeId 查询单任务
func (j *JobsGorm) TaskTakeId(tx *gorm.DB, id uint) (result jobs_gorm_model.Task) {
tx.Where("id = ?", id).Take(&result)
return result
}
// TaskTake 查询单任务
func (j *JobsGorm) TaskTake(tx *gorm.DB, customId string) (result jobs_gorm_model.Task) {
tx.Where("custom_id = ?", customId).Take(&result)
return result
}
// 查询单任务
func (j *JobsGorm) taskTake(tx *gorm.DB, customId, status string) (result jobs_gorm_model.Task) {
tx.Where("custom_id = ?", customId).Where("status = ?", status).Take(&result)
return result
}
// TaskTakeIn 查询单任务 - 任务运行
func (j *JobsGorm) TaskTakeIn(tx *gorm.DB, customId string) jobs_gorm_model.Task {
return j.taskTake(tx, customId, TASK_IN)
}
// TaskTakeSuccess 查询单任务 - 任务完成
func (j *JobsGorm) TaskTakeSuccess(tx *gorm.DB, customId string) jobs_gorm_model.Task {
return j.taskTake(tx, customId, TASK_SUCCESS)
}
// TaskTakeError 查询单任务 - 任务异常
func (j *JobsGorm) TaskTakeError(tx *gorm.DB, customId string) jobs_gorm_model.Task {
return j.taskTake(tx, customId, TASK_ERROR)
}
// TaskTakeTimeout 查询单任务 - 任务超时
func (j *JobsGorm) TaskTakeTimeout(tx *gorm.DB, customId string) jobs_gorm_model.Task {
return j.taskTake(tx, customId, TASK_TIMEOUT)
}
// TaskTakeWait 查询单任务 - 任务等待
func (j *JobsGorm) TaskTakeWait(tx *gorm.DB, customId string) jobs_gorm_model.Task {
return j.taskTake(tx, customId, TASK_WAIT)
}
// TaskTypeTake 查询单任务
func (j *JobsGorm) TaskTypeTake(tx *gorm.DB, customId, Type string) (result jobs_gorm_model.Task) {
tx.Where("custom_id = ?", customId).Where("type = ?", Type).Take(&result)
return result
}
// 查询单任务
func (j *JobsGorm) taskTypeTake(tx *gorm.DB, customId, Type, status string) (result jobs_gorm_model.Task) {
tx.Where("custom_id = ?", customId).Where("type = ?", Type).Where("status = ?", status).Take(&result)
return result
}
// TaskTypeTakeIn 查询单任务 - 任务运行
func (j *JobsGorm) TaskTypeTakeIn(tx *gorm.DB, customId, Type string) jobs_gorm_model.Task {
return j.taskTypeTake(tx, customId, Type, TASK_IN)
}
// TaskTypeTakeSuccess 查询单任务 - 任务完成
func (j *JobsGorm) TaskTypeTakeSuccess(tx *gorm.DB, customId, Type string) jobs_gorm_model.Task {
return j.taskTypeTake(tx, customId, Type, TASK_SUCCESS)
}
// TaskTypeTakeError 查询单任务 - 任务异常
func (j *JobsGorm) TaskTypeTakeError(tx *gorm.DB, customId, Type string) jobs_gorm_model.Task {
return j.taskTypeTake(tx, customId, Type, TASK_ERROR)
}
// TaskTypeTakeTimeout 查询单任务 - 任务超时
func (j *JobsGorm) TaskTypeTakeTimeout(tx *gorm.DB, customId, Type string) jobs_gorm_model.Task {
return j.taskTypeTake(tx, customId, Type, TASK_TIMEOUT)
}
// TaskTypeTakeWait 查询单任务 - 任务等待
func (j *JobsGorm) TaskTypeTakeWait(tx *gorm.DB, customId, Type string) jobs_gorm_model.Task {
return j.taskTypeTake(tx, customId, Type, TASK_WAIT)
}
// TaskFindAll 查询多任务
func (j *JobsGorm) TaskFindAll(tx *gorm.DB, frequency int64) (results []jobs_gorm_model.Task) {
tx.Where("frequency = ?", frequency).Order("id asc").Find(&results)
return results
}
// 查询多任务
func (j *JobsGorm) taskFindAll(tx *gorm.DB, frequency int64, status string) (results []jobs_gorm_model.Task) {
tx.Where("frequency = ?", frequency).Where("status = ?", status).Order("id asc").Find(&results)
return results
}
// TaskFindAllIn 查询多任务 - 任务运行
func (j *JobsGorm) TaskFindAllIn(tx *gorm.DB, frequency int64) []jobs_gorm_model.Task {
return j.taskFindAll(tx, frequency, TASK_IN)
}
// TaskFindAllSuccess 查询多任务 - 任务完成
func (j *JobsGorm) TaskFindAllSuccess(tx *gorm.DB, frequency int64) []jobs_gorm_model.Task {
return j.taskFindAll(tx, frequency, TASK_SUCCESS)
}
// TaskFindAllError 查询多任务 - 任务异常
func (j *JobsGorm) TaskFindAllError(tx *gorm.DB, frequency int64) []jobs_gorm_model.Task {
return j.taskFindAll(tx, frequency, TASK_ERROR)
}
// TaskFindAllTimeout 查询多任务 - 任务超时
func (j *JobsGorm) TaskFindAllTimeout(tx *gorm.DB, frequency int64) []jobs_gorm_model.Task {
return j.taskFindAll(tx, frequency, TASK_TIMEOUT)
}
// TaskFindAllWait 查询多任务 - 任务等待
func (j *JobsGorm) TaskFindAllWait(tx *gorm.DB, frequency int64) []jobs_gorm_model.Task {
return j.taskFindAll(tx, frequency, TASK_WAIT)
}
// EditTask 任务修改
func (j *JobsGorm) EditTask(tx *gorm.DB, id uint) *gorm.DB {
return tx.Model(&jobs_gorm_model.Task{}).Where("id = ?", id)
}
// UpdateFrequency 更新任务频率
func (j *JobsGorm) UpdateFrequency(tx *gorm.DB, id uint, frequency int64) *gorm.DB {
return j.EditTask(tx, id).
Select("frequency").
Updates(jobs_gorm_model.Task{
Frequency: frequency,
})
}
func (j *JobsGorm) taskIpTake(tx *gorm.DB, taskType, ips string) (result jobs_gorm_model.TaskIp) {
tx.Where("task_type = ?", taskType).Where("ips = ?", ips).Take(&result)
return result
}
// TaskIpUpdate 更新ip
func (j *JobsGorm) TaskIpUpdate(tx *gorm.DB, taskType, ips string) *gorm.DB {
query := j.taskIpTake(tx, taskType, ips)
if query.Id != 0 {
return tx
}
updateStatus := tx.Create(&jobs_gorm_model.TaskIp{
TaskType: taskType,
Ips: ips,
})
if updateStatus.RowsAffected == 0 {
log.Println("任务更新失败:", updateStatus.Error)
}
return updateStatus
}
// TaskIpInit 实例任务ip
func (j *JobsGorm) TaskIpInit(tx *gorm.DB, ips map[string]string) bool {
if j.config.outsideIp == "" || j.config.outsideIp == "0.0.0.0" {
return false
}
tx.Where("ips = ?", j.config.outsideIp).Delete(&jobs_gorm_model.TaskIp{}) // 删除
for k, v := range ips {
if v == "" {
j.TaskIpUpdate(tx, k, j.config.outsideIp)
} else {
find := strings.Contains(v, ",")
if find == true {
// 包含
parts := strings.Split(v, ",")
for _, vv := range parts {
if vv == j.config.outsideIp {
j.TaskIpUpdate(tx, k, j.config.outsideIp)
}
}
} else {
// 不包含
if v == j.config.outsideIp {
j.TaskIpUpdate(tx, k, j.config.outsideIp)
}
}
}
}
return true
}
// TaskLogRunTake 查询任务执行日志
func (j *JobsGorm) TaskLogRunTake(tx *gorm.DB, taskId uint, runId string) (result jobs_gorm_model.TaskLogRun) {
tx.Select("id", "os", "arch", "outside_ip", "created_at").Where("task_id = ?", taskId).Where("run_id = ?", runId).Take(&result)
return result
}

@ -7,28 +7,29 @@ import (
// Task 任务
type Task struct {
Id uint `gorm:"primaryKey;comment:记录编号" json:"id"` // 记录编号
Status string `gorm:"index;comment:状态码" json:"status"` // 状态码
Params string `gorm:"comment:参数" json:"params"` // 参数
ParamsType string `gorm:"comment:参数类型" json:"params_type"` // 参数类型
StatusDesc string `gorm:"comment:状态描述" json:"status_desc"` // 状态描述
Frequency int64 `gorm:"index;comment:频率(秒单位)" json:"frequency"` // 频率(秒单位)
Number int64 `gorm:"comment:当前次数" json:"number"` // 当前次数
MaxNumber int64 `gorm:"comment:最大次数" json:"max_number"` // 最大次数
RunId string `gorm:"index;comment:执行编号" json:"run_id"` // 执行编号
CustomId string `gorm:"index;comment:自定义编号" json:"custom_id"` // 自定义编号
CustomSequence int64 `gorm:"index;comment:自定义顺序" json:"custom_sequence"` // 自定义顺序
Type string `gorm:"index;comment:类型" json:"type"` // 类型
TypeName string `gorm:"comment:类型名称" json:"type_name"` // 类型名称
CreatedIp string `gorm:"comment:创建外网IP" json:"created_ip"` // 创建外网IP
SpecifyIp string `gorm:"index;comment:指定外网IP" json:"specify_ip"` // 指定外网IP
UpdatedIp string `gorm:"comment:更新外网IP" json:"updated_ip"` // 更新外网IP
Result string `gorm:"comment:结果" json:"result"` // 结果
CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt time.Time `gorm:"autoUpdateTime;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"index;comment:删除时间" json:"deleted_at"` // 删除时间
Id uint `gorm:"primaryKey;comment:记录编号" json:"id"` // 记录编号
Status string `gorm:"index;comment:状态码" json:"status"` // 状态码
Params string `gorm:"comment:参数" json:"params"` // 参数
ParamsType string `gorm:"comment:参数类型" json:"params_type"` // 参数类型
StatusDesc string `gorm:"comment:状态描述" json:"status_desc"` // 状态描述
Frequency int64 `gorm:"index;comment:频率(秒单位)" json:"frequency"` // 频率(秒单位)
Number int64 `gorm:"comment:当前次数" json:"number"` // 当前次数
MaxNumber int64 `gorm:"comment:最大次数" json:"max_number"` // 最大次数
RunId string `gorm:"index;comment:执行编号" json:"run_id"` // 执行编号
CustomId string `gorm:"index;comment:自定义编号" json:"custom_id"` // 自定义编号
CustomSequence int64 `gorm:"index;comment:自定义顺序" json:"custom_sequence"` // 自定义顺序
Type string `gorm:"index;comment:类型" json:"type"` // 类型
TypeName string `gorm:"comment:类型名称" json:"type_name"` // 类型名称
CreatedIp string `gorm:"default:0.0.0.0;comment:创建外网IP" json:"created_ip"` // 创建外网IP
SpecifyIp string `gorm:"default:0.0.0.0;index;comment:指定外网IP" json:"specify_ip"` // 指定外网IP
UpdatedIp string `gorm:"default:0.0.0.0;comment:更新外网IP" json:"updated_ip"` // 更新外网IP
Result string `gorm:"comment:结果" json:"result"` // 结果
NextRunTime time.Time `gorm:"comment:下次运行时间" json:"next_run_time"` // 下次运行时间
CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 创建时间
UpdatedAt time.Time `gorm:"autoUpdateTime;comment:更新时间" json:"updated_at"` // 更新时间
DeletedAt gorm.DeletedAt `gorm:"index;comment:删除时间" json:"deleted_at"` // 删除时间
}
func (m *Task) TableName() string {
func (Task) TableName() string {
return "task"
}

@ -1,12 +0,0 @@
package jobs_gorm_model
// TaskIp 任务Ip
type TaskIp struct {
Id int64 `gorm:"primaryKey;comment:记录编号" json:"id"` // 记录编号
TaskType string `gorm:"comment:任务编号" json:"task_type"` // 任务编号
Ips string `gorm:"comment:任务IP" json:"ips"` // 任务IP
}
func (m *TaskIp) TableName() string {
return "task_ip"
}

@ -4,14 +4,21 @@ import "time"
// TaskLog 任务日志模型
type TaskLog struct {
Id uint `gorm:"primaryKey;comment:记录编号" json:"id"` // 记录编号
TaskId uint `gorm:"index;comment:任务编号" json:"task_id"` // 任务编号
StatusCode int `gorm:"index;comment:状态码" json:"status_code"` // 状态码
Desc string `gorm:"comment:结果" json:"desc"` // 结果
Version string `gorm:"comment:版本" json:"version"` // 版本
CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 创建时间
LogId uint `gorm:"primaryKey;comment:日志编号" json:"log_id"` // 日志编号
TaskId uint `gorm:"index;comment:任务编号" json:"task_id"` // 任务编号
TaskRunId string `gorm:"comment:执行编号" json:"task_run_id"` // 执行编号
TaskResultCode int `gorm:"index;comment:执行状态码" json:"task_result_code"` // 执行状态码
TaskResultDesc string `gorm:"comment:执行结果" json:"task_result_desc"` // 执行结果
SystemHostName string `gorm:"comment:主机名" json:"system_host_name"` // 主机名
SystemInsideIp string `gorm:"default:0.0.0.0;comment:内网ip" json:"system_inside_ip"` // 内网ip
SystemOs string `gorm:"comment:系统类型" json:"system_os"` // 系统类型
SystemArch string `gorm:"comment:系统架构" json:"system_arch"` // 系统架构
GoVersion string `gorm:"comment:go版本" json:"go_version"` // go版本
SdkVersion string `gorm:"comment:sdk版本" json:"sdk_version"` // sdk版本
SystemOutsideIp string `gorm:"default:0.0.0.0;comment:外网ip" json:"system_outside_ip"` // 外网ip
LogTime time.Time `gorm:"autoCreateTime;comment:日志时间" json:"log_time"` // 日志时间
}
func (m *TaskLog) TableName() string {
func (TaskLog) TableName() string {
return "task_log"
}

@ -1,22 +0,0 @@
package jobs_gorm_model
import "time"
// TaskLogRun 任务执行日志模型
type TaskLogRun struct {
Id uint `gorm:"primaryKey;comment:记录编号" json:"id"` // 记录编号
TaskId uint `gorm:"index;comment:任务编号" json:"task_id"` // 任务编号
RunId string `gorm:"comment:执行编号" json:"run_id"` // 执行编号
OutsideIp string `gorm:"comment:外网ip" json:"outside_ip"` // 外网ip
InsideIp string `gorm:"comment:内网ip" json:"inside_ip"` // 内网ip
Os string `gorm:"comment:系统类型" json:"os"` // 系统类型
Arch string `gorm:"comment:系统架构" json:"arch"` // 系统架构
Gomaxprocs int `gorm:"comment:CPU核数" json:"gomaxprocs"` // CPU核数
GoVersion string `gorm:"comment:GO版本" json:"go_version"` // GO版本
MacAddrs string `gorm:"comment:Mac地址" json:"mac_addrs"` // Mac地址
CreatedAt time.Time `gorm:"autoCreateTime;comment:创建时间" json:"created_at"` // 创建时间
}
func (m *TaskLogRun) TableName() string {
return "task_log_run"
}

@ -1,38 +0,0 @@
package gojobs
import (
"context"
"github.com/go-redis/redis/v9"
"log"
)
// Publish 发布
// ctx 上下文
// channel 频道
// message 消息
func (j *JobsGorm) Publish(ctx context.Context, channel string, message interface{}) error {
publish, err := j.redisClient.Publish(ctx, channel, message).Result()
if j.config.debug == true {
log.Println("gojobs.Publish", channel, message, publish, err)
}
return err
}
type SubscribeResult struct {
err error
Message *redis.PubSub
}
// Subscribe 订阅
func (j *JobsGorm) Subscribe(ctx context.Context) SubscribeResult {
return SubscribeResult{
Message: j.redisClient.Subscribe(ctx, j.config.cornKeyPrefix+"_"+j.config.cornKeyCustom),
}
}
// PSubscribe 订阅,支持通配符匹配(ch_user_*)
func (j *JobsGorm) PSubscribe(ctx context.Context) SubscribeResult {
return SubscribeResult{
Message: j.redisClient.PSubscribe(ctx, j.config.cornKeyPrefix+"_"+j.config.cornKeyCustom+"_*"),
}
}

@ -1,96 +0,0 @@
package gojobs
import (
"context"
"errors"
"fmt"
"go.dtapp.net/gojobs/jobs_gorm_model"
"go.dtapp.net/gostring"
"log"
"math/rand"
"time"
)
// GetIssueAddress 获取下发地址
// workers 在线列表
// v 任务信息
// ---
// address 下发地址
// err 错误信息
func (j *JobsGorm) GetIssueAddress(workers []string, v *jobs_gorm_model.Task) (address string, err error) {
var (
currentIp = "" // 当前Ip
appointIpStatus = false // 指定Ip状态
)
// 赋值ip
if v.SpecifyIp != "" {
currentIp = v.SpecifyIp
appointIpStatus = true
}
// 只有一个客户端在线
if len(workers) == 1 {
if appointIpStatus == true {
// 判断是否指定某ip执行
if gostring.Contains(workers[0], currentIp) == true {
return j.config.cornKeyPrefix + "_" + v.SpecifyIp, nil
}
return address, errors.New(fmt.Sprintf("需要执行的[%s]客户端不在线", currentIp))
}
return j.config.cornKeyPrefix + "_" + workers[0], nil
}
// 优先处理指定某ip执行
if appointIpStatus == true {
for wk, wv := range workers {
if gostring.Contains(wv, currentIp) == true {
return j.config.cornKeyPrefix + "_" + workers[wk], nil
}
}
return address, errors.New(fmt.Sprintf("需要执行的[%s]客户端不在线", currentIp))
} else {
// 随机返回一个
zxIp := workers[j.random(0, len(workers))]
if zxIp == "" {
return address, errors.New("获取执行的客户端异常")
}
address = j.config.cornKeyPrefix + "_" + zxIp
return address, err
}
}
// GetSubscribeClientList 获取在线的客户端
func (j *JobsGorm) GetSubscribeClientList(ctx context.Context) ([]string, error) {
if j.config.debug == true {
log.Printf("获取在线的客户端:%s\n", j.config.cornKeyPrefix+"_*")
}
// 扫描
values, err := j.redisClient.Keys(ctx, j.config.cornKeyPrefix+"_*").Result()
if err != nil {
if err == errors.New("ERR wrong number of arguments for 'mget' command") {
return []string{}, nil
}
return nil, errors.New(fmt.Sprintf("获取失败:%s", err.Error()))
}
client := make([]string, 0, len(values))
for _, val := range values {
client = append(client, val.(string))
}
return client, nil
}
// 随机返回一个
// min最小
// max最大
func (j *JobsGorm) random(min, max int) int {
if max-min <= 0 {
return 0
}
rand.Seed(time.Now().Unix())
return rand.Intn(max-min) + min
}

@ -1,22 +0,0 @@
package gojobs
import (
"context"
"github.com/robfig/cron/v3"
"log"
"time"
)
// Ping 心跳
func (j *JobsGorm) Ping(ctx context.Context) {
c := cron.New(cron.WithSeconds())
_, _ = c.AddFunc(GetSeconds(2).Spec(), func() {
result, err := j.redisClient.Set(ctx, j.config.cornKeyPrefix+"_"+j.config.cornKeyCustom, j.config.cornKeyCustom, 3*time.Second).Result()
if j.config.debug == true {
log.Println("JOBS心跳", j.config.cornKeyPrefix+"_"+j.config.cornKeyCustom, j.config.cornKeyCustom, result, err)
}
})
c.Start()
defer c.Stop()
select {}
}

@ -1,112 +0,0 @@
package gojobs
import (
"context"
"fmt"
"github.com/go-redis/redis/v9"
"go.dtapp.net/gotime"
"testing"
)
import "go.dtapp.net/dorm"
// 下发
func TestRedisPublish(t *testing.T) {
client, err := dorm.NewRedisClient(&dorm.ConfigRedisClient{
Addr: "119.29.14.159:6379",
Password: "980202",
DB: 5,
})
if err != nil {
t.Error(err)
}
publish, err := client.Db.Publish(context.Background(), "ch_user_1", "测试").Result()
t.Log(publish)
t.Log(err)
}
// 订阅
func TestRedisPSubscribe(t *testing.T) {
client, err := dorm.NewRedisClient(&dorm.ConfigRedisClient{
Addr: "119.29.14.159:6379",
Password: "980202",
DB: 5,
})
if err != nil {
t.Error(err)
}
// 订阅channel1这个channel
sub := client.Db.PSubscribe(context.Background(), "ch_user_*")
t.Log(sub)
// sub.Channel() 返回go channel可以循环读取redis服务器发过来的消息
for msg := range sub.Channel() {
// 打印收到的消息
t.Log(msg.Channel)
t.Log(msg.Payload)
}
}
// 添加一个或者多个元素到集合,如果元素已经存在则更新分数
func TestRedisZAdd(t *testing.T) {
client, err := dorm.NewRedisClient(&dorm.ConfigRedisClient{
Addr: "119.29.14.159:6379",
Password: "980202",
DB: 5,
})
if err != nil {
t.Error(err)
}
// 删除小于当前时间的客户端
client.Db.ZRemRangeByScore(context.Background(), "cron_jobs", "0", fmt.Sprintf("(%v", gotime.Current().Timestamp())).Result()
// 添加客户端或者更新客户端
publish, err := client.Db.ZAdd(context.Background(), "cron_jobs", redis.Z{
Score: float64(gotime.Current().AfterMinute(10).Timestamp()),
Member: "127.0.0.1",
}).Result()
t.Log(publish)
t.Log(err)
}
// 返回集合中某个索引范围的元素,根据分数从小到大排序
func TestRedisZRange(t *testing.T) {
client, err := dorm.NewRedisClient(&dorm.ConfigRedisClient{
Addr: "119.29.14.159:6379",
Password: "980202",
DB: 5,
})
if err != nil {
t.Error(err)
}
vals, err := client.Db.ZRange(context.Background(), "cron_jobs", 0, -1).Result()
t.Log(err)
for _, val := range vals {
t.Log(val)
}
}
func TestRedisKeys(t *testing.T) {
client, err := dorm.NewRedisClient(&dorm.ConfigRedisClient{
Addr: "119.29.14.159:6379",
Password: "980202",
DB: 5,
})
if err != nil {
t.Error(err)
}
client.Db.Set(context.Background(), "cron_jobs_client_127.0.0.1", "127.0.0.1", 0)
client.Db.Set(context.Background(), "cron_jobs_client_43.135.79.235", "43.135.79.235", 0)
result, err := client.Keys(context.Background(), "cron_jobs_client_*").Result()
t.Log(result)
t.Log(err)
for _, val := range result {
t.Log(val)
}
}

@ -1,13 +0,0 @@
package gojobs
import "testing"
func TestJobs1(t *testing.T) {
//t.Log(NewJobsGorm(&ConfigJobsGorm{
// MainService: 0,
// Db: nil,
// Redis: nil,
//}).Run())
//t.Log(NewJobsXorm().Run())
//t.Log(NewJobsZorm().Run())
}

@ -1,21 +0,0 @@
package gojobs
import "xorm.io/xorm"
// Xorm数据库驱动
type jobsXorm struct {
db *xorm.Engine
}
// NewJobsXorm 初始化
func NewJobsXorm(db *xorm.Engine) *jobsXorm {
var (
j = &jobsXorm{}
)
j.db = db
return j
}
func (j *jobsXorm) Run() {
}

@ -1,28 +0,0 @@
package jobs_xorm_model
// Task 任务
type Task struct {
Id uint `xorm:"pk autoincr" json:"id"` // 记录编号
Status string `json:"status"` // 状态码
Params string `json:"params"` // 参数
ParamsType string `json:"params_type"` // 参数类型
StatusDesc string `json:"status_desc"` // 状态描述
Frequency int64 `json:"frequency"` // 频率(秒单位)
Number int64 `json:"number"` // 当前次数
MaxNumber int64 `json:"max_number"` // 最大次数
RunId string `json:"run_id"` // 执行编号
CustomId string `json:"custom_id"` // 自定义编号
CustomSequence int64 `json:"custom_sequence"` // 自定义顺序
Type string `json:"type"` // 类型
CreatedIp string `json:"created_ip"` // 创建外网IP
SpecifyIp string `json:"specify_ip"` // 指定外网IP
UpdatedIp string `json:"updated_ip"` // 更新外网IP
Result string `json:"result"` // 结果
CreatedAt string `xorm:"created" json:"created_at"` // 创建时间
UpdatedAt string `xorm:"created" json:"updated_at"` // 更新时间
DeletedAt string `xorm:"deleted" json:"deleted_at"` // 删除时间
}
func (m *Task) TableName() string {
return "task"
}

@ -1,12 +0,0 @@
package jobs_xorm_model
// TaskIp 任务Ip
type TaskIp struct {
Id int64 `xorm:"pk autoincr" json:"id"`
TaskType string `json:"task_type"` // 任务编号
Ips string `json:"ips"` // 任务IP
}
func (m *TaskIp) TableName() string {
return "task_ip"
}

@ -1,15 +0,0 @@
package jobs_xorm_model
// TaskLog 任务日志模型
type TaskLog struct {
Id uint `xorm:"pk autoincr" json:"id"` // 记录编号
TaskId uint `json:"task_id"` // 任务编号
StatusCode int `json:"status_code"` // 状态码
Desc string `json:"desc"` // 结果
Version int `json:"version"` // 版本
CreatedAt string `xorm:"created" json:"created_at"` // 创建时间
}
func (m *TaskLog) TableName() string {
return "task_log"
}

@ -1,20 +0,0 @@
package jobs_xorm_model
// TaskLogRun 任务执行日志模型
type TaskLogRun struct {
Id uint `xorm:"pk autoincr" json:"id"` // 记录编号
TaskId uint `json:"task_id"` // 任务编号
RunId string `json:"run_id"` // 执行编号
OutsideIp string `json:"outside_ip"` // 外网ip
InsideIp string `json:"inside_ip"` // 内网ip
Os string `json:"os"` // 系统类型
Arch string `json:"arch"` // 系统架构
Gomaxprocs int `json:"gomaxprocs"` // CPU核数
GoVersion string `json:"go_version"` // GO版本
MacAddrs string `json:"mac_addrs"` // Mac地址
CreatedAt string `xorm:"created" json:"created_at"` // 创建时间
}
func (m *TaskLogRun) TableName() string {
return "task_log_run"
}

@ -0,0 +1,24 @@
package gojobs
import (
"context"
"fmt"
"go.dtapp.net/gojobs/jobs_gorm_model"
"go.dtapp.net/gotime"
"time"
)
// Lock 上锁
func (c *Client) Lock(ctx context.Context, info jobs_gorm_model.Task, id any) (string, error) {
return c.cache.redisLockClient.Lock(ctx, fmt.Sprintf("%s%s%v%s%v", c.cache.lockKeyPrefix, c.cache.lockKeySeparator, info.Type, c.cache.lockKeySeparator, id), fmt.Sprintf("[Lock] 已在%s@%s机器上锁成功时间%v", c.config.systemInsideIp, c.config.systemOutsideIp, gotime.Current().Format()), time.Duration(info.Frequency)*3*time.Second)
}
// Unlock Lock 解锁
func (c *Client) Unlock(ctx context.Context, info jobs_gorm_model.Task, id any) error {
return c.cache.redisLockClient.Unlock(ctx, fmt.Sprintf("%s%s%v%s%v", c.cache.lockKeyPrefix, c.cache.lockKeySeparator, info.Type, c.cache.lockKeySeparator, id))
}
// LockForever 永远上锁
func (c *Client) LockForever(ctx context.Context, info jobs_gorm_model.Task, id any) (string, error) {
return c.cache.redisLockClient.LockForever(ctx, fmt.Sprintf("%s%s%v%s%v", c.cache.lockKeyPrefix, c.cache.lockKeySeparator, info.Type, c.cache.lockKeySeparator, id), fmt.Sprintf("[LockForever] 已在%s@%s机器永远上锁成功时间%v", c.config.systemInsideIp, c.config.systemOutsideIp, gotime.Current().Format()))
}

@ -0,0 +1,24 @@
package gojobs
import (
"context"
"fmt"
"go.dtapp.net/gojobs/jobs_gorm_model"
"go.dtapp.net/gotime"
"time"
)
// LockCustomId 上锁
func (c *Client) LockCustomId(ctx context.Context, info jobs_gorm_model.Task) (string, error) {
return c.cache.redisLockClient.Lock(ctx, fmt.Sprintf("%s%s%v%s%v", c.cache.lockKeyPrefix, c.cache.lockKeySeparator, info.Type, c.cache.lockKeySeparator, info.CustomId), fmt.Sprintf("[LockCustomId] 已在%s@%s机器上锁成功时间%v", c.config.systemInsideIp, c.config.systemOutsideIp, gotime.Current().Format()), time.Duration(info.Frequency)*3*time.Second)
}
// UnlockCustomId 解锁
func (c *Client) UnlockCustomId(ctx context.Context, info jobs_gorm_model.Task) error {
return c.cache.redisLockClient.Unlock(ctx, fmt.Sprintf("%s%s%v%s%v", c.cache.lockKeyPrefix, c.cache.lockKeySeparator, info.Type, c.cache.lockKeySeparator, info.CustomId))
}
// LockForeverCustomId 永远上锁
func (c *Client) LockForeverCustomId(ctx context.Context, info jobs_gorm_model.Task) (string, error) {
return c.cache.redisLockClient.LockForever(ctx, fmt.Sprintf("%s%s%v%s%v", c.cache.lockKeyPrefix, c.cache.lockKeySeparator, info.Type, c.cache.lockKeySeparator, info.CustomId), fmt.Sprintf("[LockCustomId] 已在%s@%s机器永远上锁成功时间%v", c.config.systemInsideIp, c.config.systemOutsideIp, gotime.Current().Format()))
}

@ -0,0 +1,24 @@
package gojobs
import (
"context"
"fmt"
"go.dtapp.net/gojobs/jobs_gorm_model"
"go.dtapp.net/gotime"
"time"
)
// LockId 上锁
func (c *Client) LockId(ctx context.Context, info jobs_gorm_model.Task) (string, error) {
return c.cache.redisLockClient.Lock(ctx, fmt.Sprintf("%s%s%v%s%v", c.cache.lockKeyPrefix, c.cache.lockKeySeparator, info.Type, c.cache.lockKeySeparator, info.Id), fmt.Sprintf("[LockId] 已在%s@%s机器上锁成功时间%v", c.config.systemInsideIp, c.config.systemOutsideIp, gotime.Current().Format()), time.Duration(info.Frequency)*3*time.Second)
}
// UnlockId 解锁
func (c *Client) UnlockId(ctx context.Context, info jobs_gorm_model.Task) error {
return c.cache.redisLockClient.Unlock(ctx, fmt.Sprintf("%s%s%v%s%v", c.cache.lockKeyPrefix, c.cache.lockKeySeparator, info.Type, c.cache.lockKeySeparator, info.Id))
}
// LockForeverId 永远上锁
func (c *Client) LockForeverId(ctx context.Context, info jobs_gorm_model.Task) (string, error) {
return c.cache.redisLockClient.LockForever(ctx, fmt.Sprintf("%s%s%v%s%v", c.cache.lockKeyPrefix, c.cache.lockKeySeparator, info.Type, c.cache.lockKeySeparator, info.Id), fmt.Sprintf("[LockForeverId] 已在%s@%s机器永远上锁成功时间%v", c.config.systemInsideIp, c.config.systemOutsideIp, gotime.Current().Format()))
}

@ -1,51 +0,0 @@
package gojobs
import (
"context"
"github.com/robfig/cron/v3"
"go.dtapp.net/dorm"
"log"
"testing"
)
func TestMasterRedis(t *testing.T) {
client, err := dorm.NewRedisClient(&dorm.ConfigRedisClient{
Addr: "119.29.14.159:6379",
Password: "980202",
DB: 5,
})
if err != nil {
t.Error(err)
}
// 创建一个cron实例 精确到秒
c := cron.New(cron.WithSeconds())
// 每隔5秒执行一次
_, _ = c.AddFunc(GetSeconds(5).Spec(), func() {
log.Println("每隔5秒执行一次")
publish, err := client.Db.Publish(context.Background(), "test_cron_127.0.0.1", "每隔5秒执行一次").Result()
t.Log(publish)
t.Log(err)
})
// 每隔10秒执行一次
_, _ = c.AddFunc(GetSeconds(10).Spec(), func() {
log.Println("每隔10秒执行一次")
publish, err := client.Db.Publish(context.Background(), "test_cron_127.0.0.1", "每隔10秒执行一次").Result()
t.Log(publish)
t.Log(err)
})
// 启动任务
c.Start()
// 关闭任务
defer c.Stop()
select {}
}

@ -0,0 +1,66 @@
package gojobs
import (
"context"
"go.dtapp.net/gojobs/jobs_gorm_model"
"go.dtapp.net/gotime"
"go.dtapp.net/gotrace_id"
)
// 创建模型
func (c *Client) autoMigrateTask(ctx context.Context) {
err := c.gormClient.GetDb().AutoMigrate(&jobs_gorm_model.Task{})
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("创建模型:%s", err)
}
}
// 创建模型
func (c *Client) autoMigrateTaskLog(ctx context.Context) {
err := c.gormClient.GetDb().AutoMigrate(&jobs_gorm_model.TaskLog{})
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("创建模型:%s", err)
}
}
// GormTaskLogDelete 删除
func (c *Client) GormTaskLogDelete(ctx context.Context, hour int64) error {
err := c.gormClient.GetDb().Where("log_time < ?", gotime.Current().BeforeHour(hour).Format()).Delete(&jobs_gorm_model.TaskLog{}).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("删除失败:%s", err)
}
return err
}
// TaskLogRecord 记录
func (c *Client) TaskLogRecord(ctx context.Context, task jobs_gorm_model.Task, taskResultCode int, taskResultDesc string) {
runId := gotrace_id.GetTraceIdContext(ctx)
c.GormTaskLogRecord(ctx, task, runId, taskResultCode, taskResultDesc)
if c.mongoConfig.stats {
c.MongoTaskLogRecord(ctx, task, runId, taskResultCode, taskResultDesc)
}
}
// GormTaskLogRecord 记录
func (c *Client) GormTaskLogRecord(ctx context.Context, task jobs_gorm_model.Task, runId string, taskResultCode int, taskResultDesc string) {
taskLog := jobs_gorm_model.TaskLog{
TaskId: task.Id,
TaskRunId: runId,
TaskResultCode: taskResultCode,
TaskResultDesc: taskResultDesc,
SystemHostName: c.config.systemHostname,
SystemInsideIp: c.config.systemInsideIp,
SystemOs: c.config.systemOs,
SystemArch: c.config.systemKernel,
GoVersion: c.config.goVersion,
SdkVersion: c.config.sdkVersion,
SystemOutsideIp: c.config.systemOutsideIp,
}
err := c.gormClient.GetDb().Create(&taskLog).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("记录失败:%s", err)
c.zapLog.WithTraceId(ctx).Sugar().Errorf("记录数据:%+v", taskLog)
}
}

@ -0,0 +1,118 @@
package gojobs
import (
"context"
"go.dtapp.net/dorm"
"go.dtapp.net/gojobs/jobs_gorm_model"
"go.dtapp.net/golog"
"go.dtapp.net/gotime"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
// TaskLog 任务日志模型
type TaskLog struct {
LogId primitive.ObjectID `json:"log_id,omitempty" bson:"_id,omitempty"` //【记录】编号
LogTime primitive.DateTime `json:"log_time,omitempty" bson:"log_time"` //【记录】时间
Task struct {
Id uint `json:"id" bson:"id"` //【任务】编号
RunId string `json:"run_id" bson:"run_id"` //【任务】执行编号
ResultCode int `json:"result_code" bson:"result_code"` //【任务】执行状态码
ResultDesc string `json:"result_desc" bson:"result_desc"` //【任务】执行结果
ResultTime string `json:"result_time" bson:"result_time"` //【任务】执行时间
} `json:"task,omitempty" bson:"task,omitempty"` //【任务】信息
System struct {
Hostname string `json:"hostname" bson:"hostname"` //【系统】主机名
Os string `json:"os" bson:"os"` //【系统】系统类型
Version string `json:"version" bson:"version"` //【系统】系统版本
Kernel string `json:"kernel" bson:"kernel"` //【系统】系统内核
KernelVersion string `json:"kernel_version" bson:"kernel_version"` //【系统】系统内核版本
BootTime string `json:"boot_time" bson:"boot_time"` //【系统】系统开机时间
CpuCores int `json:"cpu_cores,omitempty" bson:"cpu_cores,omitempty"` //【系统】CPU核数
CpuModelName string `json:"cpu_model_name,omitempty" bson:"cpu_model_name,omitempty"` //【系统】CPU型号名称
CpuMhz float64 `json:"cpu_mhz,omitempty" bson:"cpu_mhz,omitempty"` //【系统】CPU兆赫
InsideIp string `json:"inside_ip" bson:"inside_ip"` //【系统】内网ip
OutsideIp string `json:"outside_ip" bson:"outside_ip"` //【系统】外网ip
GoVersion string `json:"go_version,omitempty" bson:"go_version,omitempty"` //【系统】go版本
SdkVersion string `json:"sdk_version,omitempty" bson:"sdk_version,omitempty"` //【系统】sdk版本
MongoVersion string `json:"mongo_version,omitempty" bson:"mongo_version,omitempty"` //【系统】mongo版本
MongoSdkVersion string `json:"mongo_sdk_version,omitempty" bson:"mongo_sdk_version,omitempty"` //【系统】mongo sdk版本
RedisVersion string `json:"redis_version,omitempty" bson:"redis_version,omitempty"` //【系统】redis版本
RedisSdkVersion string `json:"redis_sdk_version,omitempty" bson:"redis_sdk_version,omitempty"` //【系统】redis sdk版本
LogVersion string `json:"log_version,omitempty" bson:"log_version,omitempty"` //【系统】log版本
} `json:"system,omitempty" bson:"system,omitempty"` //【系统】信息
}
func (TaskLog) CollectionName() string {
return "task_log"
}
// 创建时间序列集合
func (TaskLog) createCollection(ctx context.Context, zapLog *golog.ZapLog, db *dorm.MongoClient, databaseName string) {
err := db.Database(databaseName).CreateCollection(ctx, TaskLog{}.CollectionName(), options.CreateCollection().SetTimeSeriesOptions(options.TimeSeries().SetTimeField("log_time")))
if err != nil {
zapLog.WithTraceId(ctx).Sugar().Errorf("创建时间序列集合:%s", err)
}
}
// 创建索引
func (TaskLog) createIndexes(ctx context.Context, zapLog *golog.ZapLog, db *dorm.MongoClient, databaseName string) {
_, err := db.Database(databaseName).Collection(TaskLog{}.CollectionName()).CreateManyIndexes(ctx, []mongo.IndexModel{{
Keys: bson.D{{
Key: "log_time",
Value: -1,
}},
}})
if err != nil {
zapLog.WithTraceId(ctx).Sugar().Errorf("创建索引:%s", err)
}
}
// MongoTaskLogRecord 记录
func (c *Client) MongoTaskLogRecord(ctx context.Context, task jobs_gorm_model.Task, runId string, taskResultCode int, taskResultDesc string) {
taskLog := TaskLog{
LogId: primitive.NewObjectID(),
LogTime: primitive.NewDateTimeFromTime(gotime.Current().Time),
}
taskLog.Task.Id = task.Id
taskLog.Task.RunId = runId
taskLog.Task.ResultCode = taskResultCode
taskLog.Task.ResultDesc = taskResultDesc
taskLog.Task.ResultTime = gotime.Current().Format()
taskLog.System.Hostname = c.config.systemHostname //【系统】主机名
taskLog.System.Os = c.config.systemOs //【系统】系统类型
taskLog.System.Version = c.config.systemVersion //【系统】系统版本
taskLog.System.Kernel = c.config.systemKernel //【系统】系统内核
taskLog.System.KernelVersion = c.config.systemKernelVersion //【系统】系统内核版本
taskLog.System.BootTime = gotime.SetCurrent(gotime.SetCurrentUnix(int64(c.config.systemBootTime)).Time).Format() //【系统】系统开机时间
taskLog.System.CpuCores = c.config.cpuCores //【系统】CPU核数
taskLog.System.CpuModelName = c.config.cpuModelName //【程序】CPU型号名称
taskLog.System.CpuMhz = c.config.cpuMhz //【系统】CPU兆赫
taskLog.System.InsideIp = c.config.systemInsideIp //【系统】内网ip
taskLog.System.OutsideIp = c.config.systemOutsideIp //【系统】外网ip
taskLog.System.GoVersion = c.config.goVersion //【系统】Go版本
taskLog.System.SdkVersion = c.config.sdkVersion //【系统】Sdk版本
taskLog.System.MongoVersion = c.config.mongoVersion //【系统】mongo版本
taskLog.System.MongoSdkVersion = c.config.mongoSdkVersion //【系统】mongo sdk版本
taskLog.System.RedisVersion = c.config.redisVersion //【系统】redis版本
taskLog.System.RedisSdkVersion = c.config.redisSdkVersion //【系统】redis sdk版本
taskLog.System.LogVersion = c.config.logVersion //【系统】log版本
_, err := c.mongoClient.Database(c.mongoConfig.databaseName).Collection(TaskLog{}.CollectionName()).InsertOne(ctx, taskLog)
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("记录失败:%s", err)
c.zapLog.WithTraceId(ctx).Sugar().Errorf("记录数据:%+v", taskLog)
}
}
// MongoTaskLogDelete 删除
func (c *Client) MongoTaskLogDelete(ctx context.Context, hour int64) (*mongo.DeleteResult, error) {
filter := bson.D{{"log_time", bson.D{{"$lt", primitive.NewDateTimeFromTime(gotime.Current().BeforeHour(hour).Time)}}}}
return c.mongoClient.Database(c.mongoConfig.databaseName).Collection(TaskLog{}.CollectionName()).DeleteMany(ctx, filter)
}

@ -1,221 +0,0 @@
// 版本
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.0
// protoc v3.19.4
// source: pb/basics.proto
// 包名
package pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// 请求消息
type BasicsRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
}
func (x *BasicsRequest) Reset() {
*x = BasicsRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_basics_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BasicsRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BasicsRequest) ProtoMessage() {}
func (x *BasicsRequest) ProtoReflect() protoreflect.Message {
mi := &file_pb_basics_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BasicsRequest.ProtoReflect.Descriptor instead.
func (*BasicsRequest) Descriptor() ([]byte, []int) {
return file_pb_basics_proto_rawDescGZIP(), []int{0}
}
func (x *BasicsRequest) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
// 响应消息
type BasicsResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
}
func (x *BasicsResponse) Reset() {
*x = BasicsResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_basics_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *BasicsResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*BasicsResponse) ProtoMessage() {}
func (x *BasicsResponse) ProtoReflect() protoreflect.Message {
mi := &file_pb_basics_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use BasicsResponse.ProtoReflect.Descriptor instead.
func (*BasicsResponse) Descriptor() ([]byte, []int) {
return file_pb_basics_proto_rawDescGZIP(), []int{1}
}
func (x *BasicsResponse) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
var File_pb_basics_proto protoreflect.FileDescriptor
var file_pb_basics_proto_rawDesc = []byte{
0x0a, 0x0f, 0x70, 0x62, 0x2f, 0x62, 0x61, 0x73, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x02, 0x70, 0x62, 0x22, 0x29, 0x0a, 0x0d, 0x42, 0x61, 0x73, 0x69, 0x63, 0x73, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
0x22, 0x2a, 0x0a, 0x0e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0x6a, 0x0a, 0x06,
0x42, 0x61, 0x73, 0x69, 0x63, 0x73, 0x12, 0x2f, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x11,
0x2e, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
0x74, 0x1a, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x73, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x2f, 0x0a, 0x04, 0x50, 0x6f, 0x6e, 0x67, 0x12,
0x11, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x42, 0x61, 0x73, 0x69, 0x63, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x2e, 0x2f, 0x70,
0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_pb_basics_proto_rawDescOnce sync.Once
file_pb_basics_proto_rawDescData = file_pb_basics_proto_rawDesc
)
func file_pb_basics_proto_rawDescGZIP() []byte {
file_pb_basics_proto_rawDescOnce.Do(func() {
file_pb_basics_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_basics_proto_rawDescData)
})
return file_pb_basics_proto_rawDescData
}
var file_pb_basics_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_pb_basics_proto_goTypes = []interface{}{
(*BasicsRequest)(nil), // 0: pb.BasicsRequest
(*BasicsResponse)(nil), // 1: pb.BasicsResponse
}
var file_pb_basics_proto_depIdxs = []int32{
0, // 0: pb.Basics.Ping:input_type -> pb.BasicsRequest
0, // 1: pb.Basics.Pong:input_type -> pb.BasicsRequest
1, // 2: pb.Basics.Ping:output_type -> pb.BasicsResponse
1, // 3: pb.Basics.Pong:output_type -> pb.BasicsResponse
2, // [2:4] is the sub-list for method output_type
0, // [0:2] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_pb_basics_proto_init() }
func file_pb_basics_proto_init() {
if File_pb_basics_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_pb_basics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BasicsRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_basics_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*BasicsResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pb_basics_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_pb_basics_proto_goTypes,
DependencyIndexes: file_pb_basics_proto_depIdxs,
MessageInfos: file_pb_basics_proto_msgTypes,
}.Build()
File_pb_basics_proto = out.File
file_pb_basics_proto_rawDesc = nil
file_pb_basics_proto_goTypes = nil
file_pb_basics_proto_depIdxs = nil
}

@ -1,25 +0,0 @@
//
syntax = "proto3";
//
package pb;
//
option go_package = "../pb";
//
service Basics{
//
rpc Ping(BasicsRequest) returns (BasicsResponse){};
//
rpc Pong(BasicsRequest) returns (BasicsResponse){};
}
//
message BasicsRequest {
string message = 1;
}
//
message BasicsResponse {
string message = 1;
}

@ -1,145 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.19.4
// source: pb/basics.proto
package pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// BasicsClient is the client API for Basics service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type BasicsClient interface {
// 心跳
Ping(ctx context.Context, in *BasicsRequest, opts ...grpc.CallOption) (*BasicsResponse, error)
// 心跳
Pong(ctx context.Context, in *BasicsRequest, opts ...grpc.CallOption) (*BasicsResponse, error)
}
type basicsClient struct {
cc grpc.ClientConnInterface
}
func NewBasicsClient(cc grpc.ClientConnInterface) BasicsClient {
return &basicsClient{cc}
}
func (c *basicsClient) Ping(ctx context.Context, in *BasicsRequest, opts ...grpc.CallOption) (*BasicsResponse, error) {
out := new(BasicsResponse)
err := c.cc.Invoke(ctx, "/pb.Basics/Ping", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *basicsClient) Pong(ctx context.Context, in *BasicsRequest, opts ...grpc.CallOption) (*BasicsResponse, error) {
out := new(BasicsResponse)
err := c.cc.Invoke(ctx, "/pb.Basics/Pong", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// BasicsServer is the server API for Basics service.
// All implementations must embed UnimplementedBasicsServer
// for forward compatibility
type BasicsServer interface {
// 心跳
Ping(context.Context, *BasicsRequest) (*BasicsResponse, error)
// 心跳
Pong(context.Context, *BasicsRequest) (*BasicsResponse, error)
mustEmbedUnimplementedBasicsServer()
}
// UnimplementedBasicsServer must be embedded to have forward compatible implementations.
type UnimplementedBasicsServer struct {
}
func (UnimplementedBasicsServer) Ping(context.Context, *BasicsRequest) (*BasicsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Ping not implemented")
}
func (UnimplementedBasicsServer) Pong(context.Context, *BasicsRequest) (*BasicsResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Pong not implemented")
}
func (UnimplementedBasicsServer) mustEmbedUnimplementedBasicsServer() {}
// UnsafeBasicsServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to BasicsServer will
// result in compilation errors.
type UnsafeBasicsServer interface {
mustEmbedUnimplementedBasicsServer()
}
func RegisterBasicsServer(s grpc.ServiceRegistrar, srv BasicsServer) {
s.RegisterService(&Basics_ServiceDesc, srv)
}
func _Basics_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BasicsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(BasicsServer).Ping(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pb.Basics/Ping",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BasicsServer).Ping(ctx, req.(*BasicsRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Basics_Pong_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(BasicsRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(BasicsServer).Pong(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pb.Basics/Pong",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(BasicsServer).Pong(ctx, req.(*BasicsRequest))
}
return interceptor(ctx, in, info, handler)
}
// Basics_ServiceDesc is the grpc.ServiceDesc for Basics service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Basics_ServiceDesc = grpc.ServiceDesc{
ServiceName: "pb.Basics",
HandlerType: (*BasicsServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Ping",
Handler: _Basics_Ping_Handler,
},
{
MethodName: "Pong",
Handler: _Basics_Pong_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "pb/basics.proto",
}

@ -1,440 +0,0 @@
// 版本
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.0
// protoc v3.19.4
// source: pb/pubsub.proto
// 包名
package pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// 请求消息
type PublishRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"`
Ip string `protobuf:"bytes,4,opt,name=ip,proto3" json:"ip,omitempty"`
}
func (x *PublishRequest) Reset() {
*x = PublishRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_pubsub_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PublishRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PublishRequest) ProtoMessage() {}
func (x *PublishRequest) ProtoReflect() protoreflect.Message {
mi := &file_pb_pubsub_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PublishRequest.ProtoReflect.Descriptor instead.
func (*PublishRequest) Descriptor() ([]byte, []int) {
return file_pb_pubsub_proto_rawDescGZIP(), []int{0}
}
func (x *PublishRequest) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *PublishRequest) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
func (x *PublishRequest) GetMethod() string {
if x != nil {
return x.Method
}
return ""
}
func (x *PublishRequest) GetIp() string {
if x != nil {
return x.Ip
}
return ""
}
// 响应消息
type PublishResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
Ip string `protobuf:"bytes,3,opt,name=ip,proto3" json:"ip,omitempty"`
}
func (x *PublishResponse) Reset() {
*x = PublishResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_pubsub_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PublishResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PublishResponse) ProtoMessage() {}
func (x *PublishResponse) ProtoReflect() protoreflect.Message {
mi := &file_pb_pubsub_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PublishResponse.ProtoReflect.Descriptor instead.
func (*PublishResponse) Descriptor() ([]byte, []int) {
return file_pb_pubsub_proto_rawDescGZIP(), []int{1}
}
func (x *PublishResponse) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *PublishResponse) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
func (x *PublishResponse) GetIp() string {
if x != nil {
return x.Ip
}
return ""
}
// 请求消息
type SubscribeRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"`
Ip string `protobuf:"bytes,4,opt,name=ip,proto3" json:"ip,omitempty"`
}
func (x *SubscribeRequest) Reset() {
*x = SubscribeRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_pubsub_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SubscribeRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SubscribeRequest) ProtoMessage() {}
func (x *SubscribeRequest) ProtoReflect() protoreflect.Message {
mi := &file_pb_pubsub_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SubscribeRequest.ProtoReflect.Descriptor instead.
func (*SubscribeRequest) Descriptor() ([]byte, []int) {
return file_pb_pubsub_proto_rawDescGZIP(), []int{2}
}
func (x *SubscribeRequest) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *SubscribeRequest) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
func (x *SubscribeRequest) GetMethod() string {
if x != nil {
return x.Method
}
return ""
}
func (x *SubscribeRequest) GetIp() string {
if x != nil {
return x.Ip
}
return ""
}
// 响应消息
type SubscribeResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
Method string `protobuf:"bytes,3,opt,name=method,proto3" json:"method,omitempty"`
}
func (x *SubscribeResponse) Reset() {
*x = SubscribeResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_pubsub_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *SubscribeResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SubscribeResponse) ProtoMessage() {}
func (x *SubscribeResponse) ProtoReflect() protoreflect.Message {
mi := &file_pb_pubsub_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SubscribeResponse.ProtoReflect.Descriptor instead.
func (*SubscribeResponse) Descriptor() ([]byte, []int) {
return file_pb_pubsub_proto_rawDescGZIP(), []int{3}
}
func (x *SubscribeResponse) GetId() string {
if x != nil {
return x.Id
}
return ""
}
func (x *SubscribeResponse) GetValue() string {
if x != nil {
return x.Value
}
return ""
}
func (x *SubscribeResponse) GetMethod() string {
if x != nil {
return x.Method
}
return ""
}
var File_pb_pubsub_proto protoreflect.FileDescriptor
var file_pb_pubsub_proto_rawDesc = []byte{
0x0a, 0x0f, 0x70, 0x62, 0x2f, 0x70, 0x75, 0x62, 0x73, 0x75, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x12, 0x02, 0x70, 0x62, 0x22, 0x5e, 0x0a, 0x0e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a,
0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d,
0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28,
0x09, 0x52, 0x02, 0x69, 0x70, 0x22, 0x47, 0x0a, 0x0f, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x0e,
0x0a, 0x02, 0x69, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70, 0x22, 0x60,
0x0a, 0x10, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d, 0x65, 0x74, 0x68,
0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64,
0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x70,
0x22, 0x51, 0x0a, 0x11, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02,
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6d,
0x65, 0x74, 0x68, 0x6f, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6d, 0x65, 0x74,
0x68, 0x6f, 0x64, 0x32, 0x78, 0x0a, 0x06, 0x50, 0x75, 0x62, 0x53, 0x75, 0x62, 0x12, 0x32, 0x0a,
0x07, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x12, 0x12, 0x2e, 0x70, 0x62, 0x2e, 0x50, 0x75,
0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x13, 0x2e, 0x70,
0x62, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x3a, 0x0a, 0x09, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x14,
0x2e, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x15, 0x2e, 0x70, 0x62, 0x2e, 0x53, 0x75, 0x62, 0x73, 0x63, 0x72,
0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x42, 0x07, 0x5a,
0x05, 0x2e, 0x2e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_pb_pubsub_proto_rawDescOnce sync.Once
file_pb_pubsub_proto_rawDescData = file_pb_pubsub_proto_rawDesc
)
func file_pb_pubsub_proto_rawDescGZIP() []byte {
file_pb_pubsub_proto_rawDescOnce.Do(func() {
file_pb_pubsub_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_pubsub_proto_rawDescData)
})
return file_pb_pubsub_proto_rawDescData
}
var file_pb_pubsub_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_pb_pubsub_proto_goTypes = []interface{}{
(*PublishRequest)(nil), // 0: pb.PublishRequest
(*PublishResponse)(nil), // 1: pb.PublishResponse
(*SubscribeRequest)(nil), // 2: pb.SubscribeRequest
(*SubscribeResponse)(nil), // 3: pb.SubscribeResponse
}
var file_pb_pubsub_proto_depIdxs = []int32{
0, // 0: pb.PubSub.Publish:input_type -> pb.PublishRequest
2, // 1: pb.PubSub.Subscribe:input_type -> pb.SubscribeRequest
1, // 2: pb.PubSub.Publish:output_type -> pb.PublishResponse
3, // 3: pb.PubSub.Subscribe:output_type -> pb.SubscribeResponse
2, // [2:4] is the sub-list for method output_type
0, // [0:2] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_pb_pubsub_proto_init() }
func file_pb_pubsub_proto_init() {
if File_pb_pubsub_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_pb_pubsub_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PublishRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_pubsub_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PublishResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_pubsub_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SubscribeRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_pubsub_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*SubscribeResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pb_pubsub_proto_rawDesc,
NumEnums: 0,
NumMessages: 4,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_pb_pubsub_proto_goTypes,
DependencyIndexes: file_pb_pubsub_proto_depIdxs,
MessageInfos: file_pb_pubsub_proto_msgTypes,
}.Build()
File_pb_pubsub_proto = out.File
file_pb_pubsub_proto_rawDesc = nil
file_pb_pubsub_proto_goTypes = nil
file_pb_pubsub_proto_depIdxs = nil
}

@ -1,46 +0,0 @@
//
syntax = "proto3";
//
package pb;
//
option go_package = "../pb";
//
service PubSub {
// []
rpc Publish (PublishRequest) returns (PublishResponse);
// []
rpc Subscribe (SubscribeRequest) returns (stream SubscribeResponse);
}
//
message PublishRequest {
string id = 1;
string value = 2;
string method = 3;
string ip = 4;
}
//
message PublishResponse {
string id = 1;
string value = 2;
string ip = 3;
}
//
message SubscribeRequest {
string id = 1;
string value = 2;
string method = 3;
string ip = 4;
}
//
message SubscribeResponse {
string id = 1;
string value = 2;
string method = 3;
}

@ -1,77 +0,0 @@
package pb
import (
"context"
"go.dtapp.net/gojobs/pubsub"
"log"
"strings"
"time"
)
type PubSubServerService struct {
pub *pubsub.Publisher
UnimplementedPubSubServer
}
func NewPubSubServerService() *PubSubServerService {
return &PubSubServerService{
// 新建一个Publisher对象
pub: pubsub.NewPublisher(time.Millisecond*100, 10),
}
}
// Publish 实现发布方法
func (p *PubSubServerService) Publish(ctx context.Context, req *PublishRequest) (*PublishResponse, error) {
log.Printf("[服务中转]{发布}编号:%s 类型:%s ip地址%s\n", req.GetId(), req.GetValue(), req.GetIp())
// 发布消息
p.pub.Publish(req.GetValue())
return &PublishResponse{
Id: req.GetId(),
Value: req.GetValue(),
Ip: req.GetIp(),
}, nil
}
// Subscribe 实现订阅方法
func (p *PubSubServerService) Subscribe(req *SubscribeRequest, stream PubSub_SubscribeServer) error {
// SubscribeTopic 增加一个使用函数过滤器的订阅者
// func(v interface{}) 定义函数过滤的规则
// SubscribeTopic 返回一个chan interface{}
ch := p.pub.SubscribeTopic(func(v interface{}) bool {
log.Printf("[服务中转]{订阅}主题:%v\n", v)
// 接收数据是string并且key是以arg为前缀的
if key, ok := v.(string); ok {
if strings.HasPrefix(key, req.GetValue()) {
return true
}
}
return false
})
log.Printf("[服务中转]{订阅}编号:%s 类型:%s 方法:%s ip地址%s\n", req.GetId(), req.GetValue(), req.GetMethod(), req.GetIp())
log.Println("[服务中转]{订阅}工作线:", ch)
log.Println("[服务中转]{订阅}当前工作线数量:", p.pub.Len())
// 服务器遍历chan并将其中信息发送给订阅客户端
for v := range ch {
log.Println("[服务中转]{订阅}for ch", ch)
log.Println("[服务中转]{订阅}for v", v)
err := stream.Send(&SubscribeResponse{
Id: req.GetId(),
Value: req.GetValue(),
Method: req.GetMethod(),
})
if err != nil {
log.Println("[服务中转]{订阅}任务分配失败 ", err.Error())
return err
}
}
return nil
}

@ -1,173 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.19.4
// source: pb/pubsub.proto
package pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// PubSubClient is the client API for PubSub service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type PubSubClient interface {
// [发布] 消息
Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*PublishResponse, error)
// [订阅] 消息
Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (PubSub_SubscribeClient, error)
}
type pubSubClient struct {
cc grpc.ClientConnInterface
}
func NewPubSubClient(cc grpc.ClientConnInterface) PubSubClient {
return &pubSubClient{cc}
}
func (c *pubSubClient) Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*PublishResponse, error) {
out := new(PublishResponse)
err := c.cc.Invoke(ctx, "/pb.PubSub/Publish", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *pubSubClient) Subscribe(ctx context.Context, in *SubscribeRequest, opts ...grpc.CallOption) (PubSub_SubscribeClient, error) {
stream, err := c.cc.NewStream(ctx, &PubSub_ServiceDesc.Streams[0], "/pb.PubSub/Subscribe", opts...)
if err != nil {
return nil, err
}
x := &pubSubSubscribeClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type PubSub_SubscribeClient interface {
Recv() (*SubscribeResponse, error)
grpc.ClientStream
}
type pubSubSubscribeClient struct {
grpc.ClientStream
}
func (x *pubSubSubscribeClient) Recv() (*SubscribeResponse, error) {
m := new(SubscribeResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// PubSubServer is the server API for PubSub service.
// All implementations must embed UnimplementedPubSubServer
// for forward compatibility
type PubSubServer interface {
// [发布] 消息
Publish(context.Context, *PublishRequest) (*PublishResponse, error)
// [订阅] 消息
Subscribe(*SubscribeRequest, PubSub_SubscribeServer) error
mustEmbedUnimplementedPubSubServer()
}
// UnimplementedPubSubServer must be embedded to have forward compatible implementations.
type UnimplementedPubSubServer struct {
}
func (UnimplementedPubSubServer) Publish(context.Context, *PublishRequest) (*PublishResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method Publish not implemented")
}
func (UnimplementedPubSubServer) Subscribe(*SubscribeRequest, PubSub_SubscribeServer) error {
return status.Errorf(codes.Unimplemented, "method Subscribe not implemented")
}
func (UnimplementedPubSubServer) mustEmbedUnimplementedPubSubServer() {}
// UnsafePubSubServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to PubSubServer will
// result in compilation errors.
type UnsafePubSubServer interface {
mustEmbedUnimplementedPubSubServer()
}
func RegisterPubSubServer(s grpc.ServiceRegistrar, srv PubSubServer) {
s.RegisterService(&PubSub_ServiceDesc, srv)
}
func _PubSub_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PublishRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(PubSubServer).Publish(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pb.PubSub/Publish",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(PubSubServer).Publish(ctx, req.(*PublishRequest))
}
return interceptor(ctx, in, info, handler)
}
func _PubSub_Subscribe_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(SubscribeRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(PubSubServer).Subscribe(m, &pubSubSubscribeServer{stream})
}
type PubSub_SubscribeServer interface {
Send(*SubscribeResponse) error
grpc.ServerStream
}
type pubSubSubscribeServer struct {
grpc.ServerStream
}
func (x *pubSubSubscribeServer) Send(m *SubscribeResponse) error {
return x.ServerStream.SendMsg(m)
}
// PubSub_ServiceDesc is the grpc.ServiceDesc for PubSub service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var PubSub_ServiceDesc = grpc.ServiceDesc{
ServiceName: "pb.PubSub",
HandlerType: (*PubSubServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Publish",
Handler: _PubSub_Publish_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Subscribe",
Handler: _PubSub_Subscribe_Handler,
ServerStreams: true,
},
},
Metadata: "pb/pubsub.proto",
}

@ -1,234 +0,0 @@
// 版本
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.0
// protoc v3.19.4
// source: pb/task.proto
// 包名
package pb
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// 请求消息
type TaskRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
}
func (x *TaskRequest) Reset() {
*x = TaskRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_task_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TaskRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TaskRequest) ProtoMessage() {}
func (x *TaskRequest) ProtoReflect() protoreflect.Message {
mi := &file_pb_task_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TaskRequest.ProtoReflect.Descriptor instead.
func (*TaskRequest) Descriptor() ([]byte, []int) {
return file_pb_task_proto_rawDescGZIP(), []int{0}
}
func (x *TaskRequest) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
// 响应消息
type TaskResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
}
func (x *TaskResponse) Reset() {
*x = TaskResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_pb_task_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *TaskResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*TaskResponse) ProtoMessage() {}
func (x *TaskResponse) ProtoReflect() protoreflect.Message {
mi := &file_pb_task_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use TaskResponse.ProtoReflect.Descriptor instead.
func (*TaskResponse) Descriptor() ([]byte, []int) {
return file_pb_task_proto_rawDescGZIP(), []int{1}
}
func (x *TaskResponse) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
var File_pb_task_proto protoreflect.FileDescriptor
var file_pb_task_proto_rawDesc = []byte{
0x0a, 0x0d, 0x70, 0x62, 0x2f, 0x74, 0x61, 0x73, 0x6b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x02, 0x70, 0x62, 0x22, 0x27, 0x0a, 0x0b, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x28, 0x0a, 0x0c,
0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07,
0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x32, 0xfb, 0x01, 0x0a, 0x04, 0x54, 0x61, 0x73, 0x6b, 0x12,
0x30, 0x0a, 0x09, 0x55, 0x6e, 0x61, 0x72, 0x79, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0f, 0x2e, 0x70,
0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e,
0x70, 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x00, 0x12, 0x3c, 0x0a, 0x13, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61,
0x6d, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x61,
0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x54,
0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12,
0x3c, 0x0a, 0x13, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x69,
0x6e, 0x67, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0f, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x70, 0x62, 0x2e, 0x54, 0x61, 0x73,
0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x45, 0x0a,
0x1a, 0x42, 0x69, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x53, 0x74,
0x72, 0x65, 0x61, 0x6d, 0x69, 0x6e, 0x67, 0x54, 0x61, 0x73, 0x6b, 0x12, 0x0f, 0x2e, 0x70, 0x62,
0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x10, 0x2e, 0x70,
0x62, 0x2e, 0x54, 0x61, 0x73, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00,
0x28, 0x01, 0x30, 0x01, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x2e, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_pb_task_proto_rawDescOnce sync.Once
file_pb_task_proto_rawDescData = file_pb_task_proto_rawDesc
)
func file_pb_task_proto_rawDescGZIP() []byte {
file_pb_task_proto_rawDescOnce.Do(func() {
file_pb_task_proto_rawDescData = protoimpl.X.CompressGZIP(file_pb_task_proto_rawDescData)
})
return file_pb_task_proto_rawDescData
}
var file_pb_task_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_pb_task_proto_goTypes = []interface{}{
(*TaskRequest)(nil), // 0: pb.TaskRequest
(*TaskResponse)(nil), // 1: pb.TaskResponse
}
var file_pb_task_proto_depIdxs = []int32{
0, // 0: pb.Task.UnaryTask:input_type -> pb.TaskRequest
0, // 1: pb.Task.ServerStreamingTask:input_type -> pb.TaskRequest
0, // 2: pb.Task.ClientStreamingTask:input_type -> pb.TaskRequest
0, // 3: pb.Task.BidirectionalStreamingTask:input_type -> pb.TaskRequest
1, // 4: pb.Task.UnaryTask:output_type -> pb.TaskResponse
1, // 5: pb.Task.ServerStreamingTask:output_type -> pb.TaskResponse
1, // 6: pb.Task.ClientStreamingTask:output_type -> pb.TaskResponse
1, // 7: pb.Task.BidirectionalStreamingTask:output_type -> pb.TaskResponse
4, // [4:8] is the sub-list for method output_type
0, // [0:4] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_pb_task_proto_init() }
func file_pb_task_proto_init() {
if File_pb_task_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_pb_task_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TaskRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_pb_task_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*TaskResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_pb_task_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_pb_task_proto_goTypes,
DependencyIndexes: file_pb_task_proto_depIdxs,
MessageInfos: file_pb_task_proto_msgTypes,
}.Build()
File_pb_task_proto = out.File
file_pb_task_proto_rawDesc = nil
file_pb_task_proto_goTypes = nil
file_pb_task_proto_depIdxs = nil
}

@ -1,30 +0,0 @@
//
syntax = "proto3";
//
package pb;
//
option go_package = "../pb";
//
service Task{
//
rpc UnaryTask(TaskRequest) returns (TaskResponse){};
//
rpc ServerStreamingTask(TaskRequest) returns (stream TaskResponse){};
//
rpc ClientStreamingTask(stream TaskRequest) returns (TaskResponse){};
//
rpc BidirectionalStreamingTask(stream TaskRequest) returns (stream TaskResponse){};
}
//
message TaskRequest {
string message = 1;
}
//
message TaskResponse {
string message = 1;
}

@ -1,315 +0,0 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.19.4
// source: pb/task.proto
package pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// TaskClient is the client API for Task service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type TaskClient interface {
// 普通一元方法
UnaryTask(ctx context.Context, in *TaskRequest, opts ...grpc.CallOption) (*TaskResponse, error)
// 服务端推送流
ServerStreamingTask(ctx context.Context, in *TaskRequest, opts ...grpc.CallOption) (Task_ServerStreamingTaskClient, error)
// 客户端推送流
ClientStreamingTask(ctx context.Context, opts ...grpc.CallOption) (Task_ClientStreamingTaskClient, error)
// 双向推送流
BidirectionalStreamingTask(ctx context.Context, opts ...grpc.CallOption) (Task_BidirectionalStreamingTaskClient, error)
}
type taskClient struct {
cc grpc.ClientConnInterface
}
func NewTaskClient(cc grpc.ClientConnInterface) TaskClient {
return &taskClient{cc}
}
func (c *taskClient) UnaryTask(ctx context.Context, in *TaskRequest, opts ...grpc.CallOption) (*TaskResponse, error) {
out := new(TaskResponse)
err := c.cc.Invoke(ctx, "/pb.Task/UnaryTask", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *taskClient) ServerStreamingTask(ctx context.Context, in *TaskRequest, opts ...grpc.CallOption) (Task_ServerStreamingTaskClient, error) {
stream, err := c.cc.NewStream(ctx, &Task_ServiceDesc.Streams[0], "/pb.Task/ServerStreamingTask", opts...)
if err != nil {
return nil, err
}
x := &taskServerStreamingTaskClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type Task_ServerStreamingTaskClient interface {
Recv() (*TaskResponse, error)
grpc.ClientStream
}
type taskServerStreamingTaskClient struct {
grpc.ClientStream
}
func (x *taskServerStreamingTaskClient) Recv() (*TaskResponse, error) {
m := new(TaskResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *taskClient) ClientStreamingTask(ctx context.Context, opts ...grpc.CallOption) (Task_ClientStreamingTaskClient, error) {
stream, err := c.cc.NewStream(ctx, &Task_ServiceDesc.Streams[1], "/pb.Task/ClientStreamingTask", opts...)
if err != nil {
return nil, err
}
x := &taskClientStreamingTaskClient{stream}
return x, nil
}
type Task_ClientStreamingTaskClient interface {
Send(*TaskRequest) error
CloseAndRecv() (*TaskResponse, error)
grpc.ClientStream
}
type taskClientStreamingTaskClient struct {
grpc.ClientStream
}
func (x *taskClientStreamingTaskClient) Send(m *TaskRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *taskClientStreamingTaskClient) CloseAndRecv() (*TaskResponse, error) {
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
m := new(TaskResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *taskClient) BidirectionalStreamingTask(ctx context.Context, opts ...grpc.CallOption) (Task_BidirectionalStreamingTaskClient, error) {
stream, err := c.cc.NewStream(ctx, &Task_ServiceDesc.Streams[2], "/pb.Task/BidirectionalStreamingTask", opts...)
if err != nil {
return nil, err
}
x := &taskBidirectionalStreamingTaskClient{stream}
return x, nil
}
type Task_BidirectionalStreamingTaskClient interface {
Send(*TaskRequest) error
Recv() (*TaskResponse, error)
grpc.ClientStream
}
type taskBidirectionalStreamingTaskClient struct {
grpc.ClientStream
}
func (x *taskBidirectionalStreamingTaskClient) Send(m *TaskRequest) error {
return x.ClientStream.SendMsg(m)
}
func (x *taskBidirectionalStreamingTaskClient) Recv() (*TaskResponse, error) {
m := new(TaskResponse)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// TaskServer is the server API for Task service.
// All implementations must embed UnimplementedTaskServer
// for forward compatibility
type TaskServer interface {
// 普通一元方法
UnaryTask(context.Context, *TaskRequest) (*TaskResponse, error)
// 服务端推送流
ServerStreamingTask(*TaskRequest, Task_ServerStreamingTaskServer) error
// 客户端推送流
ClientStreamingTask(Task_ClientStreamingTaskServer) error
// 双向推送流
BidirectionalStreamingTask(Task_BidirectionalStreamingTaskServer) error
mustEmbedUnimplementedTaskServer()
}
// UnimplementedTaskServer must be embedded to have forward compatible implementations.
type UnimplementedTaskServer struct {
}
func (UnimplementedTaskServer) UnaryTask(context.Context, *TaskRequest) (*TaskResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method UnaryTask not implemented")
}
func (UnimplementedTaskServer) ServerStreamingTask(*TaskRequest, Task_ServerStreamingTaskServer) error {
return status.Errorf(codes.Unimplemented, "method ServerStreamingTask not implemented")
}
func (UnimplementedTaskServer) ClientStreamingTask(Task_ClientStreamingTaskServer) error {
return status.Errorf(codes.Unimplemented, "method ClientStreamingTask not implemented")
}
func (UnimplementedTaskServer) BidirectionalStreamingTask(Task_BidirectionalStreamingTaskServer) error {
return status.Errorf(codes.Unimplemented, "method BidirectionalStreamingTask not implemented")
}
func (UnimplementedTaskServer) mustEmbedUnimplementedTaskServer() {}
// UnsafeTaskServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to TaskServer will
// result in compilation errors.
type UnsafeTaskServer interface {
mustEmbedUnimplementedTaskServer()
}
func RegisterTaskServer(s grpc.ServiceRegistrar, srv TaskServer) {
s.RegisterService(&Task_ServiceDesc, srv)
}
func _Task_UnaryTask_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(TaskRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TaskServer).UnaryTask(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/pb.Task/UnaryTask",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TaskServer).UnaryTask(ctx, req.(*TaskRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Task_ServerStreamingTask_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(TaskRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(TaskServer).ServerStreamingTask(m, &taskServerStreamingTaskServer{stream})
}
type Task_ServerStreamingTaskServer interface {
Send(*TaskResponse) error
grpc.ServerStream
}
type taskServerStreamingTaskServer struct {
grpc.ServerStream
}
func (x *taskServerStreamingTaskServer) Send(m *TaskResponse) error {
return x.ServerStream.SendMsg(m)
}
func _Task_ClientStreamingTask_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(TaskServer).ClientStreamingTask(&taskClientStreamingTaskServer{stream})
}
type Task_ClientStreamingTaskServer interface {
SendAndClose(*TaskResponse) error
Recv() (*TaskRequest, error)
grpc.ServerStream
}
type taskClientStreamingTaskServer struct {
grpc.ServerStream
}
func (x *taskClientStreamingTaskServer) SendAndClose(m *TaskResponse) error {
return x.ServerStream.SendMsg(m)
}
func (x *taskClientStreamingTaskServer) Recv() (*TaskRequest, error) {
m := new(TaskRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func _Task_BidirectionalStreamingTask_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(TaskServer).BidirectionalStreamingTask(&taskBidirectionalStreamingTaskServer{stream})
}
type Task_BidirectionalStreamingTaskServer interface {
Send(*TaskResponse) error
Recv() (*TaskRequest, error)
grpc.ServerStream
}
type taskBidirectionalStreamingTaskServer struct {
grpc.ServerStream
}
func (x *taskBidirectionalStreamingTaskServer) Send(m *TaskResponse) error {
return x.ServerStream.SendMsg(m)
}
func (x *taskBidirectionalStreamingTaskServer) Recv() (*TaskRequest, error) {
m := new(TaskRequest)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
// Task_ServiceDesc is the grpc.ServiceDesc for Task service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Task_ServiceDesc = grpc.ServiceDesc{
ServiceName: "pb.Task",
HandlerType: (*TaskServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "UnaryTask",
Handler: _Task_UnaryTask_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "ServerStreamingTask",
Handler: _Task_ServerStreamingTask_Handler,
ServerStreams: true,
},
{
StreamName: "ClientStreamingTask",
Handler: _Task_ClientStreamingTask_Handler,
ClientStreams: true,
},
{
StreamName: "BidirectionalStreamingTask",
Handler: _Task_BidirectionalStreamingTask_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "pb/task.proto",
}

@ -1,125 +0,0 @@
package pubsub
import (
"sync"
"time"
)
// 等待组放在共享内存池中减少GC
var wgPool = sync.Pool{New: func() interface{} { return new(sync.WaitGroup) }}
// NewPublisher
// 第一个参数控制发布时最大阻塞时间
// 第二个参数是缓冲区大小控制每个订阅者的chan缓冲区大小
func NewPublisher(publishTimeout time.Duration, buffer int) *Publisher {
return &Publisher{
buffer: buffer,
timeout: publishTimeout,
subscribers: make(map[subscriber]topicFunc),
}
}
type subscriber chan interface{}
type topicFunc func(v interface{}) bool
type Publisher struct {
m sync.RWMutex // 控制订阅者map并发读写安全
buffer int // 每个订阅者chan缓冲区大小
timeout time.Duration // 发布阻塞超时时间
subscribers map[subscriber]topicFunc
}
// Len 返回订阅者数量
func (p *Publisher) Len() int {
p.m.RLock()
i := len(p.subscribers)
p.m.RUnlock()
return i
}
// Subscribe 无Topic订阅
func (p *Publisher) Subscribe() chan interface{} {
return p.SubscribeTopic(nil)
}
// SubscribeTopic 通过Topic订阅
func (p *Publisher) SubscribeTopic(topic topicFunc) chan interface{} {
ch := make(chan interface{}, p.buffer)
p.m.Lock()
p.subscribers[ch] = topic
p.m.Unlock()
return ch
}
// SubscribeTopicWithBuffer 通过自定义chan缓冲区大小定义新的订阅者
func (p *Publisher) SubscribeTopicWithBuffer(topic topicFunc, buffer int) chan interface{} {
ch := make(chan interface{}, buffer)
p.m.Lock()
p.subscribers[ch] = topic
p.m.Unlock()
return ch
}
// Evict 移除某个订阅者
func (p *Publisher) Evict(sub chan interface{}) {
p.m.Lock()
_, exists := p.subscribers[sub]
if exists {
delete(p.subscribers, sub)
close(sub)
}
p.m.Unlock()
}
// Publish 发布消息
func (p *Publisher) Publish(v interface{}) {
p.m.RLock()
if len(p.subscribers) == 0 {
p.m.RUnlock()
return
}
wg := wgPool.Get().(*sync.WaitGroup)
for sub, topic := range p.subscribers {
wg.Add(1)
go p.sendTopic(sub, topic, v, wg)
}
wg.Wait()
wgPool.Put(wg)
p.m.RUnlock()
}
// Close 关闭服务
func (p *Publisher) Close() {
p.m.Lock()
for sub := range p.subscribers {
delete(p.subscribers, sub)
close(sub)
}
p.m.Unlock()
}
// 真正发布消息的逻辑通过Timer根据传入的timeout控制每次发布消息最大阻塞时长
func (p *Publisher) sendTopic(sub subscriber, topic topicFunc, v interface{}, wg *sync.WaitGroup) {
defer wg.Done()
if topic != nil && !topic(v) {
return
}
// 如果接收器不可用,请在选择“不阻止”下发送
if p.timeout > 0 {
timeout := time.NewTimer(p.timeout)
defer timeout.Stop()
select {
case sub <- v:
case <-timeout.C:
}
return
}
select {
case sub <- v:
default:
}
}

@ -0,0 +1,37 @@
package gojobs
import (
"context"
"github.com/go-redis/redis/v9"
)
// Publish 发布
// ctx 上下文
// channel 频道
// message 消息
func (c *Client) Publish(ctx context.Context, channel string, message interface{}) error {
publish, err := c.cache.redisClient.Publish(ctx, channel, message).Result()
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("发布失败:%s %s %v %s\n", channel, message, publish, err)
}
return err
}
type SubscribeResult struct {
err error
Message *redis.PubSub
}
// Subscribe 订阅
func (c *Client) Subscribe(ctx context.Context) SubscribeResult {
return SubscribeResult{
Message: c.cache.redisClient.Subscribe(ctx, c.cache.cornKeyPrefix+"_"+c.cache.cornKeyCustom),
}
}
// PSubscribe 订阅,支持通配符匹配(ch_user_*)
func (c *Client) PSubscribe(ctx context.Context) SubscribeResult {
return SubscribeResult{
Message: c.cache.redisClient.PSubscribe(ctx, c.cache.cornKeyPrefix+"_"+c.cache.cornKeyCustom+"_*"),
}
}

@ -0,0 +1,90 @@
package gojobs
import (
"context"
"errors"
"fmt"
"go.dtapp.net/goip"
"go.dtapp.net/gojobs/jobs_gorm_model"
"go.dtapp.net/gostring"
"math/rand"
"time"
)
// GetIssueAddress 获取下发地址
// workers 在线列表
// v 任务信息
// ---
// address 下发地址
// err 错误信息
func (c *Client) GetIssueAddress(ctx context.Context, workers []string, v *jobs_gorm_model.Task) (string, error) {
var (
currentIp = "" // 当前Ip
appointIpStatus = false // 指定Ip状态
)
if v.SpecifyIp != "" {
v.SpecifyIp = goip.IsIp(v.SpecifyIp)
}
// 赋值ip
if v.SpecifyIp != "" && v.SpecifyIp != SpecifyIpNull {
currentIp = v.SpecifyIp
appointIpStatus = true
}
// 只有一个客户端在线
if len(workers) == 1 {
if appointIpStatus {
// 判断是否指定某ip执行
if gostring.Contains(workers[0], currentIp) {
c.zapLog.WithTraceId(ctx).Sugar().Info("只有一个客户端在线指定某ip执行", workers[0], currentIp)
return workers[0], nil
}
return "", errors.New(fmt.Sprintf("需要执行的[%s]客户端不在线", currentIp))
}
return workers[0], nil
}
// 优先处理指定某ip执行
if appointIpStatus {
for wk, wv := range workers {
if gostring.Contains(wv, currentIp) {
c.zapLog.WithTraceId(ctx).Sugar().Info("优先处理指定某ip执行", workers[wk], currentIp)
return workers[wk], nil
}
}
return "", errors.New(fmt.Sprintf("需要执行的[%s]客户端不在线", currentIp))
} else {
// 随机返回一个
address := workers[c.random(0, len(workers))]
if address == "" {
return address, errors.New("获取执行的客户端异常")
}
c.zapLog.WithTraceId(ctx).Sugar().Info("随机返回一个:", address, currentIp)
return address, nil
}
}
// GetSubscribeClientList 获取在线的客户端
func (c *Client) GetSubscribeClientList(ctx context.Context) (client []string, err error) {
// 查询活跃的channel
client, err = c.cache.redisClient.PubSubChannels(ctx, c.cache.cornKeyPrefix+"_*").Result()
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("获取在线的客户端失败:%s%v", c.cache.cornKeyPrefix+"_*", err)
}
return client, err
}
// 随机返回一个
// min最小
// max最大
func (c *Client) random(min, max int) int {
if max-min <= 0 {
return 0
}
rand.Seed(time.Now().Unix())
return rand.Intn(max-min) + min
}

@ -0,0 +1,98 @@
package gojobs
import (
"context"
"go.dtapp.net/gojobs/jobs_gorm_model"
"go.dtapp.net/gotime"
"go.dtapp.net/gotrace_id"
)
// Run 运行
func (c *Client) Run(ctx context.Context, task jobs_gorm_model.Task, taskResultCode int, taskResultDesc string) {
runId := gotrace_id.GetTraceIdContext(ctx)
if runId == "" {
c.zapLog.WithTraceId(ctx).Sugar().Error("上下文没有跟踪编号")
return
}
c.GormTaskLogRecord(ctx, task, runId, taskResultCode, taskResultDesc)
if c.mongoConfig.stats {
c.MongoTaskLogRecord(ctx, task, runId, taskResultCode, taskResultDesc)
}
switch taskResultCode {
case 0:
err := c.EditTask(c.gormClient.GetDb(), task.Id).
Select("run_id", "result", "next_run_time").
Updates(jobs_gorm_model.Task{
RunId: runId,
Result: taskResultDesc,
NextRunTime: gotime.Current().AfterSeconds(task.Frequency).Time,
}).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("保存失败:%s", err.Error())
}
return
case CodeSuccess:
// 执行成功
err := c.EditTask(c.gormClient.GetDb(), task.Id).
Select("status_desc", "number", "run_id", "updated_ip", "result", "next_run_time").
Updates(jobs_gorm_model.Task{
StatusDesc: "执行成功",
Number: task.Number + 1,
RunId: runId,
UpdatedIp: c.config.systemOutsideIp,
Result: taskResultDesc,
NextRunTime: gotime.Current().AfterSeconds(task.Frequency).Time,
}).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("保存失败:%s", err.Error())
}
case CodeEnd:
// 执行成功、提前结束
err := c.EditTask(c.gormClient.GetDb(), task.Id).
Select("status", "status_desc", "number", "updated_ip", "result", "next_run_time").
Updates(jobs_gorm_model.Task{
Status: TASK_SUCCESS,
StatusDesc: "结束执行",
Number: task.Number + 1,
UpdatedIp: c.config.systemOutsideIp,
Result: taskResultDesc,
NextRunTime: gotime.Current().Time,
}).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("保存失败:%s", err.Error())
}
case CodeError:
// 执行失败
err := c.EditTask(c.gormClient.GetDb(), task.Id).
Select("status_desc", "number", "run_id", "updated_ip", "result", "next_run_time").
Updates(jobs_gorm_model.Task{
StatusDesc: "执行失败",
Number: task.Number + 1,
RunId: runId,
UpdatedIp: c.config.systemOutsideIp,
Result: taskResultDesc,
NextRunTime: gotime.Current().AfterSeconds(task.Frequency).Time,
}).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("保存失败:%s", err.Error())
}
}
if task.MaxNumber != 0 {
if task.Number+1 >= task.MaxNumber {
// 关闭执行
err := c.EditTask(c.gormClient.GetDb(), task.Id).
Select("status").
Updates(jobs_gorm_model.Task{
Status: TASK_TIMEOUT,
}).Error
if err != nil {
c.zapLog.WithTraceId(ctx).Sugar().Errorf("保存失败:%s", err.Error())
}
}
}
return
}

@ -0,0 +1,73 @@
package gojobs
import (
"context"
"errors"
"go.dtapp.net/gojobs/jobs_gorm_model"
)
type TaskLockOperation struct {
client *Client // 实例
task jobs_gorm_model.Task // 任务
}
func (c *Client) NewLock(task jobs_gorm_model.Task) (*TaskLockOperation, error) {
if task.Id == 0 {
return nil, errors.New("任务数据不正常")
}
return &TaskLockOperation{
client: c,
task: task,
}, nil
}
// Lock 上锁
func (tlo *TaskLockOperation) Lock(ctx context.Context, id any) error {
_, err := tlo.client.Lock(ctx, tlo.task, id)
return err
}
// Unlock 解锁
func (tlo *TaskLockOperation) Unlock(ctx context.Context, id any) error {
return tlo.client.Unlock(ctx, tlo.task, id)
}
// LockForever 永远上锁
func (tlo *TaskLockOperation) LockForever(ctx context.Context, id any) error {
_, err := tlo.client.LockForever(ctx, tlo.task, id)
return err
}
// LockId 上锁
func (tlo *TaskLockOperation) LockId(ctx context.Context) error {
_, err := tlo.client.LockId(ctx, tlo.task)
return err
}
// UnlockId 解锁
func (tlo *TaskLockOperation) UnlockId(ctx context.Context) error {
return tlo.client.UnlockId(ctx, tlo.task)
}
// LockForeverId 永远上锁
func (tlo *TaskLockOperation) LockForeverId(ctx context.Context) error {
_, err := tlo.client.LockForeverId(ctx, tlo.task)
return err
}
// LockCustomId 上锁
func (tlo *TaskLockOperation) LockCustomId(ctx context.Context) error {
_, err := tlo.client.LockCustomId(ctx, tlo.task)
return err
}
// UnlockCustomId 解锁
func (tlo *TaskLockOperation) UnlockCustomId(ctx context.Context) error {
return tlo.client.UnlockCustomId(ctx, tlo.task)
}
// LockForeverCustomId 永远上锁
func (tlo *TaskLockOperation) LockForeverCustomId(ctx context.Context) error {
_, err := tlo.client.LockForeverCustomId(ctx, tlo.task)
return err
}

@ -1,25 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
/.tags

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2016 Bastien Gysler
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -1,82 +0,0 @@
# goxml2json [![CircleCI](https://circleci.com/gh/basgys/goxml2json.svg?style=svg)](https://circleci.com/gh/basgys/goxml2json)
Go package that converts XML to JSON
### Install
go get -u github.com/basgys/goxml2json
### Importing
import github.com/basgys/goxml2json
### Usage
**Code example**
```go
package main
import (
"fmt"
"strings"
xj "github.com/basgys/goxml2json"
)
func main() {
// xml is an io.Reader
xml := strings.NewReader(`<?xml version="1.0" encoding="UTF-8"?><hello>world</hello>`)
json, err := xj.Convert(xml)
if err != nil {
panic("That's embarrassing...")
}
fmt.Println(json.String())
// {"hello": "world"}
}
```
**Input**
```xml
<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="CGImap 0.0.2">
<bounds minlat="54.0889580" minlon="12.2487570" maxlat="54.0913900" maxlon="12.2524800"/>
<foo>bar</foo>
</osm>
```
**Output**
```json
{
"osm": {
"-version": "0.6",
"-generator": "CGImap 0.0.2",
"bounds": {
"-minlat": "54.0889580",
"-minlon": "12.2487570",
"-maxlat": "54.0913900",
"-maxlon": "12.2524800"
},
"foo": "bar"
}
}
```
### Contributing
Feel free to contribute to this project if you want to fix/extend/improve it.
### Contributors
- [DirectX](https://github.com/directx)
- [samuelhug](https://github.com/samuelhug)
### TODO
* Extract data types in JSON (numbers, boolean, ...)
* Categorise errors
* Option to prettify the JSON output
* Benchmark

@ -1,25 +0,0 @@
package xml2json
import (
"bytes"
"io"
)
// Convert converts the given XML document to JSON
func Convert(r io.Reader) (*bytes.Buffer, error) {
// Decode XML document
root := &Node{}
err := NewDecoder(r).Decode(root)
if err != nil {
return nil, err
}
// Then encode it in JSON
buf := new(bytes.Buffer)
err = NewEncoder(buf).Encode(root)
if err != nil {
return nil, err
}
return buf, nil
}

@ -1,140 +0,0 @@
package xml2json
import (
"encoding/xml"
"io"
"unicode"
"golang.org/x/net/html/charset"
)
const (
attrPrefix = "-"
contentPrefix = "#"
)
// A Decoder reads and decodes XML objects from an input stream.
type Decoder struct {
r io.Reader
err error
attributePrefix string
contentPrefix string
}
type element struct {
parent *element
n *Node
label string
}
func (dec *Decoder) SetAttributePrefix(prefix string) {
dec.attributePrefix = prefix
}
func (dec *Decoder) SetContentPrefix(prefix string) {
dec.contentPrefix = prefix
}
func (dec *Decoder) DecodeWithCustomPrefixes(root *Node, contentPrefix string, attributePrefix string) error {
dec.contentPrefix = contentPrefix
dec.attributePrefix = attributePrefix
return dec.Decode(root)
}
// NewDecoder returns a new decoder that reads from r.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: r}
}
// Decode reads the next JSON-encoded value from its
// input and stores it in the value pointed to by v.
func (dec *Decoder) Decode(root *Node) error {
if dec.contentPrefix == "" {
dec.contentPrefix = contentPrefix
}
if dec.attributePrefix == "" {
dec.attributePrefix = attrPrefix
}
xmlDec := xml.NewDecoder(dec.r)
// That will convert the charset if the provided XML is non-UTF-8
xmlDec.CharsetReader = charset.NewReaderLabel
// Create first element from the root node
elem := &element{
parent: nil,
n: root,
}
for {
t, _ := xmlDec.Token()
if t == nil {
break
}
switch se := t.(type) {
case xml.StartElement:
// Build new a new current element and link it to its parent
elem = &element{
parent: elem,
n: &Node{},
label: se.Name.Local,
}
// Extract attributes as children
for _, a := range se.Attr {
elem.n.AddChild(dec.attributePrefix+a.Name.Local, &Node{Data: a.Value})
}
case xml.CharData:
// Extract XML data (if any)
elem.n.Data = trimNonGraphic(string(xml.CharData(se)))
case xml.EndElement:
// And add it to its parent list
if elem.parent != nil {
elem.parent.n.AddChild(elem.label, elem.n)
}
// Then change the current element to its parent
elem = elem.parent
}
}
return nil
}
// trimNonGraphic returns a slice of the string s, with all leading and trailing
// non graphic characters and spaces removed.
//
// Graphic characters include letters, marks, numbers, punctuation, symbols,
// and spaces, from categories L, M, N, P, S, Zs.
// Spacing characters are set by category Z and property Pattern_White_Space.
func trimNonGraphic(s string) string {
if s == "" {
return s
}
var first *int
var last int
for i, r := range []rune(s) {
if !unicode.IsGraphic(r) || unicode.IsSpace(r) {
continue
}
if first == nil {
f := i // copy i
first = &f
last = i
} else {
last = i
}
}
// If first is nil, it means there are no graphic characters
if first == nil {
return ""
}
return string([]rune(s)[*first : last+1])
}

@ -1,2 +0,0 @@
// Package xml2json is an XML to JSON converter
package xml2json

@ -1,197 +0,0 @@
package xml2json
import (
"bytes"
"io"
"unicode/utf8"
)
// An Encoder writes JSON objects to an output stream.
type Encoder struct {
w io.Writer
err error
contentPrefix string
attributePrefix string
}
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w}
}
func (enc *Encoder) SetAttributePrefix(prefix string) {
enc.attributePrefix = prefix
}
func (enc *Encoder) SetContentPrefix(prefix string) {
enc.contentPrefix = prefix
}
func (enc *Encoder) EncodeWithCustomPrefixes(root *Node, contentPrefix string, attributePrefix string) error {
enc.contentPrefix = contentPrefix
enc.attributePrefix = attributePrefix
return enc.Encode(root)
}
// Encode writes the JSON encoding of v to the stream
func (enc *Encoder) Encode(root *Node) error {
if enc.err != nil {
return enc.err
}
if root == nil {
return nil
}
if enc.contentPrefix == "" {
enc.contentPrefix = contentPrefix
}
if enc.attributePrefix == "" {
enc.attributePrefix = attrPrefix
}
enc.err = enc.format(root, 0)
// Terminate each value with a newline.
// This makes the output look a little nicer
// when debugging, and some kind of space
// is required if the encoded value was a number,
// so that the reader knows there aren't more
// digits coming.
enc.write("\n")
return enc.err
}
func (enc *Encoder) format(n *Node, lvl int) error {
if n.IsComplex() {
enc.write("{")
// Add data as an additional attibute (if any)
if len(n.Data) > 0 {
enc.write("\"")
enc.write(enc.contentPrefix)
enc.write("content")
enc.write("\": ")
enc.write(sanitiseString(n.Data))
enc.write(", ")
}
i := 0
tot := len(n.Children)
for label, children := range n.Children {
enc.write("\"")
enc.write(label)
enc.write("\": ")
if len(children) > 1 {
// Array
enc.write("[")
for j, c := range children {
enc.format(c, lvl+1)
if j < len(children)-1 {
enc.write(", ")
}
}
enc.write("]")
} else {
// Map
enc.format(children[0], lvl+1)
}
if i < tot-1 {
enc.write(", ")
}
i++
}
enc.write("}")
} else {
// TODO : Extract data type
enc.write(sanitiseString(n.Data))
}
return nil
}
func (enc *Encoder) write(s string) {
enc.w.Write([]byte(s))
}
// https://golang.org/src/encoding/json/encode.go?s=5584:5627#L788
var hex = "0123456789abcdef"
func sanitiseString(s string) string {
var buf bytes.Buffer
buf.WriteByte('"')
start := 0
for i := 0; i < len(s); {
if b := s[i]; b < utf8.RuneSelf {
if 0x20 <= b && b != '\\' && b != '"' && b != '<' && b != '>' && b != '&' {
i++
continue
}
if start < i {
buf.WriteString(s[start:i])
}
switch b {
case '\\', '"':
buf.WriteByte('\\')
buf.WriteByte(b)
case '\n':
buf.WriteByte('\\')
buf.WriteByte('n')
case '\r':
buf.WriteByte('\\')
buf.WriteByte('r')
case '\t':
buf.WriteByte('\\')
buf.WriteByte('t')
default:
// This encodes bytes < 0x20 except for \n and \r,
// as well as <, > and &. The latter are escaped because they
// can lead to security holes when user-controlled strings
// are rendered into JSON and served to some browsers.
buf.WriteString(`\u00`)
buf.WriteByte(hex[b>>4])
buf.WriteByte(hex[b&0xF])
}
i++
start = i
continue
}
c, size := utf8.DecodeRuneInString(s[i:])
if c == utf8.RuneError && size == 1 {
if start < i {
buf.WriteString(s[start:i])
}
buf.WriteString(`\ufffd`)
i += size
start = i
continue
}
// U+2028 is LINE SEPARATOR.
// U+2029 is PARAGRAPH SEPARATOR.
// They are both technically valid characters in JSON strings,
// but don't work in JSONP, which has to be evaluated as JavaScript,
// and can lead to security holes there. It is valid JSON to
// escape them, so we do so unconditionally.
// See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion.
if c == '\u2028' || c == '\u2029' {
if start < i {
buf.WriteString(s[start:i])
}
buf.WriteString(`\u202`)
buf.WriteByte(hex[c&0xF])
i += size
start = i
continue
}
i += size
}
if start < len(s) {
buf.WriteString(s[start:])
}
buf.WriteByte('"')
return buf.String()
}

@ -1,25 +0,0 @@
package xml2json
// Node is a data element on a tree
type Node struct {
Children map[string]Nodes
Data string
}
// Nodes is a list of nodes
type Nodes []*Node
// AddChild appends a node to the list of children
func (n *Node) AddChild(s string, c *Node) {
// Lazy lazy
if n.Children == nil {
n.Children = map[string]Nodes{}
}
n.Children[s] = append(n.Children[s], c)
}
// IsComplex returns whether it is a complex type (has children)
func (n *Node) IsComplex() bool {
return len(n.Children) > 0
}

@ -1,22 +0,0 @@
Copyright (c) 2016 Caleb Spare
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -1,69 +0,0 @@
# xxhash
[![Go Reference](https://pkg.go.dev/badge/github.com/cespare/xxhash/v2.svg)](https://pkg.go.dev/github.com/cespare/xxhash/v2)
[![Test](https://github.com/cespare/xxhash/actions/workflows/test.yml/badge.svg)](https://github.com/cespare/xxhash/actions/workflows/test.yml)
xxhash is a Go implementation of the 64-bit
[xxHash](http://cyan4973.github.io/xxHash/) algorithm, XXH64. This is a
high-quality hashing algorithm that is much faster than anything in the Go
standard library.
This package provides a straightforward API:
```
func Sum64(b []byte) uint64
func Sum64String(s string) uint64
type Digest struct{ ... }
func New() *Digest
```
The `Digest` type implements hash.Hash64. Its key methods are:
```
func (*Digest) Write([]byte) (int, error)
func (*Digest) WriteString(string) (int, error)
func (*Digest) Sum64() uint64
```
This implementation provides a fast pure-Go implementation and an even faster
assembly implementation for amd64.
## Compatibility
This package is in a module and the latest code is in version 2 of the module.
You need a version of Go with at least "minimal module compatibility" to use
github.com/cespare/xxhash/v2:
* 1.9.7+ for Go 1.9
* 1.10.3+ for Go 1.10
* Go 1.11 or later
I recommend using the latest release of Go.
## Benchmarks
Here are some quick benchmarks comparing the pure-Go and assembly
implementations of Sum64.
| input size | purego | asm |
| --- | --- | --- |
| 5 B | 979.66 MB/s | 1291.17 MB/s |
| 100 B | 7475.26 MB/s | 7973.40 MB/s |
| 4 KB | 17573.46 MB/s | 17602.65 MB/s |
| 10 MB | 17131.46 MB/s | 17142.16 MB/s |
These numbers were generated on Ubuntu 18.04 with an Intel i7-8700K CPU using
the following commands under Go 1.11.2:
```
$ go test -tags purego -benchtime 10s -bench '/xxhash,direct,bytes'
$ go test -benchtime 10s -bench '/xxhash,direct,bytes'
```
## Projects using this package
- [InfluxDB](https://github.com/influxdata/influxdb)
- [Prometheus](https://github.com/prometheus/prometheus)
- [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics)
- [FreeCache](https://github.com/coocood/freecache)
- [FastCache](https://github.com/VictoriaMetrics/fastcache)

@ -1,235 +0,0 @@
// Package xxhash implements the 64-bit variant of xxHash (XXH64) as described
// at http://cyan4973.github.io/xxHash/.
package xxhash
import (
"encoding/binary"
"errors"
"math/bits"
)
const (
prime1 uint64 = 11400714785074694791
prime2 uint64 = 14029467366897019727
prime3 uint64 = 1609587929392839161
prime4 uint64 = 9650029242287828579
prime5 uint64 = 2870177450012600261
)
// NOTE(caleb): I'm using both consts and vars of the primes. Using consts where
// possible in the Go code is worth a small (but measurable) performance boost
// by avoiding some MOVQs. Vars are needed for the asm and also are useful for
// convenience in the Go code in a few places where we need to intentionally
// avoid constant arithmetic (e.g., v1 := prime1 + prime2 fails because the
// result overflows a uint64).
var (
prime1v = prime1
prime2v = prime2
prime3v = prime3
prime4v = prime4
prime5v = prime5
)
// Digest implements hash.Hash64.
type Digest struct {
v1 uint64
v2 uint64
v3 uint64
v4 uint64
total uint64
mem [32]byte
n int // how much of mem is used
}
// New creates a new Digest that computes the 64-bit xxHash algorithm.
func New() *Digest {
var d Digest
d.Reset()
return &d
}
// Reset clears the Digest's state so that it can be reused.
func (d *Digest) Reset() {
d.v1 = prime1v + prime2
d.v2 = prime2
d.v3 = 0
d.v4 = -prime1v
d.total = 0
d.n = 0
}
// Size always returns 8 bytes.
func (d *Digest) Size() int { return 8 }
// BlockSize always returns 32 bytes.
func (d *Digest) BlockSize() int { return 32 }
// Write adds more data to d. It always returns len(b), nil.
func (d *Digest) Write(b []byte) (n int, err error) {
n = len(b)
d.total += uint64(n)
if d.n+n < 32 {
// This new data doesn't even fill the current block.
copy(d.mem[d.n:], b)
d.n += n
return
}
if d.n > 0 {
// Finish off the partial block.
copy(d.mem[d.n:], b)
d.v1 = round(d.v1, u64(d.mem[0:8]))
d.v2 = round(d.v2, u64(d.mem[8:16]))
d.v3 = round(d.v3, u64(d.mem[16:24]))
d.v4 = round(d.v4, u64(d.mem[24:32]))
b = b[32-d.n:]
d.n = 0
}
if len(b) >= 32 {
// One or more full blocks left.
nw := writeBlocks(d, b)
b = b[nw:]
}
// Store any remaining partial block.
copy(d.mem[:], b)
d.n = len(b)
return
}
// Sum appends the current hash to b and returns the resulting slice.
func (d *Digest) Sum(b []byte) []byte {
s := d.Sum64()
return append(
b,
byte(s>>56),
byte(s>>48),
byte(s>>40),
byte(s>>32),
byte(s>>24),
byte(s>>16),
byte(s>>8),
byte(s),
)
}
// Sum64 returns the current hash.
func (d *Digest) Sum64() uint64 {
var h uint64
if d.total >= 32 {
v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4
h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4)
h = mergeRound(h, v1)
h = mergeRound(h, v2)
h = mergeRound(h, v3)
h = mergeRound(h, v4)
} else {
h = d.v3 + prime5
}
h += d.total
i, end := 0, d.n
for ; i+8 <= end; i += 8 {
k1 := round(0, u64(d.mem[i:i+8]))
h ^= k1
h = rol27(h)*prime1 + prime4
}
if i+4 <= end {
h ^= uint64(u32(d.mem[i:i+4])) * prime1
h = rol23(h)*prime2 + prime3
i += 4
}
for i < end {
h ^= uint64(d.mem[i]) * prime5
h = rol11(h) * prime1
i++
}
h ^= h >> 33
h *= prime2
h ^= h >> 29
h *= prime3
h ^= h >> 32
return h
}
const (
magic = "xxh\x06"
marshaledSize = len(magic) + 8*5 + 32
)
// MarshalBinary implements the encoding.BinaryMarshaler interface.
func (d *Digest) MarshalBinary() ([]byte, error) {
b := make([]byte, 0, marshaledSize)
b = append(b, magic...)
b = appendUint64(b, d.v1)
b = appendUint64(b, d.v2)
b = appendUint64(b, d.v3)
b = appendUint64(b, d.v4)
b = appendUint64(b, d.total)
b = append(b, d.mem[:d.n]...)
b = b[:len(b)+len(d.mem)-d.n]
return b, nil
}
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
func (d *Digest) UnmarshalBinary(b []byte) error {
if len(b) < len(magic) || string(b[:len(magic)]) != magic {
return errors.New("xxhash: invalid hash state identifier")
}
if len(b) != marshaledSize {
return errors.New("xxhash: invalid hash state size")
}
b = b[len(magic):]
b, d.v1 = consumeUint64(b)
b, d.v2 = consumeUint64(b)
b, d.v3 = consumeUint64(b)
b, d.v4 = consumeUint64(b)
b, d.total = consumeUint64(b)
copy(d.mem[:], b)
d.n = int(d.total % uint64(len(d.mem)))
return nil
}
func appendUint64(b []byte, x uint64) []byte {
var a [8]byte
binary.LittleEndian.PutUint64(a[:], x)
return append(b, a[:]...)
}
func consumeUint64(b []byte) ([]byte, uint64) {
x := u64(b)
return b[8:], x
}
func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) }
func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) }
func round(acc, input uint64) uint64 {
acc += input * prime2
acc = rol31(acc)
acc *= prime1
return acc
}
func mergeRound(acc, val uint64) uint64 {
val = round(0, val)
acc ^= val
acc = acc*prime1 + prime4
return acc
}
func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) }
func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) }
func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) }
func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) }
func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) }
func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) }
func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) }
func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) }

@ -1,13 +0,0 @@
// +build !appengine
// +build gc
// +build !purego
package xxhash
// Sum64 computes the 64-bit xxHash digest of b.
//
//go:noescape
func Sum64(b []byte) uint64
//go:noescape
func writeBlocks(d *Digest, b []byte) int

@ -1,215 +0,0 @@
// +build !appengine
// +build gc
// +build !purego
#include "textflag.h"
// Register allocation:
// AX h
// SI pointer to advance through b
// DX n
// BX loop end
// R8 v1, k1
// R9 v2
// R10 v3
// R11 v4
// R12 tmp
// R13 prime1v
// R14 prime2v
// DI prime4v
// round reads from and advances the buffer pointer in SI.
// It assumes that R13 has prime1v and R14 has prime2v.
#define round(r) \
MOVQ (SI), R12 \
ADDQ $8, SI \
IMULQ R14, R12 \
ADDQ R12, r \
ROLQ $31, r \
IMULQ R13, r
// mergeRound applies a merge round on the two registers acc and val.
// It assumes that R13 has prime1v, R14 has prime2v, and DI has prime4v.
#define mergeRound(acc, val) \
IMULQ R14, val \
ROLQ $31, val \
IMULQ R13, val \
XORQ val, acc \
IMULQ R13, acc \
ADDQ DI, acc
// func Sum64(b []byte) uint64
TEXT ·Sum64(SB), NOSPLIT, $0-32
// Load fixed primes.
MOVQ ·prime1v(SB), R13
MOVQ ·prime2v(SB), R14
MOVQ ·prime4v(SB), DI
// Load slice.
MOVQ b_base+0(FP), SI
MOVQ b_len+8(FP), DX
LEAQ (SI)(DX*1), BX
// The first loop limit will be len(b)-32.
SUBQ $32, BX
// Check whether we have at least one block.
CMPQ DX, $32
JLT noBlocks
// Set up initial state (v1, v2, v3, v4).
MOVQ R13, R8
ADDQ R14, R8
MOVQ R14, R9
XORQ R10, R10
XORQ R11, R11
SUBQ R13, R11
// Loop until SI > BX.
blockLoop:
round(R8)
round(R9)
round(R10)
round(R11)
CMPQ SI, BX
JLE blockLoop
MOVQ R8, AX
ROLQ $1, AX
MOVQ R9, R12
ROLQ $7, R12
ADDQ R12, AX
MOVQ R10, R12
ROLQ $12, R12
ADDQ R12, AX
MOVQ R11, R12
ROLQ $18, R12
ADDQ R12, AX
mergeRound(AX, R8)
mergeRound(AX, R9)
mergeRound(AX, R10)
mergeRound(AX, R11)
JMP afterBlocks
noBlocks:
MOVQ ·prime5v(SB), AX
afterBlocks:
ADDQ DX, AX
// Right now BX has len(b)-32, and we want to loop until SI > len(b)-8.
ADDQ $24, BX
CMPQ SI, BX
JG fourByte
wordLoop:
// Calculate k1.
MOVQ (SI), R8
ADDQ $8, SI
IMULQ R14, R8
ROLQ $31, R8
IMULQ R13, R8
XORQ R8, AX
ROLQ $27, AX
IMULQ R13, AX
ADDQ DI, AX
CMPQ SI, BX
JLE wordLoop
fourByte:
ADDQ $4, BX
CMPQ SI, BX
JG singles
MOVL (SI), R8
ADDQ $4, SI
IMULQ R13, R8
XORQ R8, AX
ROLQ $23, AX
IMULQ R14, AX
ADDQ ·prime3v(SB), AX
singles:
ADDQ $4, BX
CMPQ SI, BX
JGE finalize
singlesLoop:
MOVBQZX (SI), R12
ADDQ $1, SI
IMULQ ·prime5v(SB), R12
XORQ R12, AX
ROLQ $11, AX
IMULQ R13, AX
CMPQ SI, BX
JL singlesLoop
finalize:
MOVQ AX, R12
SHRQ $33, R12
XORQ R12, AX
IMULQ R14, AX
MOVQ AX, R12
SHRQ $29, R12
XORQ R12, AX
IMULQ ·prime3v(SB), AX
MOVQ AX, R12
SHRQ $32, R12
XORQ R12, AX
MOVQ AX, ret+24(FP)
RET
// writeBlocks uses the same registers as above except that it uses AX to store
// the d pointer.
// func writeBlocks(d *Digest, b []byte) int
TEXT ·writeBlocks(SB), NOSPLIT, $0-40
// Load fixed primes needed for round.
MOVQ ·prime1v(SB), R13
MOVQ ·prime2v(SB), R14
// Load slice.
MOVQ b_base+8(FP), SI
MOVQ b_len+16(FP), DX
LEAQ (SI)(DX*1), BX
SUBQ $32, BX
// Load vN from d.
MOVQ d+0(FP), AX
MOVQ 0(AX), R8 // v1
MOVQ 8(AX), R9 // v2
MOVQ 16(AX), R10 // v3
MOVQ 24(AX), R11 // v4
// We don't need to check the loop condition here; this function is
// always called with at least one block of data to process.
blockLoop:
round(R8)
round(R9)
round(R10)
round(R11)
CMPQ SI, BX
JLE blockLoop
// Copy vN back to d.
MOVQ R8, 0(AX)
MOVQ R9, 8(AX)
MOVQ R10, 16(AX)
MOVQ R11, 24(AX)
// The number of bytes written is SI minus the old base pointer.
SUBQ b_base+8(FP), SI
MOVQ SI, ret+32(FP)
RET

@ -1,76 +0,0 @@
// +build !amd64 appengine !gc purego
package xxhash
// Sum64 computes the 64-bit xxHash digest of b.
func Sum64(b []byte) uint64 {
// A simpler version would be
// d := New()
// d.Write(b)
// return d.Sum64()
// but this is faster, particularly for small inputs.
n := len(b)
var h uint64
if n >= 32 {
v1 := prime1v + prime2
v2 := prime2
v3 := uint64(0)
v4 := -prime1v
for len(b) >= 32 {
v1 = round(v1, u64(b[0:8:len(b)]))
v2 = round(v2, u64(b[8:16:len(b)]))
v3 = round(v3, u64(b[16:24:len(b)]))
v4 = round(v4, u64(b[24:32:len(b)]))
b = b[32:len(b):len(b)]
}
h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4)
h = mergeRound(h, v1)
h = mergeRound(h, v2)
h = mergeRound(h, v3)
h = mergeRound(h, v4)
} else {
h = prime5
}
h += uint64(n)
i, end := 0, len(b)
for ; i+8 <= end; i += 8 {
k1 := round(0, u64(b[i:i+8:len(b)]))
h ^= k1
h = rol27(h)*prime1 + prime4
}
if i+4 <= end {
h ^= uint64(u32(b[i:i+4:len(b)])) * prime1
h = rol23(h)*prime2 + prime3
i += 4
}
for ; i < end; i++ {
h ^= uint64(b[i]) * prime5
h = rol11(h) * prime1
}
h ^= h >> 33
h *= prime2
h ^= h >> 29
h *= prime3
h ^= h >> 32
return h
}
func writeBlocks(d *Digest, b []byte) int {
v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4
n := len(b)
for len(b) >= 32 {
v1 = round(v1, u64(b[0:8:len(b)]))
v2 = round(v2, u64(b[8:16:len(b)]))
v3 = round(v3, u64(b[16:24:len(b)]))
v4 = round(v4, u64(b[24:32:len(b)]))
b = b[32:len(b):len(b)]
}
d.v1, d.v2, d.v3, d.v4 = v1, v2, v3, v4
return n - len(b)
}

@ -1,15 +0,0 @@
// +build appengine
// This file contains the safe implementations of otherwise unsafe-using code.
package xxhash
// Sum64String computes the 64-bit xxHash digest of s.
func Sum64String(s string) uint64 {
return Sum64([]byte(s))
}
// WriteString adds more data to d. It always returns len(s), nil.
func (d *Digest) WriteString(s string) (n int, err error) {
return d.Write([]byte(s))
}

@ -1,57 +0,0 @@
// +build !appengine
// This file encapsulates usage of unsafe.
// xxhash_safe.go contains the safe implementations.
package xxhash
import (
"unsafe"
)
// In the future it's possible that compiler optimizations will make these
// XxxString functions unnecessary by realizing that calls such as
// Sum64([]byte(s)) don't need to copy s. See https://golang.org/issue/2205.
// If that happens, even if we keep these functions they can be replaced with
// the trivial safe code.
// NOTE: The usual way of doing an unsafe string-to-[]byte conversion is:
//
// var b []byte
// bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
// bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data
// bh.Len = len(s)
// bh.Cap = len(s)
//
// Unfortunately, as of Go 1.15.3 the inliner's cost model assigns a high enough
// weight to this sequence of expressions that any function that uses it will
// not be inlined. Instead, the functions below use a different unsafe
// conversion designed to minimize the inliner weight and allow both to be
// inlined. There is also a test (TestInlining) which verifies that these are
// inlined.
//
// See https://github.com/golang/go/issues/42739 for discussion.
// Sum64String computes the 64-bit xxHash digest of s.
// It may be faster than Sum64([]byte(s)) by avoiding a copy.
func Sum64String(s string) uint64 {
b := *(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)}))
return Sum64(b)
}
// WriteString adds more data to d. It always returns len(s), nil.
// It may be faster than Write([]byte(s)) by avoiding a copy.
func (d *Digest) WriteString(s string) (n int, err error) {
d.Write(*(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)})))
// d.Write always returns len(s), nil.
// Ignoring the return output and returning these fixed values buys a
// savings of 6 in the inliner's cost model.
return len(s), nil
}
// sliceHeader is similar to reflect.SliceHeader, but it assumes that the layout
// of the first two words is the same as the layout of a string.
type sliceHeader struct {
s string
cap int
}

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2017-2020 Damian Gryski <damian@gryski.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@ -1,79 +0,0 @@
package rendezvous
type Rendezvous struct {
nodes map[string]int
nstr []string
nhash []uint64
hash Hasher
}
type Hasher func(s string) uint64
func New(nodes []string, hash Hasher) *Rendezvous {
r := &Rendezvous{
nodes: make(map[string]int, len(nodes)),
nstr: make([]string, len(nodes)),
nhash: make([]uint64, len(nodes)),
hash: hash,
}
for i, n := range nodes {
r.nodes[n] = i
r.nstr[i] = n
r.nhash[i] = hash(n)
}
return r
}
func (r *Rendezvous) Lookup(k string) string {
// short-circuit if we're empty
if len(r.nodes) == 0 {
return ""
}
khash := r.hash(k)
var midx int
var mhash = xorshiftMult64(khash ^ r.nhash[0])
for i, nhash := range r.nhash[1:] {
if h := xorshiftMult64(khash ^ nhash); h > mhash {
midx = i + 1
mhash = h
}
}
return r.nstr[midx]
}
func (r *Rendezvous) Add(node string) {
r.nodes[node] = len(r.nstr)
r.nstr = append(r.nstr, node)
r.nhash = append(r.nhash, r.hash(node))
}
func (r *Rendezvous) Remove(node string) {
// find index of node to remove
nidx := r.nodes[node]
// remove from the slices
l := len(r.nstr)
r.nstr[nidx] = r.nstr[l]
r.nstr = r.nstr[:l]
r.nhash[nidx] = r.nhash[l]
r.nhash = r.nhash[:l]
// update the map
delete(r.nodes, node)
moved := r.nstr[nidx]
r.nodes[moved] = nidx
}
func xorshiftMult64(x uint64) uint64 {
x ^= x >> 12 // a
x ^= x << 25 // b
x ^= x >> 27 // c
return x * 2685821657736338717
}

@ -1,26 +0,0 @@
language: go
sudo: false
go:
- 1.8.x
- 1.9.x
- 1.10.x
- 1.11.x
- 1.12.x
- master
git:
depth: 10
matrix:
fast_finish: true
include:
- go: 1.11.x
env: GO111MODULE=on
- go: 1.12.x
env: GO111MODULE=on
script:
- go test -v -covermode=count -coverprofile=coverage.out
after_success:
- bash <(curl -s https://codecov.io/bash)

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014 Manuel Martínez-Almeida
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@ -1,58 +0,0 @@
# Server-Sent Events
[![GoDoc](https://godoc.org/github.com/gin-contrib/sse?status.svg)](https://godoc.org/github.com/gin-contrib/sse)
[![Build Status](https://travis-ci.org/gin-contrib/sse.svg)](https://travis-ci.org/gin-contrib/sse)
[![codecov](https://codecov.io/gh/gin-contrib/sse/branch/master/graph/badge.svg)](https://codecov.io/gh/gin-contrib/sse)
[![Go Report Card](https://goreportcard.com/badge/github.com/gin-contrib/sse)](https://goreportcard.com/report/github.com/gin-contrib/sse)
Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. The Server-Sent Events EventSource API is [standardized as part of HTML5[1] by the W3C](http://www.w3.org/TR/2009/WD-eventsource-20091029/).
- [Read this great SSE introduction by the HTML5Rocks guys](http://www.html5rocks.com/en/tutorials/eventsource/basics/)
- [Browser support](http://caniuse.com/#feat=eventsource)
## Sample code
```go
import "github.com/gin-contrib/sse"
func httpHandler(w http.ResponseWriter, req *http.Request) {
// data can be a primitive like a string, an integer or a float
sse.Encode(w, sse.Event{
Event: "message",
Data: "some data\nmore data",
})
// also a complex type, like a map, a struct or a slice
sse.Encode(w, sse.Event{
Id: "124",
Event: "message",
Data: map[string]interface{}{
"user": "manu",
"date": time.Now().Unix(),
"content": "hi!",
},
})
}
```
```
event: message
data: some data\\nmore data
id: 124
event: message
data: {"content":"hi!","date":1431540810,"user":"manu"}
```
## Content-Type
```go
fmt.Println(sse.ContentType)
```
```
text/event-stream
```
## Decoding support
There is a client-side implementation of SSE coming soon.

@ -1,116 +0,0 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package sse
import (
"bytes"
"io"
"io/ioutil"
)
type decoder struct {
events []Event
}
func Decode(r io.Reader) ([]Event, error) {
var dec decoder
return dec.decode(r)
}
func (d *decoder) dispatchEvent(event Event, data string) {
dataLength := len(data)
if dataLength > 0 {
//If the data buffer's last character is a U+000A LINE FEED (LF) character, then remove the last character from the data buffer.
data = data[:dataLength-1]
dataLength--
}
if dataLength == 0 && event.Event == "" {
return
}
if event.Event == "" {
event.Event = "message"
}
event.Data = data
d.events = append(d.events, event)
}
func (d *decoder) decode(r io.Reader) ([]Event, error) {
buf, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
var currentEvent Event
var dataBuffer *bytes.Buffer = new(bytes.Buffer)
// TODO (and unit tests)
// Lines must be separated by either a U+000D CARRIAGE RETURN U+000A LINE FEED (CRLF) character pair,
// a single U+000A LINE FEED (LF) character,
// or a single U+000D CARRIAGE RETURN (CR) character.
lines := bytes.Split(buf, []byte{'\n'})
for _, line := range lines {
if len(line) == 0 {
// If the line is empty (a blank line). Dispatch the event.
d.dispatchEvent(currentEvent, dataBuffer.String())
// reset current event and data buffer
currentEvent = Event{}
dataBuffer.Reset()
continue
}
if line[0] == byte(':') {
// If the line starts with a U+003A COLON character (:), ignore the line.
continue
}
var field, value []byte
colonIndex := bytes.IndexRune(line, ':')
if colonIndex != -1 {
// If the line contains a U+003A COLON character character (:)
// Collect the characters on the line before the first U+003A COLON character (:),
// and let field be that string.
field = line[:colonIndex]
// Collect the characters on the line after the first U+003A COLON character (:),
// and let value be that string.
value = line[colonIndex+1:]
// If value starts with a single U+0020 SPACE character, remove it from value.
if len(value) > 0 && value[0] == ' ' {
value = value[1:]
}
} else {
// Otherwise, the string is not empty but does not contain a U+003A COLON character character (:)
// Use the whole line as the field name, and the empty string as the field value.
field = line
value = []byte{}
}
// The steps to process the field given a field name and a field value depend on the field name,
// as given in the following list. Field names must be compared literally,
// with no case folding performed.
switch string(field) {
case "event":
// Set the event name buffer to field value.
currentEvent.Event = string(value)
case "id":
// Set the event stream's last event ID to the field value.
currentEvent.Id = string(value)
case "retry":
// If the field value consists of only characters in the range U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9),
// then interpret the field value as an integer in base ten, and set the event stream's reconnection time to that integer.
// Otherwise, ignore the field.
currentEvent.Id = string(value)
case "data":
// Append the field value to the data buffer,
dataBuffer.Write(value)
// then append a single U+000A LINE FEED (LF) character to the data buffer.
dataBuffer.WriteString("\n")
default:
//Otherwise. The field is ignored.
continue
}
}
// Once the end of the file is reached, the user agent must dispatch the event one final time.
d.dispatchEvent(currentEvent, dataBuffer.String())
return d.events, nil
}

@ -1,110 +0,0 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package sse
import (
"encoding/json"
"fmt"
"io"
"net/http"
"reflect"
"strconv"
"strings"
)
// Server-Sent Events
// W3C Working Draft 29 October 2009
// http://www.w3.org/TR/2009/WD-eventsource-20091029/
const ContentType = "text/event-stream"
var contentType = []string{ContentType}
var noCache = []string{"no-cache"}
var fieldReplacer = strings.NewReplacer(
"\n", "\\n",
"\r", "\\r")
var dataReplacer = strings.NewReplacer(
"\n", "\ndata:",
"\r", "\\r")
type Event struct {
Event string
Id string
Retry uint
Data interface{}
}
func Encode(writer io.Writer, event Event) error {
w := checkWriter(writer)
writeId(w, event.Id)
writeEvent(w, event.Event)
writeRetry(w, event.Retry)
return writeData(w, event.Data)
}
func writeId(w stringWriter, id string) {
if len(id) > 0 {
w.WriteString("id:")
fieldReplacer.WriteString(w, id)
w.WriteString("\n")
}
}
func writeEvent(w stringWriter, event string) {
if len(event) > 0 {
w.WriteString("event:")
fieldReplacer.WriteString(w, event)
w.WriteString("\n")
}
}
func writeRetry(w stringWriter, retry uint) {
if retry > 0 {
w.WriteString("retry:")
w.WriteString(strconv.FormatUint(uint64(retry), 10))
w.WriteString("\n")
}
}
func writeData(w stringWriter, data interface{}) error {
w.WriteString("data:")
switch kindOfData(data) {
case reflect.Struct, reflect.Slice, reflect.Map:
err := json.NewEncoder(w).Encode(data)
if err != nil {
return err
}
w.WriteString("\n")
default:
dataReplacer.WriteString(w, fmt.Sprint(data))
w.WriteString("\n\n")
}
return nil
}
func (r Event) Render(w http.ResponseWriter) error {
r.WriteContentType(w)
return Encode(w, r)
}
func (r Event) WriteContentType(w http.ResponseWriter) {
header := w.Header()
header["Content-Type"] = contentType
if _, exist := header["Cache-Control"]; !exist {
header["Cache-Control"] = noCache
}
}
func kindOfData(data interface{}) reflect.Kind {
value := reflect.ValueOf(data)
valueType := value.Kind()
if valueType == reflect.Ptr {
valueType = value.Elem().Kind()
}
return valueType
}

@ -1,24 +0,0 @@
package sse
import "io"
type stringWriter interface {
io.Writer
WriteString(string) (int, error)
}
type stringWrapper struct {
io.Writer
}
func (w stringWrapper) WriteString(str string) (int, error) {
return w.Writer.Write([]byte(str))
}
func checkWriter(writer io.Writer) stringWriter {
if w, ok := writer.(stringWriter); ok {
return w
} else {
return stringWrapper{writer}
}
}

@ -1,7 +0,0 @@
vendor/*
!vendor/vendor.json
coverage.out
count.out
test
profile.out
tmp.out

@ -1,39 +0,0 @@
run:
timeout: 5m
linters:
enable:
- asciicheck
- depguard
- dogsled
- durationcheck
- errcheck
- errorlint
- exportloopref
- gci
- gofmt
- goimports
- gosec
- misspell
- nakedret
- nilerr
- nolintlint
- revive
- wastedassign
issues:
exclude-rules:
- linters:
- structcheck
- unused
text: "`data` is unused"
- linters:
- staticcheck
text: "SA1019:"
- linters:
- revive
text: "var-naming:"
- linters:
- revive
text: "exported:"
- path: _test\.go
linters:
- gosec # security is not make sense in tests

@ -1,57 +0,0 @@
project_name: gin
builds:
-
# If true, skip the build.
# Useful for library projects.
# Default is false
skip: true
changelog:
# Set it to true if you wish to skip the changelog generation.
# This may result in an empty release notes on GitHub/GitLab/Gitea.
skip: false
# Changelog generation implementation to use.
#
# Valid options are:
# - `git`: uses `git log`;
# - `github`: uses the compare GitHub API, appending the author login to the changelog.
# - `gitlab`: uses the compare GitLab API, appending the author name and email to the changelog.
# - `github-native`: uses the GitHub release notes generation API, disables the groups feature.
#
# Defaults to `git`.
use: git
# Sorts the changelog by the commit's messages.
# Could either be asc, desc or empty
# Default is empty
sort: asc
# Group commits messages by given regex and title.
# Order value defines the order of the groups.
# Proving no regex means all commits will be grouped under the default group.
# Groups are disabled when using github-native, as it already groups things by itself.
#
# Default is no groups.
groups:
- title: Features
regexp: "^.*feat[(\\w)]*:+.*$"
order: 0
- title: 'Bug fixes'
regexp: "^.*fix[(\\w)]*:+.*$"
order: 1
- title: 'Enhancements'
regexp: "^.*chore[(\\w)]*:+.*$"
order: 2
- title: Others
order: 999
filters:
# Commit messages matching the regexp listed here will be removed from
# the changelog
# Default is empty
exclude:
- '^docs'
- 'CICD'
- typo

@ -1,406 +0,0 @@
List of all the awesome people working to make Gin the best Web Framework in Go.
## gin 1.x series authors
**Gin Core Team:** Bo-Yi Wu (@appleboy), thinkerou (@thinkerou), Javier Provecho (@javierprovecho)
## gin 0.x series authors
**Maintainers:** Manu Martinez-Almeida (@manucorporat), Javier Provecho (@javierprovecho)
------
People and companies, who have contributed, in alphabetical order.
- 178inaba <178inaba@users.noreply.github.com>
- A. F <hello@clivern.com>
- ABHISHEK SONI <abhishek.rocks26@gmail.com>
- Abhishek Chanda <achanda@users.noreply.github.com>
- Abner Chen <houjunchen@gmail.com>
- AcoNCodes <acongame@gmail.com>
- Adam Dratwinski <adam.dratwinski@gmail.com>
- Adam Mckaig <adam.mckaig@gmail.com>
- Adam Zielinski <MusicAdam@users.noreply.github.com>
- Adonis <donileo@gmail.com>
- Alan Wang <azzwacb9001@126.com>
- Albin Gilles <gilles.albin@gmail.com>
- Aleksandr Didenko <aa.didenko@yandex.ru>
- Alessandro (Ale) Segala <43508+ItalyPaleAle@users.noreply.github.com>
- Alex <AWulkan@users.noreply.github.com>
- Alexander <alexanderchenmh@gmail.com>
- Alexander Lokhman <alex.lokhman@gmail.com>
- Alexander Melentyev <55826637+alexander-melentyev@users.noreply.github.com>
- Alexander Nyquist <nyquist.alexander@gmail.com>
- Allen Ren <kulong0105@gmail.com>
- AllinGo <tanhp@outlook.com>
- Ammar Bandukwala <ammar@ammar.io>
- An Xiao (Luffy) <hac@zju.edu.cn>
- Andre Dublin <81dublin@gmail.com>
- Andrew Szeto <github@jabagawee.com>
- Andrey Abramov <andreyabramov.aaa@gmail.com>
- Andrey Nering <andrey.nering@gmail.com>
- Andrey Smirnov <Smirnov.Andrey@gmail.com>
- Andrii Bubis <firstrow@gmail.com>
- André Bazaglia <bazaglia@users.noreply.github.com>
- Andy Pan <panjf2000@gmail.com>
- Antoine GIRARD <sapk@users.noreply.github.com>
- Anup Kumar Panwar <1anuppanwar@gmail.com>
- Aravinth Sundaram <gosh.aravind@gmail.com>
- Artem <horechek@gmail.com>
- Ashwani <ashwanisharma686@gmail.com>
- Aurelien Regat-Barrel <arb@cyberkarma.net>
- Austin Heap <me@austinheap.com>
- Barnabus <jbampton@users.noreply.github.com>
- Bo-Yi Wu <appleboy.tw@gmail.com>
- Boris Borshevsky <BorisBorshevsky@gmail.com>
- Boyi Wu <p581581@gmail.com>
- BradyBromley <51128276+BradyBromley@users.noreply.github.com>
- Brendan Fosberry <brendan@shopkeep.com>
- Brian Wigginton <brianwigginton@gmail.com>
- Carlos Eduardo <carlosedp@gmail.com>
- Chad Russell <chaddouglasrussell@gmail.com>
- Charles <cxjava@gmail.com>
- Christian Muehlhaeuser <muesli@gmail.com>
- Christian Persson <saser@live.se>
- Christopher Harrington <ironiridis@gmail.com>
- Damon Zhao <yijun.zhao@outlook.com>
- Dan Markham <dmarkham@gmail.com>
- Dang Nguyen <hoangdang.me@gmail.com>
- Daniel Krom <kromdan@gmail.com>
- Daniel M. Lambea <dmlambea@gmail.com>
- Danieliu <liudanking@gmail.com>
- David Irvine <aviddiviner@gmail.com>
- David Zhang <crispgm@gmail.com>
- Davor Kapsa <dvrkps@users.noreply.github.com>
- DeathKing <DeathKing@users.noreply.github.com>
- Dennis Cho <47404603+forest747@users.noreply.github.com>
- Dmitry Dorogin <dmirogin@ya.ru>
- Dmitry Kutakov <vkd.castle@gmail.com>
- Dmitry Sedykh <dmitrys@d3h.local>
- Don2Quixote <35610661+Don2Quixote@users.noreply.github.com>
- Donn Pebe <iam@donnpebe.com>
- Dustin Decker <dustindecker@protonmail.com>
- Eason Lin <easonlin404@gmail.com>
- Edward Betts <edward@4angle.com>
- Egor Seredin <4819888+agmt@users.noreply.github.com>
- Emmanuel Goh <emmanuel@visenze.com>
- Equim <sayaka@ekyu.moe>
- Eren A. Akyol <eren@redmc.me>
- Eric_Lee <xplzv@126.com>
- Erik Bender <erik.bender@develerik.dev>
- Ethan Kan <ethankan@neoplot.com>
- Evgeny Persienko <e.persienko@office.ngs.ru>
- Faisal Alam <ifaisalalam@gmail.com>
- Fareed Dudhia <fareeddudhia@googlemail.com>
- Filip Figiel <figiel.filip@gmail.com>
- Florian Polster <couchpolster@icqmail.com>
- Frank Bille <github@frankbille.dk>
- Franz Bettag <franz@bett.ag>
- Ganlv <ganlvtech@users.noreply.github.com>
- Gaozhen Ying <yinggaozhen@hotmail.com>
- George Gabolaev <gabolaev98@gmail.com>
- George Kirilenko <necryin@users.noreply.github.com>
- Georges Varouchas <georges.varouchas@gmail.com>
- Gordon Tyler <gordon@doxxx.net>
- Harindu Perera <harinduenator@gmail.com>
- Helios <674876158@qq.com>
- Henry Kwan <piengeng@users.noreply.github.com>
- Henry Yee <henry@yearning.io>
- Himanshu Mishra <OrkoHunter@users.noreply.github.com>
- Hiroyuki Tanaka <h.tanaka.0325@gmail.com>
- Ibraheem Ahmed <ibrah1440@gmail.com>
- Ignacio Galindo <joiggama@gmail.com>
- Igor H. Vieira <zignd.igor@gmail.com>
- Ildar1111 <54001462+Ildar1111@users.noreply.github.com>
- Iskander (Alex) Sharipov <iskander.sharipov@intel.com>
- Ismail Gjevori <isgjevori@protonmail.com>
- Ivan Chen <allenivan@gmail.com>
- JINNOUCHI Yasushi <delphinus@remora.cx>
- James Pettyjohn <japettyjohn@users.noreply.github.com>
- Jamie Stackhouse <jamie.stackhouse@redspace.com>
- Jason Lee <jawc@hotmail.com>
- Javier Provecho <j.provecho@dartekstudios.com>
- Javier Provecho <javier.provecho@bq.com>
- Javier Provecho <javiertitan@gmail.com>
- Javier Provecho Fernandez <j.provecho@dartekstudios.com>
- Javier Provecho Fernandez <javiertitan@gmail.com>
- Jean-Christophe Lebreton <jclebreton@gmail.com>
- Jeff <laojianzi1994@gmail.com>
- Jeremy Loy <jeremy.b.loy@icloud.com>
- Jim Filippou <p3160253@aueb.gr>
- Jimmy Pettersson <jimmy@expertmaker.com>
- John Bampton <jbampton@users.noreply.github.com>
- Johnny Dallas <johnnydallas0308@gmail.com>
- Johnny Dallas <theonlyjohnny@theonlyjohnny.sh>
- Jonathan (JC) Chen <jc@dijonkitchen.org>
- Josep Jesus Bigorra Algaba <42377845+averageflow@users.noreply.github.com>
- Josh Horowitz <joshua.m.horowitz@gmail.com>
- Joshua Loper <josh.el3@gmail.com>
- Julien Schmidt <github@julienschmidt.com>
- Jun Kimura <jksmphone@gmail.com>
- Justin Beckwith <justin.beckwith@gmail.com>
- Justin Israel <justinisrael@gmail.com>
- Justin Mayhew <mayhew@live.ca>
- Jérôme Laforge <jerome-laforge@users.noreply.github.com>
- Kacper Bąk <56700396+53jk1@users.noreply.github.com>
- Kamron Batman <kamronbatman@users.noreply.github.com>
- Kane Rogers <kane@cleanstream.com.au>
- Kaushik Neelichetty <kaushikneelichetty6132@gmail.com>
- Keiji Yoshida <yoshida.keiji.84@gmail.com>
- Kel Cecil <kel.cecil@listhub.com>
- Kevin Mulvey <kmulvey@linux.com>
- Kevin Zhu <ipandtcp@gmail.com>
- Kirill Motkov <motkov.kirill@gmail.com>
- Klemen Sever <ksever@student.42.fr>
- Kristoffer A. Iversen <kristoffer.a.iversen@gmail.com>
- Krzysztof Szafrański <k.p.szafranski@gmail.com>
- Kumar McMillan <kumar.mcmillan@gmail.com>
- Kyle Mcgill <email@kylescottmcgill.com>
- Lanco <35420416+lancoLiu@users.noreply.github.com>
- Levi Olson <olson.levi@gmail.com>
- Lin Kao-Yuan <mosdeo@gmail.com>
- Linus Unnebäck <linus@folkdatorn.se>
- Lucas Clemente <lucas@clemente.io>
- Ludwig Valda Vasquez <bredov@gmail.com>
- Luis GG <lggomez@users.noreply.github.com>
- MW Lim <williamchange@gmail.com>
- Maksimov Sergey <konjoot@gmail.com>
- Manjusaka <lizheao940510@gmail.com>
- Manu MA <manu.mtza@gmail.com>
- Manu MA <manu.valladolid@gmail.com>
- Manu Mtz-Almeida <manu.valladolid@gmail.com>
- Manu Mtz.-Almeida <manu.valladolid@gmail.com>
- Manuel Alonso <manuelalonso@invisionapp.com>
- Mara Kim <hacker.root@gmail.com>
- Mario Kostelac <mario@intercom.io>
- Martin Karlsch <martin@karlsch.com>
- Matt Newberry <mnewberry@opentable.com>
- Matt Williams <gh@mattyw.net>
- Matthieu MOREL <mmorel-35@users.noreply.github.com>
- Max Hilbrunner <mhilbrunner@users.noreply.github.com>
- Maxime Soulé <btik-git@scoubidou.com>
- MetalBreaker <johnymichelson@gmail.com>
- Michael Puncel <mpuncel@squareup.com>
- MichaelDeSteven <51652084+MichaelDeSteven@users.noreply.github.com>
- Mike <38686456+icy4ever@users.noreply.github.com>
- Mike Stipicevic <mst@ableton.com>
- Miki Tebeka <miki.tebeka@gmail.com>
- Miles <MilesLin@users.noreply.github.com>
- Mirza Ceric <mirza.ceric@b2match.com>
- Mykyta Semenistyi <nikeiwe@gmail.com>
- Naoki Takano <honten@tinkermode.com>
- Ngalim Siregar <ngalim.siregar@gmail.com>
- Ni Hao <supernihaooo@qq.com>
- Nick Gerakines <nick@gerakines.net>
- Nikifor Seryakov <nikandfor@gmail.com>
- Notealot <714804968@qq.com>
- Olivier Mengué <dolmen@cpan.org>
- Olivier Robardet <orobardet@users.noreply.github.com>
- Pablo Moncada <pablo.moncada@bq.com>
- Pablo Moncada <pmoncadaisla@gmail.com>
- Panmax <967168@qq.com>
- Peperoncino <2wua4nlyi@gmail.com>
- Philipp Meinen <philipp@bind.ch>
- Pierre Massat <pierre@massat.io>
- Qt <golang.chen@gmail.com>
- Quentin ROYER <aydendevg@gmail.com>
- README Bot <35302948+codetriage-readme-bot@users.noreply.github.com>
- Rafal Zajac <rzajac@gmail.com>
- Rahul Datta Roy <rahuldroy@users.noreply.github.com>
- Rajiv Kilaparti <rajivk085@gmail.com>
- Raphael Gavache <raphael.gavache@datadoghq.com>
- Ray Rodriguez <rayrod2030@gmail.com>
- Regner Blok-Andersen <shadowdf@gmail.com>
- Remco <remco@dutchcoders.io>
- Rex Lee(李俊) <duguying2008@gmail.com>
- Richard Lee <dlackty@gmail.com>
- Riverside <wangyb65@gmail.com>
- Robert Wilkinson <wilkinson.robert.a@gmail.com>
- Rogier Lommers <rogier@lommers.org>
- Rohan Pai <me@rohanpai.com>
- Romain Beuque <rbeuque74@gmail.com>
- Roman Belyakovsky <ihryamzik@gmail.com>
- Roman Zaynetdinov <627197+zaynetro@users.noreply.github.com>
- Roman Zaynetdinov <roman.zaynetdinov@lekane.com>
- Ronald Petty <ronald.petty@rx-m.com>
- Ross Wolf <31489089+rw-access@users.noreply.github.com>
- Roy Lou <roylou@gmail.com>
- Rubi <14269809+codenoid@users.noreply.github.com>
- Ryan <46182144+ryanker@users.noreply.github.com>
- Ryan J. Yoder <me@ryanjyoder.com>
- SRK.Lyu <superalsrk@gmail.com>
- Sai <sairoutine@gmail.com>
- Samuel Abreu <sdepaula@gmail.com>
- Santhosh Kumar <santhoshkumarr1096@gmail.com>
- Sasha Melentyev <sasha@melentyev.io>
- Sasha Myasoedov <msoedov@gmail.com>
- Segev Finer <segev208@gmail.com>
- Sergey Egorov <egorovhome@gmail.com>
- Sergey Fedchenko <seregayoga@bk.ru>
- Sergey Gonimar <sergey.gonimar@gmail.com>
- Sergey Ponomarev <me@sergey-ponomarev.ru>
- Serica <943914044@qq.com>
- Shamus Taylor <Shamus03@me.com>
- Shilin Wang <jarvisfironman@gmail.com>
- Shuo <openset.wang@gmail.com>
- Skuli Oskarsson <skuli@codeiak.io>
- Snawoot <vladislav-ex-github@vm-0.com>
- Sridhar Ratnakumar <srid@srid.ca>
- Steeve Chailloux <steeve@chaahk.com>
- Sudhir Mishra <sudhirxps@gmail.com>
- Suhas Karanth <sudo-suhas@users.noreply.github.com>
- TaeJun Park <miking38@gmail.com>
- Tatsuya Hoshino <tatsuya7.hoshino7@gmail.com>
- Tevic <tevic.tt@gmail.com>
- Tevin Jeffrey <tev.jeffrey@gmail.com>
- The Gitter Badger <badger@gitter.im>
- Thibault Jamet <tjamet@users.noreply.github.com>
- Thomas Boerger <thomas@webhippie.de>
- Thomas Schaffer <loopfz@gmail.com>
- Tommy Chu <tommychu2256@gmail.com>
- Tudor Roman <tudurom@gmail.com>
- Uwe Dauernheim <djui@users.noreply.github.com>
- Valentine Oragbakosi <valentine13400@gmail.com>
- Vas N <pnvasanth@users.noreply.github.com>
- Vasilyuk Vasiliy <By-Vasiliy@users.noreply.github.com>
- Victor Castell <victor@victorcastell.com>
- Vince Yuan <vince.yuan@gmail.com>
- Vyacheslav Dubinin <vyacheslav.dubinin@gmail.com>
- Waynerv <ampedee@gmail.com>
- Weilin Shi <934587911@qq.com>
- Xudong Cai <fifsky@gmail.com>
- Yasuhiro Matsumoto <mattn.jp@gmail.com>
- Yehezkiel Syamsuhadi <ybs@ybs.im>
- Yoshiki Nakagawa <yyoshiki41@gmail.com>
- Yoshiyuki Kinjo <yskkin+github@gmail.com>
- Yue Yang <g1enyy0ung@gmail.com>
- ZYunH <zyunhjob@163.com>
- Zach Newburgh <zach.newburgh@gmail.com>
- Zasda Yusuf Mikail <zasdaym@gmail.com>
- ZhangYunHao <zyunhjob@163.com>
- ZhiFeng Hu <hufeng1987@gmail.com>
- Zhu Xi <zhuxi910511@163.com>
- a2tt <usera2tt@gmail.com>
- ahuigo <1781999+ahuigo@users.noreply.github.com>
- ali <anio@users.noreply.github.com>
- aljun <salameryy@163.com>
- andrea <crypto.andrea@protonmail.ch>
- andriikushch <andrii.kushch@gmail.com>
- anoty <anjunyou@foxmail.com>
- awkj <hzzbiu@gmail.com>
- axiaoxin <254606826@qq.com>
- bbiao <bbbiao@gmail.com>
- bestgopher <84328409@qq.com>
- betahu <zhong.wenhuang@foxmail.com>
- bigwheel <k.bigwheel+eng@gmail.com>
- bn4t <17193640+bn4t@users.noreply.github.com>
- bullgare <bullgare@gmail.com>
- chainhelen <chainhelen@gmail.com>
- chenyang929 <chenyang929code@gmail.com>
- chriswhelix <chris.williams@helix.com>
- collinmsn <4130944@qq.com>
- cssivision <cssivision@gmail.com>
- danielalves <alves.lopes.dan@gmail.com>
- delphinus <delphinus@remora.cx>
- dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
- dickeyxxx <jeff@dickeyxxx.com>
- edebernis <emeric.debernis@gmail.com>
- error10 <error@ioerror.us>
- esplo <esplo@users.noreply.github.com>
- eudore <30709860+eudore@users.noreply.github.com>
- ffhelicopter <32922889+ffhelicopter@users.noreply.github.com>
- filikos <11477309+filikos@users.noreply.github.com>
- forging2012 <forging2012@users.noreply.github.com>
- goqihoo <goqihoo@gmail.com>
- grapeVine <treeui.old@gmail.com>
- guonaihong <guonaihong@qq.com>
- heige <daheige@users.noreply.github.com>
- heige <zhuwei313@hotmail.com>
- hellojukay <hellojukay@163.com>
- henrylee2cn <henrylee2cn@gmail.com>
- htobenothing <htobenothing@gmail.com>
- iamhesir <78344375+iamhesir@users.noreply.github.com>
- ijaa <kailiu2013@gmail.com>
- ishanray <ishan.iipm@gmail.com>
- ishanray <ishanray@users.noreply.github.com>
- itcloudy <272685110@qq.com>
- jarodsong6 <jarodsong6@gmail.com>
- jasonrhansen <jasonrodneyhansen@gmail.com>
- jincheng9 <perfume0607@gmail.com>
- joeADSP <75027008+joeADSP@users.noreply.github.com>
- junfengye <junfeng.yejf@gmail.com>
- kaiiak <aNxFi37X@outlook.com>
- kebo <kevinke2020@outlook.com>
- keke <19yamashita15@gmail.com>
- kishor kunal raj <68464660+kishorkunal-raj@users.noreply.github.com>
- kyledinh <kyledinh@gmail.com>
- lantw44 <lantw44@gmail.com>
- likakuli <1154584512@qq.com>
- linfangrong <linfangrong.liuxin@qq.com>
- linzi <873804682@qq.com>
- llgoer <yanghuxiao@vip.qq.com>
- long-road <13412081338@163.com>
- mbesancon <mathieu.besancon@gmail.com>
- mehdy <mehdy.khoshnoody@gmail.com>
- metal A-wing <freedom.awing.777@gmail.com>
- micanzhang <micanzhang@gmail.com>
- minarc <ragnhildmowinckel@gmail.com>
- mllu <mornlyn@gmail.com>
- mopemoepe <yutaka.matsubara@gmail.com>
- msoedov <msoedov@gmail.com>
- mstmdev <mstmdev@gmail.com>
- novaeye <fcoffee@gmail.com>
- olebedev <oolebedev@gmail.com>
- phithon <phith0n@users.noreply.github.com>
- pjgg <pablo.gonzalez.granados@gmail.com>
- qm012 <67568757+qm012@users.noreply.github.com>
- raymonder jin <rayjingithub@gmail.com>
- rns <ruslan.shvedov@gmail.com>
- root@andrea:~# <crypto.andrea@protonmail.ch>
- sekky0905 <20237968+sekky0905@users.noreply.github.com>
- senhtry <w169q169@gmail.com>
- shadrus <shadrus@gmail.com>
- silasb <silas.baronda@gmail.com>
- solos <lxl1217@gmail.com>
- songjiayang <songjiayang@users.noreply.github.com>
- sope <shenshouer@163.com>
- srt180 <30768686+srt180@users.noreply.github.com>
- stackerzzq <foo_stacker@yeah.net>
- sunshineplan <sunshineplan@users.noreply.github.com>
- syssam <s.y.s.sam.sys@gmail.com>
- techjanitor <puntme@gmail.com>
- techjanitor <techjanitor@users.noreply.github.com>
- thinkerou <thinkerou@gmail.com>
- thinkgo <49174849+thinkgos@users.noreply.github.com>
- tsirolnik <tsirolnik@users.noreply.github.com>
- tyltr <31768692+tylitianrui@users.noreply.github.com>
- vinhha96 <anhvinha1@gmail.com>
- voidman <retmain@foxmail.com>
- vz <vzvway@gmail.com>
- wei <wei840222@gmail.com>
- weibaohui <weibaohui@yeah.net>
- whirosan <whirosan@users.noreply.github.com>
- willnewrelic <will@newrelic.com>
- wssccc <wssccc@qq.com>
- wuhuizuo <wuhuizuo@126.com>
- xyb <xyb4638@gmail.com>
- y-yagi <yuuji.yaginuma@gmail.com>
- yiranzai <wuqingdzx@gmail.com>
- youzeliang <youzel@126.com>
- yugu <chenzilong_1227@foxmail.com>
- yuyabe <yuyabee@gmail.com>
- zebozhuang <zebozhuang@163.com>
- zero11-0203 <93071220+zero11-0203@users.noreply.github.com>
- zesani <7sin@outlook.co.th>
- zhanweidu <zhanweidu@163.com>
- zhing <zqwillseven@gmail.com>
- ziheng <zihenglv@gmail.com>
- zzjin <zzjin@users.noreply.github.com>
- 森 優太 <59682979+uta-mori@users.noreply.github.com>
- 杰哥 <858806258@qq.com>
- 涛叔 <hi@taoshu.in>
- 市民233 <mengrenxiong@gmail.com>
- 尹宝强 <wmdandme@gmail.com>
- 梦溪笔谈 <loongmxbt@gmail.com>
- 飞雪无情 <ls8707@gmail.com>
- 寻寻觅觅的Gopher <zoujh99@qq.com>

@ -1,666 +0,0 @@
# Benchmark System
**VM HOST:** Travis
**Machine:** Ubuntu 16.04.6 LTS x64
**Date:** May 04th, 2020
**Version:** Gin v1.6.3
**Go Version:** 1.14.2 linux/amd64
**Source:** [Go HTTP Router Benchmark](https://github.com/gin-gonic/go-http-routing-benchmark)
**Result:** [See the gist](https://gist.github.com/appleboy/b5f2ecfaf50824ae9c64dcfb9165ae5e) or [Travis result](https://travis-ci.org/github/gin-gonic/go-http-routing-benchmark/jobs/682947061)
## Static Routes: 157
```sh
Gin: 34936 Bytes
HttpServeMux: 14512 Bytes
Ace: 30680 Bytes
Aero: 34536 Bytes
Bear: 30456 Bytes
Beego: 98456 Bytes
Bone: 40224 Bytes
Chi: 83608 Bytes
Denco: 10216 Bytes
Echo: 80328 Bytes
GocraftWeb: 55288 Bytes
Goji: 29744 Bytes
Gojiv2: 105840 Bytes
GoJsonRest: 137496 Bytes
GoRestful: 816936 Bytes
GorillaMux: 585632 Bytes
GowwwRouter: 24968 Bytes
HttpRouter: 21712 Bytes
HttpTreeMux: 73448 Bytes
Kocha: 115472 Bytes
LARS: 30640 Bytes
Macaron: 38592 Bytes
Martini: 310864 Bytes
Pat: 19696 Bytes
Possum: 89920 Bytes
R2router: 23712 Bytes
Rivet: 24608 Bytes
Tango: 28264 Bytes
TigerTonic: 78768 Bytes
Traffic: 538976 Bytes
Vulcan: 369960 Bytes
```
## GithubAPI Routes: 203
```sh
Gin: 58512 Bytes
Ace: 48688 Bytes
Aero: 318568 Bytes
Bear: 84248 Bytes
Beego: 150936 Bytes
Bone: 100976 Bytes
Chi: 95112 Bytes
Denco: 36736 Bytes
Echo: 100296 Bytes
GocraftWeb: 95432 Bytes
Goji: 49680 Bytes
Gojiv2: 104704 Bytes
GoJsonRest: 141976 Bytes
GoRestful: 1241656 Bytes
GorillaMux: 1322784 Bytes
GowwwRouter: 80008 Bytes
HttpRouter: 37144 Bytes
HttpTreeMux: 78800 Bytes
Kocha: 785120 Bytes
LARS: 48600 Bytes
Macaron: 92784 Bytes
Martini: 485264 Bytes
Pat: 21200 Bytes
Possum: 85312 Bytes
R2router: 47104 Bytes
Rivet: 42840 Bytes
Tango: 54840 Bytes
TigerTonic: 95264 Bytes
Traffic: 921744 Bytes
Vulcan: 425992 Bytes
```
## GPlusAPI Routes: 13
```sh
Gin: 4384 Bytes
Ace: 3712 Bytes
Aero: 26056 Bytes
Bear: 7112 Bytes
Beego: 10272 Bytes
Bone: 6688 Bytes
Chi: 8024 Bytes
Denco: 3264 Bytes
Echo: 9688 Bytes
GocraftWeb: 7496 Bytes
Goji: 3152 Bytes
Gojiv2: 7376 Bytes
GoJsonRest: 11400 Bytes
GoRestful: 74328 Bytes
GorillaMux: 66208 Bytes
GowwwRouter: 5744 Bytes
HttpRouter: 2808 Bytes
HttpTreeMux: 7440 Bytes
Kocha: 128880 Bytes
LARS: 3656 Bytes
Macaron: 8656 Bytes
Martini: 23920 Bytes
Pat: 1856 Bytes
Possum: 7248 Bytes
R2router: 3928 Bytes
Rivet: 3064 Bytes
Tango: 5168 Bytes
TigerTonic: 9408 Bytes
Traffic: 46400 Bytes
Vulcan: 25544 Bytes
```
## ParseAPI Routes: 26
```sh
Gin: 7776 Bytes
Ace: 6704 Bytes
Aero: 28488 Bytes
Bear: 12320 Bytes
Beego: 19280 Bytes
Bone: 11440 Bytes
Chi: 9744 Bytes
Denco: 4192 Bytes
Echo: 11664 Bytes
GocraftWeb: 12800 Bytes
Goji: 5680 Bytes
Gojiv2: 14464 Bytes
GoJsonRest: 14072 Bytes
GoRestful: 116264 Bytes
GorillaMux: 105880 Bytes
GowwwRouter: 9344 Bytes
HttpRouter: 5072 Bytes
HttpTreeMux: 7848 Bytes
Kocha: 181712 Bytes
LARS: 6632 Bytes
Macaron: 13648 Bytes
Martini: 45888 Bytes
Pat: 2560 Bytes
Possum: 9200 Bytes
R2router: 7056 Bytes
Rivet: 5680 Bytes
Tango: 8920 Bytes
TigerTonic: 9840 Bytes
Traffic: 79096 Bytes
Vulcan: 44504 Bytes
```
## Static Routes
```sh
BenchmarkGin_StaticAll 62169 19319 ns/op 0 B/op 0 allocs/op
BenchmarkAce_StaticAll 65428 18313 ns/op 0 B/op 0 allocs/op
BenchmarkAero_StaticAll 121132 9632 ns/op 0 B/op 0 allocs/op
BenchmarkHttpServeMux_StaticAll 52626 22758 ns/op 0 B/op 0 allocs/op
BenchmarkBeego_StaticAll 9962 179058 ns/op 55264 B/op 471 allocs/op
BenchmarkBear_StaticAll 14894 80966 ns/op 20272 B/op 469 allocs/op
BenchmarkBone_StaticAll 18718 64065 ns/op 0 B/op 0 allocs/op
BenchmarkChi_StaticAll 10000 149827 ns/op 67824 B/op 471 allocs/op
BenchmarkDenco_StaticAll 211393 5680 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_StaticAll 49341 24343 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_StaticAll 10000 126209 ns/op 46312 B/op 785 allocs/op
BenchmarkGoji_StaticAll 27956 43174 ns/op 0 B/op 0 allocs/op
BenchmarkGojiv2_StaticAll 3430 370718 ns/op 205984 B/op 1570 allocs/op
BenchmarkGoJsonRest_StaticAll 9134 188888 ns/op 51653 B/op 1727 allocs/op
BenchmarkGoRestful_StaticAll 706 1703330 ns/op 613280 B/op 2053 allocs/op
BenchmarkGorillaMux_StaticAll 1268 924083 ns/op 153233 B/op 1413 allocs/op
BenchmarkGowwwRouter_StaticAll 63374 18935 ns/op 0 B/op 0 allocs/op
BenchmarkHttpRouter_StaticAll 109938 10902 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_StaticAll 109166 10861 ns/op 0 B/op 0 allocs/op
BenchmarkKocha_StaticAll 92258 12992 ns/op 0 B/op 0 allocs/op
BenchmarkLARS_StaticAll 65200 18387 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_StaticAll 5671 291501 ns/op 115553 B/op 1256 allocs/op
BenchmarkMartini_StaticAll 807 1460498 ns/op 125444 B/op 1717 allocs/op
BenchmarkPat_StaticAll 513 2342396 ns/op 602832 B/op 12559 allocs/op
BenchmarkPossum_StaticAll 10000 128270 ns/op 65312 B/op 471 allocs/op
BenchmarkR2router_StaticAll 16726 71760 ns/op 22608 B/op 628 allocs/op
BenchmarkRivet_StaticAll 41722 28723 ns/op 0 B/op 0 allocs/op
BenchmarkTango_StaticAll 7606 205082 ns/op 39209 B/op 1256 allocs/op
BenchmarkTigerTonic_StaticAll 26247 45806 ns/op 7376 B/op 157 allocs/op
BenchmarkTraffic_StaticAll 550 2284518 ns/op 754864 B/op 14601 allocs/op
BenchmarkVulcan_StaticAll 10000 131343 ns/op 15386 B/op 471 allocs/op
```
## Micro Benchmarks
```sh
BenchmarkGin_Param 18785022 63.9 ns/op 0 B/op 0 allocs/op
BenchmarkAce_Param 14689765 81.5 ns/op 0 B/op 0 allocs/op
BenchmarkAero_Param 23094770 51.2 ns/op 0 B/op 0 allocs/op
BenchmarkBear_Param 1417045 845 ns/op 456 B/op 5 allocs/op
BenchmarkBeego_Param 1000000 1080 ns/op 352 B/op 3 allocs/op
BenchmarkBone_Param 1000000 1463 ns/op 816 B/op 6 allocs/op
BenchmarkChi_Param 1378756 885 ns/op 432 B/op 3 allocs/op
BenchmarkDenco_Param 8557899 143 ns/op 32 B/op 1 allocs/op
BenchmarkEcho_Param 16433347 75.5 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_Param 1000000 1218 ns/op 648 B/op 8 allocs/op
BenchmarkGoji_Param 1921248 617 ns/op 336 B/op 2 allocs/op
BenchmarkGojiv2_Param 561848 2156 ns/op 1328 B/op 11 allocs/op
BenchmarkGoJsonRest_Param 1000000 1358 ns/op 649 B/op 13 allocs/op
BenchmarkGoRestful_Param 224857 5307 ns/op 4192 B/op 14 allocs/op
BenchmarkGorillaMux_Param 498313 2459 ns/op 1280 B/op 10 allocs/op
BenchmarkGowwwRouter_Param 1864354 654 ns/op 432 B/op 3 allocs/op
BenchmarkHttpRouter_Param 26269074 47.7 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_Param 2109829 557 ns/op 352 B/op 3 allocs/op
BenchmarkKocha_Param 5050216 243 ns/op 56 B/op 3 allocs/op
BenchmarkLARS_Param 19811712 59.9 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_Param 662746 2329 ns/op 1072 B/op 10 allocs/op
BenchmarkMartini_Param 279902 4260 ns/op 1072 B/op 10 allocs/op
BenchmarkPat_Param 1000000 1382 ns/op 536 B/op 11 allocs/op
BenchmarkPossum_Param 1000000 1014 ns/op 496 B/op 5 allocs/op
BenchmarkR2router_Param 1712559 707 ns/op 432 B/op 5 allocs/op
BenchmarkRivet_Param 6648086 182 ns/op 48 B/op 1 allocs/op
BenchmarkTango_Param 1221504 994 ns/op 248 B/op 8 allocs/op
BenchmarkTigerTonic_Param 891661 2261 ns/op 776 B/op 16 allocs/op
BenchmarkTraffic_Param 350059 3598 ns/op 1856 B/op 21 allocs/op
BenchmarkVulcan_Param 2517823 472 ns/op 98 B/op 3 allocs/op
BenchmarkAce_Param5 9214365 130 ns/op 0 B/op 0 allocs/op
BenchmarkAero_Param5 15369013 77.9 ns/op 0 B/op 0 allocs/op
BenchmarkBear_Param5 1000000 1113 ns/op 501 B/op 5 allocs/op
BenchmarkBeego_Param5 1000000 1269 ns/op 352 B/op 3 allocs/op
BenchmarkBone_Param5 986820 1873 ns/op 864 B/op 6 allocs/op
BenchmarkChi_Param5 1000000 1156 ns/op 432 B/op 3 allocs/op
BenchmarkDenco_Param5 3036331 400 ns/op 160 B/op 1 allocs/op
BenchmarkEcho_Param5 6447133 186 ns/op 0 B/op 0 allocs/op
BenchmarkGin_Param5 10786068 110 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_Param5 844820 1944 ns/op 920 B/op 11 allocs/op
BenchmarkGoji_Param5 1474965 827 ns/op 336 B/op 2 allocs/op
BenchmarkGojiv2_Param5 442820 2516 ns/op 1392 B/op 11 allocs/op
BenchmarkGoJsonRest_Param5 507555 2711 ns/op 1097 B/op 16 allocs/op
BenchmarkGoRestful_Param5 216481 6093 ns/op 4288 B/op 14 allocs/op
BenchmarkGorillaMux_Param5 314402 3628 ns/op 1344 B/op 10 allocs/op
BenchmarkGowwwRouter_Param5 1624660 733 ns/op 432 B/op 3 allocs/op
BenchmarkHttpRouter_Param5 13167324 92.0 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_Param5 1000000 1295 ns/op 576 B/op 6 allocs/op
BenchmarkKocha_Param5 1000000 1138 ns/op 440 B/op 10 allocs/op
BenchmarkLARS_Param5 11580613 105 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_Param5 473596 2755 ns/op 1072 B/op 10 allocs/op
BenchmarkMartini_Param5 230756 5111 ns/op 1232 B/op 11 allocs/op
BenchmarkPat_Param5 469190 3370 ns/op 888 B/op 29 allocs/op
BenchmarkPossum_Param5 1000000 1002 ns/op 496 B/op 5 allocs/op
BenchmarkR2router_Param5 1422129 844 ns/op 432 B/op 5 allocs/op
BenchmarkRivet_Param5 2263789 539 ns/op 240 B/op 1 allocs/op
BenchmarkTango_Param5 1000000 1256 ns/op 360 B/op 8 allocs/op
BenchmarkTigerTonic_Param5 175500 7492 ns/op 2279 B/op 39 allocs/op
BenchmarkTraffic_Param5 233631 5816 ns/op 2208 B/op 27 allocs/op
BenchmarkVulcan_Param5 1923416 629 ns/op 98 B/op 3 allocs/op
BenchmarkAce_Param20 4321266 281 ns/op 0 B/op 0 allocs/op
BenchmarkAero_Param20 31501641 35.2 ns/op 0 B/op 0 allocs/op
BenchmarkBear_Param20 335204 3489 ns/op 1665 B/op 5 allocs/op
BenchmarkBeego_Param20 503674 2860 ns/op 352 B/op 3 allocs/op
BenchmarkBone_Param20 298922 4741 ns/op 2031 B/op 6 allocs/op
BenchmarkChi_Param20 878181 1957 ns/op 432 B/op 3 allocs/op
BenchmarkDenco_Param20 1000000 1360 ns/op 640 B/op 1 allocs/op
BenchmarkEcho_Param20 2104946 580 ns/op 0 B/op 0 allocs/op
BenchmarkGin_Param20 4167204 290 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_Param20 173064 7514 ns/op 3796 B/op 15 allocs/op
BenchmarkGoji_Param20 458778 2651 ns/op 1247 B/op 2 allocs/op
BenchmarkGojiv2_Param20 364862 3178 ns/op 1632 B/op 11 allocs/op
BenchmarkGoJsonRest_Param20 125514 9760 ns/op 4485 B/op 20 allocs/op
BenchmarkGoRestful_Param20 101217 11964 ns/op 6715 B/op 18 allocs/op
BenchmarkGorillaMux_Param20 147654 8132 ns/op 3452 B/op 12 allocs/op
BenchmarkGowwwRouter_Param20 1000000 1225 ns/op 432 B/op 3 allocs/op
BenchmarkHttpRouter_Param20 4920895 247 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_Param20 173202 6605 ns/op 3196 B/op 10 allocs/op
BenchmarkKocha_Param20 345988 3620 ns/op 1808 B/op 27 allocs/op
BenchmarkLARS_Param20 4592326 262 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_Param20 166492 7286 ns/op 2924 B/op 12 allocs/op
BenchmarkMartini_Param20 122162 10653 ns/op 3595 B/op 13 allocs/op
BenchmarkPat_Param20 78630 15239 ns/op 4424 B/op 93 allocs/op
BenchmarkPossum_Param20 1000000 1008 ns/op 496 B/op 5 allocs/op
BenchmarkR2router_Param20 294981 4587 ns/op 2284 B/op 7 allocs/op
BenchmarkRivet_Param20 691798 2090 ns/op 1024 B/op 1 allocs/op
BenchmarkTango_Param20 842440 2505 ns/op 856 B/op 8 allocs/op
BenchmarkTigerTonic_Param20 38614 31509 ns/op 9870 B/op 119 allocs/op
BenchmarkTraffic_Param20 57633 21107 ns/op 7853 B/op 47 allocs/op
BenchmarkVulcan_Param20 1000000 1178 ns/op 98 B/op 3 allocs/op
BenchmarkAce_ParamWrite 7330743 180 ns/op 8 B/op 1 allocs/op
BenchmarkAero_ParamWrite 13833598 86.7 ns/op 0 B/op 0 allocs/op
BenchmarkBear_ParamWrite 1363321 867 ns/op 456 B/op 5 allocs/op
BenchmarkBeego_ParamWrite 1000000 1104 ns/op 360 B/op 4 allocs/op
BenchmarkBone_ParamWrite 1000000 1475 ns/op 816 B/op 6 allocs/op
BenchmarkChi_ParamWrite 1320590 892 ns/op 432 B/op 3 allocs/op
BenchmarkDenco_ParamWrite 7093605 172 ns/op 32 B/op 1 allocs/op
BenchmarkEcho_ParamWrite 8434424 161 ns/op 8 B/op 1 allocs/op
BenchmarkGin_ParamWrite 10377034 118 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_ParamWrite 1000000 1266 ns/op 656 B/op 9 allocs/op
BenchmarkGoji_ParamWrite 1874168 654 ns/op 336 B/op 2 allocs/op
BenchmarkGojiv2_ParamWrite 459032 2352 ns/op 1360 B/op 13 allocs/op
BenchmarkGoJsonRest_ParamWrite 499434 2145 ns/op 1128 B/op 18 allocs/op
BenchmarkGoRestful_ParamWrite 241087 5470 ns/op 4200 B/op 15 allocs/op
BenchmarkGorillaMux_ParamWrite 425686 2522 ns/op 1280 B/op 10 allocs/op
BenchmarkGowwwRouter_ParamWrite 922172 1778 ns/op 976 B/op 8 allocs/op
BenchmarkHttpRouter_ParamWrite 15392049 77.7 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_ParamWrite 1973385 597 ns/op 352 B/op 3 allocs/op
BenchmarkKocha_ParamWrite 4262500 281 ns/op 56 B/op 3 allocs/op
BenchmarkLARS_ParamWrite 10764410 113 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_ParamWrite 486769 2726 ns/op 1176 B/op 14 allocs/op
BenchmarkMartini_ParamWrite 264804 4842 ns/op 1176 B/op 14 allocs/op
BenchmarkPat_ParamWrite 735116 2047 ns/op 960 B/op 15 allocs/op
BenchmarkPossum_ParamWrite 1000000 1004 ns/op 496 B/op 5 allocs/op
BenchmarkR2router_ParamWrite 1592136 768 ns/op 432 B/op 5 allocs/op
BenchmarkRivet_ParamWrite 3582051 339 ns/op 112 B/op 2 allocs/op
BenchmarkTango_ParamWrite 2237337 534 ns/op 136 B/op 4 allocs/op
BenchmarkTigerTonic_ParamWrite 439608 3136 ns/op 1216 B/op 21 allocs/op
BenchmarkTraffic_ParamWrite 306979 4328 ns/op 2280 B/op 25 allocs/op
BenchmarkVulcan_ParamWrite 2529973 472 ns/op 98 B/op 3 allocs/op
```
## GitHub
```sh
BenchmarkGin_GithubStatic 15629472 76.7 ns/op 0 B/op 0 allocs/op
BenchmarkAce_GithubStatic 15542612 75.9 ns/op 0 B/op 0 allocs/op
BenchmarkAero_GithubStatic 24777151 48.5 ns/op 0 B/op 0 allocs/op
BenchmarkBear_GithubStatic 2788894 435 ns/op 120 B/op 3 allocs/op
BenchmarkBeego_GithubStatic 1000000 1064 ns/op 352 B/op 3 allocs/op
BenchmarkBone_GithubStatic 93507 12838 ns/op 2880 B/op 60 allocs/op
BenchmarkChi_GithubStatic 1387743 860 ns/op 432 B/op 3 allocs/op
BenchmarkDenco_GithubStatic 39384996 30.4 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GithubStatic 12076382 99.1 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_GithubStatic 1596495 756 ns/op 296 B/op 5 allocs/op
BenchmarkGoji_GithubStatic 6364876 189 ns/op 0 B/op 0 allocs/op
BenchmarkGojiv2_GithubStatic 550202 2098 ns/op 1312 B/op 10 allocs/op
BenchmarkGoRestful_GithubStatic 102183 12552 ns/op 4256 B/op 13 allocs/op
BenchmarkGoJsonRest_GithubStatic 1000000 1029 ns/op 329 B/op 11 allocs/op
BenchmarkGorillaMux_GithubStatic 255552 5190 ns/op 976 B/op 9 allocs/op
BenchmarkGowwwRouter_GithubStatic 15531916 77.1 ns/op 0 B/op 0 allocs/op
BenchmarkHttpRouter_GithubStatic 27920724 43.1 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_GithubStatic 21448953 55.8 ns/op 0 B/op 0 allocs/op
BenchmarkKocha_GithubStatic 21405310 56.0 ns/op 0 B/op 0 allocs/op
BenchmarkLARS_GithubStatic 13625156 89.0 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_GithubStatic 1000000 1747 ns/op 736 B/op 8 allocs/op
BenchmarkMartini_GithubStatic 187186 7326 ns/op 768 B/op 9 allocs/op
BenchmarkPat_GithubStatic 109143 11563 ns/op 3648 B/op 76 allocs/op
BenchmarkPossum_GithubStatic 1575898 770 ns/op 416 B/op 3 allocs/op
BenchmarkR2router_GithubStatic 3046231 404 ns/op 144 B/op 4 allocs/op
BenchmarkRivet_GithubStatic 11484826 105 ns/op 0 B/op 0 allocs/op
BenchmarkTango_GithubStatic 1000000 1153 ns/op 248 B/op 8 allocs/op
BenchmarkTigerTonic_GithubStatic 4929780 249 ns/op 48 B/op 1 allocs/op
BenchmarkTraffic_GithubStatic 106351 11819 ns/op 4664 B/op 90 allocs/op
BenchmarkVulcan_GithubStatic 1613271 722 ns/op 98 B/op 3 allocs/op
BenchmarkAce_GithubParam 8386032 143 ns/op 0 B/op 0 allocs/op
BenchmarkAero_GithubParam 11816200 102 ns/op 0 B/op 0 allocs/op
BenchmarkBear_GithubParam 1000000 1012 ns/op 496 B/op 5 allocs/op
BenchmarkBeego_GithubParam 1000000 1157 ns/op 352 B/op 3 allocs/op
BenchmarkBone_GithubParam 184653 6912 ns/op 1888 B/op 19 allocs/op
BenchmarkChi_GithubParam 1000000 1102 ns/op 432 B/op 3 allocs/op
BenchmarkDenco_GithubParam 3484798 352 ns/op 128 B/op 1 allocs/op
BenchmarkEcho_GithubParam 6337380 189 ns/op 0 B/op 0 allocs/op
BenchmarkGin_GithubParam 9132032 131 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_GithubParam 1000000 1446 ns/op 712 B/op 9 allocs/op
BenchmarkGoji_GithubParam 1248640 977 ns/op 336 B/op 2 allocs/op
BenchmarkGojiv2_GithubParam 383233 2784 ns/op 1408 B/op 13 allocs/op
BenchmarkGoJsonRest_GithubParam 1000000 1991 ns/op 713 B/op 14 allocs/op
BenchmarkGoRestful_GithubParam 76414 16015 ns/op 4352 B/op 16 allocs/op
BenchmarkGorillaMux_GithubParam 150026 7663 ns/op 1296 B/op 10 allocs/op
BenchmarkGowwwRouter_GithubParam 1592044 751 ns/op 432 B/op 3 allocs/op
BenchmarkHttpRouter_GithubParam 10420628 115 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_GithubParam 1403755 835 ns/op 384 B/op 4 allocs/op
BenchmarkKocha_GithubParam 2286170 533 ns/op 128 B/op 5 allocs/op
BenchmarkLARS_GithubParam 9540374 129 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_GithubParam 533154 2742 ns/op 1072 B/op 10 allocs/op
BenchmarkMartini_GithubParam 119397 9638 ns/op 1152 B/op 11 allocs/op
BenchmarkPat_GithubParam 150675 8858 ns/op 2408 B/op 48 allocs/op
BenchmarkPossum_GithubParam 1000000 1001 ns/op 496 B/op 5 allocs/op
BenchmarkR2router_GithubParam 1602886 761 ns/op 432 B/op 5 allocs/op
BenchmarkRivet_GithubParam 2986579 409 ns/op 96 B/op 1 allocs/op
BenchmarkTango_GithubParam 1000000 1356 ns/op 344 B/op 8 allocs/op
BenchmarkTigerTonic_GithubParam 388899 3429 ns/op 1176 B/op 22 allocs/op
BenchmarkTraffic_GithubParam 123160 9734 ns/op 2816 B/op 40 allocs/op
BenchmarkVulcan_GithubParam 1000000 1138 ns/op 98 B/op 3 allocs/op
BenchmarkAce_GithubAll 40543 29670 ns/op 0 B/op 0 allocs/op
BenchmarkAero_GithubAll 57632 20648 ns/op 0 B/op 0 allocs/op
BenchmarkBear_GithubAll 9234 216179 ns/op 86448 B/op 943 allocs/op
BenchmarkBeego_GithubAll 7407 243496 ns/op 71456 B/op 609 allocs/op
BenchmarkBone_GithubAll 420 2922835 ns/op 720160 B/op 8620 allocs/op
BenchmarkChi_GithubAll 7620 238331 ns/op 87696 B/op 609 allocs/op
BenchmarkDenco_GithubAll 18355 64494 ns/op 20224 B/op 167 allocs/op
BenchmarkEcho_GithubAll 31251 38479 ns/op 0 B/op 0 allocs/op
BenchmarkGin_GithubAll 43550 27364 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_GithubAll 4117 300062 ns/op 131656 B/op 1686 allocs/op
BenchmarkGoji_GithubAll 3274 416158 ns/op 56112 B/op 334 allocs/op
BenchmarkGojiv2_GithubAll 1402 870518 ns/op 352720 B/op 4321 allocs/op
BenchmarkGoJsonRest_GithubAll 2976 401507 ns/op 134371 B/op 2737 allocs/op
BenchmarkGoRestful_GithubAll 410 2913158 ns/op 910144 B/op 2938 allocs/op
BenchmarkGorillaMux_GithubAll 346 3384987 ns/op 251650 B/op 1994 allocs/op
BenchmarkGowwwRouter_GithubAll 10000 143025 ns/op 72144 B/op 501 allocs/op
BenchmarkHttpRouter_GithubAll 55938 21360 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_GithubAll 10000 153944 ns/op 65856 B/op 671 allocs/op
BenchmarkKocha_GithubAll 10000 106315 ns/op 23304 B/op 843 allocs/op
BenchmarkLARS_GithubAll 47779 25084 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_GithubAll 3266 371907 ns/op 149409 B/op 1624 allocs/op
BenchmarkMartini_GithubAll 331 3444706 ns/op 226551 B/op 2325 allocs/op
BenchmarkPat_GithubAll 273 4381818 ns/op 1483152 B/op 26963 allocs/op
BenchmarkPossum_GithubAll 10000 164367 ns/op 84448 B/op 609 allocs/op
BenchmarkR2router_GithubAll 10000 160220 ns/op 77328 B/op 979 allocs/op
BenchmarkRivet_GithubAll 14625 82453 ns/op 16272 B/op 167 allocs/op
BenchmarkTango_GithubAll 6255 279611 ns/op 63826 B/op 1618 allocs/op
BenchmarkTigerTonic_GithubAll 2008 687874 ns/op 193856 B/op 4474 allocs/op
BenchmarkTraffic_GithubAll 355 3478508 ns/op 820744 B/op 14114 allocs/op
BenchmarkVulcan_GithubAll 6885 193333 ns/op 19894 B/op 609 allocs/op
```
## Google+
```sh
BenchmarkGin_GPlusStatic 19247326 62.2 ns/op 0 B/op 0 allocs/op
BenchmarkAce_GPlusStatic 20235060 59.2 ns/op 0 B/op 0 allocs/op
BenchmarkAero_GPlusStatic 31978935 37.6 ns/op 0 B/op 0 allocs/op
BenchmarkBear_GPlusStatic 3516523 341 ns/op 104 B/op 3 allocs/op
BenchmarkBeego_GPlusStatic 1212036 991 ns/op 352 B/op 3 allocs/op
BenchmarkBone_GPlusStatic 6736242 183 ns/op 32 B/op 1 allocs/op
BenchmarkChi_GPlusStatic 1490640 814 ns/op 432 B/op 3 allocs/op
BenchmarkDenco_GPlusStatic 55006856 21.8 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_GPlusStatic 17688258 67.9 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_GPlusStatic 1829181 666 ns/op 280 B/op 5 allocs/op
BenchmarkGoji_GPlusStatic 9147451 130 ns/op 0 B/op 0 allocs/op
BenchmarkGojiv2_GPlusStatic 594015 2063 ns/op 1312 B/op 10 allocs/op
BenchmarkGoJsonRest_GPlusStatic 1264906 950 ns/op 329 B/op 11 allocs/op
BenchmarkGoRestful_GPlusStatic 231558 5341 ns/op 3872 B/op 13 allocs/op
BenchmarkGorillaMux_GPlusStatic 908418 1809 ns/op 976 B/op 9 allocs/op
BenchmarkGowwwRouter_GPlusStatic 40684604 29.5 ns/op 0 B/op 0 allocs/op
BenchmarkHttpRouter_GPlusStatic 46742804 25.7 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_GPlusStatic 32567161 36.9 ns/op 0 B/op 0 allocs/op
BenchmarkKocha_GPlusStatic 33800060 35.3 ns/op 0 B/op 0 allocs/op
BenchmarkLARS_GPlusStatic 20431858 60.0 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_GPlusStatic 1000000 1745 ns/op 736 B/op 8 allocs/op
BenchmarkMartini_GPlusStatic 442248 3619 ns/op 768 B/op 9 allocs/op
BenchmarkPat_GPlusStatic 4328004 292 ns/op 96 B/op 2 allocs/op
BenchmarkPossum_GPlusStatic 1570753 763 ns/op 416 B/op 3 allocs/op
BenchmarkR2router_GPlusStatic 3339474 355 ns/op 144 B/op 4 allocs/op
BenchmarkRivet_GPlusStatic 18570961 64.7 ns/op 0 B/op 0 allocs/op
BenchmarkTango_GPlusStatic 1388702 860 ns/op 200 B/op 8 allocs/op
BenchmarkTigerTonic_GPlusStatic 7803543 159 ns/op 32 B/op 1 allocs/op
BenchmarkTraffic_GPlusStatic 878605 2171 ns/op 1112 B/op 16 allocs/op
BenchmarkVulcan_GPlusStatic 2742446 437 ns/op 98 B/op 3 allocs/op
BenchmarkAce_GPlusParam 11626975 105 ns/op 0 B/op 0 allocs/op
BenchmarkAero_GPlusParam 16914322 71.6 ns/op 0 B/op 0 allocs/op
BenchmarkBear_GPlusParam 1405173 832 ns/op 480 B/op 5 allocs/op
BenchmarkBeego_GPlusParam 1000000 1075 ns/op 352 B/op 3 allocs/op
BenchmarkBone_GPlusParam 1000000 1557 ns/op 816 B/op 6 allocs/op
BenchmarkChi_GPlusParam 1347926 894 ns/op 432 B/op 3 allocs/op
BenchmarkDenco_GPlusParam 5513000 212 ns/op 64 B/op 1 allocs/op
BenchmarkEcho_GPlusParam 11884383 101 ns/op 0 B/op 0 allocs/op
BenchmarkGin_GPlusParam 12898952 93.1 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_GPlusParam 1000000 1194 ns/op 648 B/op 8 allocs/op
BenchmarkGoji_GPlusParam 1857229 645 ns/op 336 B/op 2 allocs/op
BenchmarkGojiv2_GPlusParam 520939 2322 ns/op 1328 B/op 11 allocs/op
BenchmarkGoJsonRest_GPlusParam 1000000 1536 ns/op 649 B/op 13 allocs/op
BenchmarkGoRestful_GPlusParam 205449 5800 ns/op 4192 B/op 14 allocs/op
BenchmarkGorillaMux_GPlusParam 395310 3188 ns/op 1280 B/op 10 allocs/op
BenchmarkGowwwRouter_GPlusParam 1851798 667 ns/op 432 B/op 3 allocs/op
BenchmarkHttpRouter_GPlusParam 18420789 65.2 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_GPlusParam 1878463 629 ns/op 352 B/op 3 allocs/op
BenchmarkKocha_GPlusParam 4495610 273 ns/op 56 B/op 3 allocs/op
BenchmarkLARS_GPlusParam 14615976 83.2 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_GPlusParam 584145 2549 ns/op 1072 B/op 10 allocs/op
BenchmarkMartini_GPlusParam 250501 4583 ns/op 1072 B/op 10 allocs/op
BenchmarkPat_GPlusParam 1000000 1645 ns/op 576 B/op 11 allocs/op
BenchmarkPossum_GPlusParam 1000000 1008 ns/op 496 B/op 5 allocs/op
BenchmarkR2router_GPlusParam 1708191 688 ns/op 432 B/op 5 allocs/op
BenchmarkRivet_GPlusParam 5795014 211 ns/op 48 B/op 1 allocs/op
BenchmarkTango_GPlusParam 1000000 1091 ns/op 264 B/op 8 allocs/op
BenchmarkTigerTonic_GPlusParam 760221 2489 ns/op 856 B/op 16 allocs/op
BenchmarkTraffic_GPlusParam 309774 4039 ns/op 1872 B/op 21 allocs/op
BenchmarkVulcan_GPlusParam 1935730 623 ns/op 98 B/op 3 allocs/op
BenchmarkAce_GPlus2Params 9158314 134 ns/op 0 B/op 0 allocs/op
BenchmarkAero_GPlus2Params 11300517 107 ns/op 0 B/op 0 allocs/op
BenchmarkBear_GPlus2Params 1239238 961 ns/op 496 B/op 5 allocs/op
BenchmarkBeego_GPlus2Params 1000000 1202 ns/op 352 B/op 3 allocs/op
BenchmarkBone_GPlus2Params 335576 3725 ns/op 1168 B/op 10 allocs/op
BenchmarkChi_GPlus2Params 1000000 1014 ns/op 432 B/op 3 allocs/op
BenchmarkDenco_GPlus2Params 4394598 280 ns/op 64 B/op 1 allocs/op
BenchmarkEcho_GPlus2Params 7851861 154 ns/op 0 B/op 0 allocs/op
BenchmarkGin_GPlus2Params 9958588 120 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_GPlus2Params 1000000 1433 ns/op 712 B/op 9 allocs/op
BenchmarkGoji_GPlus2Params 1325134 909 ns/op 336 B/op 2 allocs/op
BenchmarkGojiv2_GPlus2Params 405955 2870 ns/op 1408 B/op 14 allocs/op
BenchmarkGoJsonRest_GPlus2Params 977038 1987 ns/op 713 B/op 14 allocs/op
BenchmarkGoRestful_GPlus2Params 205018 6142 ns/op 4384 B/op 16 allocs/op
BenchmarkGorillaMux_GPlus2Params 205641 6015 ns/op 1296 B/op 10 allocs/op
BenchmarkGowwwRouter_GPlus2Params 1748542 684 ns/op 432 B/op 3 allocs/op
BenchmarkHttpRouter_GPlus2Params 14047102 87.7 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_GPlus2Params 1418673 828 ns/op 384 B/op 4 allocs/op
BenchmarkKocha_GPlus2Params 2334562 520 ns/op 128 B/op 5 allocs/op
BenchmarkLARS_GPlus2Params 11954094 101 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_GPlus2Params 491552 2890 ns/op 1072 B/op 10 allocs/op
BenchmarkMartini_GPlus2Params 120532 9545 ns/op 1200 B/op 13 allocs/op
BenchmarkPat_GPlus2Params 194739 6766 ns/op 2168 B/op 33 allocs/op
BenchmarkPossum_GPlus2Params 1201224 1009 ns/op 496 B/op 5 allocs/op
BenchmarkR2router_GPlus2Params 1575535 756 ns/op 432 B/op 5 allocs/op
BenchmarkRivet_GPlus2Params 3698930 325 ns/op 96 B/op 1 allocs/op
BenchmarkTango_GPlus2Params 1000000 1212 ns/op 344 B/op 8 allocs/op
BenchmarkTigerTonic_GPlus2Params 349350 3660 ns/op 1200 B/op 22 allocs/op
BenchmarkTraffic_GPlus2Params 169714 7862 ns/op 2248 B/op 28 allocs/op
BenchmarkVulcan_GPlus2Params 1222288 974 ns/op 98 B/op 3 allocs/op
BenchmarkAce_GPlusAll 845606 1398 ns/op 0 B/op 0 allocs/op
BenchmarkAero_GPlusAll 1000000 1009 ns/op 0 B/op 0 allocs/op
BenchmarkBear_GPlusAll 103830 11386 ns/op 5488 B/op 61 allocs/op
BenchmarkBeego_GPlusAll 82653 14784 ns/op 4576 B/op 39 allocs/op
BenchmarkBone_GPlusAll 36601 33123 ns/op 11744 B/op 109 allocs/op
BenchmarkChi_GPlusAll 95264 12831 ns/op 5616 B/op 39 allocs/op
BenchmarkDenco_GPlusAll 567681 2950 ns/op 672 B/op 11 allocs/op
BenchmarkEcho_GPlusAll 720366 1665 ns/op 0 B/op 0 allocs/op
BenchmarkGin_GPlusAll 1000000 1185 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_GPlusAll 71575 16365 ns/op 8040 B/op 103 allocs/op
BenchmarkGoji_GPlusAll 136352 9191 ns/op 3696 B/op 22 allocs/op
BenchmarkGojiv2_GPlusAll 38006 31802 ns/op 17616 B/op 154 allocs/op
BenchmarkGoJsonRest_GPlusAll 57238 21561 ns/op 8117 B/op 170 allocs/op
BenchmarkGoRestful_GPlusAll 15147 79276 ns/op 55520 B/op 192 allocs/op
BenchmarkGorillaMux_GPlusAll 24446 48410 ns/op 16112 B/op 128 allocs/op
BenchmarkGowwwRouter_GPlusAll 150112 7770 ns/op 4752 B/op 33 allocs/op
BenchmarkHttpRouter_GPlusAll 1367820 878 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_GPlusAll 166628 8004 ns/op 4032 B/op 38 allocs/op
BenchmarkKocha_GPlusAll 265694 4570 ns/op 976 B/op 43 allocs/op
BenchmarkLARS_GPlusAll 1000000 1068 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_GPlusAll 54564 23305 ns/op 9568 B/op 104 allocs/op
BenchmarkMartini_GPlusAll 16274 73845 ns/op 14016 B/op 145 allocs/op
BenchmarkPat_GPlusAll 27181 44478 ns/op 15264 B/op 271 allocs/op
BenchmarkPossum_GPlusAll 122587 10277 ns/op 5408 B/op 39 allocs/op
BenchmarkR2router_GPlusAll 130137 9297 ns/op 5040 B/op 63 allocs/op
BenchmarkRivet_GPlusAll 532438 3323 ns/op 768 B/op 11 allocs/op
BenchmarkTango_GPlusAll 86054 14531 ns/op 3656 B/op 104 allocs/op
BenchmarkTigerTonic_GPlusAll 33936 35356 ns/op 11600 B/op 242 allocs/op
BenchmarkTraffic_GPlusAll 17833 68181 ns/op 26248 B/op 341 allocs/op
BenchmarkVulcan_GPlusAll 120109 9861 ns/op 1274 B/op 39 allocs/op
```
## Parse.com
```sh
BenchmarkGin_ParseStatic 18877833 63.5 ns/op 0 B/op 0 allocs/op
BenchmarkAce_ParseStatic 19663731 60.8 ns/op 0 B/op 0 allocs/op
BenchmarkAero_ParseStatic 28967341 41.5 ns/op 0 B/op 0 allocs/op
BenchmarkBear_ParseStatic 3006984 402 ns/op 120 B/op 3 allocs/op
BenchmarkBeego_ParseStatic 1000000 1031 ns/op 352 B/op 3 allocs/op
BenchmarkBone_ParseStatic 1782482 675 ns/op 144 B/op 3 allocs/op
BenchmarkChi_ParseStatic 1453261 819 ns/op 432 B/op 3 allocs/op
BenchmarkDenco_ParseStatic 45023595 26.5 ns/op 0 B/op 0 allocs/op
BenchmarkEcho_ParseStatic 17330470 69.3 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_ParseStatic 1644006 731 ns/op 296 B/op 5 allocs/op
BenchmarkGoji_ParseStatic 7026930 170 ns/op 0 B/op 0 allocs/op
BenchmarkGojiv2_ParseStatic 517618 2037 ns/op 1312 B/op 10 allocs/op
BenchmarkGoJsonRest_ParseStatic 1227080 975 ns/op 329 B/op 11 allocs/op
BenchmarkGoRestful_ParseStatic 192458 6659 ns/op 4256 B/op 13 allocs/op
BenchmarkGorillaMux_ParseStatic 744062 2109 ns/op 976 B/op 9 allocs/op
BenchmarkGowwwRouter_ParseStatic 37781062 31.8 ns/op 0 B/op 0 allocs/op
BenchmarkHttpRouter_ParseStatic 45311223 26.5 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_ParseStatic 21383475 56.1 ns/op 0 B/op 0 allocs/op
BenchmarkKocha_ParseStatic 29953290 40.1 ns/op 0 B/op 0 allocs/op
BenchmarkLARS_ParseStatic 20036196 62.7 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_ParseStatic 1000000 1740 ns/op 736 B/op 8 allocs/op
BenchmarkMartini_ParseStatic 404156 3801 ns/op 768 B/op 9 allocs/op
BenchmarkPat_ParseStatic 1547180 772 ns/op 240 B/op 5 allocs/op
BenchmarkPossum_ParseStatic 1608991 757 ns/op 416 B/op 3 allocs/op
BenchmarkR2router_ParseStatic 3177936 385 ns/op 144 B/op 4 allocs/op
BenchmarkRivet_ParseStatic 17783205 67.4 ns/op 0 B/op 0 allocs/op
BenchmarkTango_ParseStatic 1210777 990 ns/op 248 B/op 8 allocs/op
BenchmarkTigerTonic_ParseStatic 5316440 231 ns/op 48 B/op 1 allocs/op
BenchmarkTraffic_ParseStatic 496050 2539 ns/op 1256 B/op 19 allocs/op
BenchmarkVulcan_ParseStatic 2462798 488 ns/op 98 B/op 3 allocs/op
BenchmarkAce_ParseParam 13393669 89.6 ns/op 0 B/op 0 allocs/op
BenchmarkAero_ParseParam 19836619 60.4 ns/op 0 B/op 0 allocs/op
BenchmarkBear_ParseParam 1405954 864 ns/op 467 B/op 5 allocs/op
BenchmarkBeego_ParseParam 1000000 1065 ns/op 352 B/op 3 allocs/op
BenchmarkBone_ParseParam 1000000 1698 ns/op 896 B/op 7 allocs/op
BenchmarkChi_ParseParam 1356037 873 ns/op 432 B/op 3 allocs/op
BenchmarkDenco_ParseParam 6241392 204 ns/op 64 B/op 1 allocs/op
BenchmarkEcho_ParseParam 14088100 85.1 ns/op 0 B/op 0 allocs/op
BenchmarkGin_ParseParam 17426064 68.9 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_ParseParam 1000000 1254 ns/op 664 B/op 8 allocs/op
BenchmarkGoji_ParseParam 1682574 713 ns/op 336 B/op 2 allocs/op
BenchmarkGojiv2_ParseParam 502224 2333 ns/op 1360 B/op 12 allocs/op
BenchmarkGoJsonRest_ParseParam 1000000 1401 ns/op 649 B/op 13 allocs/op
BenchmarkGoRestful_ParseParam 182623 7097 ns/op 4576 B/op 14 allocs/op
BenchmarkGorillaMux_ParseParam 482332 2477 ns/op 1280 B/op 10 allocs/op
BenchmarkGowwwRouter_ParseParam 1834873 657 ns/op 432 B/op 3 allocs/op
BenchmarkHttpRouter_ParseParam 23593393 51.0 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_ParseParam 2100160 574 ns/op 352 B/op 3 allocs/op
BenchmarkKocha_ParseParam 4837220 252 ns/op 56 B/op 3 allocs/op
BenchmarkLARS_ParseParam 18411192 66.2 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_ParseParam 571870 2398 ns/op 1072 B/op 10 allocs/op
BenchmarkMartini_ParseParam 286262 4268 ns/op 1072 B/op 10 allocs/op
BenchmarkPat_ParseParam 692906 2157 ns/op 992 B/op 15 allocs/op
BenchmarkPossum_ParseParam 1000000 1011 ns/op 496 B/op 5 allocs/op
BenchmarkR2router_ParseParam 1722735 697 ns/op 432 B/op 5 allocs/op
BenchmarkRivet_ParseParam 6058054 203 ns/op 48 B/op 1 allocs/op
BenchmarkTango_ParseParam 1000000 1061 ns/op 280 B/op 8 allocs/op
BenchmarkTigerTonic_ParseParam 890275 2277 ns/op 784 B/op 15 allocs/op
BenchmarkTraffic_ParseParam 351322 3543 ns/op 1896 B/op 21 allocs/op
BenchmarkVulcan_ParseParam 2076544 572 ns/op 98 B/op 3 allocs/op
BenchmarkAce_Parse2Params 11718074 101 ns/op 0 B/op 0 allocs/op
BenchmarkAero_Parse2Params 16264988 73.4 ns/op 0 B/op 0 allocs/op
BenchmarkBear_Parse2Params 1238322 973 ns/op 496 B/op 5 allocs/op
BenchmarkBeego_Parse2Params 1000000 1120 ns/op 352 B/op 3 allocs/op
BenchmarkBone_Parse2Params 1000000 1632 ns/op 848 B/op 6 allocs/op
BenchmarkChi_Parse2Params 1239477 955 ns/op 432 B/op 3 allocs/op
BenchmarkDenco_Parse2Params 4944133 245 ns/op 64 B/op 1 allocs/op
BenchmarkEcho_Parse2Params 10518286 114 ns/op 0 B/op 0 allocs/op
BenchmarkGin_Parse2Params 14505195 82.7 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_Parse2Params 1000000 1437 ns/op 712 B/op 9 allocs/op
BenchmarkGoji_Parse2Params 1689883 707 ns/op 336 B/op 2 allocs/op
BenchmarkGojiv2_Parse2Params 502334 2308 ns/op 1344 B/op 11 allocs/op
BenchmarkGoJsonRest_Parse2Params 1000000 1771 ns/op 713 B/op 14 allocs/op
BenchmarkGoRestful_Parse2Params 159092 7583 ns/op 4928 B/op 14 allocs/op
BenchmarkGorillaMux_Parse2Params 417548 2980 ns/op 1296 B/op 10 allocs/op
BenchmarkGowwwRouter_Parse2Params 1751737 686 ns/op 432 B/op 3 allocs/op
BenchmarkHttpRouter_Parse2Params 18089204 66.3 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_Parse2Params 1556986 777 ns/op 384 B/op 4 allocs/op
BenchmarkKocha_Parse2Params 2493082 485 ns/op 128 B/op 5 allocs/op
BenchmarkLARS_Parse2Params 15350108 78.5 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_Parse2Params 530974 2605 ns/op 1072 B/op 10 allocs/op
BenchmarkMartini_Parse2Params 247069 4673 ns/op 1152 B/op 11 allocs/op
BenchmarkPat_Parse2Params 816295 2126 ns/op 752 B/op 16 allocs/op
BenchmarkPossum_Parse2Params 1000000 1002 ns/op 496 B/op 5 allocs/op
BenchmarkR2router_Parse2Params 1569771 733 ns/op 432 B/op 5 allocs/op
BenchmarkRivet_Parse2Params 4080546 295 ns/op 96 B/op 1 allocs/op
BenchmarkTango_Parse2Params 1000000 1121 ns/op 312 B/op 8 allocs/op
BenchmarkTigerTonic_Parse2Params 399556 3470 ns/op 1168 B/op 22 allocs/op
BenchmarkTraffic_Parse2Params 314194 4159 ns/op 1944 B/op 22 allocs/op
BenchmarkVulcan_Parse2Params 1827559 664 ns/op 98 B/op 3 allocs/op
BenchmarkAce_ParseAll 478395 2503 ns/op 0 B/op 0 allocs/op
BenchmarkAero_ParseAll 715392 1658 ns/op 0 B/op 0 allocs/op
BenchmarkBear_ParseAll 59191 20124 ns/op 8928 B/op 110 allocs/op
BenchmarkBeego_ParseAll 45507 27266 ns/op 9152 B/op 78 allocs/op
BenchmarkBone_ParseAll 29328 41459 ns/op 16208 B/op 147 allocs/op
BenchmarkChi_ParseAll 48531 25053 ns/op 11232 B/op 78 allocs/op
BenchmarkDenco_ParseAll 325532 4284 ns/op 928 B/op 16 allocs/op
BenchmarkEcho_ParseAll 433771 2759 ns/op 0 B/op 0 allocs/op
BenchmarkGin_ParseAll 576316 2082 ns/op 0 B/op 0 allocs/op
BenchmarkGocraftWeb_ParseAll 41500 29692 ns/op 13728 B/op 181 allocs/op
BenchmarkGoji_ParseAll 80833 15563 ns/op 5376 B/op 32 allocs/op
BenchmarkGojiv2_ParseAll 19836 60335 ns/op 34448 B/op 277 allocs/op
BenchmarkGoJsonRest_ParseAll 32210 38027 ns/op 13866 B/op 321 allocs/op
BenchmarkGoRestful_ParseAll 6644 190842 ns/op 117600 B/op 354 allocs/op
BenchmarkGorillaMux_ParseAll 12634 95894 ns/op 30288 B/op 250 allocs/op
BenchmarkGowwwRouter_ParseAll 98152 12159 ns/op 6912 B/op 48 allocs/op
BenchmarkHttpRouter_ParseAll 933208 1273 ns/op 0 B/op 0 allocs/op
BenchmarkHttpTreeMux_ParseAll 107191 11554 ns/op 5728 B/op 51 allocs/op
BenchmarkKocha_ParseAll 184862 6225 ns/op 1112 B/op 54 allocs/op
BenchmarkLARS_ParseAll 644546 1858 ns/op 0 B/op 0 allocs/op
BenchmarkMacaron_ParseAll 26145 46484 ns/op 19136 B/op 208 allocs/op
BenchmarkMartini_ParseAll 10000 121838 ns/op 25072 B/op 253 allocs/op
BenchmarkPat_ParseAll 25417 47196 ns/op 15216 B/op 308 allocs/op
BenchmarkPossum_ParseAll 58550 20735 ns/op 10816 B/op 78 allocs/op
BenchmarkR2router_ParseAll 72732 16584 ns/op 8352 B/op 120 allocs/op
BenchmarkRivet_ParseAll 281365 4968 ns/op 912 B/op 16 allocs/op
BenchmarkTango_ParseAll 42831 28668 ns/op 7168 B/op 208 allocs/op
BenchmarkTigerTonic_ParseAll 23774 49972 ns/op 16048 B/op 332 allocs/op
BenchmarkTraffic_ParseAll 10000 104679 ns/op 45520 B/op 605 allocs/op
BenchmarkVulcan_ParseAll 64810 18108 ns/op 2548 B/op 78 allocs/op
```

@ -1,502 +0,0 @@
# Gin ChangeLog
## Gin v1.8.1
### ENHANCEMENTS
* feat(context): add ContextWithFallback feature flag [#3172](https://github.com/gin-gonic/gin/pull/3172)
## Gin v1.8.0
## Break Changes
* TrustedProxies: Add default IPv6 support and refactor [#2967](https://github.com/gin-gonic/gin/pull/2967). Please replace `RemoteIP() (net.IP, bool)` with `RemoteIP() net.IP`
* gin.Context with fallback value from gin.Context.Request.Context() [#2751](https://github.com/gin-gonic/gin/pull/2751)
### BUGFIXES
* Fixed SetOutput() panics on go 1.17 [#2861](https://github.com/gin-gonic/gin/pull/2861)
* Fix: wrong when wildcard follows named param [#2983](https://github.com/gin-gonic/gin/pull/2983)
* Fix: missing sameSite when do context.reset() [#3123](https://github.com/gin-gonic/gin/pull/3123)
### ENHANCEMENTS
* Use Header() instead of deprecated HeaderMap [#2694](https://github.com/gin-gonic/gin/pull/2694)
* RouterGroup.Handle regular match optimization of http method [#2685](https://github.com/gin-gonic/gin/pull/2685)
* Add support go-json, another drop-in json replacement [#2680](https://github.com/gin-gonic/gin/pull/2680)
* Use errors.New to replace fmt.Errorf will much better [#2707](https://github.com/gin-gonic/gin/pull/2707)
* Use Duration.Truncate for truncating precision [#2711](https://github.com/gin-gonic/gin/pull/2711)
* Get client IP when using Cloudflare [#2723](https://github.com/gin-gonic/gin/pull/2723)
* Optimize code adjust [#2700](https://github.com/gin-gonic/gin/pull/2700/files)
* Optimize code and reduce code cyclomatic complexity [#2737](https://github.com/gin-gonic/gin/pull/2737)
* Improve sliceValidateError.Error performance [#2765](https://github.com/gin-gonic/gin/pull/2765)
* Support custom struct tag [#2720](https://github.com/gin-gonic/gin/pull/2720)
* Improve router group tests [#2787](https://github.com/gin-gonic/gin/pull/2787)
* Fallback Context.Deadline() Context.Done() Context.Err() to Context.Request.Context() [#2769](https://github.com/gin-gonic/gin/pull/2769)
* Some codes optimize [#2830](https://github.com/gin-gonic/gin/pull/2830) [#2834](https://github.com/gin-gonic/gin/pull/2834) [#2838](https://github.com/gin-gonic/gin/pull/2838) [#2837](https://github.com/gin-gonic/gin/pull/2837) [#2788](https://github.com/gin-gonic/gin/pull/2788) [#2848](https://github.com/gin-gonic/gin/pull/2848) [#2851](https://github.com/gin-gonic/gin/pull/2851) [#2701](https://github.com/gin-gonic/gin/pull/2701)
* TrustedProxies: Add default IPv6 support and refactor [#2967](https://github.com/gin-gonic/gin/pull/2967)
* Test(route): expose performRequest func [#3012](https://github.com/gin-gonic/gin/pull/3012)
* Support h2c with prior knowledge [#1398](https://github.com/gin-gonic/gin/pull/1398)
* Feat attachment filename support utf8 [#3071](https://github.com/gin-gonic/gin/pull/3071)
* Feat: add StaticFileFS [#2749](https://github.com/gin-gonic/gin/pull/2749)
* Feat(context): return GIN Context from Value method [#2825](https://github.com/gin-gonic/gin/pull/2825)
* Feat: automatically SetMode to TestMode when run go test [#3139](https://github.com/gin-gonic/gin/pull/3139)
* Add TOML bining for gin [#3081](https://github.com/gin-gonic/gin/pull/3081)
* IPv6 add default trusted proxies [#3033](https://github.com/gin-gonic/gin/pull/3033)
### DOCS
* Add note about nomsgpack tag to the readme [#2703](https://github.com/gin-gonic/gin/pull/2703)
## Gin v1.7.7
### BUGFIXES
* Fixed X-Forwarded-For unsafe handling of CVE-2020-28483 [#2844](https://github.com/gin-gonic/gin/pull/2844), closed issue [#2862](https://github.com/gin-gonic/gin/issues/2862).
* Tree: updated the code logic for `latestNode` [#2897](https://github.com/gin-gonic/gin/pull/2897), closed issue [#2894](https://github.com/gin-gonic/gin/issues/2894) [#2878](https://github.com/gin-gonic/gin/issues/2878).
* Tree: fixed the misplacement of adding slashes [#2847](https://github.com/gin-gonic/gin/pull/2847), closed issue [#2843](https://github.com/gin-gonic/gin/issues/2843).
* Tree: fixed tsr with mixed static and wildcard paths [#2924](https://github.com/gin-gonic/gin/pull/2924), closed issue [#2918](https://github.com/gin-gonic/gin/issues/2918).
### ENHANCEMENTS
* TrustedProxies: make it backward-compatible [#2887](https://github.com/gin-gonic/gin/pull/2887), closed issue [#2819](https://github.com/gin-gonic/gin/issues/2819).
* TrustedPlatform: provide custom options for another CDN services [#2906](https://github.com/gin-gonic/gin/pull/2906).
### DOCS
* NoMethod: added usage annotation ([#2832](https://github.com/gin-gonic/gin/pull/2832#issuecomment-929954463)).
## Gin v1.7.6
### BUGFIXES
* bump new release to fix v1.7.5 release error by using v1.7.4 codes.
## Gin v1.7.4
### BUGFIXES
* bump new release to fix checksum mismatch
## Gin v1.7.3
### BUGFIXES
* fix level 1 router match [#2767](https://github.com/gin-gonic/gin/issues/2767), [#2796](https://github.com/gin-gonic/gin/issues/2796)
## Gin v1.7.2
### BUGFIXES
* Fix conflict between param and exact path [#2706](https://github.com/gin-gonic/gin/issues/2706). Close issue [#2682](https://github.com/gin-gonic/gin/issues/2682) [#2696](https://github.com/gin-gonic/gin/issues/2696).
## Gin v1.7.1
### BUGFIXES
* fix: data race with trustedCIDRs from [#2674](https://github.com/gin-gonic/gin/issues/2674)([#2675](https://github.com/gin-gonic/gin/pull/2675))
## Gin v1.7.0
### BUGFIXES
* fix compile error from [#2572](https://github.com/gin-gonic/gin/pull/2572) ([#2600](https://github.com/gin-gonic/gin/pull/2600))
* fix: print headers without Authorization header on broken pipe ([#2528](https://github.com/gin-gonic/gin/pull/2528))
* fix(tree): reassign fullpath when register new node ([#2366](https://github.com/gin-gonic/gin/pull/2366))
### ENHANCEMENTS
* Support params and exact routes without creating conflicts ([#2663](https://github.com/gin-gonic/gin/pull/2663))
* chore: improve render string performance ([#2365](https://github.com/gin-gonic/gin/pull/2365))
* Sync route tree to httprouter latest code ([#2368](https://github.com/gin-gonic/gin/pull/2368))
* chore: rename getQueryCache/getFormCache to initQueryCache/initFormCa ([#2375](https://github.com/gin-gonic/gin/pull/2375))
* chore(performance): improve countParams ([#2378](https://github.com/gin-gonic/gin/pull/2378))
* Remove some functions that have the same effect as the bytes package ([#2387](https://github.com/gin-gonic/gin/pull/2387))
* update:SetMode function ([#2321](https://github.com/gin-gonic/gin/pull/2321))
* remove a unused type SecureJSONPrefix ([#2391](https://github.com/gin-gonic/gin/pull/2391))
* Add a redirect sample for POST method ([#2389](https://github.com/gin-gonic/gin/pull/2389))
* Add CustomRecovery builtin middleware ([#2322](https://github.com/gin-gonic/gin/pull/2322))
* binding: avoid 2038 problem on 32-bit architectures ([#2450](https://github.com/gin-gonic/gin/pull/2450))
* Prevent panic in Context.GetQuery() when there is no Request ([#2412](https://github.com/gin-gonic/gin/pull/2412))
* Add GetUint and GetUint64 method on gin.context ([#2487](https://github.com/gin-gonic/gin/pull/2487))
* update content-disposition header to MIME-style ([#2512](https://github.com/gin-gonic/gin/pull/2512))
* reduce allocs and improve the render `WriteString` ([#2508](https://github.com/gin-gonic/gin/pull/2508))
* implement ".Unwrap() error" on Error type ([#2525](https://github.com/gin-gonic/gin/pull/2525)) ([#2526](https://github.com/gin-gonic/gin/pull/2526))
* Allow bind with a map[string]string ([#2484](https://github.com/gin-gonic/gin/pull/2484))
* chore: update tree ([#2371](https://github.com/gin-gonic/gin/pull/2371))
* Support binding for slice/array obj [Rewrite] ([#2302](https://github.com/gin-gonic/gin/pull/2302))
* basic auth: fix timing oracle ([#2609](https://github.com/gin-gonic/gin/pull/2609))
* Add mixed param and non-param paths (port of httprouter[#329](https://github.com/gin-gonic/gin/pull/329)) ([#2663](https://github.com/gin-gonic/gin/pull/2663))
* feat(engine): add trustedproxies and remoteIP ([#2632](https://github.com/gin-gonic/gin/pull/2632))
## Gin v1.6.3
### ENHANCEMENTS
* Improve performance: Change `*sync.RWMutex` to `sync.RWMutex` in context. [#2351](https://github.com/gin-gonic/gin/pull/2351)
## Gin v1.6.2
### BUGFIXES
* fix missing initial sync.RWMutex [#2305](https://github.com/gin-gonic/gin/pull/2305)
### ENHANCEMENTS
* Add set samesite in cookie. [#2306](https://github.com/gin-gonic/gin/pull/2306)
## Gin v1.6.1
### BUGFIXES
* Revert "fix accept incoming network connections" [#2294](https://github.com/gin-gonic/gin/pull/2294)
## Gin v1.6.0
### BREAKING
* chore(performance): Improve performance for adding RemoveExtraSlash flag [#2159](https://github.com/gin-gonic/gin/pull/2159)
* drop support govendor [#2148](https://github.com/gin-gonic/gin/pull/2148)
* Added support for SameSite cookie flag [#1615](https://github.com/gin-gonic/gin/pull/1615)
### FEATURES
* add yaml negotiation [#2220](https://github.com/gin-gonic/gin/pull/2220)
* FileFromFS [#2112](https://github.com/gin-gonic/gin/pull/2112)
### BUGFIXES
* Unix Socket Handling [#2280](https://github.com/gin-gonic/gin/pull/2280)
* Use json marshall in context json to fix breaking new line issue. Fixes #2209 [#2228](https://github.com/gin-gonic/gin/pull/2228)
* fix accept incoming network connections [#2216](https://github.com/gin-gonic/gin/pull/2216)
* Fixed a bug in the calculation of the maximum number of parameters [#2166](https://github.com/gin-gonic/gin/pull/2166)
* [FIX] allow empty headers on DataFromReader [#2121](https://github.com/gin-gonic/gin/pull/2121)
* Add mutex for protect Context.Keys map [#1391](https://github.com/gin-gonic/gin/pull/1391)
### ENHANCEMENTS
* Add mitigation for log injection [#2277](https://github.com/gin-gonic/gin/pull/2277)
* tree: range over nodes values [#2229](https://github.com/gin-gonic/gin/pull/2229)
* tree: remove duplicate assignment [#2222](https://github.com/gin-gonic/gin/pull/2222)
* chore: upgrade go-isatty and json-iterator/go [#2215](https://github.com/gin-gonic/gin/pull/2215)
* path: sync code with httprouter [#2212](https://github.com/gin-gonic/gin/pull/2212)
* Use zero-copy approach to convert types between string and byte slice [#2206](https://github.com/gin-gonic/gin/pull/2206)
* Reuse bytes when cleaning the URL paths [#2179](https://github.com/gin-gonic/gin/pull/2179)
* tree: remove one else statement [#2177](https://github.com/gin-gonic/gin/pull/2177)
* tree: sync httprouter update (#2173) (#2172) [#2171](https://github.com/gin-gonic/gin/pull/2171)
* tree: sync part httprouter codes and reduce if/else [#2163](https://github.com/gin-gonic/gin/pull/2163)
* use http method constant [#2155](https://github.com/gin-gonic/gin/pull/2155)
* upgrade go-validator to v10 [#2149](https://github.com/gin-gonic/gin/pull/2149)
* Refactor redirect request in gin.go [#1970](https://github.com/gin-gonic/gin/pull/1970)
* Add build tag nomsgpack [#1852](https://github.com/gin-gonic/gin/pull/1852)
### DOCS
* docs(path): improve comments [#2223](https://github.com/gin-gonic/gin/pull/2223)
* Renew README to fit the modification of SetCookie method [#2217](https://github.com/gin-gonic/gin/pull/2217)
* Fix spelling [#2202](https://github.com/gin-gonic/gin/pull/2202)
* Remove broken link from README. [#2198](https://github.com/gin-gonic/gin/pull/2198)
* Update docs on Context.Done(), Context.Deadline() and Context.Err() [#2196](https://github.com/gin-gonic/gin/pull/2196)
* Update validator to v10 [#2190](https://github.com/gin-gonic/gin/pull/2190)
* upgrade go-validator to v10 for README [#2189](https://github.com/gin-gonic/gin/pull/2189)
* Update to currently output [#2188](https://github.com/gin-gonic/gin/pull/2188)
* Fix "Custom Validators" example [#2186](https://github.com/gin-gonic/gin/pull/2186)
* Add project to README [#2165](https://github.com/gin-gonic/gin/pull/2165)
* docs(benchmarks): for gin v1.5 [#2153](https://github.com/gin-gonic/gin/pull/2153)
* Changed wording for clarity in README.md [#2122](https://github.com/gin-gonic/gin/pull/2122)
### MISC
* ci support go1.14 [#2262](https://github.com/gin-gonic/gin/pull/2262)
* chore: upgrade depend version [#2231](https://github.com/gin-gonic/gin/pull/2231)
* Drop support go1.10 [#2147](https://github.com/gin-gonic/gin/pull/2147)
* fix comment in `mode.go` [#2129](https://github.com/gin-gonic/gin/pull/2129)
## Gin v1.5.0
- [FIX] Use DefaultWriter and DefaultErrorWriter for debug messages [#1891](https://github.com/gin-gonic/gin/pull/1891)
- [NEW] Now you can parse the inline lowercase start structure [#1893](https://github.com/gin-gonic/gin/pull/1893)
- [FIX] Some code improvements [#1909](https://github.com/gin-gonic/gin/pull/1909)
- [FIX] Use encode replace json marshal increase json encoder speed [#1546](https://github.com/gin-gonic/gin/pull/1546)
- [NEW] Hold matched route full path in the Context [#1826](https://github.com/gin-gonic/gin/pull/1826)
- [FIX] Fix context.Params race condition on Copy() [#1841](https://github.com/gin-gonic/gin/pull/1841)
- [NEW] Add context param query cache [#1450](https://github.com/gin-gonic/gin/pull/1450)
- [FIX] Improve GetQueryMap performance [#1918](https://github.com/gin-gonic/gin/pull/1918)
- [FIX] Improve get post data [#1920](https://github.com/gin-gonic/gin/pull/1920)
- [FIX] Use context instead of x/net/context [#1922](https://github.com/gin-gonic/gin/pull/1922)
- [FIX] Attempt to fix PostForm cache bug [#1931](https://github.com/gin-gonic/gin/pull/1931)
- [NEW] Add support of multipart multi files [#1949](https://github.com/gin-gonic/gin/pull/1949)
- [NEW] Support bind http header param [#1957](https://github.com/gin-gonic/gin/pull/1957)
- [FIX] Drop support for go1.8 and go1.9 [#1933](https://github.com/gin-gonic/gin/pull/1933)
- [FIX] Bugfix for the FullPath feature [#1919](https://github.com/gin-gonic/gin/pull/1919)
- [FIX] Gin1.5 bytes.Buffer to strings.Builder [#1939](https://github.com/gin-gonic/gin/pull/1939)
- [FIX] Upgrade github.com/ugorji/go/codec [#1969](https://github.com/gin-gonic/gin/pull/1969)
- [NEW] Support bind unix time [#1980](https://github.com/gin-gonic/gin/pull/1980)
- [FIX] Simplify code [#2004](https://github.com/gin-gonic/gin/pull/2004)
- [NEW] Support negative Content-Length in DataFromReader [#1981](https://github.com/gin-gonic/gin/pull/1981)
- [FIX] Identify terminal on a RISC-V architecture for auto-colored logs [#2019](https://github.com/gin-gonic/gin/pull/2019)
- [BREAKING] `Context.JSONP()` now expects a semicolon (`;`) at the end [#2007](https://github.com/gin-gonic/gin/pull/2007)
- [BREAKING] Upgrade default `binding.Validator` to v9 (see [its changelog](https://github.com/go-playground/validator/releases/tag/v9.0.0)) [#1015](https://github.com/gin-gonic/gin/pull/1015)
- [NEW] Add `DisallowUnknownFields()` in `Context.BindJSON()` [#2028](https://github.com/gin-gonic/gin/pull/2028)
- [NEW] Use specific `net.Listener` with `Engine.RunListener()` [#2023](https://github.com/gin-gonic/gin/pull/2023)
- [FIX] Fix some typo [#2079](https://github.com/gin-gonic/gin/pull/2079) [#2080](https://github.com/gin-gonic/gin/pull/2080)
- [FIX] Relocate binding body tests [#2086](https://github.com/gin-gonic/gin/pull/2086)
- [FIX] Use Writer in Context.Status [#1606](https://github.com/gin-gonic/gin/pull/1606)
- [FIX] `Engine.RunUnix()` now returns the error if it can't change the file mode [#2093](https://github.com/gin-gonic/gin/pull/2093)
- [FIX] `RouterGroup.StaticFS()` leaked files. Now it closes them. [#2118](https://github.com/gin-gonic/gin/pull/2118)
- [FIX] `Context.Request.FormFile` leaked file. Now it closes it. [#2114](https://github.com/gin-gonic/gin/pull/2114)
- [FIX] Ignore walking on `form:"-"` mapping [#1943](https://github.com/gin-gonic/gin/pull/1943)
### Gin v1.4.0
- [NEW] Support for [Go Modules](https://github.com/golang/go/wiki/Modules) [#1569](https://github.com/gin-gonic/gin/pull/1569)
- [NEW] Refactor of form mapping multipart request [#1829](https://github.com/gin-gonic/gin/pull/1829)
- [FIX] Truncate Latency precision in long running request [#1830](https://github.com/gin-gonic/gin/pull/1830)
- [FIX] IsTerm flag should not be affected by DisableConsoleColor method. [#1802](https://github.com/gin-gonic/gin/pull/1802)
- [NEW] Supporting file binding [#1264](https://github.com/gin-gonic/gin/pull/1264)
- [NEW] Add support for mapping arrays [#1797](https://github.com/gin-gonic/gin/pull/1797)
- [FIX] Readme updates [#1793](https://github.com/gin-gonic/gin/pull/1793) [#1788](https://github.com/gin-gonic/gin/pull/1788) [1789](https://github.com/gin-gonic/gin/pull/1789)
- [FIX] StaticFS: Fixed Logging two log lines on 404. [#1805](https://github.com/gin-gonic/gin/pull/1805), [#1804](https://github.com/gin-gonic/gin/pull/1804)
- [NEW] Make context.Keys available as LogFormatterParams [#1779](https://github.com/gin-gonic/gin/pull/1779)
- [NEW] Use internal/json for Marshal/Unmarshal [#1791](https://github.com/gin-gonic/gin/pull/1791)
- [NEW] Support mapping time.Duration [#1794](https://github.com/gin-gonic/gin/pull/1794)
- [NEW] Refactor form mappings [#1749](https://github.com/gin-gonic/gin/pull/1749)
- [NEW] Added flag to context.Stream indicates if client disconnected in middle of stream [#1252](https://github.com/gin-gonic/gin/pull/1252)
- [FIX] Moved [examples](https://github.com/gin-gonic/examples) to stand alone Repo [#1775](https://github.com/gin-gonic/gin/pull/1775)
- [NEW] Extend context.File to allow for the content-disposition attachments via a new method context.Attachment [#1260](https://github.com/gin-gonic/gin/pull/1260)
- [FIX] Support HTTP content negotiation wildcards [#1112](https://github.com/gin-gonic/gin/pull/1112)
- [NEW] Add prefix from X-Forwarded-Prefix in redirectTrailingSlash [#1238](https://github.com/gin-gonic/gin/pull/1238)
- [FIX] context.Copy() race condition [#1020](https://github.com/gin-gonic/gin/pull/1020)
- [NEW] Add context.HandlerNames() [#1729](https://github.com/gin-gonic/gin/pull/1729)
- [FIX] Change color methods to public in the defaultLogger. [#1771](https://github.com/gin-gonic/gin/pull/1771)
- [FIX] Update writeHeaders method to use http.Header.Set [#1722](https://github.com/gin-gonic/gin/pull/1722)
- [NEW] Add response size to LogFormatterParams [#1752](https://github.com/gin-gonic/gin/pull/1752)
- [NEW] Allow ignoring field on form mapping [#1733](https://github.com/gin-gonic/gin/pull/1733)
- [NEW] Add a function to force color in console output. [#1724](https://github.com/gin-gonic/gin/pull/1724)
- [FIX] Context.Next() - recheck len of handlers on every iteration. [#1745](https://github.com/gin-gonic/gin/pull/1745)
- [FIX] Fix all errcheck warnings [#1739](https://github.com/gin-gonic/gin/pull/1739) [#1653](https://github.com/gin-gonic/gin/pull/1653)
- [NEW] context: inherits context cancellation and deadline from http.Request context for Go>=1.7 [#1690](https://github.com/gin-gonic/gin/pull/1690)
- [NEW] Binding for URL Params [#1694](https://github.com/gin-gonic/gin/pull/1694)
- [NEW] Add LoggerWithFormatter method [#1677](https://github.com/gin-gonic/gin/pull/1677)
- [FIX] CI testing updates [#1671](https://github.com/gin-gonic/gin/pull/1671) [#1670](https://github.com/gin-gonic/gin/pull/1670) [#1682](https://github.com/gin-gonic/gin/pull/1682) [#1669](https://github.com/gin-gonic/gin/pull/1669)
- [FIX] StaticFS(): Send 404 when path does not exist [#1663](https://github.com/gin-gonic/gin/pull/1663)
- [FIX] Handle nil body for JSON binding [#1638](https://github.com/gin-gonic/gin/pull/1638)
- [FIX] Support bind uri param [#1612](https://github.com/gin-gonic/gin/pull/1612)
- [FIX] recovery: fix issue with syscall import on google app engine [#1640](https://github.com/gin-gonic/gin/pull/1640)
- [FIX] Make sure the debug log contains line breaks [#1650](https://github.com/gin-gonic/gin/pull/1650)
- [FIX] Panic stack trace being printed during recovery of broken pipe [#1089](https://github.com/gin-gonic/gin/pull/1089) [#1259](https://github.com/gin-gonic/gin/pull/1259)
- [NEW] RunFd method to run http.Server through a file descriptor [#1609](https://github.com/gin-gonic/gin/pull/1609)
- [NEW] Yaml binding support [#1618](https://github.com/gin-gonic/gin/pull/1618)
- [FIX] Pass MaxMultipartMemory when FormFile is called [#1600](https://github.com/gin-gonic/gin/pull/1600)
- [FIX] LoadHTML* tests [#1559](https://github.com/gin-gonic/gin/pull/1559)
- [FIX] Removed use of sync.pool from HandleContext [#1565](https://github.com/gin-gonic/gin/pull/1565)
- [FIX] Format output log to os.Stderr [#1571](https://github.com/gin-gonic/gin/pull/1571)
- [FIX] Make logger use a yellow background and a darkgray text for legibility [#1570](https://github.com/gin-gonic/gin/pull/1570)
- [FIX] Remove sensitive request information from panic log. [#1370](https://github.com/gin-gonic/gin/pull/1370)
- [FIX] log.Println() does not print timestamp [#829](https://github.com/gin-gonic/gin/pull/829) [#1560](https://github.com/gin-gonic/gin/pull/1560)
- [NEW] Add PureJSON renderer [#694](https://github.com/gin-gonic/gin/pull/694)
- [FIX] Add missing copyright and update if/else [#1497](https://github.com/gin-gonic/gin/pull/1497)
- [FIX] Update msgpack usage [#1498](https://github.com/gin-gonic/gin/pull/1498)
- [FIX] Use protobuf on render [#1496](https://github.com/gin-gonic/gin/pull/1496)
- [FIX] Add support for Protobuf format response [#1479](https://github.com/gin-gonic/gin/pull/1479)
- [NEW] Set default time format in form binding [#1487](https://github.com/gin-gonic/gin/pull/1487)
- [FIX] Add BindXML and ShouldBindXML [#1485](https://github.com/gin-gonic/gin/pull/1485)
- [NEW] Upgrade dependency libraries [#1491](https://github.com/gin-gonic/gin/pull/1491)
## Gin v1.3.0
- [NEW] Add [`func (*Context) QueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.QueryMap), [`func (*Context) GetQueryMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetQueryMap), [`func (*Context) PostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.PostFormMap) and [`func (*Context) GetPostFormMap`](https://godoc.org/github.com/gin-gonic/gin#Context.GetPostFormMap) to support `type map[string]string` as query string or form parameters, see [#1383](https://github.com/gin-gonic/gin/pull/1383)
- [NEW] Add [`func (*Context) AsciiJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.AsciiJSON), see [#1358](https://github.com/gin-gonic/gin/pull/1358)
- [NEW] Add `Pusher()` in [`type ResponseWriter`](https://godoc.org/github.com/gin-gonic/gin#ResponseWriter) for supporting http2 push, see [#1273](https://github.com/gin-gonic/gin/pull/1273)
- [NEW] Add [`func (*Context) DataFromReader`](https://godoc.org/github.com/gin-gonic/gin#Context.DataFromReader) for serving dynamic data, see [#1304](https://github.com/gin-gonic/gin/pull/1304)
- [NEW] Add [`func (*Context) ShouldBindBodyWith`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBindBodyWith) allowing to call binding multiple times, see [#1341](https://github.com/gin-gonic/gin/pull/1341)
- [NEW] Support pointers in form binding, see [#1336](https://github.com/gin-gonic/gin/pull/1336)
- [NEW] Add [`func (*Context) JSONP`](https://godoc.org/github.com/gin-gonic/gin#Context.JSONP), see [#1333](https://github.com/gin-gonic/gin/pull/1333)
- [NEW] Support default value in form binding, see [#1138](https://github.com/gin-gonic/gin/pull/1138)
- [NEW] Expose validator engine in [`type StructValidator`](https://godoc.org/github.com/gin-gonic/gin/binding#StructValidator), see [#1277](https://github.com/gin-gonic/gin/pull/1277)
- [NEW] Add [`func (*Context) ShouldBind`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBind), [`func (*Context) ShouldBindQuery`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBindQuery) and [`func (*Context) ShouldBindJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.ShouldBindJSON), see [#1047](https://github.com/gin-gonic/gin/pull/1047)
- [NEW] Add support for `time.Time` location in form binding, see [#1117](https://github.com/gin-gonic/gin/pull/1117)
- [NEW] Add [`func (*Context) BindQuery`](https://godoc.org/github.com/gin-gonic/gin#Context.BindQuery), see [#1029](https://github.com/gin-gonic/gin/pull/1029)
- [NEW] Make [jsonite](https://github.com/json-iterator/go) optional with build tags, see [#1026](https://github.com/gin-gonic/gin/pull/1026)
- [NEW] Show query string in logger, see [#999](https://github.com/gin-gonic/gin/pull/999)
- [NEW] Add [`func (*Context) SecureJSON`](https://godoc.org/github.com/gin-gonic/gin#Context.SecureJSON), see [#987](https://github.com/gin-gonic/gin/pull/987) and [#993](https://github.com/gin-gonic/gin/pull/993)
- [DEPRECATE] `func (*Context) GetCookie` for [`func (*Context) Cookie`](https://godoc.org/github.com/gin-gonic/gin#Context.Cookie)
- [FIX] Don't display color tags if [`func DisableConsoleColor`](https://godoc.org/github.com/gin-gonic/gin#DisableConsoleColor) called, see [#1072](https://github.com/gin-gonic/gin/pull/1072)
- [FIX] Gin Mode `""` when calling [`func Mode`](https://godoc.org/github.com/gin-gonic/gin#Mode) now returns `const DebugMode`, see [#1250](https://github.com/gin-gonic/gin/pull/1250)
- [FIX] `Flush()` now doesn't overwrite `responseWriter` status code, see [#1460](https://github.com/gin-gonic/gin/pull/1460)
## Gin 1.2.0
- [NEW] Switch from godeps to govendor
- [NEW] Add support for Let's Encrypt via gin-gonic/autotls
- [NEW] Improve README examples and add extra at examples folder
- [NEW] Improved support with App Engine
- [NEW] Add custom template delimiters, see #860
- [NEW] Add Template Func Maps, see #962
- [NEW] Add \*context.Handler(), see #928
- [NEW] Add \*context.GetRawData()
- [NEW] Add \*context.GetHeader() (request)
- [NEW] Add \*context.AbortWithStatusJSON() (JSON content type)
- [NEW] Add \*context.Keys type cast helpers
- [NEW] Add \*context.ShouldBindWith()
- [NEW] Add \*context.MustBindWith()
- [NEW] Add \*engine.SetFuncMap()
- [DEPRECATE] On next release: \*context.BindWith(), see #855
- [FIX] Refactor render
- [FIX] Reworked tests
- [FIX] logger now supports cygwin
- [FIX] Use X-Forwarded-For before X-Real-Ip
- [FIX] time.Time binding (#904)
## Gin 1.1.4
- [NEW] Support google appengine for IsTerminal func
## Gin 1.1.3
- [FIX] Reverted Logger: skip ANSI color commands
## Gin 1.1
- [NEW] Implement QueryArray and PostArray methods
- [NEW] Refactor GetQuery and GetPostForm
- [NEW] Add contribution guide
- [FIX] Corrected typos in README
- [FIX] Removed additional Iota
- [FIX] Changed imports to gopkg instead of github in README (#733)
- [FIX] Logger: skip ANSI color commands if output is not a tty
## Gin 1.0rc2 (...)
- [PERFORMANCE] Fast path for writing Content-Type.
- [PERFORMANCE] Much faster 404 routing
- [PERFORMANCE] Allocation optimizations
- [PERFORMANCE] Faster root tree lookup
- [PERFORMANCE] Zero overhead, String() and JSON() rendering.
- [PERFORMANCE] Faster ClientIP parsing
- [PERFORMANCE] Much faster SSE implementation
- [NEW] Benchmarks suite
- [NEW] Bind validation can be disabled and replaced with custom validators.
- [NEW] More flexible HTML render
- [NEW] Multipart and PostForm bindings
- [NEW] Adds method to return all the registered routes
- [NEW] Context.HandlerName() returns the main handler's name
- [NEW] Adds Error.IsType() helper
- [FIX] Binding multipart form
- [FIX] Integration tests
- [FIX] Crash when binding non struct object in Context.
- [FIX] RunTLS() implementation
- [FIX] Logger() unit tests
- [FIX] Adds SetHTMLTemplate() warning
- [FIX] Context.IsAborted()
- [FIX] More unit tests
- [FIX] JSON, XML, HTML renders accept custom content-types
- [FIX] gin.AbortIndex is unexported
- [FIX] Better approach to avoid directory listing in StaticFS()
- [FIX] Context.ClientIP() always returns the IP with trimmed spaces.
- [FIX] Better warning when running in debug mode.
- [FIX] Google App Engine integration. debugPrint does not use os.Stdout
- [FIX] Fixes integer overflow in error type
- [FIX] Error implements the json.Marshaller interface
- [FIX] MIT license in every file
## Gin 1.0rc1 (May 22, 2015)
- [PERFORMANCE] Zero allocation router
- [PERFORMANCE] Faster JSON, XML and text rendering
- [PERFORMANCE] Custom hand optimized HttpRouter for Gin
- [PERFORMANCE] Misc code optimizations. Inlining, tail call optimizations
- [NEW] Built-in support for golang.org/x/net/context
- [NEW] Any(path, handler). Create a route that matches any path
- [NEW] Refactored rendering pipeline (faster and static typed)
- [NEW] Refactored errors API
- [NEW] IndentedJSON() prints pretty JSON
- [NEW] Added gin.DefaultWriter
- [NEW] UNIX socket support
- [NEW] RouterGroup.BasePath is exposed
- [NEW] JSON validation using go-validate-yourself (very powerful options)
- [NEW] Completed suite of unit tests
- [NEW] HTTP streaming with c.Stream()
- [NEW] StaticFile() creates a router for serving just one file.
- [NEW] StaticFS() has an option to disable directory listing.
- [NEW] StaticFS() for serving static files through virtual filesystems
- [NEW] Server-Sent Events native support
- [NEW] WrapF() and WrapH() helpers for wrapping http.HandlerFunc and http.Handler
- [NEW] Added LoggerWithWriter() middleware
- [NEW] Added RecoveryWithWriter() middleware
- [NEW] Added DefaultPostFormValue()
- [NEW] Added DefaultFormValue()
- [NEW] Added DefaultParamValue()
- [FIX] BasicAuth() when using custom realm
- [FIX] Bug when serving static files in nested routing group
- [FIX] Redirect using built-in http.Redirect()
- [FIX] Logger when printing the requested path
- [FIX] Documentation typos
- [FIX] Context.Engine renamed to Context.engine
- [FIX] Better debugging messages
- [FIX] ErrorLogger
- [FIX] Debug HTTP render
- [FIX] Refactored binding and render modules
- [FIX] Refactored Context initialization
- [FIX] Refactored BasicAuth()
- [FIX] NoMethod/NoRoute handlers
- [FIX] Hijacking http
- [FIX] Better support for Google App Engine (using log instead of fmt)
## Gin 0.6 (Mar 9, 2015)
- [NEW] Support multipart/form-data
- [NEW] NoMethod handler
- [NEW] Validate sub structures
- [NEW] Support for HTTP Realm Auth
- [FIX] Unsigned integers in binding
- [FIX] Improve color logger
## Gin 0.5 (Feb 7, 2015)
- [NEW] Content Negotiation
- [FIX] Solved security bug that allow a client to spoof ip
- [FIX] Fix unexported/ignored fields in binding
## Gin 0.4 (Aug 21, 2014)
- [NEW] Development mode
- [NEW] Unit tests
- [NEW] Add Content.Redirect()
- [FIX] Deferring WriteHeader()
- [FIX] Improved documentation for model binding
## Gin 0.3 (Jul 18, 2014)
- [PERFORMANCE] Normal log and error log are printed in the same call.
- [PERFORMANCE] Improve performance of NoRouter()
- [PERFORMANCE] Improve context's memory locality, reduce CPU cache faults.
- [NEW] Flexible rendering API
- [NEW] Add Context.File()
- [NEW] Add shortcut RunTLS() for http.ListenAndServeTLS
- [FIX] Rename NotFound404() to NoRoute()
- [FIX] Errors in context are purged
- [FIX] Adds HEAD method in Static file serving
- [FIX] Refactors Static() file serving
- [FIX] Using keyed initialization to fix app-engine integration
- [FIX] Can't unmarshal JSON array, #63
- [FIX] Renaming Context.Req to Context.Request
- [FIX] Check application/x-www-form-urlencoded when parsing form
## Gin 0.2b (Jul 08, 2014)
- [PERFORMANCE] Using sync.Pool to allocatio/gc overhead
- [NEW] Travis CI integration
- [NEW] Completely new logger
- [NEW] New API for serving static files. gin.Static()
- [NEW] gin.H() can be serialized into XML
- [NEW] Typed errors. Errors can be typed. Internet/external/custom.
- [NEW] Support for Godeps
- [NEW] Travis/Godocs badges in README
- [NEW] New Bind() and BindWith() methods for parsing request body.
- [NEW] Add Content.Copy()
- [NEW] Add context.LastError()
- [NEW] Add shortcut for OPTIONS HTTP method
- [FIX] Tons of README fixes
- [FIX] Header is written before body
- [FIX] BasicAuth() and changes API a little bit
- [FIX] Recovery() middleware only prints panics
- [FIX] Context.Get() does not panic anymore. Use MustGet() instead.
- [FIX] Multiple http.WriteHeader() in NotFound handlers
- [FIX] Engine.Run() panics if http server can't be set up
- [FIX] Crash when route path doesn't start with '/'
- [FIX] Do not update header when status code is negative
- [FIX] Setting response headers before calling WriteHeader in context.String()
- [FIX] Add MIT license
- [FIX] Changes behaviour of ErrorLogger() and Logger()

@ -1,46 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at teamgingonic@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

@ -1,13 +0,0 @@
## Contributing
- With issues:
- Use the search tool before opening a new issue.
- Please provide source code and commit sha if you found a bug.
- Review existing issues and provide feedback or react to them.
- With pull requests:
- Open your pull request against `master`
- Your pull request should have no more than two commits, if not you should squash them.
- It should pass all tests in the available continuous integration systems such as GitHub Actions.
- You should add/modify tests to cover your proposed code changes.
- If your pull request contains a new feature, please document it on the README.

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2014 Manuel Martínez-Almeida
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@ -1,77 +0,0 @@
GO ?= go
GOFMT ?= gofmt "-s"
GO_VERSION=$(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2)
PACKAGES ?= $(shell $(GO) list ./...)
VETPACKAGES ?= $(shell $(GO) list ./... | grep -v /examples/)
GOFILES := $(shell find . -name "*.go")
TESTFOLDER := $(shell $(GO) list ./... | grep -E 'gin$$|binding$$|render$$' | grep -v examples)
TESTTAGS ?= ""
.PHONY: test
test:
echo "mode: count" > coverage.out
for d in $(TESTFOLDER); do \
$(GO) test -tags $(TESTTAGS) -v -covermode=count -coverprofile=profile.out $$d > tmp.out; \
cat tmp.out; \
if grep -q "^--- FAIL" tmp.out; then \
rm tmp.out; \
exit 1; \
elif grep -q "build failed" tmp.out; then \
rm tmp.out; \
exit 1; \
elif grep -q "setup failed" tmp.out; then \
rm tmp.out; \
exit 1; \
fi; \
if [ -f profile.out ]; then \
cat profile.out | grep -v "mode:" >> coverage.out; \
rm profile.out; \
fi; \
done
.PHONY: fmt
fmt:
$(GOFMT) -w $(GOFILES)
.PHONY: fmt-check
fmt-check:
@diff=$$($(GOFMT) -d $(GOFILES)); \
if [ -n "$$diff" ]; then \
echo "Please run 'make fmt' and commit the result:"; \
echo "$${diff}"; \
exit 1; \
fi;
vet:
$(GO) vet $(VETPACKAGES)
.PHONY: lint
lint:
@hash golint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u golang.org/x/lint/golint; \
fi
for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
.PHONY: misspell-check
misspell-check:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -error $(GOFILES)
.PHONY: misspell
misspell:
@hash misspell > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/client9/misspell/cmd/misspell; \
fi
misspell -w $(GOFILES)
.PHONY: tools
tools:
@if [ $(GO_VERSION) -gt 15 ]; then \
$(GO) install golang.org/x/lint/golint@latest; \
$(GO) install github.com/client9/misspell/cmd/misspell@latest; \
elif [ $(GO_VERSION) -lt 16 ]; then \
$(GO) install golang.org/x/lint/golint; \
$(GO) install github.com/client9/misspell/cmd/misspell; \
fi

File diff suppressed because it is too large Load Diff

@ -1,10 +0,0 @@
// Copyright 2022 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
//go:build !go1.18
// +build !go1.18
package gin
type any = interface{}

@ -1,91 +0,0 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package gin
import (
"crypto/subtle"
"encoding/base64"
"net/http"
"strconv"
"github.com/gin-gonic/gin/internal/bytesconv"
)
// AuthUserKey is the cookie name for user credential in basic auth.
const AuthUserKey = "user"
// Accounts defines a key/value for user/pass list of authorized logins.
type Accounts map[string]string
type authPair struct {
value string
user string
}
type authPairs []authPair
func (a authPairs) searchCredential(authValue string) (string, bool) {
if authValue == "" {
return "", false
}
for _, pair := range a {
if subtle.ConstantTimeCompare(bytesconv.StringToBytes(pair.value), bytesconv.StringToBytes(authValue)) == 1 {
return pair.user, true
}
}
return "", false
}
// BasicAuthForRealm returns a Basic HTTP Authorization middleware. It takes as arguments a map[string]string where
// the key is the user name and the value is the password, as well as the name of the Realm.
// If the realm is empty, "Authorization Required" will be used by default.
// (see http://tools.ietf.org/html/rfc2617#section-1.2)
func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
if realm == "" {
realm = "Authorization Required"
}
realm = "Basic realm=" + strconv.Quote(realm)
pairs := processAccounts(accounts)
return func(c *Context) {
// Search user in the slice of allowed credentials
user, found := pairs.searchCredential(c.requestHeader("Authorization"))
if !found {
// Credentials doesn't match, we return 401 and abort handlers chain.
c.Header("WWW-Authenticate", realm)
c.AbortWithStatus(http.StatusUnauthorized)
return
}
// The user credentials was found, set user's id to key AuthUserKey in this context, the user's id can be read later using
// c.MustGet(gin.AuthUserKey).
c.Set(AuthUserKey, user)
}
}
// BasicAuth returns a Basic HTTP Authorization middleware. It takes as argument a map[string]string where
// the key is the user name and the value is the password.
func BasicAuth(accounts Accounts) HandlerFunc {
return BasicAuthForRealm(accounts, "")
}
func processAccounts(accounts Accounts) authPairs {
length := len(accounts)
assert1(length > 0, "Empty list of authorized credentials")
pairs := make(authPairs, 0, length)
for user, password := range accounts {
assert1(user != "", "User can not be empty")
value := authorizationHeader(user, password)
pairs = append(pairs, authPair{
value: value,
user: user,
})
}
return pairs
}
func authorizationHeader(user, password string) string {
base := user + ":" + password
return "Basic " + base64.StdEncoding.EncodeToString(bytesconv.StringToBytes(base))
}

@ -1,10 +0,0 @@
// Copyright 2022 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
//go:build !go1.18
// +build !go1.18
package binding
type any = interface{}

@ -1,122 +0,0 @@
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
//go:build !nomsgpack
// +build !nomsgpack
package binding
import "net/http"
// Content-Type MIME of the most common data formats.
const (
MIMEJSON = "application/json"
MIMEHTML = "text/html"
MIMEXML = "application/xml"
MIMEXML2 = "text/xml"
MIMEPlain = "text/plain"
MIMEPOSTForm = "application/x-www-form-urlencoded"
MIMEMultipartPOSTForm = "multipart/form-data"
MIMEPROTOBUF = "application/x-protobuf"
MIMEMSGPACK = "application/x-msgpack"
MIMEMSGPACK2 = "application/msgpack"
MIMEYAML = "application/x-yaml"
MIMETOML = "application/toml"
)
// Binding describes the interface which needs to be implemented for binding the
// data present in the request such as JSON request body, query parameters or
// the form POST.
type Binding interface {
Name() string
Bind(*http.Request, any) error
}
// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
// but it reads the body from supplied bytes instead of req.Body.
type BindingBody interface {
Binding
BindBody([]byte, any) error
}
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
// but it reads the Params.
type BindingUri interface {
Name() string
BindUri(map[string][]string, any) error
}
// StructValidator is the minimal interface which needs to be implemented in
// order for it to be used as the validator engine for ensuring the correctness
// of the request. Gin provides a default implementation for this using
// https://github.com/go-playground/validator/tree/v10.6.1.
type StructValidator interface {
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
// If the received type is a slice|array, the validation should be performed travel on every element.
// If the received type is not a struct or slice|array, any validation should be skipped and nil must be returned.
// If the received type is a struct or pointer to a struct, the validation should be performed.
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
// Otherwise nil must be returned.
ValidateStruct(any) error
// Engine returns the underlying validator engine which powers the
// StructValidator implementation.
Engine() any
}
// Validator is the default validator which implements the StructValidator
// interface. It uses https://github.com/go-playground/validator/tree/v10.6.1
// under the hood.
var Validator StructValidator = &defaultValidator{}
// These implement the Binding interface and can be used to bind the data
// present in the request to struct instances.
var (
JSON = jsonBinding{}
XML = xmlBinding{}
Form = formBinding{}
Query = queryBinding{}
FormPost = formPostBinding{}
FormMultipart = formMultipartBinding{}
ProtoBuf = protobufBinding{}
MsgPack = msgpackBinding{}
YAML = yamlBinding{}
Uri = uriBinding{}
Header = headerBinding{}
TOML = tomlBinding{}
)
// Default returns the appropriate Binding instance based on the HTTP method
// and the content type.
func Default(method, contentType string) Binding {
if method == http.MethodGet {
return Form
}
switch contentType {
case MIMEJSON:
return JSON
case MIMEXML, MIMEXML2:
return XML
case MIMEPROTOBUF:
return ProtoBuf
case MIMEMSGPACK, MIMEMSGPACK2:
return MsgPack
case MIMEYAML:
return YAML
case MIMETOML:
return TOML
case MIMEMultipartPOSTForm:
return FormMultipart
default: // case MIMEPOSTForm:
return Form
}
}
func validate(obj any) error {
if Validator == nil {
return nil
}
return Validator.ValidateStruct(obj)
}

@ -1,116 +0,0 @@
// Copyright 2020 Gin Core Team. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
//go:build nomsgpack
// +build nomsgpack
package binding
import "net/http"
// Content-Type MIME of the most common data formats.
const (
MIMEJSON = "application/json"
MIMEHTML = "text/html"
MIMEXML = "application/xml"
MIMEXML2 = "text/xml"
MIMEPlain = "text/plain"
MIMEPOSTForm = "application/x-www-form-urlencoded"
MIMEMultipartPOSTForm = "multipart/form-data"
MIMEPROTOBUF = "application/x-protobuf"
MIMEYAML = "application/x-yaml"
MIMETOML = "application/toml"
)
// Binding describes the interface which needs to be implemented for binding the
// data present in the request such as JSON request body, query parameters or
// the form POST.
type Binding interface {
Name() string
Bind(*http.Request, any) error
}
// BindingBody adds BindBody method to Binding. BindBody is similar with Bind,
// but it reads the body from supplied bytes instead of req.Body.
type BindingBody interface {
Binding
BindBody([]byte, any) error
}
// BindingUri adds BindUri method to Binding. BindUri is similar with Bind,
// but it reads the Params.
type BindingUri interface {
Name() string
BindUri(map[string][]string, any) error
}
// StructValidator is the minimal interface which needs to be implemented in
// order for it to be used as the validator engine for ensuring the correctness
// of the request. Gin provides a default implementation for this using
// https://github.com/go-playground/validator/tree/v10.6.1.
type StructValidator interface {
// ValidateStruct can receive any kind of type and it should never panic, even if the configuration is not right.
// If the received type is not a struct, any validation should be skipped and nil must be returned.
// If the received type is a struct or pointer to a struct, the validation should be performed.
// If the struct is not valid or the validation itself fails, a descriptive error should be returned.
// Otherwise nil must be returned.
ValidateStruct(any) error
// Engine returns the underlying validator engine which powers the
// StructValidator implementation.
Engine() any
}
// Validator is the default validator which implements the StructValidator
// interface. It uses https://github.com/go-playground/validator/tree/v10.6.1
// under the hood.
var Validator StructValidator = &defaultValidator{}
// These implement the Binding interface and can be used to bind the data
// present in the request to struct instances.
var (
JSON = jsonBinding{}
XML = xmlBinding{}
Form = formBinding{}
Query = queryBinding{}
FormPost = formPostBinding{}
FormMultipart = formMultipartBinding{}
ProtoBuf = protobufBinding{}
YAML = yamlBinding{}
Uri = uriBinding{}
Header = headerBinding{}
TOML = tomlBinding{}
)
// Default returns the appropriate Binding instance based on the HTTP method
// and the content type.
func Default(method, contentType string) Binding {
if method == "GET" {
return Form
}
switch contentType {
case MIMEJSON:
return JSON
case MIMEXML, MIMEXML2:
return XML
case MIMEPROTOBUF:
return ProtoBuf
case MIMEYAML:
return YAML
case MIMEMultipartPOSTForm:
return FormMultipart
case MIMETOML:
return TOML
default: // case MIMEPOSTForm:
return Form
}
}
func validate(obj any) error {
if Validator == nil {
return nil
}
return Validator.ValidateStruct(obj)
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save