一道并发和锁的golang IP限制访问问题(延伸思考)

原题是这篇博客
https://blog.csdn.net/qq_28163175/article/details/75287877#commentBox
一个关于并发和锁的golang面试题, 题目为在一个高并发的web服务器中,要限制IP的频繁访问,每个IP三分钟之内只能访问一次。

考虑到高并发场景,不免遇到来访ip数量爆表的情况,为了合理应用计算机资源, 不能每时每刻,都去计算那些IP已过期, 因此想法是做一个以最近一个将要过期的IP距现在还剩多少时间的Duraction作为清理任务的定时时间

核心实现

// An highlighted block
import (
	"fmt"
	"sync"
	"time"
)

type IpItem struct {
	Ip       string
	LifeSpan time.Duration // 生命周期
	CreateOn time.Time     // 创建时间
}

type IpTable struct {
	sync.RWMutex
	CleanerDuraction time.Duration     // 触发定时清理器的时间
	Cleaner          *time.Timer       // 定时清理器
	Items            map[string]IpItem // ips
}

func NewIpTable() *IpTable {
	return &IpTable{
		Items: make(map[string]IpItem),
	}
}

func (this *IpTable) Visit(ip string) bool {
	this.Lock()
	item := IpItem{
		Ip:       ip,
		LifeSpan: 3 * time.Minute,
		CreateOn: time.Now(),
	}
	if _, ok := this.Items[ip]; ok {
		this.Unlock()
		fmt.Printf("%s is limited\n", ip)
		return true
	}
	this.Items[ip] = item
	cleannerDuraction := this.CleanerDuraction
	this.Unlock()

	fmt.Printf("add %s to table\n", ip)
	if cleannerDuraction == 0 {
		this.cleanerCheck()
	}
	return false
}

func (this *IpTable) cleanerCheck() {
	this.Lock()
	defer this.Unlock()
	fmt.Printf("start timer cleaner Duraction after %.2f s\n", this.CleanerDuraction.Seconds())
	if this.Cleaner != nil {
		this.Cleaner.Stop()
	}

	// 遍历当前限制的ip, 遇到过期的将其删掉
	// 其余的则从中找到最近一个将要过期的IP并且将它还有多少时间过期作为下一次清理任务的定时时间
	now := time.Now()
	smallestDuracton := 0 * time.Second
	for ip, item := range this.Items {
		lifeSpan := item.LifeSpan
		createOn := item.CreateOn
		if now.Sub(createOn) >= lifeSpan {
			fmt.Println("delete ip", ip)
			delete(this.Items, ip)
		} else {
			if smallestDuracton == 0 || lifeSpan-now.Sub(createOn) < smallestDuracton {
				smallestDuracton = lifeSpan - now.Sub(createOn)
			}
		}
	}

	this.CleanerDuraction = smallestDuracton
	// 将最近一个将要过期的IP距离现在的时间作为启动清理任务的定时时间
	if this.CleanerDuraction > 0 {
		fn := func() {
			go this.cleanerCheck()
		}
		this.Cleaner = time.AfterFunc(this.CleanerDuraction, fn)
	}
}

测试代码

// An highlighted block

func TestIpTable(t *testing.T) {
	var (
		success int64
		wg      sync.WaitGroup
	)

	table := NewIpTable()
	for j := 0; j < 1000; j++ {
		for i := 0; i < 100; i++ {
			wg.Add(1)
			go func(index int) {
				defer wg.Done()
				ip := fmt.Sprintf("192.168.1.%d", index)
				if !table.Visit(ip) {
					atomic.AddInt64(&success, 1)
				}
			}(i)
		}
	}
	wg.Wait()
	fmt.Println("end and success is", success)
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值