利志分享
fast_forward
view_headline
开发工具箱
go教程
clickhouse教程
kafka教程
python教程
shell教程
原创杂文
打赏
开发工具箱
go教程
clickhouse教程
kafka教程
python教程
shell教程
原创杂文
打赏
go基础知识
go的环境搭建
go变量
go常量
go字符串
go数组和切片
go的map和range的使用
go的struct的使用
go的函数使用
go的interface的使用
go channel使用
go的routine使用
go的panic和recover使用
go实现http请求
go 复杂的http请求
go实现表单提交
go实现表单验证
go上传附件
go实现mysql连接
go实现redis操作
go对xml操作
go的json操作
go的base64使用
go实现websocket功能
go的单元测试
go的文件操作
go的web服务基础
golang url解析和包介绍使用
go的正则表达式-MatchString,FindString等的使用
golang实现从byte和文件中读取csv格式数据
go进阶
go的类型转换
go的map的多维应用
go的多维数组和slice使用
go的select使用
go的原子性atomic类库使用
go给图片添加水印
go给图片添加文字
go实现http的rpc服务
go实现tcp的rpc服务
go实现json格式的rpc服务
多个defer的执行问题
golang的队列机制实现同步主线程接受子协程的结果
go的值传递和引用传递以及引用类型的问题
go中的make和new的使用问题
golang读文件分析1
golang读文件分析2
golang实现自然周计算
golang实现读写excel
go实战
beego的安装和使用
beego聊天室的基本配置
beego聊天室的生成
Go 写一个类似 cron 的定时任务管理器
Go 调度器 M, P 和 G
AES对称加密算法如何用golang语言实现?
非对称加密的RSA算法如何通过golang来实现?
golang实现http2.0服务端,客户端完整案例
go实战总结
go的日期操作类使用-日常使用类库no.1
go的字符串的连接讲解-日常实战总结no.1
golang实现队列服务-日常实战总结no.2
深入理解golang的channel的使用-日常实战总结no.3
go的sync.pool在实际应用中的讲解和性能分析比较-日常实战总结no.4
go语言中一个典型的引用类型的数据使用案例的注意点-日常实战总结no.5
go的sync包的使用详解1-日常实战总结6
go的sync包的使用详解2-日常实战总结7
深度学习go判断各个类型相等-日常实战总结8
go的排序类使用讲解-日常实战总结9
go的context使用讲解
golang 网络爬虫框架gocolly
golang实现桶排序
golang处理gb2312转utf-8编码的问题
golang实现单链的添加,删除以及翻转
golang的一个err不判断引起的血案(json.Marshal的error到底要不要判断?)
如何控制golang协程的并发数量问题-panic: too many concurrent operations on a single file or socket (max 1048575)
你所要知道的redis客户端返回值知识点
golang实现连续的时间,比如连续的天,月,年等。
go深入
由引用类型引发的概念的深入理解
sync.WaitGroup深入源码理解
golang如何创建动态的struct类型以及如何转换成slice类型
深入理解go的管道数据读写
关于go的只读管道只写管道以及单向管道的理解
深入理解go的slice深入,slice扩容机制
深入理解go的函数参数传递
golang实现动态调用不同struct中不同的方法
如何配置sqlx.DB的SetMaxOpenConns SetMaxIdleConns 和 SetConnMaxLifetime来保证更好的性能
深入理解go的select原理
深入理解golang的GPM模型
精通golang的项目管理go modules
深入理解golang的GC回收机制
超级肝文-深入剖析客户端出现connect reset by peer报错相关的技术知识
Golang源码深入-Go1.15.6发起http请求流程-1
Golang源码深入-Go1.15.6发起http请求流程-2
Golang源码深入-Go1.15.6发起http请求流程-3(http2)
go应用
需求整理-手把手带大家用go开发一个匿名在线聊天室
第二篇-手把手带大家用go开发一个匿名在线聊天室
第三篇-手把手带大家用go开发一个匿名在线聊天室
go面试
【建议收藏】吐血整理Golang面试干货21问-吊打面试官-1
【建议收藏】整理Golang面试第二篇干货13问
【建议收藏】Redis知识干货汇总
【建议收藏】Mysql知识干货(mysql八股文)汇总
目录
go基础知识
go的环境搭建
go变量
go常量
go字符串
go数组和切片
go的map和range的使用
go的struct的使用
go的函数使用
go的interface的使用
go channel使用
go的routine使用
go的panic和recover使用
go实现http请求
go 复杂的http请求
go实现表单提交
go实现表单验证
go上传附件
go实现mysql连接
go实现redis操作
go对xml操作
go的json操作
go的base64使用
go实现websocket功能
go的单元测试
go的文件操作
go的web服务基础
golang url解析和包介绍使用
go的正则表达式-MatchString,FindString等的使用
golang实现从byte和文件中读取csv格式数据
go进阶
go的类型转换
go的map的多维应用
go的多维数组和slice使用
go的select使用
go的原子性atomic类库使用
go给图片添加水印
go给图片添加文字
go实现http的rpc服务
go实现tcp的rpc服务
go实现json格式的rpc服务
多个defer的执行问题
golang的队列机制实现同步主线程接受子协程的结果
go的值传递和引用传递以及引用类型的问题
go中的make和new的使用问题
golang读文件分析1
golang读文件分析2
golang实现自然周计算
golang实现读写excel
go实战
beego的安装和使用
beego聊天室的基本配置
beego聊天室的生成
Go 写一个类似 cron 的定时任务管理器
Go 调度器 M, P 和 G
AES对称加密算法如何用golang语言实现?
非对称加密的RSA算法如何通过golang来实现?
golang实现http2.0服务端,客户端完整案例
go实战总结
go的日期操作类使用-日常使用类库no.1
go的字符串的连接讲解-日常实战总结no.1
golang实现队列服务-日常实战总结no.2
深入理解golang的channel的使用-日常实战总结no.3
go的sync.pool在实际应用中的讲解和性能分析比较-日常实战总结no.4
go语言中一个典型的引用类型的数据使用案例的注意点-日常实战总结no.5
go的sync包的使用详解1-日常实战总结6
go的sync包的使用详解2-日常实战总结7
深度学习go判断各个类型相等-日常实战总结8
go的排序类使用讲解-日常实战总结9
go的context使用讲解
golang 网络爬虫框架gocolly
golang实现桶排序
golang处理gb2312转utf-8编码的问题
golang实现单链的添加,删除以及翻转
golang的一个err不判断引起的血案(json.Marshal的error到底要不要判断?)
如何控制golang协程的并发数量问题-panic: too many concurrent operations on a single file or socket (max 1048575)
你所要知道的redis客户端返回值知识点
golang实现连续的时间,比如连续的天,月,年等。
go深入
由引用类型引发的概念的深入理解
sync.WaitGroup深入源码理解
golang如何创建动态的struct类型以及如何转换成slice类型
深入理解go的管道数据读写
关于go的只读管道只写管道以及单向管道的理解
深入理解go的slice深入,slice扩容机制
深入理解go的函数参数传递
golang实现动态调用不同struct中不同的方法
如何配置sqlx.DB的SetMaxOpenConns SetMaxIdleConns 和 SetConnMaxLifetime来保证更好的性能
深入理解go的select原理
深入理解golang的GPM模型
精通golang的项目管理go modules
深入理解golang的GC回收机制
超级肝文-深入剖析客户端出现connect reset by peer报错相关的技术知识
Golang源码深入-Go1.15.6发起http请求流程-1
Golang源码深入-Go1.15.6发起http请求流程-2
Golang源码深入-Go1.15.6发起http请求流程-3(http2)
go应用
需求整理-手把手带大家用go开发一个匿名在线聊天室
第二篇-手把手带大家用go开发一个匿名在线聊天室
第三篇-手把手带大家用go开发一个匿名在线聊天室
go面试
【建议收藏】吐血整理Golang面试干货21问-吊打面试官-1
【建议收藏】整理Golang面试第二篇干货13问
【建议收藏】Redis知识干货汇总
【建议收藏】Mysql知识干货(mysql八股文)汇总
golang的一个err不判断引起的血案(json.Marshal的error到底要不要判断?)
阅读:730
分享次数:0
**问题** 很多同学都认为如果我知道json.marshal的值,我就不用判断它执行之后返回错误,包过工作5,6年的经验的一些高工也是这么认为的。然而到底要不要判断呢?我这里先不给结论,我们先来看下我们业务中出现的问题。 **事故** 我们的线上的业务后台是通过前端传方法和方法名过来识别走不同的方法和方法名实现功能,有个同学想把http.request这个变量传到方法里面进行使用,但是我们原来的http的response有做整个数据缓存的逻辑,我们的参数是通过map传入到方法名里面,这个同学在传入参数的时候把http.request当成value,key多加了个requestInfo传入方法名里面。我们的缓存代码是把整个请求参数json.Marshal之后当成key,缓存每次请求的结果在进程内存中。这个同学加完之后,放到测试环境没有特别留意缓存的数据问题,只看了自己写的功能,等到回归测试的之后,测试同学对整体功能回归了一下,发现整个后台数据只显示一个结果。然后这个同学开始定位问题,先看下参数改的请求参数: func getObjFuncParam(rw http.ResponseWriter, br []byte) (string, string, map[string]interface{}, error) { input := new(QueryModel) err := json.Unmarshal(br, &input) if err != nil { fmt.Errorf("获取参数错误") } return input.Obj, input.Func, input.Param, nil } 看起来没问题,然后看下getFuncResult方法,整个方法是做整体后台调用缓存用的,在这之前已经做了Param["requestInfo"] = *http.Request整个的赋值。看下getFuncResult的代码: func getFuncResult(rw http.ResponseWriter, obj string, funcName string, Param map[string]interface{}, paramBytes []byte) *model.ResData { expire, ok := cache.AllCacheMap[obj][funcName] _, isExport := Param["isCSV"] if !ok || isExport { result := xxxxx(rw, obj, funcName, Param) return result } // 走缓存 key, err := cache.GetQueryKey(obj, funcName, paramBytes) if err != nil { log.Error("cache.GetQueryKey", zap.Error(err)) } result, err := cache.GetCacheKey(key) if err != nil { // 分发请求 result = xxxxx(rw, obj, funcName, Param) if result.Code == errors.ADMIN_SECUSS { // TODO HANK 根据环境设置时间 // 缓存处理 cache.SetQueryData(key, result, expire) } } return result } 也没看出来问题,看起来一切逻辑正常,然后翻了一下GetCacheKey的代码。 func GetCacheKey(obj, funcName string, param map[string]interface{}) string { m := map[string]interface{}{ "obj": obj, "funcName": funcName, "param": filterSpecialParam(param, true), } bin, _ := json.Marshal(m) hash := fmt.Sprintf("%x", md5.Sum(bin)) appzaplog.Info("GetQueryKey Key", zap.String("Cache2Go Field", hash)) return hash } // filterParam -过滤特殊的参数做缓存 func filterSpecialParam(param map[string]interface{}, isFilter bool) map[string]interface{} { if !isFilter { return param } b, err := json.Marshal(param) if err != nil { return param } newMap := map[string]interface{}{} err = json.Unmarshal(b, &newMap) if err != nil { return param } //删除特殊标识 delete(newMap, "userId") delete(newMap, "userName") return newMap } 后面定位了很久才知道是整个filterSpecialParam没有做错误处理,整个都是另外一个同学协助才查到整个问题。因为filterSpecialParam出错了,每次都是直接返回空的map,主要原因是json.Marshal没有处理错误。 **复盘** 后面我们单独改造了一下filterSpecialParam,对错误进行处理,也对json.Marshal进行学习,为什么上面的会报错了,是因为http.Request里面包含函数类型,这个是不支持json.Marshal的。 针对于不能json.Marshal的我们还做了一些test。 package my_test_test import ( "encoding/json" "fmt" "reflect" "testing" ) type MyJson struct { Name string `json:"name"` Age int `json:"age"` Func func() `json:"func"` //Point unsafe.Pointer `json:"point"` //PointInt *int `json:"point_int"` } func TestJson(t *testing.T) { tt := func() {} fmt.Println(reflect.TypeOf(tt)) tmp := MyJson{} res, err := json.Marshal(&tmp) fmt.Println(string(res), err) } 报错如下: json: unsupported type: func() json: unsupported type: unsafe.Pointer 深入看了一下源码支持的有如下几种:  我这里就不详细说了,有兴趣可以看看,我这里是官方的encoding包,这个都是用反射实现的json,有个兼容官方的包,其功能和这个也是一样的,就这样的一个问题能把整个后台功能搞瘫痪,所以编程规范是多么重要。 **总结** json.Marshal的error到底要不要判断? 答:要,所有的程序的返回error都要处理,凡是error,我们最好都进行判断,错误都打错误日志,养成良好的开发习惯,这样查问题解决问题就比较快一些。
感觉本站内容不错,读后有收获?
attach_money
我要小额打赏,鼓励作者写出更好的教程
扫码关注公众号:talk_lizhi