gRPC去掉连接池, 设置keepAlive
parent
09985998ca
commit
c997ce7cab
|
@ -9,7 +9,7 @@ type Host struct {
|
||||||
Id int16 `json:"id" xorm:"smallint pk autoincr"`
|
Id int16 `json:"id" xorm:"smallint pk autoincr"`
|
||||||
Name string `json:"name" xorm:"varchar(64) notnull"` // 主机名称
|
Name string `json:"name" xorm:"varchar(64) notnull"` // 主机名称
|
||||||
Alias string `json:"alias" xorm:"varchar(32) notnull default '' "` // 主机别名
|
Alias string `json:"alias" xorm:"varchar(32) notnull default '' "` // 主机别名
|
||||||
Port int `json:"port" xorm:"notnull default 22"` // 主机端口
|
Port int `json:"port" xorm:"notnull default 5921"` // 主机端口
|
||||||
Remark string `json:"remark" xorm:"varchar(100) notnull default '' "` // 备注
|
Remark string `json:"remark" xorm:"varchar(100) notnull default '' "` // 备注
|
||||||
BaseModel `json:"-" xorm:"-"`
|
BaseModel `json:"-" xorm:"-"`
|
||||||
Selected bool `json:"-" xorm:"-"`
|
Selected bool `json:"-" xorm:"-"`
|
||||||
|
|
|
@ -22,7 +22,7 @@ type TaskLog struct {
|
||||||
StartTime time.Time `json:"start_time" xorm:"datetime created"` // 开始执行时间
|
StartTime time.Time `json:"start_time" xorm:"datetime created"` // 开始执行时间
|
||||||
EndTime time.Time `json:"end_time" xorm:"datetime updated"` // 执行完成(失败)时间
|
EndTime time.Time `json:"end_time" xorm:"datetime updated"` // 执行完成(失败)时间
|
||||||
Status Status `json:"status" xorm:"tinyint notnull index default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 3:任务取消(上次任务未执行完成) 4:异步执行
|
Status Status `json:"status" xorm:"tinyint notnull index default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 3:任务取消(上次任务未执行完成) 4:异步执行
|
||||||
Result string `json:"result" xorm:"mediumtext notnull default '' "` // 执行结果
|
Result string `json:"result" xorm:"mediumtext notnull "` // 执行结果
|
||||||
TotalTime int `json:"total_time" xorm:"-"` // 执行总时长
|
TotalTime int `json:"total_time" xorm:"-"` // 执行总时长
|
||||||
BaseModel `json:"-" xorm:"-"`
|
BaseModel `json:"-" xorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,28 +42,24 @@ func Exec(ip string, port int, taskReq *pb.TaskRequest) (string, error) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
addr := fmt.Sprintf("%s:%d", ip, port)
|
addr := fmt.Sprintf("%s:%d", ip, port)
|
||||||
conn, err := grpcpool.Pool.Get(addr)
|
c, err := grpcpool.Pool.Get(addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
isConnClosed := false
|
|
||||||
defer func() {
|
|
||||||
if !isConnClosed {
|
|
||||||
grpcpool.Pool.Put(addr, conn)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
c := pb.NewTaskClient(conn)
|
|
||||||
if taskReq.Timeout <= 0 || taskReq.Timeout > 86400 {
|
if taskReq.Timeout <= 0 || taskReq.Timeout > 86400 {
|
||||||
taskReq.Timeout = 86400
|
taskReq.Timeout = 86400
|
||||||
}
|
}
|
||||||
timeout := time.Duration(taskReq.Timeout) * time.Second
|
timeout := time.Duration(taskReq.Timeout) * time.Second
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
taskUniqueKey := generateTaskUniqueKey(ip, port, taskReq.Id)
|
taskUniqueKey := generateTaskUniqueKey(ip, port, taskReq.Id)
|
||||||
taskMap.Store(taskUniqueKey, cancel)
|
taskMap.Store(taskUniqueKey, cancel)
|
||||||
defer taskMap.Delete(taskUniqueKey)
|
defer taskMap.Delete(taskUniqueKey)
|
||||||
|
|
||||||
resp, err := c.Run(ctx, taskReq)
|
resp, err := c.Run(ctx, taskReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return parseGRPCError(err, conn, &isConnClosed)
|
return parseGRPCError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if resp.Error == "" {
|
if resp.Error == "" {
|
||||||
|
@ -73,11 +69,9 @@ func Exec(ip string, port int, taskReq *pb.TaskRequest) (string, error) {
|
||||||
return resp.Output, errors.New(resp.Error)
|
return resp.Output, errors.New(resp.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseGRPCError(err error, conn *grpc.ClientConn, connClosed *bool) (string, error) {
|
func parseGRPCError(err error) (string, error) {
|
||||||
switch grpc.Code(err) {
|
switch grpc.Code(err) {
|
||||||
case codes.Unavailable, codes.Internal:
|
case codes.Unavailable:
|
||||||
conn.Close()
|
|
||||||
*connClosed = true
|
|
||||||
return "", errUnavailable
|
return "", errUnavailable
|
||||||
case codes.DeadlineExceeded:
|
case codes.DeadlineExceeded:
|
||||||
return "", errors.New("执行超时, 强制结束")
|
return "", errors.New("执行超时, 强制结束")
|
||||||
|
|
|
@ -1,110 +1,93 @@
|
||||||
package grpcpool
|
package grpcpool
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ouqiang/gocron/internal/modules/app"
|
"github.com/ouqiang/gocron/internal/modules/app"
|
||||||
"github.com/ouqiang/gocron/internal/modules/rpc/auth"
|
"github.com/ouqiang/gocron/internal/modules/rpc/auth"
|
||||||
"github.com/silenceper/pool"
|
"github.com/ouqiang/gocron/internal/modules/rpc/proto"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/keepalive"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
backOffMaxDelay = 3 * time.Second
|
||||||
|
dialTimeout = 2 * time.Second
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Pool GRPCPool
|
Pool = &GRPCPool{
|
||||||
)
|
conns: make(map[string]*Client),
|
||||||
|
|
||||||
var (
|
|
||||||
ErrInvalidConn = errors.New("invalid connection")
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
Pool = GRPCPool{
|
|
||||||
make(map[string]pool.Pool),
|
|
||||||
sync.RWMutex{},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keepAliveParams = keepalive.ClientParameters{
|
||||||
|
Time: 20 * time.Second,
|
||||||
|
Timeout: 3 * time.Second,
|
||||||
|
PermitWithoutStream: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
conn *grpc.ClientConn
|
||||||
|
rpcClient rpc.TaskClient
|
||||||
}
|
}
|
||||||
|
|
||||||
type GRPCPool struct {
|
type GRPCPool struct {
|
||||||
// map key格式 ip:port
|
// map key格式 ip:port
|
||||||
conns map[string]pool.Pool
|
conns map[string]*Client
|
||||||
sync.RWMutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *GRPCPool) Get(addr string) (*grpc.ClientConn, error) {
|
func (p *GRPCPool) Get(addr string) (rpc.TaskClient, error) {
|
||||||
p.RLock()
|
p.mu.RLock()
|
||||||
pool, ok := p.conns[addr]
|
client, ok := p.conns[addr]
|
||||||
p.RUnlock()
|
p.mu.RUnlock()
|
||||||
if !ok {
|
|
||||||
err := p.newCommonPool(addr)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.RLock()
|
|
||||||
pool = p.conns[addr]
|
|
||||||
p.RUnlock()
|
|
||||||
conn, err := pool.Get()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return conn.(*grpc.ClientConn), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *GRPCPool) Put(addr string, conn *grpc.ClientConn) error {
|
|
||||||
p.RLock()
|
|
||||||
defer p.RUnlock()
|
|
||||||
pool, ok := p.conns[addr]
|
|
||||||
if ok {
|
if ok {
|
||||||
return pool.Put(conn)
|
return client.rpcClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrInvalidConn
|
client, err := p.factory(addr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
p.conns[addr] = client
|
||||||
|
|
||||||
|
return client.rpcClient, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 释放连接池
|
// 释放连接
|
||||||
func (p *GRPCPool) Release(addr string) {
|
func (p *GRPCPool) Release(addr string) {
|
||||||
p.Lock()
|
p.mu.Lock()
|
||||||
defer p.Unlock()
|
defer p.mu.Unlock()
|
||||||
pool, ok := p.conns[addr]
|
client, ok := p.conns[addr]
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
pool.Release()
|
|
||||||
delete(p.conns, addr)
|
delete(p.conns, addr)
|
||||||
|
client.conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 释放所有连接池
|
// 创建连接
|
||||||
func (p *GRPCPool) ReleaseAll() {
|
func (p *GRPCPool) factory(addr string) (*Client, error) {
|
||||||
p.Lock()
|
p.mu.Lock()
|
||||||
defer p.Unlock()
|
defer p.mu.Unlock()
|
||||||
for _, pool := range p.conns {
|
|
||||||
pool.Release()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化底层连接池
|
client, ok := p.conns[addr]
|
||||||
func (p *GRPCPool) newCommonPool(addr string) error {
|
|
||||||
p.Lock()
|
|
||||||
defer p.Unlock()
|
|
||||||
commonPool, ok := p.conns[addr]
|
|
||||||
if ok {
|
if ok {
|
||||||
return nil
|
return client, nil
|
||||||
}
|
}
|
||||||
poolConfig := &pool.PoolConfig{
|
opts := []grpc.DialOption{
|
||||||
InitialCap: 1,
|
grpc.WithKeepaliveParams(keepAliveParams),
|
||||||
MaxCap: 30,
|
grpc.WithBackoffMaxDelay(backOffMaxDelay),
|
||||||
Factory: func() (interface{}, error) {
|
}
|
||||||
|
|
||||||
if !app.Setting.EnableTLS {
|
if !app.Setting.EnableTLS {
|
||||||
return grpc.Dial(addr, grpc.WithInsecure())
|
opts = append(opts, grpc.WithInsecure())
|
||||||
}
|
} else {
|
||||||
|
|
||||||
server := strings.Split(addr, ":")
|
server := strings.Split(addr, ":")
|
||||||
|
|
||||||
certificate := auth.Certificate{
|
certificate := auth.Certificate{
|
||||||
CAFile: app.Setting.CAFile,
|
CAFile: app.Setting.CAFile,
|
||||||
CertFile: app.Setting.CertFile,
|
CertFile: app.Setting.CertFile,
|
||||||
|
@ -116,25 +99,21 @@ func (p *GRPCPool) newCommonPool(addr string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
opts = append(opts, grpc.WithTransportCredentials(transportCreds))
|
||||||
return grpc.Dial(addr, grpc.WithTransportCredentials(transportCreds))
|
|
||||||
},
|
|
||||||
Close: func(v interface{}) error {
|
|
||||||
conn, ok := v.(*grpc.ClientConn)
|
|
||||||
if ok && conn != nil {
|
|
||||||
return conn.Close()
|
|
||||||
}
|
|
||||||
return ErrInvalidConn
|
|
||||||
},
|
|
||||||
IdleTimeout: 3 * time.Minute,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commonPool, err := pool.NewChannelPool(poolConfig)
|
ctx, cancel := context.WithTimeout(context.Background(), dialTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
conn, err := grpc.DialContext(ctx, addr, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
p.conns[addr] = commonPool
|
client = &Client{
|
||||||
|
conn: conn,
|
||||||
|
rpcClient: rpc.NewTaskClient(conn),
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/keepalive"
|
||||||
|
|
||||||
"github.com/ouqiang/gocron/internal/modules/rpc/auth"
|
"github.com/ouqiang/gocron/internal/modules/rpc/auth"
|
||||||
pb "github.com/ouqiang/gocron/internal/modules/rpc/proto"
|
pb "github.com/ouqiang/gocron/internal/modules/rpc/proto"
|
||||||
|
@ -14,6 +17,19 @@ import (
|
||||||
|
|
||||||
type Server struct{}
|
type Server struct{}
|
||||||
|
|
||||||
|
var keepAlivePolicy = keepalive.EnforcementPolicy{
|
||||||
|
MinTime: 10 * time.Second,
|
||||||
|
PermitWithoutStream: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var keepAliveParams = keepalive.ServerParameters{
|
||||||
|
MaxConnectionIdle: 1 * time.Minute,
|
||||||
|
MaxConnectionAge: 2 * time.Hour,
|
||||||
|
MaxConnectionAgeGrace: 3 * time.Hour,
|
||||||
|
Time: 30 * time.Second,
|
||||||
|
Timeout: 3 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) {
|
func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := recover(); err != nil {
|
if err := recover(); err != nil {
|
||||||
|
@ -39,21 +55,24 @@ func Start(addr string, enableTLS bool, certificate auth.Certificate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var s *grpc.Server
|
var s *grpc.Server
|
||||||
|
opts := []grpc.ServerOption{
|
||||||
|
grpc.KeepaliveParams(keepAliveParams),
|
||||||
|
grpc.KeepaliveEnforcementPolicy(keepAlivePolicy),
|
||||||
|
}
|
||||||
if enableTLS {
|
if enableTLS {
|
||||||
tlsConfig, err := certificate.GetTLSConfigForServer()
|
tlsConfig, err := certificate.GetTLSConfigForServer()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
grpclog.Fatal(err)
|
grpclog.Fatal(err)
|
||||||
}
|
}
|
||||||
opt := grpc.Creds(credentials.NewTLS(tlsConfig))
|
opt := grpc.Creds(credentials.NewTLS(tlsConfig))
|
||||||
s = grpc.NewServer(opt)
|
opts = append(opts, opt)
|
||||||
pb.RegisterTaskServer(s, Server{})
|
|
||||||
grpclog.Printf("listen %s with TLS", addr)
|
|
||||||
} else {
|
|
||||||
s = grpc.NewServer()
|
|
||||||
pb.RegisterTaskServer(s, Server{})
|
|
||||||
grpclog.Printf("listen %s", addr)
|
|
||||||
}
|
}
|
||||||
|
s = grpc.NewServer(opts...)
|
||||||
|
pb.RegisterTaskServer(s, Server{})
|
||||||
|
grpclog.Printf("server listen on %s", addr)
|
||||||
|
|
||||||
err = s.Serve(l)
|
err = s.Serve(l)
|
||||||
|
if err != nil {
|
||||||
grpclog.Fatal(err)
|
grpclog.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ouqiang/goutil"
|
||||||
|
|
||||||
"github.com/go-macaron/binding"
|
"github.com/go-macaron/binding"
|
||||||
"github.com/jakecoffman/cron"
|
"github.com/jakecoffman/cron"
|
||||||
"github.com/ouqiang/gocron/internal/models"
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
|
@ -149,7 +151,9 @@ func Store(ctx *macaron.Context, form TaskForm) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
if taskModel.Level == models.TaskLevelParent {
|
if taskModel.Level == models.TaskLevelParent {
|
||||||
_, err = cron.Parse(form.Spec)
|
err = goutil.PanicToError(func() {
|
||||||
|
cron.Parse(form.Spec)
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return json.CommonFailure("crontab表达式解析失败", err)
|
return json.CommonFailure("crontab表达式解析失败", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ouqiang/goutil"
|
||||||
|
|
||||||
"github.com/jakecoffman/cron"
|
"github.com/jakecoffman/cron"
|
||||||
"github.com/ouqiang/gocron/internal/models"
|
"github.com/ouqiang/gocron/internal/models"
|
||||||
"github.com/ouqiang/gocron/internal/modules/app"
|
"github.com/ouqiang/gocron/internal/modules/app"
|
||||||
|
@ -160,7 +162,9 @@ func (task Task) Add(taskModel models.Task) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cronName := strconv.Itoa(taskModel.Id)
|
cronName := strconv.Itoa(taskModel.Id)
|
||||||
err := serviceCron.AddFunc(taskModel.Spec, taskFunc, cronName)
|
err := goutil.PanicToError(func() {
|
||||||
|
serviceCron.AddFunc(taskModel.Spec, taskFunc, cronName)
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("添加任务到调度器失败#", err)
|
logger.Error("添加任务到调度器失败#", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,18 +90,13 @@ type FuncJob func()
|
||||||
func (f FuncJob) Run() { f() }
|
func (f FuncJob) Run() { f() }
|
||||||
|
|
||||||
// AddFunc adds a func to the Cron to be run on the given schedule.
|
// AddFunc adds a func to the Cron to be run on the given schedule.
|
||||||
func (c *Cron) AddFunc(spec string, cmd func(), name string) error {
|
func (c *Cron) AddFunc(spec string, cmd func(), name string) {
|
||||||
return c.AddJob(spec, FuncJob(cmd), name)
|
c.AddJob(spec, FuncJob(cmd), name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddFunc adds a Job to the Cron to be run on the given schedule.
|
// AddFunc adds a Job to the Cron to be run on the given schedule.
|
||||||
func (c *Cron) AddJob(spec string, cmd Job, name string) error {
|
func (c *Cron) AddJob(spec string, cmd Job, name string) {
|
||||||
schedule, err := Parse(spec)
|
c.Schedule(Parse(spec), cmd, name)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
c.Schedule(schedule, cmd, name)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveJob removes a Job from the Cron based on name.
|
// RemoveJob removes a Job from the Cron based on name.
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package cron
|
package cron
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"math"
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -15,10 +14,7 @@ import (
|
||||||
// It accepts
|
// It accepts
|
||||||
// - Full crontab specs, e.g. "* * * * * ?"
|
// - Full crontab specs, e.g. "* * * * * ?"
|
||||||
// - Descriptors, e.g. "@midnight", "@every 1h30m"
|
// - Descriptors, e.g. "@midnight", "@every 1h30m"
|
||||||
func Parse(spec string) (Schedule, error) {
|
func Parse(spec string) Schedule {
|
||||||
if len(spec) == 0 {
|
|
||||||
return nil, fmt.Errorf("Empty spec string")
|
|
||||||
}
|
|
||||||
if spec[0] == '@' {
|
if spec[0] == '@' {
|
||||||
return parseDescriptor(spec)
|
return parseDescriptor(spec)
|
||||||
}
|
}
|
||||||
|
@ -27,7 +23,7 @@ func Parse(spec string) (Schedule, error) {
|
||||||
// (second) (minute) (hour) (day of month) (month) (day of week, optional)
|
// (second) (minute) (hour) (day of month) (month) (day of week, optional)
|
||||||
fields := strings.Fields(spec)
|
fields := strings.Fields(spec)
|
||||||
if len(fields) != 5 && len(fields) != 6 {
|
if len(fields) != 5 && len(fields) != 6 {
|
||||||
return nil, fmt.Errorf("Expected 5 or 6 fields, found %d: %s", len(fields), spec)
|
log.Panicf("Expected 5 or 6 fields, found %d: %s", len(fields), spec)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a sixth field is not provided (DayOfWeek), then it is equivalent to star.
|
// If a sixth field is not provided (DayOfWeek), then it is equivalent to star.
|
||||||
|
@ -44,7 +40,7 @@ func Parse(spec string) (Schedule, error) {
|
||||||
Dow: getField(fields[5], dow),
|
Dow: getField(fields[5], dow),
|
||||||
}
|
}
|
||||||
|
|
||||||
return schedule, nil
|
return schedule
|
||||||
}
|
}
|
||||||
|
|
||||||
// getField returns an Int with the bits set representing all of the times that
|
// getField returns an Int with the bits set representing all of the times that
|
||||||
|
@ -160,7 +156,7 @@ func all(r bounds) uint64 {
|
||||||
|
|
||||||
// parseDescriptor returns a pre-defined schedule for the expression, or panics
|
// parseDescriptor returns a pre-defined schedule for the expression, or panics
|
||||||
// if none matches.
|
// if none matches.
|
||||||
func parseDescriptor(spec string) (Schedule, error) {
|
func parseDescriptor(spec string) Schedule {
|
||||||
switch spec {
|
switch spec {
|
||||||
case "@yearly", "@annually":
|
case "@yearly", "@annually":
|
||||||
return &SpecSchedule{
|
return &SpecSchedule{
|
||||||
|
@ -170,7 +166,7 @@ func parseDescriptor(spec string) (Schedule, error) {
|
||||||
Dom: 1 << dom.min,
|
Dom: 1 << dom.min,
|
||||||
Month: 1 << months.min,
|
Month: 1 << months.min,
|
||||||
Dow: all(dow),
|
Dow: all(dow),
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
case "@monthly":
|
case "@monthly":
|
||||||
return &SpecSchedule{
|
return &SpecSchedule{
|
||||||
|
@ -180,7 +176,7 @@ func parseDescriptor(spec string) (Schedule, error) {
|
||||||
Dom: 1 << dom.min,
|
Dom: 1 << dom.min,
|
||||||
Month: all(months),
|
Month: all(months),
|
||||||
Dow: all(dow),
|
Dow: all(dow),
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
case "@weekly":
|
case "@weekly":
|
||||||
return &SpecSchedule{
|
return &SpecSchedule{
|
||||||
|
@ -190,7 +186,7 @@ func parseDescriptor(spec string) (Schedule, error) {
|
||||||
Dom: all(dom),
|
Dom: all(dom),
|
||||||
Month: all(months),
|
Month: all(months),
|
||||||
Dow: 1 << dow.min,
|
Dow: 1 << dow.min,
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
case "@daily", "@midnight":
|
case "@daily", "@midnight":
|
||||||
return &SpecSchedule{
|
return &SpecSchedule{
|
||||||
|
@ -200,7 +196,7 @@ func parseDescriptor(spec string) (Schedule, error) {
|
||||||
Dom: all(dom),
|
Dom: all(dom),
|
||||||
Month: all(months),
|
Month: all(months),
|
||||||
Dow: all(dow),
|
Dow: all(dow),
|
||||||
}, nil
|
}
|
||||||
|
|
||||||
case "@hourly":
|
case "@hourly":
|
||||||
return &SpecSchedule{
|
return &SpecSchedule{
|
||||||
|
@ -210,17 +206,18 @@ func parseDescriptor(spec string) (Schedule, error) {
|
||||||
Dom: all(dom),
|
Dom: all(dom),
|
||||||
Month: all(months),
|
Month: all(months),
|
||||||
Dow: all(dow),
|
Dow: all(dow),
|
||||||
}, nil
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const every = "@every "
|
const every = "@every "
|
||||||
if strings.HasPrefix(spec, every) {
|
if strings.HasPrefix(spec, every) {
|
||||||
duration, err := time.ParseDuration(spec[len(every):])
|
duration, err := time.ParseDuration(spec[len(every):])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Failed to parse duration %s: %s", spec, err)
|
log.Panicf("Failed to parse duration %s: %s", spec, err)
|
||||||
}
|
}
|
||||||
return Every(duration), nil
|
return Every(duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("Unrecognized descriptor: %s", spec)
|
log.Panicf("Unrecognized descriptor: %s", spec)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2016 nestgo
|
|
||||||
|
|
||||||
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,62 +0,0 @@
|
||||||
# pool
|
|
||||||
[](http://godoc.org/github.com/silenceper/pool)
|
|
||||||
|
|
||||||
Golang 实现的连接池
|
|
||||||
|
|
||||||
|
|
||||||
##功能:
|
|
||||||
|
|
||||||
- 连接池中连接类型为`interface{}`,使得更加通用
|
|
||||||
- 链接的最大空闲时间,超时的链接将关闭丢弃,可避免空闲时链接自动失效问题
|
|
||||||
- 使用channel处理池中的链接,高效
|
|
||||||
|
|
||||||
## 基本用法
|
|
||||||
|
|
||||||
```go
|
|
||||||
|
|
||||||
//factory 创建连接的方法
|
|
||||||
factory := func() (interface{}, error) { return net.Dial("tcp", "127.0.0.1:4000") }
|
|
||||||
|
|
||||||
//close 关闭链接的方法
|
|
||||||
close := func(v interface{}) error { return v.(net.Conn).Close() }
|
|
||||||
|
|
||||||
//创建一个连接池: 初始化5,最大链接30
|
|
||||||
poolConfig := &pool.PoolConfig{
|
|
||||||
InitialCap: 5,
|
|
||||||
MaxCap: 30,
|
|
||||||
Factory: factory,
|
|
||||||
Close: close,
|
|
||||||
//链接最大空闲时间,超过该时间的链接 将会关闭,可避免空闲时链接EOF,自动失效的问题
|
|
||||||
IdleTimeout: 15 * time.Second,
|
|
||||||
}
|
|
||||||
p, err := pool.NewChannelPool(poolConfig)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("err=", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//从连接池中取得一个链接
|
|
||||||
v, err := p.Get()
|
|
||||||
|
|
||||||
//do something
|
|
||||||
//conn=v.(net.Conn)
|
|
||||||
|
|
||||||
//将链接放回连接池中
|
|
||||||
p.Put(v)
|
|
||||||
|
|
||||||
//释放连接池中的所有链接
|
|
||||||
p.Release()
|
|
||||||
|
|
||||||
//查看当前链接中的数量
|
|
||||||
current := p.Len()
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
####注:
|
|
||||||
该连接池参考 [https://github.com/fatih/pool](https://github.com/fatih/pool) 实现,改变以及增加原有的一些功能。
|
|
||||||
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The MIT License (MIT) - see LICENSE for more details
|
|
|
@ -1,156 +0,0 @@
|
||||||
package pool
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
//PoolConfig 连接池相关配置
|
|
||||||
type PoolConfig struct {
|
|
||||||
//连接池中拥有的最小连接数
|
|
||||||
InitialCap int
|
|
||||||
//连接池中拥有的最大的连接数
|
|
||||||
MaxCap int
|
|
||||||
//生成连接的方法
|
|
||||||
Factory func() (interface{}, error)
|
|
||||||
//关闭链接的方法
|
|
||||||
Close func(interface{}) error
|
|
||||||
//链接最大空闲时间,超过该事件则将失效
|
|
||||||
IdleTimeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
//channelPool 存放链接信息
|
|
||||||
type channelPool struct {
|
|
||||||
mu sync.Mutex
|
|
||||||
conns chan *idleConn
|
|
||||||
factory func() (interface{}, error)
|
|
||||||
close func(interface{}) error
|
|
||||||
idleTimeout time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
type idleConn struct {
|
|
||||||
conn interface{}
|
|
||||||
t time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
//NewChannelPool 初始化链接
|
|
||||||
func NewChannelPool(poolConfig *PoolConfig) (Pool, error) {
|
|
||||||
if poolConfig.InitialCap < 0 || poolConfig.MaxCap <= 0 || poolConfig.InitialCap > poolConfig.MaxCap {
|
|
||||||
return nil, errors.New("invalid capacity settings")
|
|
||||||
}
|
|
||||||
|
|
||||||
c := &channelPool{
|
|
||||||
conns: make(chan *idleConn, poolConfig.MaxCap),
|
|
||||||
factory: poolConfig.Factory,
|
|
||||||
close: poolConfig.Close,
|
|
||||||
idleTimeout: poolConfig.IdleTimeout,
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < poolConfig.InitialCap; i++ {
|
|
||||||
conn, err := c.factory()
|
|
||||||
if err != nil {
|
|
||||||
c.Release()
|
|
||||||
return nil, fmt.Errorf("factory is not able to fill the pool: %s", err)
|
|
||||||
}
|
|
||||||
c.conns <- &idleConn{conn: conn, t: time.Now()}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//getConns 获取所有连接
|
|
||||||
func (c *channelPool) getConns() chan *idleConn {
|
|
||||||
c.mu.Lock()
|
|
||||||
conns := c.conns
|
|
||||||
c.mu.Unlock()
|
|
||||||
return conns
|
|
||||||
}
|
|
||||||
|
|
||||||
//Get 从pool中取一个连接
|
|
||||||
func (c *channelPool) Get() (interface{}, error) {
|
|
||||||
conns := c.getConns()
|
|
||||||
if conns == nil {
|
|
||||||
return nil, ErrClosed
|
|
||||||
}
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case wrapConn := <-conns:
|
|
||||||
if wrapConn == nil {
|
|
||||||
return nil, ErrClosed
|
|
||||||
}
|
|
||||||
//判断是否超时,超时则丢弃
|
|
||||||
if timeout := c.idleTimeout; timeout > 0 {
|
|
||||||
if wrapConn.t.Add(timeout).Before(time.Now()) {
|
|
||||||
//丢弃并关闭该链接
|
|
||||||
c.Close(wrapConn.conn)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return wrapConn.conn, nil
|
|
||||||
default:
|
|
||||||
conn, err := c.factory()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return conn, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Put 将连接放回pool中
|
|
||||||
func (c *channelPool) Put(conn interface{}) error {
|
|
||||||
if conn == nil {
|
|
||||||
return errors.New("connection is nil. rejecting")
|
|
||||||
}
|
|
||||||
|
|
||||||
c.mu.Lock()
|
|
||||||
defer c.mu.Unlock()
|
|
||||||
|
|
||||||
if c.conns == nil {
|
|
||||||
return c.Close(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case c.conns <- &idleConn{conn: conn, t: time.Now()}:
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
//连接池已满,直接关闭该链接
|
|
||||||
return c.Close(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Close 关闭单条连接
|
|
||||||
func (c *channelPool) Close(conn interface{}) error {
|
|
||||||
if conn == nil {
|
|
||||||
return errors.New("connection is nil. rejecting")
|
|
||||||
}
|
|
||||||
return c.close(conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
//Release 释放连接池中所有链接
|
|
||||||
func (c *channelPool) Release() {
|
|
||||||
c.mu.Lock()
|
|
||||||
conns := c.conns
|
|
||||||
c.conns = nil
|
|
||||||
c.factory = nil
|
|
||||||
closeFun := c.close
|
|
||||||
c.close = nil
|
|
||||||
c.mu.Unlock()
|
|
||||||
|
|
||||||
if conns == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
close(conns)
|
|
||||||
for wrapConn := range conns {
|
|
||||||
closeFun(wrapConn.conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Len 连接池中已有的连接
|
|
||||||
func (c *channelPool) Len() int {
|
|
||||||
return len(c.getConns())
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package pool
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
var (
|
|
||||||
//ErrClosed 连接池已经关闭Error
|
|
||||||
ErrClosed = errors.New("pool is closed")
|
|
||||||
)
|
|
||||||
|
|
||||||
//Pool 基本方法
|
|
||||||
type Pool interface {
|
|
||||||
Get() (interface{}, error)
|
|
||||||
|
|
||||||
Put(interface{}) error
|
|
||||||
|
|
||||||
Close(interface{}) error
|
|
||||||
|
|
||||||
Release()
|
|
||||||
|
|
||||||
Len() int
|
|
||||||
}
|
|
|
@ -113,10 +113,10 @@
|
||||||
"revision": ""
|
"revision": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "j22mTM0X/UI4kbff6RaPeMNH4XY=",
|
"checksumSHA1": "nzZPoKyx/lv8E/J7zZxuNQ5P1Bk=",
|
||||||
"path": "github.com/jakecoffman/cron",
|
"path": "github.com/jakecoffman/cron",
|
||||||
"revision": "57ac9950da80b6e2c12df9042429278cf8c729eb",
|
"revision": "7e2009c226a5f6fb032692106d167ab0c3720692",
|
||||||
"revisionTime": "2016-09-12T16:42:50Z"
|
"revisionTime": "2019-01-06T20:08:28Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "iKPMvbAueGfdyHcWCgzwKzm8WVo=",
|
"checksumSHA1": "iKPMvbAueGfdyHcWCgzwKzm8WVo=",
|
||||||
|
@ -154,12 +154,6 @@
|
||||||
"revision": "fcdcb7b85139b8b9ca1d8ef1d98d8593e90d117f",
|
"revision": "fcdcb7b85139b8b9ca1d8ef1d98d8593e90d117f",
|
||||||
"revisionTime": "2018-03-30T20:43:14Z"
|
"revisionTime": "2018-03-30T20:43:14Z"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"checksumSHA1": "cVGA2CJTJsCAVa5VKTM8k/ma/BU=",
|
|
||||||
"path": "github.com/silenceper/pool",
|
|
||||||
"revision": "e4c4536403d9765c8ea74cda2ee49dcb0848bc7a",
|
|
||||||
"revisionTime": "2016-11-14T14:21:55Z"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"checksumSHA1": "1keN4Q9F8uHk/Gt5YXG0oCEecKM=",
|
"checksumSHA1": "1keN4Q9F8uHk/Gt5YXG0oCEecKM=",
|
||||||
"path": "github.com/urfave/cli",
|
"path": "github.com/urfave/cli",
|
||||||
|
|
Loading…
Reference in New Issue