1、原生的json包无法解析未知格式的json数据
解决方法:使用第三方的simpleJson包解析数据
2、空map不能赋值
func main() {
var t map[string]string
t["name"]="zhangsan" //报错
}
解决方法:先初始化map
func main(){
t := make(map[string]string)
t["name"]="zhangsan" //正常
}
3、如何使用go map同时储存多种类型的value
如下定义:只能储存string类型的value。
mapStr := make(map[string]string)
如下定义:只能保存int类型的value。
mapInt := make(map[string]int)
如下定义:可以保存string、int等不同类型的value。
mapInterface := make(map[string]interface{})
mu := make([]map[string]interface{},0)
a1 := map[string]interface{} {"id": 1, "parentId": 0, "createTime": "2020-02-02T06:50:36.000+0000", "title": "商品", "level": 0,
"sort": 0, "name": "pms", "icon": "product", "hidden": 0}
a2 := map[string]interface{} {"id": 2, "parentId": 1, "createTime": "2020-02-02T06:51:50.000+0000", "title": "商品列表", "level": 1,
"sort": 0, "name": "product", "icon": "product-list", "hidden": 0}
mu = append(mu, a1)
mu = append(mu, a2)
3、url.Values添加重名Key不会覆盖,而是会变成一个数组。
var BaseArgs = url.Values{}
BaseArgs.Add("key","value1")
BaseArgs.Add("key","value2") //value1和value2会以数组的形式存储起来
正确用法:
var BaseArgs = url.Values{}
BaseArgs.Set("key","value1")
BaseArgs.Set("key","value2") //value2覆盖value1
4、打开的网络请求必须关闭,否则内存泄露多了程序会崩溃
req, err := http.NewRequest(method, requrl, nil)
_ = "错误校验"
if err != nil {
return res, err
}
client := &http.Client{}
resp, err := client.Do(req)
if resp != nil {
defer resp.Body.Close() //此处必须关闭,否则开的多了程序会崩溃
}
if err == nil { //请求成功
statuscode := resp.StatusCode
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
fmt.Println(statuscode)
}
5、多线程同时修改同一个变量,程序会崩溃。
需要加锁:
import sync
var lock sync.Mutex // 定义一个互斥锁
lock.Lock()
//操作公共变量
lock.Unlock()
6、多协程并发时,某个协程崩溃会导致整个进程崩溃
解决方法:为相关方法添加异常捕获,确保程序不崩溃
func function_name()() {
defer func() { // 必须要先声明defer,否则不能捕获到panic异常
if err := recover(); err != nil {
b := fmt.Sprint(err)
writeExceptionLog(b)
}
// fmt.Println("d")
}()
_:"这下面编写你的业务代码"
}
7、main方法结束之后,在main方法中启动的协程也会自动结束
问题现象:
func main() {
go hello() // 启动另外一个goroutine去执行hello函数
fmt.Println("main goroutine done!")
}
执行结果只打印了main goroutine done!,并没有打印hello函数中的内容
问题原因:
在程序启动时,Go程序就会为main()函数创建一个默认的goroutine。
当main()函数返回的时候该goroutine就结束了,所有在main()函数中启动的goroutine会一同结束,main函数所在的goroutine就像是权利的游戏中的夜王,其他的goroutine都是异鬼,夜王一死它转化的那些异鬼也就全部死掉了。
解决方法一:
想办法让main函数等一等hello函数,最简单粗暴的方式就是time.Sleep了。但是这样不好。
解决方法二:协程同步
import "sync"
func hello(wg *sync.WaitGroup) {
_="这里写你的业务"
wg.Done()
}
func main() {
var wg sync.WaitGroup
wg.Add(1)
go hello( &wg)
fmt.Println("main goroutine done!")
// main方法等待hello结束之后再推出
wg.Wait()
}
8、理解进程、线程、协程
- 一个进程可以有多个线程(至少有一个主线程),
- 一个线程上面可以挂载多个协程,通过调度算法合理的分配各个协程的执行时间,
- 协程和线程是多对多的关系。一个线程上面可以同时挂载多个协程。一个协程X1刚开始挂载在线程B上面执行,一段时间后,线程A上面的协程全部执行完了,但是线程B上面还有好几个协程没有执行完毕,协程X1有可能会被重新分派到线程A上去执行。
- 操作系统对协程是完全没有感知的。
- 一个GO进程默认会开启和当前机器CPU数量一致的线程数,可以手动调节最大线程数。
9、go语言代码格式化神器
go fmt myProject
10、switch 的break问题
switch无需显式执行break,每个case语句后面自带break,可以通过fallthrough强制执行紧挨着的下一个case里面的语句(无论紧挨着的下一个case的判断条件是否成立),default语句块中不允许使用fallthrough语句。
eg:
t:=1
switch {
case t<4:
fmt.Println("The integer was <= 4")
fallthrough //强制执行下面的<=5的输出
case t<0:
fmt.Println("The integer was <= 5")
fallthrough //强制执行下面的<=6的输出
case t<6:
fmt.Println("The integer was <= 6")
fallthrough //强制执行下面的default语句
default:
fmt.Println("default case")
}
最终所有输出语句都会被执行。
11、跳出代码块 for、 swtich、select
方法:使用break。用法和其他语言一样
注意:只跳出当前代码块,只跳出当前循环。
12、go 语言当中channel有什么特点,需要注意什么?
如果给一个 nil 的 channel 发送数据,会造成永远阻塞
如果从一个 nil 的 channel 中接收数据,也会造成永久阻塞
给一个已经关闭的 channel 发送数据, 会引起 pannic
从一个已经关闭的 channel 接收数据, 如果缓冲区中为空,则返回一个零值
无缓冲的 channel是同步的,而有缓冲的channel是非同步的。
13、simpleJson中单引号和双引号不一样
simplejson_Json.Get("status").MustInt() //正确
simplejson_Json.Get('status').MustInt() //错误,取json中的键值对的值时,传递的键名称必须使用双引号包裹。
14、变量作用域
同一个方法内的,一个代码块里面申请的变量,在该代码块外面不能访问。
eg:
func main() {
var t=1
if t>3{
var a=true
}else{
var a=true
}
fmt.Println("a=",a) //此处报错,提示找不到变量a
}
15、同样的Linux命令,手动执行和代码执行结果不一样
现象:手动执行以下命令会按照预期创建分区为1 、副本为3的topic; 使用代码执行以下命令会生成分区为1、副本为1的topic。
"ansible kafka1 -m shell -a'/root/kafka/kafka_2.12-2.3.0/bin/kafka-topics.sh --create --replication-factor 3 --partitions 1 --zookeeper localhost:2181 --topic test_topic'"
解决方法:
将该命令写入文件,然后执行文件。就得到了和预期一致的结果。
16、自定义的Flask服务默认使用http访问
问题现象:使用https方式访问自定义的Flask服务失败。
解决方法:改用http方式访问。
17、读写锁的应用
go语言中2个协程同时读和写同一个变量会导致程序崩溃(该异常无法被defer捕获),需要加锁。
需求:多个协程可以同时读一个公共变量,但是有读的时候不能有写。
实现:
import (
"sync"
)
//全局变量
var count int //定义全局公共变量
var rLock sync.RWMutex //定义读写锁
func Read() {
for{
rLock.RLock() //加读锁(读锁可以获取多个。当有写锁没有释放时,无法获取读锁。)
fmt.Printf("读取count数据", count)
defer rLock.RUnlock() //释放读锁
}
}
func Write() {
for{
rLock.Lock() //加写锁(写锁全局只能获取一个。且有读锁没有释放时,无法获取写锁)
count += 1
fmt.Printf("写 count 数据 ", count)
rLock.Unlock() //释放写锁
}
}
func main(){
go Read()
go Read()
go Write()
}