mirror of https://github.com/Xhofe/alist
feat(aliyundrive_open): limit rate for `List` and `Link` (close #4290)
parent
822be17fb9
commit
c77ed5fcb0
|
@ -21,6 +21,9 @@ type AliyundriveOpen struct {
|
||||||
base string
|
base string
|
||||||
|
|
||||||
DriveId string
|
DriveId string
|
||||||
|
|
||||||
|
limitList func(ctx context.Context, dir model.Obj) ([]model.Obj, error)
|
||||||
|
limitLink func(ctx context.Context, file model.Obj) (*model.Link, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *AliyundriveOpen) Config() driver.Config {
|
func (d *AliyundriveOpen) Config() driver.Config {
|
||||||
|
@ -37,6 +40,8 @@ func (d *AliyundriveOpen) Init(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
d.DriveId = utils.Json.Get(res, "default_drive_id").ToString()
|
d.DriveId = utils.Json.Get(res, "default_drive_id").ToString()
|
||||||
|
d.limitList = utils.LimitRateCtx(d.list, time.Second/4)
|
||||||
|
d.limitLink = utils.LimitRateCtx(d.link, time.Second)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +49,7 @@ func (d *AliyundriveOpen) Drop(ctx context.Context) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *AliyundriveOpen) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
func (d *AliyundriveOpen) list(ctx context.Context, dir model.Obj) ([]model.Obj, error) {
|
||||||
files, err := d.getFiles(dir.GetID())
|
files, err := d.getFiles(dir.GetID())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -54,7 +59,11 @@ func (d *AliyundriveOpen) List(ctx context.Context, dir model.Obj, args model.Li
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *AliyundriveOpen) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
func (d *AliyundriveOpen) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
|
||||||
|
return d.limitList(ctx, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) link(ctx context.Context, file model.Obj) (*model.Link, error) {
|
||||||
res, err := d.request("/adrive/v1.0/openFile/getDownloadUrl", http.MethodPost, func(req *resty.Request) {
|
res, err := d.request("/adrive/v1.0/openFile/getDownloadUrl", http.MethodPost, func(req *resty.Request) {
|
||||||
req.SetBody(base.Json{
|
req.SetBody(base.Json{
|
||||||
"drive_id": d.DriveId,
|
"drive_id": d.DriveId,
|
||||||
|
@ -73,6 +82,10 @@ func (d *AliyundriveOpen) Link(ctx context.Context, file model.Obj, args model.L
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (d *AliyundriveOpen) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
|
||||||
|
return d.limitLink(ctx, file)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *AliyundriveOpen) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
func (d *AliyundriveOpen) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
|
||||||
_, err := d.request("/adrive/v1.0/openFile/create", http.MethodPost, func(req *resty.Request) {
|
_, err := d.request("/adrive/v1.0/openFile/create", http.MethodPost, func(req *resty.Request) {
|
||||||
req.SetBody(base.Json{
|
req.SetBody(base.Json{
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func LimitRateReflect(f interface{}, interval time.Duration) func(...interface{}) []interface{} {
|
||||||
|
// Use closures to save the time of the last function call
|
||||||
|
var lastCall time.Time
|
||||||
|
|
||||||
|
fValue := reflect.ValueOf(f)
|
||||||
|
fType := fValue.Type()
|
||||||
|
|
||||||
|
if fType.Kind() != reflect.Func {
|
||||||
|
panic("f must be a function")
|
||||||
|
}
|
||||||
|
|
||||||
|
//if fType.NumOut() == 0 {
|
||||||
|
// panic("f must have at least one output parameter")
|
||||||
|
//}
|
||||||
|
|
||||||
|
outCount := fType.NumOut()
|
||||||
|
outTypes := make([]reflect.Type, outCount)
|
||||||
|
|
||||||
|
for i := 0; i < outCount; i++ {
|
||||||
|
outTypes[i] = fType.Out(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a new function, which is used to limit the function to be called only once at a specified time interval
|
||||||
|
return func(args ...interface{}) []interface{} {
|
||||||
|
// Calculate the time interval since the last function call
|
||||||
|
elapsed := time.Since(lastCall)
|
||||||
|
// If the interval is less than the specified time, wait for the remaining time
|
||||||
|
if elapsed < interval {
|
||||||
|
time.Sleep(interval - elapsed)
|
||||||
|
}
|
||||||
|
// Update the time of the last function call
|
||||||
|
lastCall = time.Now()
|
||||||
|
|
||||||
|
inCount := fType.NumIn()
|
||||||
|
in := make([]reflect.Value, inCount)
|
||||||
|
|
||||||
|
if len(args) != inCount {
|
||||||
|
panic("wrong number of arguments")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < inCount; i++ {
|
||||||
|
in[i] = reflect.ValueOf(args[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
out := fValue.Call(in)
|
||||||
|
|
||||||
|
if len(out) != outCount {
|
||||||
|
panic("function returned wrong number of values")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]interface{}, outCount)
|
||||||
|
|
||||||
|
for i := 0; i < outCount; i++ {
|
||||||
|
result[i] = out[i].Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Fn[T any, R any] func(T) (R, error)
|
||||||
|
type FnCtx[T any, R any] func(context.Context, T) (R, error)
|
||||||
|
|
||||||
|
func LimitRate[T any, R any](f Fn[T, R], interval time.Duration) Fn[T, R] {
|
||||||
|
// Use closures to save the time of the last function call
|
||||||
|
var lastCall time.Time
|
||||||
|
// Returns a new function, which is used to limit the function to be called only once at a specified time interval
|
||||||
|
return func(t T) (R, error) {
|
||||||
|
// Calculate the time interval since the last function call
|
||||||
|
elapsed := time.Since(lastCall)
|
||||||
|
// If the interval is less than the specified time, wait for the remaining time
|
||||||
|
if elapsed < interval {
|
||||||
|
time.Sleep(interval - elapsed)
|
||||||
|
}
|
||||||
|
// Update the time of the last function call
|
||||||
|
lastCall = time.Now()
|
||||||
|
// Execute the function that needs to be limited
|
||||||
|
return f(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LimitRateCtx[T any, R any](f FnCtx[T, R], interval time.Duration) FnCtx[T, R] {
|
||||||
|
// Use closures to save the time of the last function call
|
||||||
|
var lastCall time.Time
|
||||||
|
// Returns a new function, which is used to limit the function to be called only once at a specified time interval
|
||||||
|
return func(ctx context.Context, t T) (R, error) {
|
||||||
|
// Calculate the time interval since the last function call
|
||||||
|
elapsed := time.Since(lastCall)
|
||||||
|
// If the interval is less than the specified time, wait for the remaining time
|
||||||
|
if elapsed < interval {
|
||||||
|
t := time.NewTimer(interval - elapsed)
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
t.Stop()
|
||||||
|
var zero R
|
||||||
|
return zero, ctx.Err()
|
||||||
|
case <-t.C:
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update the time of the last function call
|
||||||
|
lastCall = time.Now()
|
||||||
|
// Execute the function that needs to be limited
|
||||||
|
return f(ctx, t)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package utils_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/alist-org/alist/v3/pkg/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func myFunction(a int) (int, error) {
|
||||||
|
// do something
|
||||||
|
return a + 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLimitRate(t *testing.T) {
|
||||||
|
myLimitedFunction := utils.LimitRate(myFunction, time.Second)
|
||||||
|
result, _ := myLimitedFunction(1)
|
||||||
|
t.Log(result) // Output: 2
|
||||||
|
result, _ = myLimitedFunction(2)
|
||||||
|
t.Log(result) // Output: 3
|
||||||
|
}
|
||||||
|
|
||||||
|
type Test struct {
|
||||||
|
limitFn func(string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Test) myFunction(a string) (string, error) {
|
||||||
|
// do something
|
||||||
|
return a + " world", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLimitRateStruct(t *testing.T) {
|
||||||
|
test := &Test{}
|
||||||
|
test.limitFn = utils.LimitRate(test.myFunction, time.Second)
|
||||||
|
result, _ := test.limitFn("hello")
|
||||||
|
t.Log(result) // Output: hello world
|
||||||
|
result, _ = test.limitFn("hi")
|
||||||
|
t.Log(result) // Output: hi world
|
||||||
|
}
|
||||||
|
|
||||||
|
func myFunctionCtx(ctx context.Context, a int) (int, error) {
|
||||||
|
// do something
|
||||||
|
return a + 1, nil
|
||||||
|
}
|
||||||
|
func TestLimitRateCtx(t *testing.T) {
|
||||||
|
myLimitedFunction := utils.LimitRateCtx(myFunctionCtx, time.Second)
|
||||||
|
result, _ := myLimitedFunction(context.Background(), 1)
|
||||||
|
t.Log(result) // Output: 2
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
go func() {
|
||||||
|
time.Sleep(500 * time.Millisecond)
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
result, err := myLimitedFunction(ctx, 2)
|
||||||
|
t.Log(result, err) // Output: 0 context canceled
|
||||||
|
result, _ = myLimitedFunction(context.Background(), 3)
|
||||||
|
t.Log(result) // Output: 4
|
||||||
|
}
|
Loading…
Reference in New Issue