Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View 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"],
)

View 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
}

View 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()
}
})
}