go 代码片段

1、多协程顺序打印A、B、C

//(1)使用channel控制打印次数


func main() {
	var wg sync.WaitGroup
	wg.Add(3)
	a := make(chan bool, 1)
	b := make(chan bool, 1)
	c := make(chan bool, 1)
	flag := make(chan int, 1)
	flag <- 0
	a <- true
	defer cancel()
	go func() {
		defer func() {
			wg.Done()
		}()
		for {
			select {
			case <- a:
				f := <- flag
				if  f > 10 {
					wg.Done()
					wg.Done()
					return
				} else {
					flag <- f + 1
					fmt.Println(f)
				}
				fmt.Println("A")
				b <- true
			}
		}
	}()
	go func() {
		defer func() {
			wg.Done()
		}()
		for {
			select {
			case <- b:
				f := <- flag
				if f > 10 {
					wg.Done()
					wg.Done()
					return
				} else {
					flag <- f + 1
					fmt.Println(f)
				}
				fmt.Println("B")
				c <- true
			}
		}
	}()
	go func() {
		defer func() {
			wg.Done()
		}()
		for {
			select {
			case <- c:
				f := <- flag
				if f > 10 {
					wg.Done()
					wg.Done()
					return
				} else {
					flag <- f + 1
					fmt.Println(f)
				}
				fmt.Println("C")
				a <- true
			}
		}
	}()
	wg.Wait()
	return
}

(2)使用atomic原子锁控制打印次数

func main() {
	var wg sync.WaitGroup
	wg.Add(3)
	var count int32
	a := make(chan bool, 1)
	b := make(chan bool, 1)
	c := make(chan bool, 1)
	a <- true
	go func() {
		defer func() {
			wg.Done()
		}()
		for count < 10 {
			atomic.AddInt32(&count, 1)
			<- a
			fmt.Println("A")
			b <- true
		}
	}()
	go func() {
		defer func() {
			wg.Done()
		}()
		for count < 10 {
			atomic.AddInt32(&count, 1)
			<- b
			fmt.Println("B")
			c <- true
		}
	}()
	go func() {
		defer func() {
			wg.Done()
		}()
		for count < 10 {
			atomic.AddInt32(&count, 1)
			<- c
			fmt.Println("C")
			a <- true
		}
	}()
	wg.Wait()

	return
}

(3)使用context控制打印时长

func main() {
	var wg sync.WaitGroup
	wg.Add(3)
	a := make(chan bool, 1)
	b := make(chan bool, 1)
	c := make(chan bool, 1)
	a <- true
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, time.Second * 1)
	defer cancel()
	go func() {
		defer func() {
			wg.Done()
		}()
		for {
			select {
			case <-ctx.Done():
				return
			default:
				<-a
				fmt.Println("A")
				b <- true
			}
		}
	}()
	go func() {
		defer func() {
			wg.Done()
		}()
		for {
			select {
			case <-ctx.Done():
				return
			default:
				<- b
				fmt.Println("B")
				c <- true
			}
		}
	}()
	go func() {
		defer func() {
			wg.Done()
		}()
		for {
			select {
			case <-ctx.Done():
				return
			default:
				<- c
				fmt.Println("C")
				a <- true
			}
		}
	}()
	wg.Wait()
	return
}

2. 接口限流的常见做法

(1)固定窗口: 在一定时间内设置处理次数上限,定时重置

package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

type FixedWindow struct {
	size        time.Duration // 窗口大小
	maxRequests int           // 最大请求数
	requests    int           // 当前窗口内的请求数
	lastReset   int64         // 上次窗口重置时间(秒级时间戳)
	mutex       sync.Mutex    // 重置锁
}

func NewFixedWindow(size time.Duration, maxRequests int) *FixedWindow {
	return &FixedWindow{
		size:        size,
		maxRequests: maxRequests,
		lastReset:   time.Now().Unix(),
	}
}

func(window *FixedWindow) AllowRequest(ctx context.Context, now time.Time) bool {
	window.mutex.Lock()
	defer window.mutex.Unlock()

	for{
		select {
		case <- ctx.Done():
			return false
		default:
			// 时间间隔是否满足重置条件
			if now.Unix() - window.lastReset >= int64(window.size.Seconds()) {
				window.requests = 0
				window.lastReset = now.Unix()
			}
			// 窗口内请求数是否超过最大值
			if window.requests >= window.maxRequests {
				return false
			}
			window.requests++
			return  true
		}
	}
}

