Full Code of yangwenmai/ratelimit for AI

master 44221c2292e1 cached
11 files
12.7 KB
4.6k tokens
22 symbols
1 requests
Download .txt
Repository: yangwenmai/ratelimit
Branch: master
Commit: 44221c2292e1
Files: 11
Total size: 12.7 KB

Directory structure:
gitextract_1wr6y951/

├── .gitignore
├── .travis.yml
├── LICENSE
├── Makefile
├── README.md
├── example/
│   └── main.go
├── leakybucket/
│   ├── bucket.go
│   ├── leaky_bucket_test.go
│   └── leaky_memory.go
└── simpleratelimit/
    ├── ratelimit.go
    └── ratelimit_test.go

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
bin
pkg
*.swp
.DS_Store
.idea
*.iml
.vscode/
*.overprofile
*/*.overprofile


================================================
FILE: .travis.yml
================================================
language: go
sudo: false

notifications:
  email:
    recipients:
      - yangwen.yw@gmail.com
  on_success: change
  on_failure: always

go:
  - 1.8.3

install:
  - go get github.com/go-playground/overalls
  - go get github.com/mattn/goveralls
  - go get github.com/smartystreets/goconvey
  - mkdir -p $GOPATH/src/github.com/yangwenmai
  - cd $GOPATH/src/github.com/yangwenmai/ratelimit

script:
    - overalls -project=github.com/yangwenmai/ratelimit -covermode=count -ignore='.git,_vendor'
    - goveralls -coverprofile=overalls.coverprofile -service=travis-ci -repotoken $COVERALLS_TOKEN
    - go test ./...

env:
  global:
    secure: "bopJ2qeaADkDDOTDOPfYiO3mHigtyUBM3Ec8vhc9jkQWQ9Spmd4rXnKjE0ry7ZWTBdLze5Fkgga83/5YuRaecLKYPLtYLXIF1KioJvJiuTlAkMVrlvuuOMR7YN1qq4hHybeWZI/YEvj+fPjm0VRFU7syrLzsvIX0wuhDOrW5fvDOBW/Xn0isZ0UZSLx/AmU5TJp8lYG/WMPDUSyyxMNDOT4Kv0ZExMIXlIEtzFJXSb8yuOAZVIVJ4IiFXjDeMIrc7krzQa/sqQb2OujjjJqrDqdQTcbaPpGL4q+eewXdVhG6+JQlK6pa8K2LfQTERD0fVXcxrF3AXsmOiqh4/aLpez69pqYebiwwm2SK+hcwm2AZiUg2Phrvk0gqyoDRWSEV2AgOeMxLdSWyAWjreA2H3YtbvLpe2RCXiv05WS0BYvUwmFxYUDgq679AzZ1YHZRQTZTtMmCciunQekBLb+P+qbmuyv7h60mTYGrfZLRjoPUreQ0MDqwEjB8f0mFMrq66LPlMThbsvwzUFqtfBbclWP+HVLiRnd0kKFkV0HnBt15dvYQxr80Oad+4TYYJ0WWiuy/LV9D44BDgL59CKOTIu7jd0yjP5gmBO7wJhx0UhZ8f/SZPNX8GXXx7tfmjpM2H9aA84Zf+Bl+kV2/Ra2NiTWeroblsEutv795gLFwF0i4="


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2017 maiyang

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.


================================================
FILE: Makefile
================================================
default: test

testdeps:
	@go get github.com/smartystreets/goconvey

test: testdeps
	@go test ./...


testrace: testdeps
	@go test ./... -race

testall: test testrace

bench:
	@go test ./... -run=NONE -bench=.


================================================
FILE: README.md
================================================
# ratelimit [![Build Status](https://travis-ci.org/yangwenmai/ratelimit.png?branch=master)](https://travis-ci.org/yangwenmai/ratelimit) [![Documentation](https://godoc.org/github.com/yangwenmai/ratelimit?status.svg)](http://godoc.org/github.com/yangwenmai/ratelimit) [![Go Report Card](https://goreportcard.com/badge/github.com/yangwenmai/ratelimit)](https://goreportcard.com/report/github.com/yangwenmai/ratelimit) [![Coverage Status](https://coveralls.io/repos/github/yangwenmai/ratelimit/badge.svg?branch=master)](https://coveralls.io/github/yangwenmai/ratelimit?branch=master)

基于令牌桶算法和漏桶算法来实现的限速限流,Golang实现。

## Stargazers over time

[![Stargazers over time](https://starcharts.herokuapp.com/yangwenmai/ratelimit.svg)](https://starcharts.herokuapp.com/yangwenmai/ratelimit)

# 算法介绍

## 漏桶算法

漏桶算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。

![leaky-bucket](https://github.com/yangwenmai/ratelimit/blob/master/doc/leaky-bucket.png)

漏桶算法示意图

漏桶算法可以很好地限制容量池的大小,从而防止流量暴增。

## 令牌桶算法

令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。

![token-bucket](https://github.com/yangwenmai/ratelimit/blob/master/doc/token-bucket.jpg)

令牌桶算法示意图

令牌桶算法通过发放令牌,根据令牌的rate频率做请求频率限制,容量限制等。

### 示例

```go
package main

import (
	"log"
	"time"

	"github.com/yangwenmai/ratelimit/leakybucket"
	"github.com/yangwenmai/ratelimit/simpleratelimit"
)

func main() {
    // rate limit: simple
	rl := simpleratelimit.New(10, time.Second)

	for i := 0; i < 100; i++ {
		log.Printf("limit result: %v\n", rl.Limit())
	}
	log.Printf("limit result: %v\n", rl.Limit())

    // rate limit: leaky-bucket
	lb := leakybucket.New()
	b, err := lb.Create("leaky_bucket", 10, time.Second)
	if err != nil {
		log.Println(err)
	}
	log.Printf("bucket capacity:%v", b.Capacity())

    // rate limit: token-bucket
}
```

# 参考资料

1. [限流:漏桶算法和令牌桶算法](http://maiyang.github.io/技术/算法/2017/05/28/rate-limit-algorithm)
2. [维基百科:Token_bucket](https://en.wikipedia.org/wiki/Token_bucket)
3. [维基百科:Leaky_bucket](https://en.wikipedia.org/wiki/Leaky_bucket)
4. [接口限流实践](http://www.cnblogs.com/LBSer/p/4083131.html)
5. [流量调整和限流技术](http://colobu.com/2014/11/13/rate-limiting/)


================================================
FILE: example/main.go
================================================
package main

import (
	"log"
	"time"

	"github.com/yangwenmai/ratelimit/leakybucket"
	"github.com/yangwenmai/ratelimit/simpleratelimit"
)

func main() {
	// rate limit: simple
	rl := simpleratelimit.New(10, time.Second)

	for i := 0; i < 100; i++ {
		log.Printf("limit result: %v\n", rl.Limit())
	}
	log.Printf("limit result: %v\n", rl.Limit())

	// rate limit: leaky-bucket
	lb := leakybucket.New()
	b, err := lb.Create("leaky_bucket", 10, time.Second)
	if err != nil {
		log.Println(err)
	}
	log.Printf("bucket capacity:%v", b.Capacity())

	// rate limit: token-bucket
}


================================================
FILE: leakybucket/bucket.go
================================================
package leakybucket

import (
	"errors"
	"time"
)

var (
	// ErrorFull is returned when the amount requested to add exceeds the remaining space in the bucket.
	ErrorFull = errors.New("add exceeds free capacity")
)

// BucketI interface for interacting with leaky buckets: https://en.wikipedia.org/wiki/Leaky_bucket
type BucketI interface {
	// Capacity of the bucket.
	Capacity() uint
	// Remaining space in the bucket.
	Remaining() uint
	// Reset returns when the bucket will be drained.
	Reset() time.Time
	// Add to the bucket. Returns bucket state after adding.
	Add(uint) (BucketState, error)
}

// BucketState is a snapshot of a bucket's properties.
type BucketState struct {
	Capacity  uint
	Remaining uint
	Reset     time.Time
}

// StorageI interface for generating buckets keyed by a string.
type StorageI interface {
	// Create a bucket with a name, capacity, and rate.
	// rate is how long it takes for full capacity to drain.
	Create(name string, capacity uint, rate time.Duration) (BucketI, error)
}


================================================
FILE: leakybucket/leaky_bucket_test.go
================================================
package leakybucket

import (
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestLimit(t *testing.T) {
	Convey("start testing", t, func() {
		Convey("create capacity 100", func() {
			s := New()
			bucket, err := s.Create("testCreateBucket", 100, time.Second)
			if err != nil {
				t.Error(err)
			}
			if capacity := bucket.Capacity(); capacity != 100 {
				t.Fatalf("expected capacity of %d, got %d", 100, capacity)
			}
		})
		Convey("same name", func() {
			s := New()
			bucket, err := s.Create("testSame", 10, time.Second)
			if err != nil {
				t.Error(err)
			}
			bucket2, err := s.Create("testSame", 100, time.Second)
			if err != nil {
				t.Error(err)
			}
			if bucket != bucket2 {
				t.Fatalf("not same.")
			}
		})
		Convey("add()", func() {
			s := New()
			bucket, err := s.Create("testAdd", 100, time.Second)
			if err != nil {
				t.Fatal(err)
			}

			addAndTestRemaining := func(add, remaining uint) {
				if state, err := bucket.Add(add); err != nil {
					t.Fatal(err)
				} else if bucket.Remaining() != state.Remaining {
					t.Fatalf("expected bucket and state remaining to match, bucket is %d, state is %d",
						bucket.Remaining(), state.Remaining)
				} else if state.Remaining != remaining {
					t.Fatalf("expected %d remaining, got %d", remaining, state.Remaining)
				}
			}
			addAndTestRemaining(1, 99)
			addAndTestRemaining(3, 96)
			addAndTestRemaining(90, 6)
			if _, err := bucket.Add(7); err == nil {
				t.Fatalf("expected ErrorFull, received no error")
			} else if err != ErrorFull {
				t.Fatalf("expected ErrorFull, received %v", err)
			}
		})
		Convey("Reset()", func() {
			s := New()
			bucket, err := s.Create("testReset", 1, time.Millisecond)
			if err != nil {
				t.Fatal(err)
			}
			if _, err := bucket.Add(1); err != nil {
				t.Fatal(err)
			}
			time.Sleep(time.Millisecond * 2)
			bucket.Reset()
			if bucket.Remaining() != 1 {
				t.Fatalf("expected 1, got %d", bucket.Remaining())
			}
			state, err := bucket.Add(1)
			if err != nil {
				t.Fatal(err)
			}
			if state.Remaining != 0 {
				t.Fatalf("expected full bucket, got %d", state.Remaining)
			}
			if state.Reset.Unix() < time.Now().Unix() {
				t.Fatalf("reset time is in the past")
			}
			if bucket.Reset().Unix() < time.Now().Unix() {
				t.Fatalf("reset time is in the past")
			}
		})
	})
}

func BenchmarkLimit(b *testing.B) {
	Convey("start testing benchmark", b, func() {
	})
}


================================================
FILE: leakybucket/leaky_memory.go
================================================
package leakybucket

import (
	"sync"
	"time"
)

type bucket struct {
	capacity  uint
	remaining uint
	reset     time.Time
	rate      time.Duration
	mutex     sync.Mutex
}

// Capacity return
func (b *bucket) Capacity() uint {
	return b.capacity
}

// Remaining space in the bucket.
func (b *bucket) Remaining() uint {
	return b.remaining
}

// Reset returns when the bucket will be drained.
func (b *bucket) Reset() time.Time {
	b.remaining = b.capacity
	return b.reset
}

// Add to the bucket.
func (b *bucket) Add(amount uint) (BucketState, error) {
	b.mutex.Lock()
	defer b.mutex.Unlock()
	if time.Now().After(b.reset) {
		b.reset = time.Now().Add(b.rate)
		b.remaining = b.capacity
	}
	if amount > b.remaining {
		return BucketState{Capacity: b.capacity, Remaining: b.remaining, Reset: b.reset}, ErrorFull
	}
	b.remaining -= amount
	return BucketState{Capacity: b.capacity, Remaining: b.remaining, Reset: b.reset}, nil
}

// Storage is a non thread-safe in-memory leaky bucket factory.
type Storage struct {
	buckets map[string]*bucket
}

// New initializes the in-memory bucket store.
func New() *Storage {
	return &Storage{
		buckets: make(map[string]*bucket),
	}
}

// Create a bucket.
func (s *Storage) Create(name string, capacity uint, rate time.Duration) (BucketI, error) {
	b, ok := s.buckets[name]
	if ok {
		return b, nil
	}
	b = &bucket{
		capacity:  capacity,
		remaining: capacity,
		reset:     time.Now().Add(rate),
		rate:      rate,
	}
	s.buckets[name] = b
	return b, nil
}


================================================
FILE: simpleratelimit/ratelimit.go
================================================
package simpleratelimit

import (
	"sync/atomic"
	"time"
)

// RateLimiter 限速器
type RateLimiter struct {
	rate      uint64
	allowance uint64
	max       uint64
	unit      uint64
	lastCheck uint64
}

// New 创建RateLimiter实例
func New(rate int, per time.Duration) *RateLimiter {
	nano := uint64(per)
	if nano < 1 {
		nano = uint64(time.Second)
	}
	if rate < 1 {
		rate = 1
	}

	return &RateLimiter{
		rate:      uint64(rate),
		allowance: uint64(rate) * nano,
		max:       uint64(rate) * nano,
		unit:      nano,

		lastCheck: unixNano(),
	}
}

// Limit 判断是否超过限制
func (rl *RateLimiter) Limit() bool {
	now := unixNano()
	// 计算上一次调用到现在过了多少纳秒
	passed := now - atomic.SwapUint64(&rl.lastCheck, now)

	rate := atomic.LoadUint64(&rl.rate)
	current := atomic.AddUint64(&rl.allowance, passed*rate)

	if max := atomic.LoadUint64(&rl.max); current > max {
		atomic.AddUint64(&rl.allowance, max-current)
		current = max
	}

	if current < rl.unit {
		return true
	}

	// 没有超过限额
	atomic.AddUint64(&rl.allowance, -rl.unit)
	return false
}

// UpdateRate 更新速率值
func (rl *RateLimiter) UpdateRate(rate int) {
	atomic.StoreUint64(&rl.rate, uint64(rate))
	atomic.StoreUint64(&rl.max, uint64(rate)*rl.unit)
}

// Undo 重置上一次调用Limit(),返回没有使用过的限额
func (rl *RateLimiter) Undo() {
	current := atomic.AddUint64(&rl.allowance, rl.unit)

	if max := atomic.LoadUint64(&rl.max); current > max {
		atomic.AddUint64(&rl.allowance, max-current)
	}
}

// unixNano 当前时间(纳秒)
func unixNano() uint64 {
	return uint64(time.Now().UnixNano())
}


================================================
FILE: simpleratelimit/ratelimit_test.go
================================================
package simpleratelimit

import (
	"testing"
	"time"

	. "github.com/smartystreets/goconvey/convey"
)

func TestLimit(t *testing.T) {
	Convey("start testing New(0, 0)", t, func() {
		rl := New(0, 0*time.Second)
		So(rl.Limit(), ShouldEqual, false)
	})
	Convey("start testing limit", t, func() {
		rl := New(1, time.Second)
		for i := 0; i < 10; i++ {
			if i == 0 {
				So(rl.Limit(), ShouldEqual, false)
			} else {
				So(rl.Limit(), ShouldEqual, true)
			}
		}
	})
	Convey("start testing updateRate", t, func() {
		rl := New(1, time.Second)
		for i := 0; i < 2; i++ {
			if i == 0 {
				So(rl.Limit(), ShouldEqual, false)
			} else {
				rl.UpdateRate(2)
				So(rl.Limit(), ShouldEqual, true)
			}
		}
	})
	Convey("start testing undo", t, func() {
		rl := New(1, time.Second)
		for i := 0; i < 10; i++ {
			if i == 0 {
				So(rl.Limit(), ShouldEqual, false)
			} else {
				rl.Undo()
				So(rl.Limit(), ShouldEqual, false)
			}
		}
		for i := 0; i < 10; i++ {
			rl.Limit()
		}
		rl.Undo()
	})
}

func BenchmarkLimit(b *testing.B) {
	Convey("start testing benchmark", b, func() {
		rl := New(1, time.Second)
		for i := 0; i < b.N; i++ {
			rl.Limit()
		}
	})
}
Download .txt
gitextract_1wr6y951/

├── .gitignore
├── .travis.yml
├── LICENSE
├── Makefile
├── README.md
├── example/
│   └── main.go
├── leakybucket/
│   ├── bucket.go
│   ├── leaky_bucket_test.go
│   └── leaky_memory.go
└── simpleratelimit/
    ├── ratelimit.go
    └── ratelimit_test.go
Download .txt
SYMBOL INDEX (22 symbols across 6 files)

FILE: example/main.go
  function main (line 11) | func main() {

FILE: leakybucket/bucket.go
  type BucketI (line 14) | type BucketI interface
  type BucketState (line 26) | type BucketState struct
  type StorageI (line 33) | type StorageI interface

FILE: leakybucket/leaky_bucket_test.go
  function TestLimit (line 10) | func TestLimit(t *testing.T) {
  function BenchmarkLimit (line 93) | func BenchmarkLimit(b *testing.B) {

FILE: leakybucket/leaky_memory.go
  type bucket (line 8) | type bucket struct
    method Capacity (line 17) | func (b *bucket) Capacity() uint {
    method Remaining (line 22) | func (b *bucket) Remaining() uint {
    method Reset (line 27) | func (b *bucket) Reset() time.Time {
    method Add (line 33) | func (b *bucket) Add(amount uint) (BucketState, error) {
  type Storage (line 48) | type Storage struct
    method Create (line 60) | func (s *Storage) Create(name string, capacity uint, rate time.Duratio...
  function New (line 53) | func New() *Storage {

FILE: simpleratelimit/ratelimit.go
  type RateLimiter (line 9) | type RateLimiter struct
    method Limit (line 38) | func (rl *RateLimiter) Limit() bool {
    method UpdateRate (line 61) | func (rl *RateLimiter) UpdateRate(rate int) {
    method Undo (line 67) | func (rl *RateLimiter) Undo() {
  function New (line 18) | func New(rate int, per time.Duration) *RateLimiter {
  function unixNano (line 76) | func unixNano() uint64 {

FILE: simpleratelimit/ratelimit_test.go
  function TestLimit (line 10) | func TestLimit(t *testing.T) {
  function BenchmarkLimit (line 53) | func BenchmarkLimit(b *testing.B) {
Condensed preview — 11 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (15K chars).
[
  {
    "path": ".gitignore",
    "chars": 75,
    "preview": "bin\npkg\n*.swp\n.DS_Store\n.idea\n*.iml\n.vscode/\n*.overprofile\n*/*.overprofile\n"
  },
  {
    "path": ".travis.yml",
    "chars": 1327,
    "preview": "language: go\nsudo: false\n\nnotifications:\n  email:\n    recipients:\n      - yangwen.yw@gmail.com\n  on_success: change\n  on"
  },
  {
    "path": "LICENSE",
    "chars": 1064,
    "preview": "MIT License\n\nCopyright (c) 2017 maiyang\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
  },
  {
    "path": "Makefile",
    "chars": 210,
    "preview": "default: test\n\ntestdeps:\n\t@go get github.com/smartystreets/goconvey\n\ntest: testdeps\n\t@go test ./...\n\n\ntestrace: testdeps"
  },
  {
    "path": "README.md",
    "chars": 2163,
    "preview": "# ratelimit [![Build Status](https://travis-ci.org/yangwenmai/ratelimit.png?branch=master)](https://travis-ci.org/yangwe"
  },
  {
    "path": "example/main.go",
    "chars": 574,
    "preview": "package main\n\nimport (\n\t\"log\"\n\t\"time\"\n\n\t\"github.com/yangwenmai/ratelimit/leakybucket\"\n\t\"github.com/yangwenmai/ratelimit/"
  },
  {
    "path": "leakybucket/bucket.go",
    "chars": 1014,
    "preview": "package leakybucket\n\nimport (\n\t\"errors\"\n\t\"time\"\n)\n\nvar (\n\t// ErrorFull is returned when the amount requested to add exce"
  },
  {
    "path": "leakybucket/leaky_bucket_test.go",
    "chars": 2438,
    "preview": "package leakybucket\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/smartystreets/goconvey/convey\"\n)\n\nfunc TestLimit(t *tes"
  },
  {
    "path": "leakybucket/leaky_memory.go",
    "chars": 1495,
    "preview": "package leakybucket\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype bucket struct {\n\tcapacity  uint\n\tremaining uint\n\treset     time.Ti"
  },
  {
    "path": "simpleratelimit/ratelimit.go",
    "chars": 1500,
    "preview": "package simpleratelimit\n\nimport (\n\t\"sync/atomic\"\n\t\"time\"\n)\n\n// RateLimiter 限速器\ntype RateLimiter struct {\n\trate      uint"
  },
  {
    "path": "simpleratelimit/ratelimit_test.go",
    "chars": 1164,
    "preview": "package simpleratelimit\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t. \"github.com/smartystreets/goconvey/convey\"\n)\n\nfunc TestLimit(t "
  }
]

About this extraction

This page contains the full source code of the yangwenmai/ratelimit GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 11 files (12.7 KB), approximately 4.6k tokens, and a symbol index with 22 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!