1、golang 使用json序列化报 NaN +Inf 解决方法
在使用JSON包或者无论是encoding下面的那个工具包,在序列化和反向序列化数据的时候,如果报NaN 或者Inf等字样,表示需要序列化的数据有问题。
NaN:代表不是一个数 Not a number。
Inf :代表阶码溢出,前面的加减符号代表高地位溢出,也就是小数点位后面无限大,使用不能很好的序列化。
解决办法:
NaN:使用fmt.Sprintf("%0.2f",浮点数) 输出一个字符串,判断字符串是否为“NaN”,如果是,给定一个初始值。
Inf :使用fmt.Sprintf("%0.2f",浮点数) 输出一个字符串的固定位的浮点数,再把这个浮点数转为float 即可 。
2、Golang利用select实现超时机制
在使用goroutine的时候,避免程序超时造成阻塞。(比如文件的读取)
解决办法:
可以使用select来设置超时
//新开一个协程
go func() {
for {
select {
case num := <-ch: //如果有数据,下面打印。但是有可能ch一直没数据
fmt.Println("num = ", num)
case <-time.After(3 * time.Second): //上面的ch如果一直没数据会阻塞,那么select也会检测其他case条件,检测到后3秒超时
fmt.Println("超时")
quit <- true //写入
}
}
}() //别忘了()
3、map的线程安全使用
go中并发使用map时,因为map不是并发安全的,会报错:fatal error: concurrent map writes
避免、解决办法
-
尽量少定义全局map变量
-
如果必须定义全局map变量,可以加锁
1. 优化1.可以采用cow策略,read不加锁,每次修改copy修改,再赋值
2. 优化2.可以采用分片锁,减小锁的粒度 -
使用sync.Map
主要方法:
1. Store(key, value interface{}) — 说明: 存储一个设置的键值。
2. LoadOrStore(key, value interface{}) (actual interface{}, loaded
bool) — 说明: 返回键的现有值(如果存在),否则存储并返回给定的值,如果是读取则返回true,如果是存储返回false。
3. Load(key interface{}) (value interface{}, ok bool) — 说明:读取存储在map中的值,如果没有值,则返回nil。OK的结果表示是否在map中找到值。
4. Delete(key interface{}) — 说明: 删除键对应的值。
5. Range(f func(key, value interface{}) bool) — 说明: 循环读取map中的值。参考:
1、https://www.jianshu.com/p/2ef0566c75d9
2、https://studygolang.com/articles/29582
3、https://www.jianshu.com/p/1d45e31343d8
4、float64的四舍五入
由于受浮点数精度的影响
import (
"math"
)
// Round 四舍五入,ROUND_HALF_UP 模式实现
// 返回将 val 根据指定精度 precision(十进制小数点后数字的数目)进行四舍五入的结果。precision 也可以是负数或零。
func Round(val float64, precision int) float64 {
p := math.Pow10(precision)
return math.Floor(val*p+0.5) / p
}
5、 for-select因定义空default导致CPU占用达到100%
var ch chan int
for {
select {
case <-ch:
return
default:
}
}
对于select语句,每个case的IO事件都是阻塞的,监听IO事件是不会占用CPU至满的。
造成CPU占用的原因是这个空default,因为当case的条件不满足时,循环将会走default,然后执行下一个循环,这就造成了死循环,因此在使用for-select语句的时候不能定义空的default。
解决办法:
default里面使用:time.Sleep(),并且sleep时间结束之后做return处理。
6、Golang 数组 注意细节
- 数组是多个相同类型数据的组合,一个数组一旦声明/定义了,其长度是固定的,不能动态变化。
- var arr[]int,这个arr就是slice切片。
- 数组中的元素可以是任何数据类型,包括值类型和引用类型,但不能混用(不能int和float混用)
- 数组创建后,如果没有赋值,是有默认值的,数值类型默认是0;字符串类型默认"",bool类型默认false
- 使用数组的步骤:1.声明数组并开辟空间。2.给数组各个元素赋值。3.使用数组
- 数组的下标是从0开始的
- 数组下标必须在指定范围内使用,否则报panic,数组越界(var arr [5]int,则有效下标为0-4)
- Golang的数组属于值类型,在默认情况下是值传递,因此会进行拷贝, 数组间不会有影响
- 如果想在函数中修改数组的元素,可以使用引用传递修改(指针)
7、go 的 url 中的通配符 “:” 和 “*”
- 通配符以’:’(参数)或’*’(通配符)开头。
- 通配符名称不能包含:和*。
- 通配符不能没有有名称。
背景:
在反向代理的时候,有如下接口:
api/v1/test
api/v1/test/jiekou
api/v1/test666/jiekou
api/v1/test666/jiekou1
现希望前面4个接口转发到 6666 端口,api/v1/test666/jiekou 和 api/v1/test666/jiekou1 转发到 8888 端口.
解决办法:
[proxy-1]
ReqPrefix = "api/v1/:param/*path"
Target = 'http://172.66.0.8:6666'
[proxy-2]
ReqPrefix = "api/v1/:param"
Target = 'http://172.66.0.8:6666'
[proxy-3]
ReqPrefix = "api/v1/test666/*path"
Target = 'http://172.66.0.8:8888'