Compare commits
66 Commits
Author | SHA1 | Date |
---|---|---|
李光春 | 9ffa6c2c05 | 1 year ago |
李光春 | cd06d7d7e9 | 1 year ago |
李光春 | 7f12ec6a01 | 2 years ago |
李光春 | 9469dcc3d6 | 2 years ago |
李光春 | 78ed7bdfd8 | 2 years ago |
李光春 | 3e404e4454 | 2 years ago |
李光春 | bb6f68d444 | 2 years ago |
李光春 | 60b50bf6f8 | 2 years ago |
李光春 | 11884afa4f | 2 years ago |
李光春 | 625deb44ca | 2 years ago |
李光春 | d84a9656a7 | 2 years ago |
李光春 | 3d5d7c3a7d | 2 years ago |
李光春 | 3a9b47cc7f | 2 years ago |
李光春 | 5d910fb616 | 2 years ago |
李光春 | a2f2689d10 | 2 years ago |
李光春 | 883fcec2cb | 2 years ago |
李光春 | 2c6cf23f1b | 2 years ago |
李光春 | e015e8f77d | 2 years ago |
李光春 | d7a75aaac3 | 2 years ago |
李光春 | e4858bb504 | 2 years ago |
李光春 | 44fdf7efe0 | 2 years ago |
李光春 | dcbcbbe079 | 2 years ago |
李光春 | 21c597f0f5 | 2 years ago |
李光春 | 19adea0395 | 2 years ago |
李光春 | 882cb83ec8 | 2 years ago |
李光春 | 1d5d63b0de | 2 years ago |
李光春 | 4e1dc1e1b1 | 2 years ago |
李光春 | b53a7490ce | 2 years ago |
李光春 | 917a716c90 | 2 years ago |
李光春 | fbb4ccb197 | 2 years ago |
李光春 | 1bb38af4d7 | 2 years ago |
李光春 | 6b3b29d0c2 | 2 years ago |
李光春 | 1d1be8330b | 2 years ago |
李光春 | 79b18ef741 | 2 years ago |
李光春 | 049585a1fa | 2 years ago |
李光春 | bab21c66bc | 2 years ago |
李光春 | c66aa424b3 | 2 years ago |
李光春 | 649ace79cf | 2 years ago |
李光春 | f7b6e6c8bc | 2 years ago |
李光春 | d57ee8daaf | 2 years ago |
李光春 | 7cf8704ec8 | 2 years ago |
李光春 | 48494e8a9e | 2 years ago |
李光春 | a89dea2d47 | 2 years ago |
李光春 | 082767de39 | 2 years ago |
李光春 | a20d2b6d80 | 2 years ago |
李光春 | 5014f8a92a | 2 years ago |
李光春 | 44366e6c9a | 2 years ago |
李光春 | 8a56a93570 | 2 years ago |
李光春 | 7a14377892 | 2 years ago |
李光春 | d5f6ecadda | 2 years ago |
李光春 | 8e88606e91 | 2 years ago |
李光春 | 08ee16063c | 2 years ago |
李光春 | a03babb8b6 | 2 years ago |
李光春 | 5e80f3edf0 | 2 years ago |
李光春 | 3705004c3d | 2 years ago |
李光春 | 6a2a5447b9 | 2 years ago |
李光春 | 7bfa68d812 | 2 years ago |
李光春 | 7b6ca6f957 | 2 years ago |
李光春 | 663eab78a8 | 2 years ago |
李光春 | 84d516deff | 2 years ago |
李光春 | b76dd4fb57 | 2 years ago |
李光春 | 590b7bf7b2 | 2 years ago |
李光春 | 68cafc549f | 2 years ago |
李光春 | 1ebc8cf708 | 2 years ago |
李光春 | 32f2f777b1 | 2 years ago |
李光春 | 02ced2c278 | 2 years ago |
@ -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
|
||||
}
|
@ -1,3 +1,6 @@
|
||||
package gojobs
|
||||
|
||||
const Version = "1.0.55"
|
||||
const (
|
||||
Version = "1.0.113"
|
||||
SpecifyIpNull = "0.0.0.0"
|
||||
)
|
||||
|
@ -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,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
|
||||
}
|
@ -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,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,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,22 +0,0 @@
|
||||
package gojobs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go.dtapp.net/gojobs/jobs_gorm_model"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Lock 上锁
|
||||
func (j *JobsGorm) Lock(info jobs_gorm_model.Task, id any) (string, error) {
|
||||
return j.lockClient.Lock(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(info jobs_gorm_model.Task, id any) error {
|
||||
return j.lockClient.Unlock(fmt.Sprintf("%s%s%v%s%v", j.config.lockKeyPrefix, j.config.lockKeySeparator, info.Type, j.config.lockKeySeparator, id))
|
||||
}
|
||||
|
||||
// LockForever 永远上锁
|
||||
func (j *JobsGorm) LockForever(info jobs_gorm_model.Task, id any) (string, error) {
|
||||
return j.lockClient.LockForever(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
|
||||
}
|
@ -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"
|
||||
}
|
@ -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,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"
|
||||
}
|
@ -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,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",
|
||||
}
|
@ -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,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,3 +0,0 @@
|
||||
*.rdb
|
||||
testdata/*/
|
||||
.idea/
|
@ -1,4 +0,0 @@
|
||||
run:
|
||||
concurrency: 8
|
||||
deadline: 5m
|
||||
tests: false
|
@ -1,4 +0,0 @@
|
||||
semi: false
|
||||
singleQuote: true
|
||||
proseWrap: always
|
||||
printWidth: 100
|
@ -1,70 +0,0 @@
|
||||
# [9.0.0-beta.2](https://github.com/go-redis/redis/compare/v9.0.0-beta.1...v9.0.0-beta.2) (2022-07-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#2114](https://github.com/go-redis/redis/issues/2114) for redis-server not support Hello ([b6d2a92](https://github.com/go-redis/redis/commit/b6d2a925297e3e516eb5c76c114c1c9fcd5b68c5))
|
||||
* additional node failures in clustered pipelined reads ([03376a5](https://github.com/go-redis/redis/commit/03376a5d9c7dfd7197b14ce13b24a0431a07a663))
|
||||
* disregard failed pings in updateLatency() for cluster nodes ([64f972f](https://github.com/go-redis/redis/commit/64f972fbeae401e52a2c066a0e1c922af617e15c))
|
||||
* don't panic when test cannot start ([9e16c79](https://github.com/go-redis/redis/commit/9e16c79951e7769621b7320f1ecdf04baf539b82))
|
||||
* handle panic in ringShards Hash function when Ring got closed ([a80b84f](https://github.com/go-redis/redis/commit/a80b84f01f9fc0d3e6f08445ba21f7e07880775e)), closes [#2126](https://github.com/go-redis/redis/issues/2126)
|
||||
* ignore Nil error when reading EntriesRead ([89d6dfe](https://github.com/go-redis/redis/commit/89d6dfe09a88321d445858c1c5b24d2757b95a3e))
|
||||
* log errors from cmdsInfoCache ([fa4d1ea](https://github.com/go-redis/redis/commit/fa4d1ea8398cd729ad5cbaaff88e4b8805393945))
|
||||
* provide a signal channel to end heartbeat goroutine ([f032c12](https://github.com/go-redis/redis/commit/f032c126db3e2c1a239ce1790b0ab81994df75cf))
|
||||
* remove conn reaper from the pool and uptrace option names ([f6a8adc](https://github.com/go-redis/redis/commit/f6a8adc50cdaec30527f50d06468f9176ee674fe))
|
||||
* replace heartbeat signal channel with context.WithCancel ([20d0ca2](https://github.com/go-redis/redis/commit/20d0ca235efff48ad48cc05b98790b825d4ba979))
|
||||
|
||||
|
||||
|
||||
# [9.0.0-beta.1](https://github.com/go-redis/redis/compare/v8.11.5...v9.0.0-beta.1) (2022-06-04)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **#1943:** xInfoConsumer.Idle should be time.Duration instead of int64
|
||||
([#2052](https://github.com/go-redis/redis/issues/2052))
|
||||
([997ab5e](https://github.com/go-redis/redis/commit/997ab5e7e3ddf53837917013a4babbded73e944f)),
|
||||
closes [#1943](https://github.com/go-redis/redis/issues/1943)
|
||||
- add XInfoConsumers test
|
||||
([6f1a1ac](https://github.com/go-redis/redis/commit/6f1a1ac284ea3f683eeb3b06a59969e8424b6376))
|
||||
- fix tests
|
||||
([3a722be](https://github.com/go-redis/redis/commit/3a722be81180e4d2a9cf0a29dc9a1ee1421f5859))
|
||||
- remove test(XInfoConsumer.idle), not a stable return value when tested.
|
||||
([f5fbb36](https://github.com/go-redis/redis/commit/f5fbb367e7d9dfd7f391fc535a7387002232fa8a))
|
||||
- update ChannelWithSubscriptions to accept options
|
||||
([c98c5f0](https://github.com/go-redis/redis/commit/c98c5f0eebf8d254307183c2ce702a48256b718d))
|
||||
- update COMMAND parser for Redis 7
|
||||
([b0bb514](https://github.com/go-redis/redis/commit/b0bb514059249e01ed7328c9094e5b8a439dfb12))
|
||||
- use redis over ssh channel([#2057](https://github.com/go-redis/redis/issues/2057))
|
||||
([#2060](https://github.com/go-redis/redis/issues/2060))
|
||||
([3961b95](https://github.com/go-redis/redis/commit/3961b9577f622a3079fe74f8fc8da12ba67a77ff))
|
||||
|
||||
### Features
|
||||
|
||||
- add ClientUnpause
|
||||
([91171f5](https://github.com/go-redis/redis/commit/91171f5e19a261dc4cfbf8706626d461b6ba03e4))
|
||||
- add NewXPendingResult for unit testing XPending
|
||||
([#2066](https://github.com/go-redis/redis/issues/2066))
|
||||
([b7fd09e](https://github.com/go-redis/redis/commit/b7fd09e59479bc6ed5b3b13c4645a3620fd448a3))
|
||||
- add WriteArg and Scan net.IP([#2062](https://github.com/go-redis/redis/issues/2062))
|
||||
([7d5167e](https://github.com/go-redis/redis/commit/7d5167e8624ac1515e146ed183becb97dadb3d1a))
|
||||
- **pool:** add check for badConnection
|
||||
([a8a7665](https://github.com/go-redis/redis/commit/a8a7665ddf8cc657c5226b1826a8ee83dab4b8c1)),
|
||||
closes [#2053](https://github.com/go-redis/redis/issues/2053)
|
||||
- provide a username and password callback method, so that the plaintext username and password will
|
||||
not be stored in the memory, and the username and password will only be generated once when the
|
||||
CredentialsProvider is called. After the method is executed, the username and password strings on
|
||||
the stack will be released. ([#2097](https://github.com/go-redis/redis/issues/2097))
|
||||
([56a3dbc](https://github.com/go-redis/redis/commit/56a3dbc7b656525eb88e0735e239d56e04a23bee))
|
||||
- upgrade to Redis 7
|
||||
([d09c27e](https://github.com/go-redis/redis/commit/d09c27e6046129fd27b1d275e5a13a477bd7f778))
|
||||
|
||||
## v9 UNRELEASED
|
||||
|
||||
- Added support for [RESP3](https://github.com/antirez/RESP3/blob/master/spec.md) protocol.
|
||||
- Removed `Pipeline.Close` since there is no real need to explicitly manage pipeline resources.
|
||||
`Pipeline.Discard` is still available if you want to reset commands for some reason.
|
||||
- Replaced `*redis.Z` with `redis.Z` since it is small enough to be passed as value.
|
||||
- Renamed `MaxConnAge` to `ConnMaxLifetime`.
|
||||
- Renamed `IdleTimeout` to `ConnMaxIdleTime`.
|
||||
- Removed connection reaper in favor of `MaxIdleConns`.
|
||||
- Removed `WithContext`.
|
@ -1,25 +0,0 @@
|
||||
Copyright (c) 2013 The github.com/go-redis/redis Authors.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1,34 +0,0 @@
|
||||
PACKAGE_DIRS := $(shell find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; | sort)
|
||||
|
||||
test: testdeps
|
||||
go test ./...
|
||||
go test ./... -short -race
|
||||
go test ./... -run=NONE -bench=. -benchmem
|
||||
env GOOS=linux GOARCH=386 go test ./...
|
||||
go vet
|
||||
|
||||
testdeps: testdata/redis/src/redis-server
|
||||
|
||||
bench: testdeps
|
||||
go test ./... -test.run=NONE -test.bench=. -test.benchmem
|
||||
|
||||
.PHONY: all test testdeps bench
|
||||
|
||||
testdata/redis:
|
||||
mkdir -p $@
|
||||
wget -qO- https://download.redis.io/releases/redis-7.0.0.tar.gz | tar xvz --strip-components=1 -C $@
|
||||
|
||||
testdata/redis/src/redis-server: testdata/redis
|
||||
cd $< && make all
|
||||
|
||||
fmt:
|
||||
gofmt -w -s ./
|
||||
goimports -w -local github.com/go-redis/redis ./
|
||||
|
||||
go_mod_tidy:
|
||||
set -e; for dir in $(PACKAGE_DIRS); do \
|
||||
echo "go mod tidy in $${dir}"; \
|
||||
(cd "$${dir}" && \
|
||||
go get -u ./... && \
|
||||
go mod tidy -compat=1.17); \
|
||||
done
|
@ -1,195 +0,0 @@
|
||||
# Redis client for Go
|
||||
|
||||
![build workflow](https://github.com/go-redis/redis/actions/workflows/build.yml/badge.svg)
|
||||
[![PkgGoDev](https://pkg.go.dev/badge/github.com/go-redis/redis/v8)](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc)
|
||||
[![Documentation](https://img.shields.io/badge/redis-documentation-informational)](https://redis.uptrace.dev/)
|
||||
[![Chat](https://discordapp.com/api/guilds/752070105847955518/widget.png)](https://discord.gg/rWtp5Aj)
|
||||
|
||||
> go-redis is brought to you by :star: [**uptrace/uptrace**](https://github.com/uptrace/uptrace).
|
||||
> Uptrace is an open source and blazingly fast
|
||||
> [distributed tracing tool](https://get.uptrace.dev/compare/distributed-tracing-tools.html) powered
|
||||
> by OpenTelemetry and ClickHouse. Give it a star as well!
|
||||
|
||||
## Sponsors
|
||||
|
||||
### Upstash: Serverless Database for Redis
|
||||
|
||||
<a href="https://upstash.com/?utm_source=goredis"><img align="right" width="320" src="https://raw.githubusercontent.com/upstash/sponsorship/master/redis.png" alt="Upstash"></a>
|
||||
|
||||
Upstash is a Serverless Database with Redis/REST API and durable storage. It is the perfect database
|
||||
for your applications thanks to its per request pricing and low latency data.
|
||||
|
||||
[Start for free in 30 seconds!](https://upstash.com/?utm_source=goredis)
|
||||
|
||||
<br clear="both"/>
|
||||
|
||||
## Resources
|
||||
|
||||
- [Documentation](https://redis.uptrace.dev)
|
||||
- [Discussions](https://github.com/go-redis/redis/discussions)
|
||||
- [Chat](https://discord.gg/rWtp5Aj)
|
||||
- [Reference](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc)
|
||||
- [Examples](https://pkg.go.dev/github.com/go-redis/redis/v8?tab=doc#pkg-examples)
|
||||
|
||||
## Ecosystem
|
||||
|
||||
- [Redis Mock](https://github.com/go-redis/redismock)
|
||||
- [Distributed Locks](https://github.com/bsm/redislock)
|
||||
- [Redis Cache](https://github.com/go-redis/cache)
|
||||
- [Rate limiting](https://github.com/go-redis/redis_rate)
|
||||
|
||||
This client also works with [kvrocks](https://github.com/KvrocksLabs/kvrocks), a distributed key
|
||||
value NoSQL database that uses RocksDB as storage engine and is compatible with Redis protocol.
|
||||
|
||||
## Features
|
||||
|
||||
- Redis 3 commands except QUIT, MONITOR, and SYNC.
|
||||
- Automatic connection pooling with
|
||||
- [Pub/Sub](https://redis.uptrace.dev/guide/go-redis-pubsub.html).
|
||||
- [Pipelines and transactions](https://redis.uptrace.dev/guide/go-redis-pipelines.html).
|
||||
- [Scripting](https://redis.uptrace.dev/guide/lua-scripting.html).
|
||||
- [Redis Sentinel](https://redis.uptrace.dev/guide/go-redis-sentinel.html).
|
||||
- [Redis Cluster](https://redis.uptrace.dev/guide/go-redis-cluster.html).
|
||||
- [Redis Ring](https://redis.uptrace.dev/guide/ring.html).
|
||||
- [Redis Performance Monitoring](https://redis.uptrace.dev/guide/redis-performance-monitoring.html).
|
||||
|
||||
## Installation
|
||||
|
||||
go-redis supports 2 last Go versions and requires a Go version with
|
||||
[modules](https://github.com/golang/go/wiki/Modules) support. So make sure to initialize a Go
|
||||
module:
|
||||
|
||||
```shell
|
||||
go mod init github.com/my/repo
|
||||
```
|
||||
|
||||
If you are using **Redis 6**, install go-redis/**v8**:
|
||||
|
||||
```shell
|
||||
go get github.com/go-redis/redis/v8
|
||||
```
|
||||
|
||||
If you are using **Redis 7**, install go-redis/**v9**:
|
||||
|
||||
```shell
|
||||
go get github.com/go-redis/redis/v9
|
||||
```
|
||||
|
||||
## Quickstart
|
||||
|
||||
```go
|
||||
import (
|
||||
"context"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var ctx = context.Background()
|
||||
|
||||
func ExampleClient() {
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: "localhost:6379",
|
||||
Password: "", // no password set
|
||||
DB: 0, // use default DB
|
||||
})
|
||||
|
||||
err := rdb.Set(ctx, "key", "value", 0).Err()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
val, err := rdb.Get(ctx, "key").Result()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println("key", val)
|
||||
|
||||
val2, err := rdb.Get(ctx, "key2").Result()
|
||||
if err == redis.Nil {
|
||||
fmt.Println("key2 does not exist")
|
||||
} else if err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
fmt.Println("key2", val2)
|
||||
}
|
||||
// Output: key value
|
||||
// key2 does not exist
|
||||
}
|
||||
```
|
||||
|
||||
## Look and feel
|
||||
|
||||
Some corner cases:
|
||||
|
||||
```go
|
||||
// SET key value EX 10 NX
|
||||
set, err := rdb.SetNX(ctx, "key", "value", 10*time.Second).Result()
|
||||
|
||||
// SET key value keepttl NX
|
||||
set, err := rdb.SetNX(ctx, "key", "value", redis.KeepTTL).Result()
|
||||
|
||||
// SORT list LIMIT 0 2 ASC
|
||||
vals, err := rdb.Sort(ctx, "list", &redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result()
|
||||
|
||||
// ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2
|
||||
vals, err := rdb.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{
|
||||
Min: "-inf",
|
||||
Max: "+inf",
|
||||
Offset: 0,
|
||||
Count: 2,
|
||||
}).Result()
|
||||
|
||||
// ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM
|
||||
vals, err := rdb.ZInterStore(ctx, "out", &redis.ZStore{
|
||||
Keys: []string{"zset1", "zset2"},
|
||||
Weights: []int64{2, 3}
|
||||
}).Result()
|
||||
|
||||
// EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello"
|
||||
vals, err := rdb.Eval(ctx, "return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result()
|
||||
|
||||
// custom command
|
||||
res, err := rdb.Do(ctx, "set", "key", "value").Result()
|
||||
```
|
||||
|
||||
## Run the test
|
||||
|
||||
go-redis will start a redis-server and run the test cases.
|
||||
|
||||
The paths of redis-server bin file and redis config file are defined in `main_test.go`:
|
||||
|
||||
```go
|
||||
var (
|
||||
redisServerBin, _ = filepath.Abs(filepath.Join("testdata", "redis", "src", "redis-server"))
|
||||
redisServerConf, _ = filepath.Abs(filepath.Join("testdata", "redis", "redis.conf"))
|
||||
)
|
||||
```
|
||||
|
||||
For local testing, you can change the variables to refer to your local files, or create a soft link
|
||||
to the corresponding folder for redis-server and copy the config file to `testdata/redis/`:
|
||||
|
||||
```shell
|
||||
ln -s /usr/bin/redis-server ./go-redis/testdata/redis/src
|
||||
cp ./go-redis/testdata/redis.conf ./go-redis/testdata/redis/
|
||||
```
|
||||
|
||||
Lastly, run:
|
||||
|
||||
```shell
|
||||
go test
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- [Golang ORM](https://bun.uptrace.dev) for PostgreSQL, MySQL, MSSQL, and SQLite
|
||||
- [Golang PostgreSQL](https://bun.uptrace.dev/postgres/)
|
||||
- [Golang HTTP router](https://bunrouter.uptrace.dev/)
|
||||
- [Golang ClickHouse ORM](https://github.com/uptrace/go-clickhouse)
|
||||
|
||||
## Contributors
|
||||
|
||||
Thanks to all the people who already contributed!
|
||||
|
||||
<a href="https://github.com/go-redis/redis/graphs/contributors">
|
||||
<img src="https://contributors-img.web.app/image?repo=go-redis/redis" />
|
||||
</a>
|
@ -1,15 +0,0 @@
|
||||
# Releasing
|
||||
|
||||
1. Run `release.sh` script which updates versions in go.mod files and pushes a new branch to GitHub:
|
||||
|
||||
```shell
|
||||
TAG=v1.0.0 ./scripts/release.sh
|
||||
```
|
||||
|
||||
2. Open a pull request and wait for the build to finish.
|
||||
|
||||
3. Merge the pull request and run `tag.sh` to create tags for packages:
|
||||
|
||||
```shell
|
||||
TAG=v1.0.0 ./scripts/tag.sh
|
||||
```
|
File diff suppressed because it is too large
Load Diff
@ -1,109 +0,0 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
func (c *ClusterClient) DBSize(ctx context.Context) *IntCmd {
|
||||
cmd := NewIntCmd(ctx, "dbsize")
|
||||
_ = c.hooks.process(ctx, cmd, func(ctx context.Context, _ Cmder) error {
|
||||
var size int64
|
||||
err := c.ForEachMaster(ctx, func(ctx context.Context, master *Client) error {
|
||||
n, err := master.DBSize(ctx).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
atomic.AddInt64(&size, n)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
cmd.SetErr(err)
|
||||
} else {
|
||||
cmd.val = size
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *ClusterClient) ScriptLoad(ctx context.Context, script string) *StringCmd {
|
||||
cmd := NewStringCmd(ctx, "script", "load", script)
|
||||
_ = c.hooks.process(ctx, cmd, func(ctx context.Context, _ Cmder) error {
|
||||
mu := &sync.Mutex{}
|
||||
err := c.ForEachShard(ctx, func(ctx context.Context, shard *Client) error {
|
||||
val, err := shard.ScriptLoad(ctx, script).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
if cmd.Val() == "" {
|
||||
cmd.val = val
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
cmd.SetErr(err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *ClusterClient) ScriptFlush(ctx context.Context) *StatusCmd {
|
||||
cmd := NewStatusCmd(ctx, "script", "flush")
|
||||
_ = c.hooks.process(ctx, cmd, func(ctx context.Context, _ Cmder) error {
|
||||
err := c.ForEachShard(ctx, func(ctx context.Context, shard *Client) error {
|
||||
return shard.ScriptFlush(ctx).Err()
|
||||
})
|
||||
if err != nil {
|
||||
cmd.SetErr(err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (c *ClusterClient) ScriptExists(ctx context.Context, hashes ...string) *BoolSliceCmd {
|
||||
args := make([]interface{}, 2+len(hashes))
|
||||
args[0] = "script"
|
||||
args[1] = "exists"
|
||||
for i, hash := range hashes {
|
||||
args[2+i] = hash
|
||||
}
|
||||
cmd := NewBoolSliceCmd(ctx, args...)
|
||||
|
||||
result := make([]bool, len(hashes))
|
||||
for i := range result {
|
||||
result[i] = true
|
||||
}
|
||||
|
||||
_ = c.hooks.process(ctx, cmd, func(ctx context.Context, _ Cmder) error {
|
||||
mu := &sync.Mutex{}
|
||||
err := c.ForEachShard(ctx, func(ctx context.Context, shard *Client) error {
|
||||
val, err := shard.ScriptExists(ctx, hashes...).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
for i, v := range val {
|
||||
result[i] = result[i] && v
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
cmd.SetErr(err)
|
||||
} else {
|
||||
cmd.val = result
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return cmd
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,4 +0,0 @@
|
||||
/*
|
||||
Package redis implements a Redis client.
|
||||
*/
|
||||
package redis
|
@ -1,144 +0,0 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/go-redis/redis/v9/internal/pool"
|
||||
"github.com/go-redis/redis/v9/internal/proto"
|
||||
)
|
||||
|
||||
// ErrClosed performs any operation on the closed client will return this error.
|
||||
var ErrClosed = pool.ErrClosed
|
||||
|
||||
type Error interface {
|
||||
error
|
||||
|
||||
// RedisError is a no-op function but
|
||||
// serves to distinguish types that are Redis
|
||||
// errors from ordinary errors: a type is a
|
||||
// Redis error if it has a RedisError method.
|
||||
RedisError()
|
||||
}
|
||||
|
||||
var _ Error = proto.RedisError("")
|
||||
|
||||
func shouldRetry(err error, retryTimeout bool) bool {
|
||||
switch err {
|
||||
case io.EOF, io.ErrUnexpectedEOF:
|
||||
return true
|
||||
case nil, context.Canceled, context.DeadlineExceeded:
|
||||
return false
|
||||
}
|
||||
|
||||
if v, ok := err.(timeoutError); ok {
|
||||
if v.Timeout() {
|
||||
return retryTimeout
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
s := err.Error()
|
||||
if s == "ERR max number of clients reached" {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(s, "LOADING ") {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(s, "READONLY ") {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(s, "CLUSTERDOWN ") {
|
||||
return true
|
||||
}
|
||||
if strings.HasPrefix(s, "TRYAGAIN ") {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isRedisError(err error) bool {
|
||||
_, ok := err.(proto.RedisError)
|
||||
return ok
|
||||
}
|
||||
|
||||
func isBadConn(err error, allowTimeout bool, addr string) bool {
|
||||
switch err {
|
||||
case nil:
|
||||
return false
|
||||
case context.Canceled, context.DeadlineExceeded:
|
||||
return true
|
||||
}
|
||||
|
||||
if isRedisError(err) {
|
||||
switch {
|
||||
case isReadOnlyError(err):
|
||||
// Close connections in read only state in case domain addr is used
|
||||
// and domain resolves to a different Redis Server. See #790.
|
||||
return true
|
||||
case isMovedSameConnAddr(err, addr):
|
||||
// Close connections when we are asked to move to the same addr
|
||||
// of the connection. Force a DNS resolution when all connections
|
||||
// of the pool are recycled
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if allowTimeout {
|
||||
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func isMovedError(err error) (moved bool, ask bool, addr string) {
|
||||
if !isRedisError(err) {
|
||||
return
|
||||
}
|
||||
|
||||
s := err.Error()
|
||||
switch {
|
||||
case strings.HasPrefix(s, "MOVED "):
|
||||
moved = true
|
||||
case strings.HasPrefix(s, "ASK "):
|
||||
ask = true
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
ind := strings.LastIndex(s, " ")
|
||||
if ind == -1 {
|
||||
return false, false, ""
|
||||
}
|
||||
addr = s[ind+1:]
|
||||
return
|
||||
}
|
||||
|
||||
func isLoadingError(err error) bool {
|
||||
return strings.HasPrefix(err.Error(), "LOADING ")
|
||||
}
|
||||
|
||||
func isReadOnlyError(err error) bool {
|
||||
return strings.HasPrefix(err.Error(), "READONLY ")
|
||||
}
|
||||
|
||||
func isMovedSameConnAddr(err error, addr string) bool {
|
||||
redisError := err.Error()
|
||||
if !strings.HasPrefix(redisError, "MOVED ") {
|
||||
return false
|
||||
}
|
||||
return strings.HasSuffix(redisError, " "+addr)
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type timeoutError interface {
|
||||
Timeout() bool
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func AppendArg(b []byte, v interface{}) []byte {
|
||||
switch v := v.(type) {
|
||||
case nil:
|
||||
return append(b, "<nil>"...)
|
||||
case string:
|
||||
return appendUTF8String(b, Bytes(v))
|
||||
case []byte:
|
||||
return appendUTF8String(b, v)
|
||||
case int:
|
||||
return strconv.AppendInt(b, int64(v), 10)
|
||||
case int8:
|
||||
return strconv.AppendInt(b, int64(v), 10)
|
||||
case int16:
|
||||
return strconv.AppendInt(b, int64(v), 10)
|
||||
case int32:
|
||||
return strconv.AppendInt(b, int64(v), 10)
|
||||
case int64:
|
||||
return strconv.AppendInt(b, v, 10)
|
||||
case uint:
|
||||
return strconv.AppendUint(b, uint64(v), 10)
|
||||
case uint8:
|
||||
return strconv.AppendUint(b, uint64(v), 10)
|
||||
case uint16:
|
||||
return strconv.AppendUint(b, uint64(v), 10)
|
||||
case uint32:
|
||||
return strconv.AppendUint(b, uint64(v), 10)
|
||||
case uint64:
|
||||
return strconv.AppendUint(b, v, 10)
|
||||
case float32:
|
||||
return strconv.AppendFloat(b, float64(v), 'f', -1, 64)
|
||||
case float64:
|
||||
return strconv.AppendFloat(b, v, 'f', -1, 64)
|
||||
case bool:
|
||||
if v {
|
||||
return append(b, "true"...)
|
||||
}
|
||||
return append(b, "false"...)
|
||||
case time.Time:
|
||||
return v.AppendFormat(b, time.RFC3339Nano)
|
||||
default:
|
||||
return append(b, fmt.Sprint(v)...)
|
||||
}
|
||||
}
|
||||
|
||||
func appendUTF8String(dst []byte, src []byte) []byte {
|
||||
dst = append(dst, src...)
|
||||
return dst
|
||||
}
|
@ -1,78 +0,0 @@
|
||||
package hashtag
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-redis/redis/v9/internal/rand"
|
||||
)
|
||||
|
||||
const slotNumber = 16384
|
||||
|
||||
// CRC16 implementation according to CCITT standards.
|
||||
// Copyright 2001-2010 Georges Menie (www.menie.org)
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// http://redis.io/topics/cluster-spec#appendix-a-crc16-reference-implementation-in-ansi-c
|
||||
var crc16tab = [256]uint16{
|
||||
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
|
||||
0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef,
|
||||
0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6,
|
||||
0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de,
|
||||
0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485,
|
||||
0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d,
|
||||
0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
|
||||
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc,
|
||||
0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
|
||||
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b,
|
||||
0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12,
|
||||
0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a,
|
||||
0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41,
|
||||
0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
|
||||
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70,
|
||||
0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78,
|
||||
0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f,
|
||||
0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
|
||||
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e,
|
||||
0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256,
|
||||
0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
|
||||
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
|
||||
0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c,
|
||||
0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634,
|
||||
0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab,
|
||||
0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3,
|
||||
0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
|
||||
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
|
||||
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9,
|
||||
0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1,
|
||||
0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8,
|
||||
0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0,
|
||||
}
|
||||
|
||||
func Key(key string) string {
|
||||
if s := strings.IndexByte(key, '{'); s > -1 {
|
||||
if e := strings.IndexByte(key[s+1:], '}'); e > 0 {
|
||||
return key[s+1 : s+e+1]
|
||||
}
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func RandomSlot() int {
|
||||
return rand.Intn(slotNumber)
|
||||
}
|
||||
|
||||
// Slot returns a consistent slot number between 0 and 16383
|
||||
// for any given string key.
|
||||
func Slot(key string) int {
|
||||
if key == "" {
|
||||
return RandomSlot()
|
||||
}
|
||||
key = Key(key)
|
||||
return int(crc16sum(key)) % slotNumber
|
||||
}
|
||||
|
||||
func crc16sum(key string) (crc uint16) {
|
||||
for i := 0; i < len(key); i++ {
|
||||
crc = (crc << 8) ^ crc16tab[(byte(crc>>8)^key[i])&0x00ff]
|
||||
}
|
||||
return
|
||||
}
|
@ -1,201 +0,0 @@
|
||||
package hscan
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// decoderFunc represents decoding functions for default built-in types.
|
||||
type decoderFunc func(reflect.Value, string) error
|
||||
|
||||
var (
|
||||
// List of built-in decoders indexed by their numeric constant values (eg: reflect.Bool = 1).
|
||||
decoders = []decoderFunc{
|
||||
reflect.Bool: decodeBool,
|
||||
reflect.Int: decodeInt,
|
||||
reflect.Int8: decodeInt8,
|
||||
reflect.Int16: decodeInt16,
|
||||
reflect.Int32: decodeInt32,
|
||||
reflect.Int64: decodeInt64,
|
||||
reflect.Uint: decodeUint,
|
||||
reflect.Uint8: decodeUint8,
|
||||
reflect.Uint16: decodeUint16,
|
||||
reflect.Uint32: decodeUint32,
|
||||
reflect.Uint64: decodeUint64,
|
||||
reflect.Float32: decodeFloat32,
|
||||
reflect.Float64: decodeFloat64,
|
||||
reflect.Complex64: decodeUnsupported,
|
||||
reflect.Complex128: decodeUnsupported,
|
||||
reflect.Array: decodeUnsupported,
|
||||
reflect.Chan: decodeUnsupported,
|
||||
reflect.Func: decodeUnsupported,
|
||||
reflect.Interface: decodeUnsupported,
|
||||
reflect.Map: decodeUnsupported,
|
||||
reflect.Ptr: decodeUnsupported,
|
||||
reflect.Slice: decodeSlice,
|
||||
reflect.String: decodeString,
|
||||
reflect.Struct: decodeUnsupported,
|
||||
reflect.UnsafePointer: decodeUnsupported,
|
||||
}
|
||||
|
||||
// Global map of struct field specs that is populated once for every new
|
||||
// struct type that is scanned. This caches the field types and the corresponding
|
||||
// decoder functions to avoid iterating through struct fields on subsequent scans.
|
||||
globalStructMap = newStructMap()
|
||||
)
|
||||
|
||||
func Struct(dst interface{}) (StructValue, error) {
|
||||
v := reflect.ValueOf(dst)
|
||||
|
||||
// The destination to scan into should be a struct pointer.
|
||||
if v.Kind() != reflect.Ptr || v.IsNil() {
|
||||
return StructValue{}, fmt.Errorf("redis.Scan(non-pointer %T)", dst)
|
||||
}
|
||||
|
||||
v = v.Elem()
|
||||
if v.Kind() != reflect.Struct {
|
||||
return StructValue{}, fmt.Errorf("redis.Scan(non-struct %T)", dst)
|
||||
}
|
||||
|
||||
return StructValue{
|
||||
spec: globalStructMap.get(v.Type()),
|
||||
value: v,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Scan scans the results from a key-value Redis map result set to a destination struct.
|
||||
// The Redis keys are matched to the struct's field with the `redis` tag.
|
||||
func Scan(dst interface{}, keys []interface{}, vals []interface{}) error {
|
||||
if len(keys) != len(vals) {
|
||||
return errors.New("args should have the same number of keys and vals")
|
||||
}
|
||||
|
||||
strct, err := Struct(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Iterate through the (key, value) sequence.
|
||||
for i := 0; i < len(vals); i++ {
|
||||
key, ok := keys[i].(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
val, ok := vals[i].(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := strct.Scan(key, val); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeBool(f reflect.Value, s string) error {
|
||||
b, err := strconv.ParseBool(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SetBool(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeInt8(f reflect.Value, s string) error {
|
||||
return decodeNumber(f, s, 8)
|
||||
}
|
||||
|
||||
func decodeInt16(f reflect.Value, s string) error {
|
||||
return decodeNumber(f, s, 16)
|
||||
}
|
||||
|
||||
func decodeInt32(f reflect.Value, s string) error {
|
||||
return decodeNumber(f, s, 32)
|
||||
}
|
||||
|
||||
func decodeInt64(f reflect.Value, s string) error {
|
||||
return decodeNumber(f, s, 64)
|
||||
}
|
||||
|
||||
func decodeInt(f reflect.Value, s string) error {
|
||||
return decodeNumber(f, s, 0)
|
||||
}
|
||||
|
||||
func decodeNumber(f reflect.Value, s string, bitSize int) error {
|
||||
v, err := strconv.ParseInt(s, 10, bitSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SetInt(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeUint8(f reflect.Value, s string) error {
|
||||
return decodeUnsignedNumber(f, s, 8)
|
||||
}
|
||||
|
||||
func decodeUint16(f reflect.Value, s string) error {
|
||||
return decodeUnsignedNumber(f, s, 16)
|
||||
}
|
||||
|
||||
func decodeUint32(f reflect.Value, s string) error {
|
||||
return decodeUnsignedNumber(f, s, 32)
|
||||
}
|
||||
|
||||
func decodeUint64(f reflect.Value, s string) error {
|
||||
return decodeUnsignedNumber(f, s, 64)
|
||||
}
|
||||
|
||||
func decodeUint(f reflect.Value, s string) error {
|
||||
return decodeUnsignedNumber(f, s, 0)
|
||||
}
|
||||
|
||||
func decodeUnsignedNumber(f reflect.Value, s string, bitSize int) error {
|
||||
v, err := strconv.ParseUint(s, 10, bitSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SetUint(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeFloat32(f reflect.Value, s string) error {
|
||||
v, err := strconv.ParseFloat(s, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SetFloat(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
// although the default is float64, but we better define it.
|
||||
func decodeFloat64(f reflect.Value, s string) error {
|
||||
v, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.SetFloat(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeString(f reflect.Value, s string) error {
|
||||
f.SetString(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeSlice(f reflect.Value, s string) error {
|
||||
// []byte slice ([]uint8).
|
||||
if f.Type().Elem().Kind() == reflect.Uint8 {
|
||||
f.SetBytes([]byte(s))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeUnsupported(v reflect.Value, s string) error {
|
||||
return fmt.Errorf("redis.Scan(unsupported %s)", v.Type())
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
package hscan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// structMap contains the map of struct fields for target structs
|
||||
// indexed by the struct type.
|
||||
type structMap struct {
|
||||
m sync.Map
|
||||
}
|
||||
|
||||
func newStructMap() *structMap {
|
||||
return new(structMap)
|
||||
}
|
||||
|
||||
func (s *structMap) get(t reflect.Type) *structSpec {
|
||||
if v, ok := s.m.Load(t); ok {
|
||||
return v.(*structSpec)
|
||||
}
|
||||
|
||||
spec := newStructSpec(t, "redis")
|
||||
s.m.Store(t, spec)
|
||||
return spec
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// structSpec contains the list of all fields in a target struct.
|
||||
type structSpec struct {
|
||||
m map[string]*structField
|
||||
}
|
||||
|
||||
func (s *structSpec) set(tag string, sf *structField) {
|
||||
s.m[tag] = sf
|
||||
}
|
||||
|
||||
func newStructSpec(t reflect.Type, fieldTag string) *structSpec {
|
||||
numField := t.NumField()
|
||||
out := &structSpec{
|
||||
m: make(map[string]*structField, numField),
|
||||
}
|
||||
|
||||
for i := 0; i < numField; i++ {
|
||||
f := t.Field(i)
|
||||
|
||||
tag := f.Tag.Get(fieldTag)
|
||||
if tag == "" || tag == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
tag = strings.Split(tag, ",")[0]
|
||||
if tag == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Use the built-in decoder.
|
||||
out.set(tag, &structField{index: i, fn: decoders[f.Type.Kind()]})
|
||||
}
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// structField represents a single field in a target struct.
|
||||
type structField struct {
|
||||
index int
|
||||
fn decoderFunc
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
type StructValue struct {
|
||||
spec *structSpec
|
||||
value reflect.Value
|
||||
}
|
||||
|
||||
func (s StructValue) Scan(key string, value string) error {
|
||||
field, ok := s.spec.m[key]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if err := field.fn(s.value.Field(field.index), value); err != nil {
|
||||
t := s.value.Type()
|
||||
return fmt.Errorf("cannot scan redis.result %s into struct field %s.%s of type %s, error-%s",
|
||||
value, t.Name(), t.Field(field.index).Name, t.Field(field.index).Type, err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v9/internal/rand"
|
||||
)
|
||||
|
||||
func RetryBackoff(retry int, minBackoff, maxBackoff time.Duration) time.Duration {
|
||||
if retry < 0 {
|
||||
panic("not reached")
|
||||
}
|
||||
if minBackoff == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
d := minBackoff << uint(retry)
|
||||
if d < minBackoff {
|
||||
return maxBackoff
|
||||
}
|
||||
|
||||
d = minBackoff + time.Duration(rand.Int63n(int64(d)))
|
||||
|
||||
if d > maxBackoff || d < minBackoff {
|
||||
d = maxBackoff
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Logging interface {
|
||||
Printf(ctx context.Context, format string, v ...interface{})
|
||||
}
|
||||
|
||||
type logger struct {
|
||||
log *log.Logger
|
||||
}
|
||||
|
||||
func (l *logger) Printf(ctx context.Context, format string, v ...interface{}) {
|
||||
_ = l.log.Output(2, fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
// Logger calls Output to print to the stderr.
|
||||
// Arguments are handled in the manner of fmt.Print.
|
||||
var Logger Logging = &logger{
|
||||
log: log.New(os.Stderr, "redis: ", log.LstdFlags|log.Lshortfile),
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
Copyright 2014 The Camlistore Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// A Once will perform a successful action exactly once.
|
||||
//
|
||||
// Unlike a sync.Once, this Once's func returns an error
|
||||
// and is re-armed on failure.
|
||||
type Once struct {
|
||||
m sync.Mutex
|
||||
done uint32
|
||||
}
|
||||
|
||||
// Do calls the function f if and only if Do has not been invoked
|
||||
// without error for this instance of Once. In other words, given
|
||||
// var once Once
|
||||
// if once.Do(f) is called multiple times, only the first call will
|
||||
// invoke f, even if f has a different value in each invocation unless
|
||||
// f returns an error. A new instance of Once is required for each
|
||||
// function to execute.
|
||||
//
|
||||
// Do is intended for initialization that must be run exactly once. Since f
|
||||
// is niladic, it may be necessary to use a function literal to capture the
|
||||
// arguments to a function to be invoked by Do:
|
||||
// err := config.once.Do(func() error { return config.init(filename) })
|
||||
func (o *Once) Do(f func() error) error {
|
||||
if atomic.LoadUint32(&o.done) == 1 {
|
||||
return nil
|
||||
}
|
||||
// Slow-path.
|
||||
o.m.Lock()
|
||||
defer o.m.Unlock()
|
||||
var err error
|
||||
if o.done == 0 {
|
||||
err = f()
|
||||
if err == nil {
|
||||
atomic.StoreUint32(&o.done, 1)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
@ -1,125 +0,0 @@
|
||||
package pool
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v9/internal/proto"
|
||||
)
|
||||
|
||||
var noDeadline = time.Time{}
|
||||
|
||||
type Conn struct {
|
||||
usedAt int64 // atomic
|
||||
netConn net.Conn
|
||||
|
||||
rd *proto.Reader
|
||||
bw *bufio.Writer
|
||||
wr *proto.Writer
|
||||
|
||||
Inited bool
|
||||
pooled bool
|
||||
createdAt time.Time
|
||||
}
|
||||
|
||||
func NewConn(netConn net.Conn) *Conn {
|
||||
cn := &Conn{
|
||||
netConn: netConn,
|
||||
createdAt: time.Now(),
|
||||
}
|
||||
cn.rd = proto.NewReader(netConn)
|
||||
cn.bw = bufio.NewWriter(netConn)
|
||||
cn.wr = proto.NewWriter(cn.bw)
|
||||
cn.SetUsedAt(time.Now())
|
||||
return cn
|
||||
}
|
||||
|
||||
func (cn *Conn) UsedAt() time.Time {
|
||||
unix := atomic.LoadInt64(&cn.usedAt)
|
||||
return time.Unix(unix, 0)
|
||||
}
|
||||
|
||||
func (cn *Conn) SetUsedAt(tm time.Time) {
|
||||
atomic.StoreInt64(&cn.usedAt, tm.Unix())
|
||||
}
|
||||
|
||||
func (cn *Conn) SetNetConn(netConn net.Conn) {
|
||||
cn.netConn = netConn
|
||||
cn.rd.Reset(netConn)
|
||||
cn.bw.Reset(netConn)
|
||||
}
|
||||
|
||||
func (cn *Conn) Write(b []byte) (int, error) {
|
||||
return cn.netConn.Write(b)
|
||||
}
|
||||
|
||||
func (cn *Conn) RemoteAddr() net.Addr {
|
||||
if cn.netConn != nil {
|
||||
return cn.netConn.RemoteAddr()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cn *Conn) WithReader(ctx context.Context, timeout time.Duration, fn func(rd *proto.Reader) error) error {
|
||||
if timeout != 0 {
|
||||
if err := cn.netConn.SetReadDeadline(cn.deadline(ctx, timeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return fn(cn.rd)
|
||||
}
|
||||
|
||||
func (cn *Conn) WithWriter(
|
||||
ctx context.Context, timeout time.Duration, fn func(wr *proto.Writer) error,
|
||||
) error {
|
||||
if timeout != 0 {
|
||||
if err := cn.netConn.SetWriteDeadline(cn.deadline(ctx, timeout)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if cn.bw.Buffered() > 0 {
|
||||
cn.bw.Reset(cn.netConn)
|
||||
}
|
||||
|
||||
if err := fn(cn.wr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return cn.bw.Flush()
|
||||
}
|
||||
|
||||
func (cn *Conn) Close() error {
|
||||
return cn.netConn.Close()
|
||||
}
|
||||
|
||||
func (cn *Conn) deadline(ctx context.Context, timeout time.Duration) time.Time {
|
||||
tm := time.Now()
|
||||
cn.SetUsedAt(tm)
|
||||
|
||||
if timeout > 0 {
|
||||
tm = tm.Add(timeout)
|
||||
}
|
||||
|
||||
if ctx != nil {
|
||||
deadline, ok := ctx.Deadline()
|
||||
if ok {
|
||||
if timeout == 0 {
|
||||
return deadline
|
||||
}
|
||||
if deadline.Before(tm) {
|
||||
return deadline
|
||||
}
|
||||
return tm
|
||||
}
|
||||
}
|
||||
|
||||
if timeout > 0 {
|
||||
return tm
|
||||
}
|
||||
|
||||
return noDeadline
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue