-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmap.go
164 lines (141 loc) · 3.27 KB
/
map.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
package algorithm
import (
"context"
"fmt"
"sync"
"sync/atomic"
"time"
)
/*
实现阻塞读且并发安全的map
思路
1、阻塞使用chan
2、并发安全使用sync.RWMutex
*/
type MyMap interface {
Set(key string, val interface{}) //存入key /val,如果该key读取的goroutine挂起,则唤醒。此方法不会阻塞,时刻都可以立即执行并返回
Get(key string, timeout time.Duration) interface{} //读取一个key,如果key不存在阻塞,等待key存在或者超时
}
type Key struct {
c chan bool // 用于唤醒获取该key阻塞的goroutine
value interface{}
isExist bool // true表示已存在,false表示未存在,有goroutine阻塞
}
type Map struct {
m map[string]*Key
RWm sync.RWMutex
}
func NewMap() *Map {
return &Map{
m: make(map[string]*Key),
RWm: sync.RWMutex{},
}
}
func (m *Map) Set(key string, val interface{}) {
m.RWm.Lock()
defer m.RWm.Unlock()
v, ok := m.m[key]
if !ok || v.isExist { // 该key不存在于map中
tempKey := Key{
value: val,
isExist: true,
}
m.m[key] = &tempKey
return
}
if !v.isExist { // 该key已经存在,有goroutine阻塞
v.isExist = true
v.value = val
close(v.c)
}
}
func (m *Map) Get(key string, t time.Duration) interface{} {
m.RWm.RLock()
defer m.RWm.RUnlock()
val, ok := m.m[key]
if ok && val.isExist {
return val.value
}
if !ok { // 不存在该key,阻塞或超时
tempKey := Key{
c: make(chan bool),
value: val,
}
m.m[key] = &tempKey
go func(t time.Duration) {
time.Sleep(t)
close(tempKey.c)
}(t)
<-tempKey.c
if tempKey.isExist {
return val.value
}
return nil
}
return nil
}
// ==================
/*
场景:在一个高并发的web服务器中,要限制IP的频繁访问。现模拟100个IP同时并发访问服务器,每个IP要重复访问1000次。
每个IP一分钟之内只能访问一次。修改以下代码完成该过程,要求能成功输出 success:100
思路
1、子线程:(定时器)定期检查map,删除超时的key,WithCancel避免孤儿线程
2、go中map并发不安全,需要加锁
3、atomic.AddInt64对临界值操作
*/
type Ban struct {
visitIPs map[string]time.Time
lock sync.Mutex
}
func NewBan(ctx context.Context) *Ban {
o := &Ban{visitIPs: make(map[string]time.Time)}
go func() {
timer := time.NewTimer(time.Minute * 1)
for {
select {
case <-timer.C: // 定时器
o.lock.Lock()
for k, v := range o.visitIPs {
if time.Since(v) >= time.Minute*1 {
delete(o.visitIPs, k)
}
}
o.lock.Unlock()
timer.Reset(time.Minute * 1)
case <-ctx.Done(): // 子线程退出
return
}
}
}()
return o
}
func (o *Ban) visit(ip string) bool {
o.lock.Lock()
defer o.lock.Unlock()
if _, ok := o.visitIPs[ip]; ok {
return true
}
o.visitIPs[ip] = time.Now()
return false
}
func Filter() {
success := int64(0)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
ban := NewBan(ctx)
wait := &sync.WaitGroup{}
wait.Add(1000 * 100)
for i := 0; i < 1000; i++ {
for j := 0; j < 100; j++ {
go func(j int) {
defer wait.Done()
ip := fmt.Sprintf("192.168.1.%d", j)
if !ban.visit(ip) {
atomic.AddInt64(&success, 1)
}
}(j)
}
}
wait.Wait()
fmt.Println("success:", success)
}