http handlefunc 上的 Golang 地图更新
Golang map update on http handlefunc
我对go语言的map有疑问。
我想处理客户端 (http
) 并使用 map
(key (client IP) value pair) 保存他们的一些信息...
http
使用新线程处理每个 http 客户端,所以我认为更改(添加、删除、编辑)map
数据是不安全的……我的操作安全吗?
package main
import (
"net/http"
)
func main() {
var clientsData map[string]string
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
// Is this map adding, safe or i have to use thread lock (mutex or ...) ?
clientsData[request.RemoteAddr] = ...
})
http.ListenAndServe("127.0.0.10:8090", nil)
}
如果你只读,那是安全的。
如果你需要写作,你需要一些方法来完成它“thread-safe”
第一个想法是使用sync.Mutex来保护对地图的访问
然而这可能成为一个瓶颈,因为你可能有多个并行的请求,但每次只能写入一个。我们说的是纳秒……
第二种方法可以使用read/write mutex 来控制读写。许多 goroutines 可以读取,但每次只能写入一个。
包同步和 sync/atomic 中还有其他选项。
还有一种额外的方法需要考虑:如果你只需要写入这个映射,你可以考虑使用缓冲通道发送一个 key/value 的结构,以便在单个 goroutine 中使用(负责存储它进入地图)
如你所见,这种方法有很多
如果对您的应用程序有意义,则优势。
您甚至可以使用通道通过回调来安全读取/写入,但这是另一回事了。
如有疑问,请编写单元测试并使用竞争条件检测器
简化 https://eli.thegreenplace.net/2019/on-concurrency-in-go-http-servers/ 中的示例允许构建一个表明它不安全的简单示例。
使用像这样的简单程序:
package main
import (
"net/http"
)
func main() {
counters := map[string]int{}
name := "test"
counters[name] = 0
http.HandleFunc("/test", func(w http.ResponseWriter, req *http.Request) {
counters[name]++
})
http.ListenAndServe(":8000", nil)
}
并刺激使用:
ab -n 20000 -c 200 "127.0.0.1:8000/test"
产生如下异常:
goroutine 158 [running]:
runtime.throw({0x64d95a, 0x81faa0})
/usr/local/go/src/runtime/panic.go:1198 +0x71 fp=0xc000384980 sp=0xc000384950 pc=0x4348f1
runtime.mapaccess2_faststr(0x697360, 0xc0003a4ba0, {0x644851, 0x4})
/usr/local/go/src/runtime/map_faststr.go:116 +0x3d4 fp=0xc0003849e8 sp=0xc000384980 pc=0x413d34
main.main.func1({0x69bf00, 0xc0003a4b60}, 0x0)
/home/test/gohttp/main.go:13 +0x46 fp=0xc000384a48 sp=0xc0003849e8 pc=0x5eba86
net/http.HandlerFunc.ServeHTTP(0x0, {0x69bf00, 0xc0003a4b60}, 0x0)
我对go语言的map有疑问。
我想处理客户端 (http
) 并使用 map
(key (client IP) value pair) 保存他们的一些信息...
http
使用新线程处理每个 http 客户端,所以我认为更改(添加、删除、编辑)map
数据是不安全的……我的操作安全吗?
package main
import (
"net/http"
)
func main() {
var clientsData map[string]string
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
// Is this map adding, safe or i have to use thread lock (mutex or ...) ?
clientsData[request.RemoteAddr] = ...
})
http.ListenAndServe("127.0.0.10:8090", nil)
}
如果你只读,那是安全的。
如果你需要写作,你需要一些方法来完成它“thread-safe”
第一个想法是使用sync.Mutex来保护对地图的访问
然而这可能成为一个瓶颈,因为你可能有多个并行的请求,但每次只能写入一个。我们说的是纳秒……
第二种方法可以使用read/write mutex 来控制读写。许多 goroutines 可以读取,但每次只能写入一个。
包同步和 sync/atomic 中还有其他选项。
还有一种额外的方法需要考虑:如果你只需要写入这个映射,你可以考虑使用缓冲通道发送一个 key/value 的结构,以便在单个 goroutine 中使用(负责存储它进入地图)
如你所见,这种方法有很多 如果对您的应用程序有意义,则优势。
您甚至可以使用通道通过回调来安全读取/写入,但这是另一回事了。
如有疑问,请编写单元测试并使用竞争条件检测器
简化 https://eli.thegreenplace.net/2019/on-concurrency-in-go-http-servers/ 中的示例允许构建一个表明它不安全的简单示例。
使用像这样的简单程序:
package main
import (
"net/http"
)
func main() {
counters := map[string]int{}
name := "test"
counters[name] = 0
http.HandleFunc("/test", func(w http.ResponseWriter, req *http.Request) {
counters[name]++
})
http.ListenAndServe(":8000", nil)
}
并刺激使用:
ab -n 20000 -c 200 "127.0.0.1:8000/test"
产生如下异常:
goroutine 158 [running]:
runtime.throw({0x64d95a, 0x81faa0})
/usr/local/go/src/runtime/panic.go:1198 +0x71 fp=0xc000384980 sp=0xc000384950 pc=0x4348f1
runtime.mapaccess2_faststr(0x697360, 0xc0003a4ba0, {0x644851, 0x4})
/usr/local/go/src/runtime/map_faststr.go:116 +0x3d4 fp=0xc0003849e8 sp=0xc000384980 pc=0x413d34
main.main.func1({0x69bf00, 0xc0003a4b60}, 0x0)
/home/test/gohttp/main.go:13 +0x46 fp=0xc000384a48 sp=0xc0003849e8 pc=0x5eba86
net/http.HandlerFunc.ServeHTTP(0x0, {0x69bf00, 0xc0003a4b60}, 0x0)