Create & Init Project...
This commit is contained in:
39
library/container/queue/aqm/BUILD
Normal file
39
library/container/queue/aqm/BUILD
Normal file
@ -0,0 +1,39 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["codel_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
rundir = ".",
|
||||
tags = ["automanaged"],
|
||||
deps = ["//library/ecode:go_default_library"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["codel.go"],
|
||||
importpath = "go-common/library/container/queue/aqm",
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//library/ecode:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
185
library/container/queue/aqm/codel.go
Normal file
185
library/container/queue/aqm/codel.go
Normal file
@ -0,0 +1,185 @@
|
||||
package aqm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go-common/library/ecode"
|
||||
)
|
||||
|
||||
// Config codel config.
|
||||
type Config struct {
|
||||
Target int64 // target queue delay (default 20 ms).
|
||||
Internal int64 // sliding minimum time window width (default 500 ms)
|
||||
}
|
||||
|
||||
// Stat is the Statistics of codel.
|
||||
type Stat struct {
|
||||
Dropping bool
|
||||
FaTime int64
|
||||
DropNext int64
|
||||
Packets int
|
||||
}
|
||||
|
||||
type packet struct {
|
||||
ch chan bool
|
||||
ts int64
|
||||
}
|
||||
|
||||
var defaultConf = &Config{
|
||||
Target: 50,
|
||||
Internal: 500,
|
||||
}
|
||||
|
||||
// Queue queue is CoDel req buffer queue.
|
||||
type Queue struct {
|
||||
pool sync.Pool
|
||||
packets chan packet
|
||||
|
||||
mux sync.RWMutex
|
||||
conf *Config
|
||||
count int64
|
||||
dropping bool // Equal to 1 if in drop state
|
||||
faTime int64 // Time when we'll declare we're above target (0 if below)
|
||||
dropNext int64 // Packets dropped since going into drop state
|
||||
}
|
||||
|
||||
// Default new a default codel queue.
|
||||
func Default() *Queue {
|
||||
return New(defaultConf)
|
||||
}
|
||||
|
||||
// New new codel queue.
|
||||
func New(conf *Config) *Queue {
|
||||
if conf == nil {
|
||||
conf = defaultConf
|
||||
}
|
||||
q := &Queue{
|
||||
packets: make(chan packet, 2048),
|
||||
conf: conf,
|
||||
}
|
||||
q.pool.New = func() interface{} {
|
||||
return make(chan bool)
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// Reload set queue config.
|
||||
func (q *Queue) Reload(c *Config) {
|
||||
if c == nil || c.Internal <= 0 || c.Target <= 0 {
|
||||
return
|
||||
}
|
||||
// TODO codel queue size
|
||||
q.mux.Lock()
|
||||
q.conf = c
|
||||
q.mux.Unlock()
|
||||
}
|
||||
|
||||
// Stat return the statistics of codel
|
||||
func (q *Queue) Stat() Stat {
|
||||
q.mux.Lock()
|
||||
defer q.mux.Unlock()
|
||||
return Stat{
|
||||
Dropping: q.dropping,
|
||||
FaTime: q.faTime,
|
||||
DropNext: q.dropNext,
|
||||
Packets: len(q.packets),
|
||||
}
|
||||
}
|
||||
|
||||
// Push req into CoDel request buffer queue.
|
||||
// if return error is nil,the caller must call q.Done() after finish request handling
|
||||
func (q *Queue) Push(ctx context.Context) (err error) {
|
||||
r := packet{
|
||||
ch: q.pool.Get().(chan bool),
|
||||
ts: time.Now().UnixNano() / int64(time.Millisecond),
|
||||
}
|
||||
select {
|
||||
case q.packets <- r:
|
||||
default:
|
||||
err = ecode.LimitExceed
|
||||
q.pool.Put(r.ch)
|
||||
}
|
||||
if err == nil {
|
||||
select {
|
||||
case drop := <-r.ch:
|
||||
if drop {
|
||||
err = ecode.LimitExceed
|
||||
}
|
||||
q.pool.Put(r.ch)
|
||||
case <-ctx.Done():
|
||||
err = ecode.Deadline
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Pop req from CoDel request buffer queue.
|
||||
func (q *Queue) Pop() {
|
||||
for {
|
||||
select {
|
||||
case p := <-q.packets:
|
||||
drop := q.judge(p)
|
||||
select {
|
||||
case p.ch <- drop:
|
||||
if !drop {
|
||||
return
|
||||
}
|
||||
default:
|
||||
q.pool.Put(p.ch)
|
||||
}
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (q *Queue) controlLaw(now int64) int64 {
|
||||
q.dropNext = now + int64(float64(q.conf.Internal)/math.Sqrt(float64(q.count)))
|
||||
return q.dropNext
|
||||
}
|
||||
|
||||
// judge decide if the packet should drop or not.
|
||||
func (q *Queue) judge(p packet) (drop bool) {
|
||||
now := time.Now().UnixNano() / int64(time.Millisecond)
|
||||
sojurn := now - p.ts
|
||||
q.mux.Lock()
|
||||
defer q.mux.Unlock()
|
||||
if sojurn < q.conf.Target {
|
||||
q.faTime = 0
|
||||
} else if q.faTime == 0 {
|
||||
q.faTime = now + q.conf.Internal
|
||||
} else if now >= q.faTime {
|
||||
drop = true
|
||||
}
|
||||
if q.dropping {
|
||||
if !drop {
|
||||
// sojourn time below target - leave dropping state
|
||||
q.dropping = false
|
||||
} else if now > q.dropNext {
|
||||
q.count++
|
||||
q.dropNext = q.controlLaw(q.dropNext)
|
||||
drop = true
|
||||
return
|
||||
}
|
||||
} else if drop && (now-q.dropNext < q.conf.Internal || now-q.faTime >= q.conf.Internal) {
|
||||
q.dropping = true
|
||||
// If we're in a drop cycle, the drop rate that controlled the queue
|
||||
// on the last cycle is a good starting point to control it now.
|
||||
if now-q.dropNext < q.conf.Internal {
|
||||
if q.count > 2 {
|
||||
q.count = q.count - 2
|
||||
} else {
|
||||
q.count = 1
|
||||
}
|
||||
} else {
|
||||
q.count = 1
|
||||
}
|
||||
q.dropNext = q.controlLaw(now)
|
||||
drop = true
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
97
library/container/queue/aqm/codel_test.go
Normal file
97
library/container/queue/aqm/codel_test.go
Normal file
@ -0,0 +1,97 @@
|
||||
package aqm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go-common/library/ecode"
|
||||
)
|
||||
|
||||
var testConf = &Config{
|
||||
Target: 20,
|
||||
Internal: 500,
|
||||
}
|
||||
|
||||
var qps = time.Microsecond * 2000
|
||||
|
||||
func TestCoDel1200(t *testing.T) {
|
||||
q := New(testConf)
|
||||
drop := new(int64)
|
||||
tm := new(int64)
|
||||
delay := time.Millisecond * 3000
|
||||
testPush(q, qps, delay, drop, tm)
|
||||
fmt.Printf("qps %v process time %v drop %d timeout %d \n", int64(time.Second/qps), delay, *drop, *tm)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
|
||||
func TestCoDel200(t *testing.T) {
|
||||
q := New(testConf)
|
||||
drop := new(int64)
|
||||
tm := new(int64)
|
||||
delay := time.Millisecond * 2000
|
||||
testPush(q, qps, delay, drop, tm)
|
||||
fmt.Printf("qps %v process time %v drop %d timeout %d \n", int64(time.Second/qps), delay, *drop, *tm)
|
||||
time.Sleep(time.Second)
|
||||
|
||||
}
|
||||
|
||||
func TestCoDel100(t *testing.T) {
|
||||
q := New(testConf)
|
||||
drop := new(int64)
|
||||
tm := new(int64)
|
||||
delay := time.Millisecond * 1000
|
||||
testPush(q, qps, delay, drop, tm)
|
||||
fmt.Printf("qps %v process time %v drop %d timeout %d \n", int64(time.Second/qps), delay, *drop, *tm)
|
||||
|
||||
}
|
||||
|
||||
func TestCoDel50(t *testing.T) {
|
||||
q := New(testConf)
|
||||
drop := new(int64)
|
||||
tm := new(int64)
|
||||
delay := time.Millisecond * 500
|
||||
testPush(q, qps, delay, drop, tm)
|
||||
fmt.Printf("qps %v process time %v drop %d timeout %d \n", int64(time.Second/qps), delay, *drop, *tm)
|
||||
}
|
||||
|
||||
func testPush(q *Queue, sleep time.Duration, delay time.Duration, drop *int64, tm *int64) {
|
||||
var group sync.WaitGroup
|
||||
for i := 0; i < 5000; i++ {
|
||||
time.Sleep(sleep)
|
||||
group.Add(1)
|
||||
go func() {
|
||||
defer group.Done()
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Millisecond*1000))
|
||||
defer cancel()
|
||||
if err := q.Push(ctx); err != nil {
|
||||
if err == ecode.LimitExceed {
|
||||
atomic.AddInt64(drop, 1)
|
||||
} else {
|
||||
atomic.AddInt64(tm, 1)
|
||||
}
|
||||
} else {
|
||||
time.Sleep(delay)
|
||||
q.Pop()
|
||||
}
|
||||
}()
|
||||
}
|
||||
group.Wait()
|
||||
}
|
||||
|
||||
func BenchmarkAQM(b *testing.B) {
|
||||
q := Default()
|
||||
b.RunParallel(func(p *testing.PB) {
|
||||
for p.Next() {
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Millisecond*5))
|
||||
err := q.Push(ctx)
|
||||
if err == nil {
|
||||
q.Pop()
|
||||
}
|
||||
cancel()
|
||||
}
|
||||
})
|
||||
}
|
Reference in New Issue
Block a user