golang map并发读写异常导致服务崩溃

昨天突然接到报警说服务端口丢失,也就是服务崩溃了。

1, 先看错误日志,发现是调用json.Marshal时出错了,错误原因是:concurrent map iteration and map write,即并发读写map。

fatal error: concurrent map iteration and map write

goroutine 543965 [running]:
runtime.throw(0xb74baf, 0x26)
    /usr/local/go1.10.1/src/runtime/panic.go:616 +0x81 fp=0xc420c90a98 sp=0xc420c90a78 pc=0x42c7a1
runtime.mapiternext(0xc42160d5c0)
    /usr/local/go1.10.1/src/runtime/hashmap.go:747 +0x55c fp=0xc420c90b28 sp=0xc420c90a98 pc=0x40a73c
runtime.mapiterinit(0xa7ff20, 0xc4205372f0, 0xc42160d5c0)
    /usr/local/go1.10.1/src/runtime/hashmap.go:737 +0x1f1 fp=0xc420c90b50 sp=0xc420c90b28 pc=0x40a0f1
reflect.mapiterinit(0xa7ff20, 0xc4205372f0, 0x15)
    /usr/local/go1.10.1/src/runtime/hashmap.go:1217 +0x54 fp=0xc420c90b80 sp=0xc420c90b50 pc=0x40b814
reflect.Value.MapKeys(0xa7ff20, 0xc4205372f0, 0x15, 0x0, 0x409322, 0xc420c90d10)
    /usr/local/go1.10.1/src/reflect/value.go:1114 +0xdd fp=0xc420c90c28 sp=0xc420c90b80 pc=0x4b4afd
encoding/json.(*mapEncoder).encode(0xc421098ee8, 0xc4230bd810, 0xa7ff20, 0xc4205372f0, 0x15, 0xa70100)
    /usr/local/go1.10.1/src/encoding/json/encode.go:668 +0xad fp=0xc420c90d88 sp=0xc420c90c28 pc=0x4f42cd
encoding/json.(*mapEncoder).(encoding/json.encode)-fm(0xc4230bd810, 0xa7ff20, 0xc4205372f0, 0x15, 0xc420530100)
    /usr/local/go1.10.1/src/encoding/json/encode.go:700 +0x64 fp=0xc420c90dc8 sp=0xc420c90d88 pc=0x4feab4
encoding/json.(*encodeState).reflectValue(0xc4230bd810, 0xa7ff20, 0xc4205372f0, 0x15, 0x100)
    /usr/local/go1.10.1/src/encoding/json/encode.go:325 +0x82 fp=0xc420c90e00 sp=0xc420c90dc8 pc=0x4f1cf2
encoding/json.(*encodeState).marshal(0xc4230bd810, 0xa7ff20, 0xc4205372f0, 0xc420780100, 0x0, 0x0)
    /usr/local/go1.10.1/src/encoding/json/encode.go:298 +0xa5 fp=0xc420c90e38 sp=0xc420c90e00 pc=0x4f19e5
encoding/json.Marshal(0xa7ff20, 0xc4205372f0, 0xc420783850, 0xc420783857, 0xc420c90f30, 0x0, 0x0)
    /usr/local/go1.10.1/src/encoding/json/encode.go:161 +0x5f fp=0xc420c90e80 sp=0xc420c90e38 pc=0x4f13ef
xxx.BuildKeyList(0xc42051a2e8, 0xc420783850, 0xb, 0xc420c910e0, 0x1, 0x1,
 0xc4205372f0, 0x0, 0x2, 0xc4214e96b0, ...)
    xxx/custom.go:85 +0x59 fp=0xc420c90f60 sp=0xc420c90e80 pc=0x774dd9
xxx.BuildKeyList(0xc420783850, 0xb, 0xc420c910e0, 0x1, 0x1, 0xc4205372f0, 0xb, 0xc4207cd0a0, 0x13, 0xc42245c8a0, ...)
    xxx/dao.go:42 +0x79 fp=0xc420c90fd0 sp=0xc420c90f60 pc=0x834b59
xxx/logic.getFeatureMap(0xbde540, 0xc4214e9410, 0xc422561700, 0x4, 0x4, 0x2100035d178a8, 0xabce00)
    xxx/calc_plan.go:114 +0x4af fp=0xc420c911f8 sp=0xc420c90fd0 pc=0x9cd4df

2, xxx/custom.go的85行

if jsonParamsByte, e := json.Marshal(params); e == nil {

完整函数如下:

func (cus *FeatureClient) BuildKeyList(domain string, keys []string, params map[string]string) (featureKeys []string, err error) {
    var jsonParams string
    if params == nil || len(params) == 0 {
        jsonParams = "{}"
    }
    if jsonParamsByte, e := json.Marshal(params); e == nil { // 85行
        jsonParams = string(jsonParamsByte)
    } else {
        return nil, e
    }
    ......
}

即json.Marshal(params)出错了,错误原因是:concurrent map iteration and map write,map并发读写异常服务崩溃,然后找到下游的调用方。

3, xxx/dao.go文件中的42行:

func BuildKeyList(domain string, keys []string, params map[string]string) (featureKeys []string, err error) {
    return Client.BuildKeyList(domain, keys, params) // 42行
}

4, xxx/calc_plan.go文件的114行:

func getFeatureMap(ctx context.Context, filters []*model.FiltersType, driverId int64) map[string]string {
    var features []string
    for _, filterRule := range filters {
        param := filterRule.FilterMap
        param["driver_id"] = strconv.FormatInt(driverId, 10)
        featureKeys, _ := ufs.BuildKeyList(domain, []string{key}, param) // 114行
        features = append(features, featureKeys...)
    }
    ...
}

看起来就是传入的param这个map被多个协程并发写了,filterRule.FilterMap是全局变量,所以这里需要修改为深拷贝。

5, 深拷贝的函数

// DeepCopyMap map[string]string 类型实现深拷贝
func DeepCopyMap(params map[string]string) map[string]string {
    result := map[string]string{}
    for k, v := range params {
        result[k] = v
    }
    return result
}

6, 调用深拷贝实现

param := filterRule.FilterMap修改为 param := DeepCopyMap(filterRule.FilterMap)

原文地址:https://www.cnblogs.com/sxpujs/p/12419602.html

时间: 2024-09-28 15:51:22

golang map并发读写异常导致服务崩溃的相关文章

golang中map并发读写问题及解决方法

一.map并发读写问题 如果map由多协程同时读和写就会出现 fatal error:concurrent map read and map write的错误 如下代码很容易就出现map并发读写问题 func main(){ c := make(map[string]int)       go func() {//开一个协程写map            for j := 0; j < 1000000; j++ {              c[fmt.Sprintf("%d",

WPF 线程中异常导致程序崩溃

原文:WPF 线程中异常导致程序崩溃 一般我们WPF中都加全局捕获,避免出现异常导致崩溃. Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException; AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException)

Golang 在电商即时通讯服务建设中的实践

马蜂窝技术原创文章,更多干货请搜索公众号:mfwtech ?即时通讯(IM)功能对于电商平台来说非常重要,特别是旅游电商. 从商品复杂性来看,一个旅游商品可能会包括用户在未来一段时间的衣.食.住.行等方方面面:从消费金额来看,往往单次消费额度较大:对目的地的陌生.在行程中可能的问题,这些因素使用户在购买前.中.后都存在和商家沟通的强烈需求.可以说,一个好用的 IM 可以在一定程度上对企业电商业务的 GMV 起到促进作用. 本文我们将结合马蜂窝旅游电商 IM 服务的发展历程,重点介绍基于 Go 的

HBase一次客户端读写异常解读分析与优化全过程(干货)

大数据时代,HBase作为一款扩展性极佳的分布式存储系统,越来越多地受到各种业务的青睐,以求在大数据存储的前提下实现高效的随机读写操作.对于业务方来讲,一方面关注HBase本身服务的读写性能,另一方面也需要更多地关注HBase客户端参数的具体意义.这篇文章就从一个具体的HBase客户端异常入手,定位异常发生的原因以及相应的客户端参数优化. 案发现场 最近某业务在使用HBase客户端读取数据时出现了大量线程block的情况,业务方保留了当时的线程堆栈信息,如下图所示: 看到这样的问题,首先从日志和

解决ArcGIS中因SDE或数据库配置问题而导致服务荡掉的一种思路

1.背景 最近连续有两个项目现场出现了AGS服务荡掉的问题,一个是通州现场,一个是福州现场. 1.1通州现场的问题描述和解决思路 通州现场环境为ArcGIS9.2,使用IMS发布的地图服务,其问题表现为每隔两天左右,其地形图服务便会崩溃一次,重启地形图服务后地图可以正常显示. 因为IMS中地图的出图为动态出图,所以其出图时需要通过连接SDE,此问题的出现很可能是SDE中最大连接数的问题. 1.2福州现场的问题描述和解决思路 福州现场环境为ArcGIS10.0,使用的ArcGIS Server发布

php中并发读写文件冲突的解决方案(文件锁应用示例)

PHP(外文名: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言.语法吸收了C语言.Java和Perl的特点,入门门槛较低,易于学习,使用广泛,主要适用于Web开发领域.PHP的文件后缀名为php. 本文为大家讲解的是php中并发读写文件冲突的解决方案(文件锁应用示例),感兴趣的同学参考下. 在这里提供4种高并发读写文件的方案,各有优点,可以根据自己的情况解决php并发读写文件冲突的问题. 对于日IP不高或者说并发数不是很大的应用,一般不用考虑这

php中并发读写文件冲突的解决方案

在这里提供4种高并发读写文件的方案,各有优点,可以根据自己的情况解决php并发读写文件冲突的问题. 对于日IP不高或者说并发数不是很大的应用,一般不用考虑这些!用一般的文件操作方法完全没有问题.但如果并发高,在我们对文件进行读写操作时,很有可能多个进程对进一文件进行操作,如果这时不对文件的访问进行相应的独占,就容易造成数据丢失.例如:一个在线聊天室(这里假定把聊天内容写入文件),在同一时刻,用户A和用户B都要操作数据保存文件,首先是A打开了文件,然后更新里面的数据,但这里B也正好也打开了同一个文

Sqlite3并发读写注意事项

最近项目中涉及到sqlite并发读写的问题,参考一些文档并结合自己的实践,对sqlite3并发问题总结了几点: sqlite3的锁及事务类型 sqlite3总共有三种事务类型:BEGIN [DEFERRED /IMMEDIATE / EXCLUSIVE] TRANSCATION,五种锁,按锁的级别依次是:UNLOCKED /SHARED /RESERVERD /PENDING /EXCLUSIVE.当执行select即读操作时,需要获取到SHARED锁(共享锁),当执行insert/update

网站导致浏览器崩溃的原因总结(多款浏览器)

面试某公司的时候,面试官问到,导致浏览器崩溃的原因有哪些?愚辈不才,仅回答出了内存泄漏.其实在网页在装载的过程中,常常由于种种原因使浏览器的反映变的很慢,或造成浏览器失去响应,甚至会导致机器无法进行其他的操作. 对于访客,如果登录您网站,浏览器就立刻崩溃,我想这对谁都是无法容忍的,对此总结了网站导致浏览器崩溃的原因: 1. 内存泄漏 还是先谈下内存泄漏,网站由于内存泄漏的而照成崩溃有两种情况,服务器的崩溃和浏览器的崩溃.内存泄漏所造成的问题是显而易见的,它使得已分配的内存的引用就会丢失,只要系统