场景:在一个高并发的web服务器中,要限制IP的频繁访问。现模拟100个IP同时并发访问服务器,每个IP要重复访问1000次。每个IP三分钟之内只能访问一次。修改以下代码完成该过程,要求能成功输出 success:100。

面试提供的初始代码:

package main

import (
        "fmt"
        "time"
)

type Ban struct {
        visitIPs map[string]time.Time
}

func NewBan() *Ban {
        return &Ban{visitIPs: make(map[string]time.Time)}
}

func (o *Ban) visit(ip string) bool {
        if _, ok := o.visitIPs[ip]; ok {
                return true
        }
        o.visitIPs[ip] = time.Now()
        return false
}

func main() {
        success := 0
        ban := NewBan()
        for i := 0; i < 1000; i++ {
                for j := 0; j < 100; j++ {
                        go func() {
                                ip := fmt.Sprintf("192.168.1.%d", j)
                                if !ban.visit(ip) {
                                        success++
                                }
                        }()
                }
        }
        fmt.Println("success:", success)
}

代码分析: 1、没有检测goroutine是否执行完成; 2、并发写map没有加锁; 3、没有判断IP三分钟的访问限制; 4、success变量自增应该使用atomic包;

补充修改后的代码:

package main

import (
        "fmt"
        "sync"
        "sync/atomic"
        "time"
)

type Ban struct {
        visitIPs map[string]time.Time
        lock     *sync.Mutex
}

func NewBan() *Ban {
        return &Ban{
                visitIPs: make(map[string]time.Time),
                lock:     new(sync.Mutex),
        }
}

func (o *Ban) visit(ip string) bool {
        o.lock.Lock()
        defer o.lock.Unlock()

        if v, ok := o.visitIPs[ip]; ok {
                if time.Now().Sub(v).Minutes() >= 3 {
                        delete(o.visitIPs, ip)
                        o.visitIPs[ip] = time.Now()
                        return false
                }
                return true
        }
        o.visitIPs[ip] = time.Now()
        return false
}

func main() {
        wgp := &sync.WaitGroup{}
        var success int64
        ban := NewBan()
        for i := 0; i < 1000; i++ {
                for j := 0; j < 100; j++ {
                        wgp.Add(1)
                        t := j
                        go func() {
                                defer wgp.Done()
                                ip := fmt.Sprintf("192.168.1.%d", t)
                                if !ban.visit(ip) {
                                        atomic.AddInt64(&success, 1)
                                }
                        }()
                }
        }
        wgp.Wait()
        fmt.Println("success:", success)
}

results matching ""

    No results matching ""