1.goroutine&线程
- 大小: 线程1~8MB 协程2KB
- 调度: 线程由操作系统调度 每个线程占用cpu的一段时间 线程的上下文切换涉及 用户态->内核态->用户态的转换、成本us级
协程由runtime调度 每个协程占用线程的一段时间 协程的上下文切换 只在用户态 成本ns级 - 阻塞: 线程被内核挂起让出cpu,涉及上下文切换
G在执行用户态调用时阻塞 G被runtime挂起,立刻运行下一个准备好了的协程
G在执行系统调用时阻塞 handoff: 见下图 - 调用: 系统调用: 例如读写文件、读写socket(go这里做的比较好阻塞时只有G被挂起)
用户态调用: 例如等channel可写/读 等锁 等条件变量 ⬑ 协程调度
2.内存逃逸
- 定义: 内存的生命周期变化 导致原本分配在栈上的内存逃逸到堆上需要GC 比如子函数返回内存的指针、内存被另外一个协程使用
- 如何查看逃逸
developer@srv-005:/data/src/phgames/api/rpc/knock/games/goldmaster $ go build -tags goldmaster -gcflags="-m" -o /dev/null # phgames/api/rpc/knock/games/goldmaster ./knock.go:29:9: &Tap{...} escapes to heap ./knock.go:45:20: make([]*protoKnock.GoldMasterSpin, 0) escapes to heap ./playerExt.go:33:9: &PlayerExt{...} escapes to heap - 编译参数
-gcflags="all=-N -l" //开发服使用 -N关闭优化 -l关闭内联
3.GC
- GC的触发条件: Runtime会维护一个NextGC变量(下次GC阈值) & 定时触发 & 业务代码调用runti.GC()生成环境一般不用会影响性能
- 黑灰白 白:不可达内存/或者更准确一点GC不知道它存在的内存(所有内存初始状态为白) 黑:可达内存 灰:等待变黑的内存(以及子指针待扫描)
- GC流程 stw: stop the world
Root Scan //stw扫描所有根对象 全局变量/栈/CPU寄存器 ↓ 并发Mark //1.顺藤摸瓜 把根对象里面的子对象加入到灰队列,自己变成黑 //2.写屏障:有如果有指针改变指向,把指针后面指向的内存加到灰队列里面去 //3.消费灰队列,自己变黑,子对象加入到灰队列 这是个递归循环 //4.灰队列消费差不多以后执行下一步 ↓ Mark Termination //消费灰队列 //这里要stw的原因 1.把可达的新内存加入到灰队列里面并不是一个原子操作 涉及寄存器硬件延时 会导致新内存还没被标识灰在下一步会被GC干掉 //2.业务协程可以疯狂的创建白色内存,需要stw停下来,最后扫描一次栈 ↓ Concurrent Sweep //标识为白的内存回收 - 如何减少GC压力
sync.Pool(对象池) 对象复用 减少临时对象 预分配slice strings.Builder(字符串拼接 扩容次数少)
4.http&websocket&socket
- http: 一问一答,服务器不能主动发消息给客户端,客户端想拿到服务器的主动消息 只能轮询。文本协议,属于应用层
- websocket: 解决了http上面的痛点,支持服务器向客户端主动推送消息。属于应用层
- socket: 操作系统提供的底层接口 不属于4/7层协议
5.进程间通讯
- socket -> (tcp/udp)
- gRPC
包括:系列化/反序列化/连接池/http2多路复用(无序)/消息头/粘包拆包/Keepalive/重连(部分场景)/超时
不包括:业务处理顺序/业务心跳/幂等/消息去重
htt2多路复用:可以简单理解为把消息分解成帧用一个tcp发送,就会导致消息到达服务器是无序的。
实际是消息被分成帧在多个stream上分时复用一个tcp链路发给服务器,同一个消息的多个帧只能在一个stream上,在同一个stream的消息到达服务器是有序的 - HTTP
- MQ
- redis
6.进程内通讯
- channel 环形队列+不可读/写阻塞协程且把协程加入到对应队列(sendq recvq)+可读/写状态从0->1唤醒+线程安全
不会扩容,初始化时容量就固定了 make(chan T, 64)
make(chan T) 无缓冲接收双方必须都到场 否则到场方阻塞
v, ok := <-ch ok=false为写已关闭 关闭时v为默认初始化值
向已关闭的channel写数据会panic 所以谁写谁关闭 多个协程写channel的时候 如果要关闭可以用屏障确保多个协程都不会再写channel才能关闭 - 共享内存+锁
- 条件变量
- atomic
- WaitGroup屏障
7.context
- 用于请求生命周期管理。客户端断开、超时、主动取消时,服务器可以通过case <-ctx.Done():或者ctx.Err()!=nil感知到。
主要应用于等io的时候,例如mysql查询,go服务器收到客户端取消以后可以回收mysql连接,mysql会停止查询 - ctx树结构,ctx2, cancel2 := context.WithCancel(ctx1) ctx2是ctx1的子节点,父节点取消任务时子节点会收到通知,子节点取消任务时父节点不会受到通知
- ctx可以携带traceId,在打印日志的时候把traceId打印出来可以知道是哪一次请求的日志,避免两次相同的请求打印的日志混淆
8.go的多态 reflect&interface&any
- interface c++的纯虚类
- any等价interface{} 任意数据的容器
- reflect拆解容器的工具
reflect.Type //类型 -- Name() //类型名 -- Kind() //类型底层类型 struct/slice/map/ptr/int/string/bool/func -- Elem() //获取内部元素类型 比如获取ptr的指向类型 slice内的元素类型 map内的元素类型 -- Key() //map 的key -- In/Out //函数 参数/返回值的类型 -- NumMethod //类 公开函数个数 -- Size //占用内存大小reflect.ValueOf //值val := reflect.ValueOf(&User{}).Elem() typ := reflect.TypeOf(&User{}).Elem() for i := 0; i < val.NumField(); i++ { //遍历结构体字段 field := val.Field(i) //第i个字段值 fieldName := typ.Field(i).Name //第i个字段的名字 } for i := 0; i < typ.NumMethod(); i++ { //遍历调用类的函数 m := typ.Method(i) fn := m.Func fn.Call([]reflect.Value{}) }
9.go项目命令
go mod init project-name //新项目 创建go.mod文件
go env -w GOPROXY=https://goproxy.cn,direct //设置七牛代理
go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/ //设置阿里云代理
go env GOPROXY //查看环境变量
go mod tidy //自动整理依赖
go get github.com/gin-gonic/gin@v1.9.1 //添加依赖
go mod graph //查看依赖关系树
go build //编译
go run //当作脚本来执行文档更新时间: 2026-06-30 16:28 作者:morninglu