func main() {
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, time.Second * 2)
	defer cancel()
	window := NewFixedWindow(time.Second * 1,5)
	for {
		select {
		case <-ctx.Done():
			return
		default:
			now := time.Now()
			format := now.Format("15:04:05.0000000")
			if window.AllowRequest(ctx, now) {
				fmt.Println(format + " 请求已执行")
			} else {
				fmt.Println(format + " 请求被限流,无法执行")
			}
			time.Sleep(100 * time.Millisecond)
		}
	}
}

(2)滑动窗口:设定单位时间内请求数上限,清除不在窗口内的请求

package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

type slidingWindow struct {
	size        time.Duration // 窗口大小
	maxRequests int           // 最大请求数
	requests    []time.Time   // 当前窗口内的请求时间
	mutex       sync.Mutex    // 请求锁
}

func NewSlidingWindow(windowSize time.Duration, maxRequests int) *slidingWindow {
	return &slidingWindow{
		size:  windowSize,
		maxRequests: maxRequests,
		requests:    make([]time.Time, 0),
	}
}

func (window *slidingWindow) AllowRequest(ctx context.Context, now time.Time) bool {
	window.mutex.Lock()
	defer window.mutex.Unlock()
    for{
		select {
		case <- ctx.Done():
			return false
		default:
			// 移除不在窗口时间内的请求
			for len(window.requests) > 0 && now.Sub(window.requests[0]) > window.size {
				window.requests = window.requests[1:]
			}

			// 窗口内请求数是否超过最大值
			if len(window.requests) >= window.maxRequests {
				return false
			}

			window.requests = append(window.requests, now)
			return  true
		}
	}
}

func main() {
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, 3 * time.Second)
	defer cancel()
	window := NewSlidingWindow(1 * time.Second, 4) // 每秒最多允许4个请求
	for{
		select {
		case <- ctx.Done():
			return
		default:
			now := time.Now()
			format :=  now.Format("15:04:05.0000000")
			if window.AllowRequest(ctx, now) {
				fmt.Println(format + " 请求已执行")
			} else {
				fmt.Println(format + " 请求被限流,无法执行")
			}
			time.Sleep(100 * time.Millisecond)
		}
	}
}


(3)漏桶:漏桶内容量未满即可处理请求

package main

import (
	"context"
	"fmt"
	"time"
)

type LeakyBucket struct {
	rate          float64 // 漏桶速率,个/秒
	capacity      int     // 漏桶容量
	standing      int     // 桶内水量
	lastLeakyTime int64   // 上次漏水的时间戳
}

func NewLeakyBucket(rate float64, cap int) *LeakyBucket {
	return &LeakyBucket{
		rate:          rate,
		capacity:      cap,
		standing:      0,
		lastLeakyTime: time.Now().Unix(),
	}
}

// AllowRequest 检查是否处理本次请求,是就返回true,否则返回false
func (bucket *LeakyBucket) AllowRequest(ctx context.Context, now time.Time) bool {
	for {
		select {
		case <-ctx.Done():
			return false
		default:
			// 漏水,根据时间间隔计算漏掉的水量
			time := now.Unix() - bucket.lastLeakyTime
			amount := int(float64(time) * bucket.rate)
			fmt.Printf("amount: %d\n", amount)
			if amount > 0 {
				// 更新桶内水量
				bucket.standing -= amount
				if bucket.standing < 0 {
					bucket.standing = 0
				}
			}

			// 判断当前存量是否超过容量
			if bucket.standing >= bucket.capacity {
				return false
			}

			bucket.standing++
			bucket.lastLeakyTime = now.Unix()
			return true
		}
	}
}

func main() {
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, time.Second*2)
	defer cancel()
	bucket := NewLeakyBucket(4, 5)
	for {
		select {
		case <-ctx.Done():
			return
		default:
			now := time.Now()
			format := now.Format("15:04:05.0000000")
			if bucket.AllowRequest(ctx, now) {
				fmt.Println(format + " 请求已执行")
			} else {
				fmt.Println(format + " 请求被限流,无法执行")
			}
			time.Sleep(100 * time.Millisecond)
		}
	}
}

(4)令牌桶:以固定速率向桶内补充令牌,请求到来时根据桶内令牌判断是否能处理

package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

