go - 如何解决 Golang WAF 服务的竞争条件?
问题描述
首先,对不起所有糟糕的英语。
我尝试在 Golang 中开发 WAF(Web 应用程序防火墙)服务。一切都map[string]*Struct{}
在记忆中。当请求到来时,我将请求标头的主机设置为映射到处理函数中。
host,err := GetHost(r.Host)
func GetHost(host string) (*Host,error){
split, _, err := net.SplitHostPort(host)
if err == nil {
host = split
}
if data, val := hosts[host]; val {
return data, nil
}
return nil,errors.New("host not found!)
}
//hosts is a map for all host, key is host and value is host struct.
问题是,当服务收到大量请求时,地图会变得混乱。例如; host isexample.com
但hosts["example.com"]
给出了另一个无关紧要的值。
type Server struct {
mu sync.RWMutex
Host *models.Host
}
func (c *Server) handler(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mutex.Lock()
defer mutex.Unlock()
var err error
c.Host, err = GetHost(r.Host)
if err != nil {
w.WriteHeader(http.StatusBadGateway)
w.Write([]byte(r.Host + " not found!!"))
return
}
//it's going on..Edited part
所以,我尝试使用mutex
andwg
来解决这个问题,但它没有用。打开任何建议。
解决方案
您认为您需要从 HTTP 处理程序序列化对共享状态的访问是正确的(因为服务器在专用 goroutine 中处理每个 HTTP 请求)。否则,您的程序确实会遇到同步错误,这可能会在执行过程中表现为数据竞争,正如您所经历的那样;Go 工具链提供的竞赛检测器很可能会检测到它。
可以说,序列化对该共享状态的访问的最简单方法是使用一些互斥锁。但是,您需要小心。您的延迟调用mutex.Unlock
是有问题的,至少有一个,可能有两个原因:
- 一般来说,您应该努力使关键部分
Lock
(代码中被调用and包围的部分Unlock
)尽可能地“小”和“便宜”。简而言之,临界区应该只做内存处理,而不是 I/O 的东西。在这里,在每个请求的整个处理过程中都需要持有锁,这可能会导致您的服务器出现大量争用。 - 尽管您在处理程序中省略了代码的结尾,但我猜(?)您稍后也会获取锁以更新映射(如果之前没有遇到当前请求的主机)。但是,由于包导出的互斥锁类型都不
sync
是可重入的,因此您可能会遇到死锁:由于调用Unlock
被延迟,互斥锁只会在您的处理程序终止时被释放。
一种解决方案是避开defer
和限制对GetHost
函数的调用的关键部分。
另一个改进是消除全局状态,以获得更好的可测试性等。您可以通过简单地将地图走私存储在结构的字段中并声明为 on 的方法来使hosts
地图成为非全局的。Server
GetHost
*Server
type Server struct {
mu sync.RWMutex
Host *models.Host
hosts map[string]*Host
}
func (srv *Server) GetHost(host string) (*Host, error){
split, _, err := net.SplitHostPort(host)
if err == nil {
host = split
}
if data, exists := srv.hosts[host]; exists {
return data, nil
}
return nil, errors.New("host not found!")
}
func (srv *Server) handler(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
mutex.Lock()
srv.Host, err := srv.GetHost(r.Host)
mutex.Unlock()
if err != nil {
w.WriteHeader(http.StatusBadGateway)
w.Write([]byte(r.Host + " not found!!"))
return
}
// possibly acquire and release the lock again
// for further treatment of the hosts map
})
}
我可能遗漏了一些东西,但必须承认我没有看到为每个请求更新结构Host
字段的意义......Server
推荐阅读
- c# - 在 ASP.Net Core 2.2 中播种 MongoDB 的最佳方法是什么
- matlab - 使用 parfeval() 时的 Matlab-GUI 警告:“无法保存 App Designer 应用程序对象。”
- spacy - 是否可以获得 IOB 格式的预测?- NER
- c++ - 带有范围滑块的 OpenCV HSV 过滤器不起作用
- laravel - [Vue 警告]:挂载钩子错误:“ReferenceError: google 未定义
- java - 使用 java 8 流 api 做数组列表的深拷贝,但得到构建时间错误
- flask - Flask 模板中的表单状态丢失
- excel - Excel,从一组重复记录中查找最新修改行的公式
- java - 为什么我的代码无休止地测试(n 皇后问题 / java / blue)
- javascript - 带有 javascript 的 Laravel sanctum SPA 应用程序