golang安装,常用框架安装,记忆点
0.安装
虚拟机扩容
【Linux干货分享】LVM快速扩容虚拟机磁盘_哔哩哔哩_bilibili
newvim
安装
sudo add-apt-repository ppa:neovim-ppa/stable
sudo apt-get update
sudo apt-get install -y neovim
最强Vim新手指南,手把手教你打造只属于自己的代码编辑器!_哔哩哔哩_bilibili
linux 文本三剑客
【比刷剧还爽 】B站唯一讲的最好的Linux三剑客教程,深入理解grep、sed、awk!_哔哩哔哩_bilibili
安装go
All releases - The Go Programming Language
Download and install - The Go Programming Language
设置为GO111MODULE=on
go mod init test
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
go env
如果已经有代码,那么就用下面的,能够让go.mod根据代码自动get库
go mod tidy
安装并配置mysql,能够在bench和服务器访问
ubuntu22.04安装mysql8并授权远程登陆_哔哩哔哩_bilibili
安装配置redis
wget http://download.redis.io/releases/redis-6.2.5.tar.gz
tar -zxvf redis-6.2.5.tar.gz
cd redis-6.2.5
make
cd deps
make hiredis linenoise lua jemalloc
cd hiredis
sudo make install
cd ../lua
sudo make install
cd ../..
sudo make install
//后台运行
vi redis.conf
//修改
stop-writes-on-bgsave-error = no
daemonize yes
cd src
redis-server ../redis.conf
安装gin
有关模板和gin模板
lesson01_内容介绍_哔哩哔哩_bilibili
go get -u github.com/gin-gonic/gin
安装gorm
https://www.bilibili.com/video/BV1xg411t7RZ/?spm_id_from=333.337.search-card.all.click&vd_source=3116d28903cf9e9d76cd88eaef0fda0f
go get -u gorm.io/gorm
安装viper,用来解析配置文件yaml
go get -u github.com/spf13/viper
logrus,用来记录日志
go get -u github.com/sirupsen/logrus
swaggo,自动api文档
go get -u github.com/go-openapi/swag
govalidator,提供对文本格式校验
go get -u github.com/asaskevich/govalidator
安装protobuf和grpc
grpc官网 Quick start | Go | gRPC
使用snap安装protobuf
sudo apt install snapd
sudo snap install protobuf --classic
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
export PATH="$PATH:$(go env GOPATH)/bin"
source $HOME/.profile
网络相关:
1. GO HTTP
Golang http 标准库底层原理解析_哔哩哔哩_bilibili
2. Websocket,长轮询
websocket是什么?和HTTP是什么区别?长轮询是什么?服务器推是什么?_哔哩哔哩_bilibili
数据结构和其他
1.iota
iota在const变量中使用,用来初始化枚举类,初始值为0
numbers := []int{1, 2, 3, 4, 5} // 创建一个长度为5的切片
const (
BEIJING = iota+1
SHANGHAI
SHENZHEN
)
2.defer
类似于c++的析构函数,会在函数结束时调用被修饰的函数,多个defer会按照栈的方式调用,即先定义的后调用。会先执行return后defer
3.slice
切片,在函数传参时如果传入切片,那么会是引用传递
下面,数组大小为5,申请空间为10,和c++vector一样
numbers := make([]int, 5, 10) // 创建一个长度为5的切片
直接初始化:
numbers := []int{1, 2, 3, 4, 5} // 创建一个长度为5的切片
numbers = append(numbers, 4, 5) // numbers 现在是 [1, 2, 3, 4, 5]
如果,用make后再append,那么会在5后面插入,而不是10后面
删除。注意,[:]新生成的数组只是在原数组上加了两个新的范围指针,没有复制新的内存
这里把后面的数据追加到0号数据,这样会删除0号,其中。。。用来展开
numbers := []int{1, 2, 3, 4, 5}
if len(numbers) > 0 {
numbers = append(numbers[:0], numbers[1:]...)
}
扩容,一共四种情况
4.interface
类似于类中的函数,不同的interface可以相互继承,直接在interface中添加就行,用这种方式实现继承,一个struct必须要实现interface的所有方法才可以实现多态
也可以做万能类型
5.万能指针
类似于模板参数
func myfunc(arg interface){
//断言
value,ok:=arg.(string)
if !ok{
fmt.Println("not string")
}else{
fmt.Println("string")
fmt.Println(value)
}
}
6.pair映射
每个数据都用pair<type,value>
这里,alltype万能类型会传递a的pair
var a string
a = "aasdad"
var allType interface{}
allType = a
func reflectNum(arg interface{}){
inputType := reflect.TypeOf(arg)
inputValue := reflect.ValueOf(arg)
Println("type:",reflect.TypeOf(arg))
Println("value:",reflect.ValueOf(arg))
for i:=0;i<inputType.NumField();i++{
field := inputType.Field(i)
value := inputValue.Field(i).Interface()
Printf("name:%s: %v=%v\n", field.name, field.type, value)
}
}
7.init
每个包都会有默认的init函数,如果只想执行init,不想执行其他函数,那么用
import _ "xxx"
8.结构体标签和json
package main
import (
"encoding/json"
"fmt"
)
type Movie struct{
Title string `json:"title"`
Year int `json:"year"`
Price int `json:"rmb"`
Actor []string `json:"actors"`
}
func main(){
movie := Movie{"喜剧之王", 2000, 10, []string{"xingye", "zhangbozhi"}}
jsonStr, err:=json.Marshal(movie)
if err!=nil{
fmt.Println("json marshal error", err)
return
}
// fmt.Printf("jsonStr = %s\n", jsonStr)
mymovie := Movie{}
err = json.Unmarshal(jsonStr, &mymovie)
if err!=nil{
fmt.Println("json unmarshal error", err)
return
}
fmt.Printf("%v \n", mymovie)
}
9.GMP
解说Golang GMP 实现原理_哔哩哔哩_bilibili
一、复用线程
查找流程
每61次调度,会从全局队列调度一次。
否则,先找本地队列,调度失败则找全局队列,调度失败则找网络协程,调度失败则窃取其他p的一半goroutine(尝试4次,只要有一次成功窃取就停止)到本地p队列中。
即本地->全局->网络->窃取
每61次调度->全局->...
hand off机制
当用户主动让出p时,首先会把gouroutine的状态由running转换成runnable,之后goroutine和线程解绑,之后把goroutine放置到全局队列中。
gouroutine被动阻塞时,把goroutine的状态转换成waiting,之后和线程解绑。之后哪个p唤醒了gouroutine,则把它放在这个p的当前队列中。
当goroutine正常结束时,把状态换成dead,之后解绑,进行下一轮的调度。
抢占调度,当发起系统调用时,m繁忙,当没有空闲m时,需要给p抢占一个新的m
二、利用并行
三、抢占
以前的线程需要主动让出cpu,但是现在有了限制时间
四、全局G队列
当main gorotine结束时,子gorotine会立即结束
10.channel
用来协程之间的通信,本身可以让main go和子go同步,不会让main go提前结束
package main
import (
"fmt"
)
func main(){
c :=make(chan int)
go func(){
defer fmt.Println("goprotine结束")
fmt.Println("goroutine 正在运行。。。")
c <- 666
}()
num := <-c
fmt.Println(num)
}
defer fmt.Println("goprotine结束") 永远会在num := <-c之后执行
当缓存超出,那么会阻塞,等待其他go协程取出channel元素, 然后协程才会继续进行
当缓存为空,取数据也会阻塞
往已经关闭的channel发送数据会报panic错误
从已经关闭的channel读数据,需要看缓存中是否有数据,如果没有则报错,否则读取
对于nil channel,不管读写都会报错
func main(){
c := make(chan int, 4)
go func(){
for i:=0; i< 4;i++{
c<-i
}
close(c)
}()
for{
if data,ok<-c;ok{
fmt.Println(data)
}else{
break
}
}
}
可以简化为
for data:= range c{
fmt.Println(data)
}
select
会在case中寻找满足条件,哪个先满足选哪个,如果都不满则,则会走default,如果没有default,则阻塞
func ff(c, quit chan int){
x,y :=1,1
for{
select{
case c<-x:
x = y
y = x + y
case <-quit:
fmt.Println("quit")
return
default:
return
}
}
}
func main(){
c:=make(chan int, 6)
quit:=make(chan int)
go func(){
for i:=0;i<6;i++{
fmt.Println(<-c)
}
quit<-0
}()
ff(c,quit)
}
context
context_interface_哔哩哔哩_bilibili
用来进行传输数据,取消信号等
有context.Context 为interface,一个Context实例
context.Background() 创建一个父context,启动goroutine
ctx.Done() 判断协程是否结束
ctx = context.WithValue(context.Context, string,string) 传入父context,值key,value,可以在其他协程中使用
ctx.Value(key) 获得对应context的key的value
ctx,cancel := context.WithCancel(context.Context)取消一个协程
cancel()会取消当前线程
ctx,cancel := context.WithDeadline(context.Context,time.Now().Add(time.Second)) 在一秒后自动删除协程
ctx,cancel := context.WithTimeout(context.Context, time.Second) 在一秒后自动删除协程
11.内存管理
页索引&heapArena_哔哩哔哩_bilibili
p为处理器,每个mcache都会有自己的一部分缓存,首先程序需要在mcache找是否有合适的缓存,这一部分没有锁。如果没有需要找mcentral,mcentral共有67个等级,每个等级的预留缓存大小也不相同,程序根据自己的大小,去mcentral寻找是否有合适的缓存,由于可能有多线程,所以需要加锁处理,但是锁粒度较小。如果没有,再去mheap找,这里锁粒度较大。如果还是没有,需要从操作系统申请更多缓存。
mcentral根据大小划分等级。
在mspan中加标签,确定哪些页被程序占用。
在 Go 语言的内存管理系统中,mspan
作为内存分配的基本单位,通常表示的是一个或多个连续的内存页(page)。然而,对于较小的内存请求,Go 语言采用了特殊的优化技术来减少内存浪费,这就是所谓的“小对象分配优化。所以可能会有内存碎片产生
nospan,写错了。
如果mspan中都是基础类型,那么最低位为0,如果只要有1个指针,那么最低位就会为1.只要最低位为1,就需要给整个mspan检查,看是否需要给指针数据释放内存
mcache内容,等级0为无内存上限,所以总共136种。
mcenctral内容。每一个mcentral对应一个mspan,同时把span分成空闲列表和满列表
在mheap中,会给所有的页加标签,如果为0则自由,如果为1则被mspan申请了。
由于在mspan中可能有很多span,那么找到哪些span空闲就是问题,使用基数树
有一个64位的pallocsum,首位不用,剩下的分为3个部分,分别是start,max和end,表示首次出现空闲页的span序号,连续空闲空间最大的span序号,以及最后出现空闲span的序号。图画错了,不是连续大小。和span分配位图取与来获得对应的数据
同时,给pallocsum进行分级,把位逐渐的细粒度化,最后一级的单位为512个页
P申请内存过程。P在小于16B为微对象,16B到32KB为小对象,大于32KB为大对象,对应mspan的0号等级,即直接在mheap找。
mcache的tiny allocator,以2B的倍数为单位
12.垃圾回收
并发三色标记_哔哩哔哩_bilibili
三色标记法
把自身存活并且所指向的结点也被标记的结点,变成黑色结点
把自身存活,但是指向结点没有被标记的结点,变成灰色结点
把未扫描的结点,加上白色结点
采用dijkstra算法
第一步,把根节点(包括全局变量,栈上局部变量)变黑,根节点指向的结点变灰,并从灰色结点逐步扫描
第二步,从灰色结点扫描,并把周围所有可以置成灰色的结点变灰
第三步,如果所有结点都变灰,那么结点变黑,如果有结点是白色,则对白色结点的引用
第四步,从第二步循环
第五步,如果有结点依旧是白色,则垃圾回收
并发三色标记法
在并发下,可能出现漏标的问题
也有可能出现多标的问题
混合写屏障机制
可以看出,漏标的原因有两点,一是黑色结点指向了白色结点,二是灰色对象删除了对白色对象的引用,导致白色结点不会被扫描,由此可以引出强弱三色不变式
强三色不变式:插入写屏障
白色不能被黑色对象引用
做法为,如果黑色对象引用了一个白色对象,那么就把黑色对象变灰
弱三色不变式:删除写屏障
不会删除对白色对象的引用
做法为,如果一个白色对象被引用,那么就把白色对象变灰,并不会删除对这个对象的引用
为什么需要混合两者:
如果频繁的使用屏障调用函数,那么开销很大,因此屏障只会作用于非栈对象
那么,看看能否解决漏标和多标的问题:
13.map
map不能够并行的读写,或并行写,只能并行读
map采用桶数组。每一个桶有8个key-value pair。
可以说,map解决hash冲突的方式是拉链法和开放寻址法的结合。
对比:c++ map的扩容条件是hash因子达到0.75
map的数据结构:
包括两个map。其中,溢出桶用来存放hash桶溢出的数据,避免桶因扩容导致的频繁的再hash
14.sync.map
作为map的可并行版本,下面是数据结构
可以看到,相对于map,多了一个dirty map和锁,misses。
dirty map用来把读操作和写错做分开,从而减少因并行导致的加锁行为。
删除数据时,当删除read map数据,则把数据指针变成nil,称之为软删除态。当删除dirty 数据,则物理删除,为硬删除态。
在读数据时,如果在read map中读不到数据,则进入dirty map中读取,并且misses++,当misses达到阈值时,则把dirty map作为新read map,新dirty map暂时为空。
在写数据时,需要先查看read map是否有数据,因为可能是修改操作。当dirty map为nil时,且需要写入到dirty map,需要把read map复制dirty map,同时遍历read map,把非删除态数据复制,软删除态数据进行物理删除。
可以看到,如果频繁的把数据从dirty map复制到read map,那么sync.map效率不会很高。
具体来说,可以看到,当misses频繁增加,则会有很大性能影响。所以当有频繁读取新加数据时,会造成sync.map性能不如直接加锁的性能好。