type TokenBucket struct {
	rate         float64    // 令牌添加到桶中的速率,个/秒
	capacity     int        // 令牌桶容量
	standing     int        // 令牌桶内令牌数量
	lastDripTime int64      // 上次更新令牌数量的时间
	mutex        sync.Mutex // 互斥锁
}

func NewTokenBucket(rate float64, cap int) *TokenBucket {
	return &TokenBucket{
		rate:         rate,
		capacity:     cap,
		standing:     0,
		lastDripTime: time.Now().Unix(),
	}
}

// AllowRequest 检查是否可以从桶中移除一个令牌。如果可以,它移除一个令牌并返回 true。如果不可以,它返回 false。
func (bucket *TokenBucket) AllowRequest(ctx context.Context, now time.Time) bool {
	bucket.mutex.Lock()
	defer bucket.mutex.Unlock()

	for {
		select {
		case <-ctx.Done():
			return false
		default:
			// 计算从上次更新令牌数量到现在应该补充的令牌数量
			leakyNum := int(float64(now.Unix()-bucket.lastDripTime) * bucket.rate)
			if leakyNum > 0 {
				bucket.lastDripTime = now.Unix()

				bucket.standing += leakyNum
				if bucket.standing > bucket.capacity {
					bucket.standing = bucket.capacity
				}
			}

			// 判断当前是否有可以消耗的令牌
			if bucket.standing > 0 {
				bucket.standing--
				return true
			}
			return false
		}
	}

}

func main() {
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, time.Second*2)
	defer cancel()
	bucket := NewTokenBucket(4, 5)
	for {
		select {
		case <-ctx.Done():
			return
		default:
			now := time.Now()
			format := now.Format("15:04:05.0000000")
			if bucket.AllowRequest(ctx, now) {
				fmt.Println(format + " 请求已执行")
			} else {
				fmt.Println(format + " 请求被限流,无法执行")
			}
			time.Sleep(100 * time.Millisecond)
		}
	}
}

四种算法的对比

算法优点缺点适用场景
固定窗口- 简单直观,易于实现
- 适用于稳定的流量控制
- 易于实现速率控制
.- 无法应对短时间内的突发流量
- 流量不均匀时可能导致突发流量
- 稳定的流量控制,不需要保证请求均匀分布的场景
滑动窗口- 平滑处理突发流量
- 颗粒度更小,可以提供更精确的限流控制
.- 实现相对复杂
- 需要维护滑动窗口的状态
- 存在较高的内存消耗
- 需要平滑处理突发流量的场景
漏桶- 平滑处理突发流量
- 可以固定输出速率,有效防止过载
-对突发流量的处理不够灵活
.- 对突发流量的处理不够灵活
- 无法应对流量波动的情况
- 需要固定输出速率的场景
-避免流量的突然增大对系统的冲击的场景
令牌桶- 平滑处理突发流量
- 可以动态调整限流规则
- 能适应不同时间段的流量变化
.- 实现相对复杂
- 需要维护令牌桶的状态
- 需要动态调整限流规则的场景
- 需要平滑处理突发流量的场景

todo

// 1.将容量设置为带缓冲的通道类型,起一个协程做动态补充
package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

type bucket struct {
	rate     float64
	standing chan int
	capacity int
}

func NewBucket(rate float64, cap int) *bucket {
	return &bucket{
		rate:     rate,
		capacity: cap,
		standing: make(chan int, cap),
	}
}

func (b *bucket) replenish(ctx context.Context, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			return
		default:
			if len(b.standing) >= b.capacity {
				return
			}
			time.Sleep(time.Second / time.Duration(b.rate))
			b.standing <- 1
			fmt.Printf("当前容量:%d\n", len(b.standing))
		}
	}
}

func main() {
	ctx := context.Background()
	ctx, cancel := context.WithTimeout(ctx, time.Second*2)
	defer cancel()
	b := NewBucket(2, 6)
	var wg sync.WaitGroup
	wg.Add(1)
	go b.replenish(ctx, &wg)
	wg.Wait()
}

3. 截取切片

func subSlice[S ~[]E, E any](s S, skip, limit int64) S {
	if skip == 0 && limit == 0 {
		return s
	}

	start := skip
	if start < 0 {
		start = 0
	}

	if limit < 0 {
		limit = 0
	}
	end := start + limit

	count := int64(len(s))
	if start >= count {
		return nil
	}
	if end == 0 || end > count {
		end = count
	}

	return s[start:end]
}
  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值