golang net/http输出context引起的map panic

前言:

最近遇到一个很奇怪的问题,在net/http里打印http.Request的context会出现panic。具体的错误error, fatal error: concurrent map read and map write 。
我们知道golang的map不是线程安全的,多个协程不加锁对map并发读写会触发data race,然后panic异常退出。

该文章后续仍在不断的更新修改中, 请移步到原文地址 http://xiaorui.cc/?p=5919

感谢

另外,感谢曹大佬一语惊醒梦中人,不然不会短时间内定位问题。 哈哈 曹大佬的信息,https://github.com/cch123

分析问题

下面是出现net/http context panic的问题代码,代码的逻辑很简单,就是定义一个api,然后打印context而已。把服务运行起来后,我们可以用ab, wrk来进行压测,来制造data race竞争的场景。

下面是wrk压测时,net/http服务的异常信息。

通过panic出来的协程调用栈信息可以分析出,fmt print会不断的递归反射及遍历解析context里的数据。 通过上面的panic信息我们可以得知根本问题是由于map的并发读写造成的,这也就说 context 内部是有map的。
我们在正常情况下打印http.Request context,可以看到两个map,一个是listeners,一个是activeConn。另外在activeConn这个结构里还能看到很多的conn。说明这个context不单单是含有这个请求本身需要的上下文信息了,而且还包含了该server对象。

我们再来分析下 net/http的代码里对activeConn map的修改逻辑。不管是初始化,新增,删除都有加锁。但是他的锁的范围只是 net/http server的锁。fmt.Printf里对server对象的activeConn map遍历打印自然不受影响。
那么,自然就会有造成 map 的panic。

这里还有个问题,这个server是从哪里传给每个请求的handler的。

如何解决? 或者说安全打印

别直接把context都输出打印就可以了。😅

总结:

出问题,就要看源码。话说对外总是吹嘘net/http代码看过好几遍,但出问题还是要回炉分析代码,说明net/http的源码看的还是不精呀。 😅😅😅


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc