技术分享之《Golang高性能实战》

前言:

      2018年后就一直计划在公司做一次技术分享,但迟迟没有确定好主题。正好公司好多同事都开始学习golang,  正好到了由入门到晋级的阶段。 我这边golang也有一两年的经验了,对于golang在高并发场景上的设计有一定的理解,所以 有了这次的分享。 分享的主题是 golang高性能实战, 偏高性能方面的设计,golang本身的描述其实不多,更多的是架构及框架的设计。

      ppt中描述的项目是一个高频的cdn管理系统,我们在开发中遇到了不少的问题,大多数是可以通过pprof分析cpu, heap图来分析和解决的,配合perf top, strace 我们是可以统计syscall系统调用及stack的统计。对于有些tcp的状态,我们会使用tcpdump来抓包分析。

      对于ppt中描述的 分段锁map, 顺序化排队,时间轮,rpc设计,分布式限频,分布式选主,reactor架构等等设计思路都会在以后的博文里更新说明的。

该文章后续仍在不断更新中, 请移步到原文地址  http://xiaorui.cc/?p=5138

ppt的分享地址:

ppt在github中的地址,https://github.com/rfyiamcool/share_ppt  , 记得给我star和follow啊.

slideshare的分享地址,https://www.slideshare.net/rfyiamcool/golang-93339108

ppt的下载地址, http://xiaorui.cc/static/golang_fast.pdf 

从PPT里抽取出来的文本内容:

# xiaorui.cc

1. github.com/rfyiamcool xiaorui.cc Golang ⾼高性能实战
2. CDN 刷新系统 是一个高性能、可扩 展的分布式系统. 支持全网刷新及预缓存业务.
3. 架构及框架 ⽬目录 Contents 1 调优2 深度排雷雷3 上线部署4 总结5
4. 架构及框架设计1
5. 架构设计 EngingSrv SLBClient EngingSrv Proxy Proxy Proxy HA http rpc
6. 框架设计 Poller Pusher Notify Saver TimerRester RingBuffer Chan Chan Chan Chan disPatcher DelayCtl EventBus ChanChan
7. 框架设计 • 模块之间解耦 • 复⽤用性强 • ⽅方便便监控 • 分布式扩展 • 读写⾼高的场景 • 使⽤用ringbuffer避免chan的锁消耗 • EventBus • 控制功能worker池的start / stop
8. 技术选型 语⾔言: Golang 框架: Gin 压缩: Snappy 序列列化: Json-iterator ORM: Gorm 客户端: imroc/req ⽇日志: Logrus 配置: Toml 缓存: Redis 持久化: Mysql
9. 设计调优2
10. 基本优化 提前预估size, make 之 临时的map, slice,可采⽤用sync.pool ⼤大于32kb,也可使⽤用 sync.pool
11. 基本优化 不不要滥⽤用time.After,多⽤用事件通知 不不要滥⽤用goroutine, 减少gc压⼒力力 不不要滥⽤用锁, 引起runtime上下⽂文切换 使⽤用unsafe装换类型 不不要总是[]byte、String转换.
12. 基本优化 减少使⽤用reflect反射使⽤用 减少不不必要的defer调⽤用 使⽤用atomic实现⽆无锁逻辑
13. 业务调优 减少⽹网络io的消耗 批量量接⼝口 缩减⽹网络调⽤用链 使⽤用连接池 缓存内存化 减少同步逻辑 使⽤用压缩, 节省带宽
14. 通信协议更更换 HTTP vs RPC
15. 全局任务状态 Map Syncer Rwlock Map Rwlock Caller CRC32 &mod . . . . . 64 region in Pool各种嵌套 MAP
16. 全局任务状态-code
17. 缓存多副本 Caller Buffer Syncer RR Sched Rwlock Buffer Rwlock Buffer Rwlock
18. 操作顺序排队 MysqlDB Channel Poller Channel Channel Pusher CRC32 &mod insert ! Update ! Poller Poller Pusher Pusher Saver Saver Saver 如果update先被插⼊入?
19. 操作顺序排队-CODE
20. 时间轮设计 struct timerEntry { heap cas } Ticker callBack callBack 小顶堆 最大程度减少 锁冲突 !!! 回调队列 callercallBack 按照刻度扫描
21. 分布式选主 Engine Engine set key hostname ex 10 nx set ex 过期 nx 原⼦子 for{ if set(key, nx, ex) { expire(10) } time.Sleep(n) }
22. 区分快慢队列列 Fast Queue Slow Queue Worker Worker Worker Worker • 避免单⼀一队列列因慢任务阻塞 • 排队执⾏行行慢任务, 减少后端压⼒力力 提⾼高吞吐 !
23. 设备下线延迟队列列 queue (task) queue queue set(host) Control 下线/ 上线 proxy proxy proxypipeline push • redis pipeline 减少RTT消耗 • lua 保证组合命令的原⼦子性 bulk put 吞吐提升 30%
24. 排查死锁技巧 Acquie()Caller Caller ⽅方便便调试, 抽象锁⽅方法. 加⼊入 代码⾏行行 及 随机id acquie加⼊入, release删除. 单⼀一的就是死锁了了Release()
25. 排查OOM fatal error: runtime: out of memory runtime stack: runtime.throw(0x665297, 0x16) /usr/local/go/src/runtime/panic.go:596 +0x95 runtime.sysMap(0xc4a0200000, 0x80000000, 0x0, 0x79f638) /usr/local/go/src/runtime/mem_linux.go:216 +0x1d0 runtime.(*mheap).sysAlloc(0x786b20, 0x80000000, 0x0) /usr/local/go/src/runtime/malloc.go:440 +0x374 追踪内存泄露露点 OR 增⻓长点
26. 开放stats状态接⼝口 { "alert_queue": 30, "input_task_queue": 400, "notify_queue": 1092, "pushing_queue": 311, "reporting_queue": 612, "saver_queue": 3197, "tasks_in_cache": 13189, "timer_tasks": 13189, "timestamp": 1521682815 "qps_proxy": { "15m": 9031, "1m": 10221, "5m": 11005, "avg": 91110, "count": 897811 }, "qps_boss": { "15m": 5113, "1m": 5200, "5m": 5300, "avg": 5001, "count": 297811 }, }
27. 加⼊入分布式限频 Engine counter INCRBY() SYNC() RESET() Engine 全局计数 异步同步 5s sync incr counter INCRBY() SYNC() RESET() 5s sync lua解决并发修正的计数的问题 !
28. 深度排雷雷3
29. SystemTool perf top go tool pprof iostat、vmstat、top strace lsof、netstat、sar wrk
30. Established 暴暴增问题
31. Established 暴暴增问题 net/http 连接池的维度是 http.Transport. 问题代码出在, 不不断的实例例化 http.Transport
32. TimeWait 暴增到 4W … 连接池引起的TimeWait暴暴增 TimeWait 暴增到 3W … TimeWait 暴增到 3.5W … TimeWait 暴增到 5W …
33. TimeWait 暴增到 4W …TimeWait 暴增到 3W … TimeWait 暴增到 3.5W … TimeWait 暴增到 5W … http.DefaultTransport.(*http.Transport).MaxIdleConnsPerHost = 200 const DefaultMaxIdleConnsPerHost = 2 t := &http.Transport{ … DialContext: (&net.Dialer{ …. }).DialContext, MaxIdleConnsPerHost: 200, MaxIdleConns: 1000, … 连接池引起的TimeWait暴暴增
34. 系统线程暴暴涨
35. 系统线程暴暴涨 strace -fp pid -s 512 write(8, …… mmap(NULL, 10489856, PROT_READ|PROT_… mprotect(0x7f04d1e6d000, 4096,… clone(child_stack=0x7f04d286cff0, … clone(child_stack=0x7f04d286cff0, lsof -P -p pid -n refresh_master 2184 root 8u REG … refresh_master/logs/http.log • 线程不会被回收 • 每个线程8M内存 • 增加调度消耗 • cpu cache miss • more
36. 忙轮询 ... for { select { case res = <-dataQueue: retrun res case <-done: return res default: // do something } return res } ... • perf top 可以看出 select chan奇⾼ • strace ⼏乎⽆调⽤, 单纯cpu指令 • top 直接 100%
37. 忙轮询 • perf top 可以看出 select chan奇⾼ • strace ⼏乎⽆调⽤ • top 直接 100%
38. 定时器器 | 断续器器 空跑 1000个协程,sleep 100ms的⽕火焰图表现
39. 定时器器 | 断续器器 futex、pselect6 syscall 出奇的⾼高 !
40. 定时器器 | 断续器器 解决⽅方法: • ⾃自定义定时器器 • 减少select的超时逻辑 • sleep 时间周期放⼤大 time.After time.NewTicker
41. Nginx的time_wait问题
42. Nginx的time_wait问题 upstream ats{ server 127.0.0.1:8080; … keepalive 100; } location / { proxy_set_header Host $host; proxy_pass http://ats; proxy_http_version 1.1; proxy_set_header Connection ""; }
43. defer的消耗 go 1.10 汇编: call runtime.deferreturn, add xx SP return
44. Json性能问题
45. Json 性能问题 • 上层代码 • 直接替换 • 第三⽅库修改⽅法 • 修改Vendor相关库的引⽤ . • 使⽤Monkey的替换标准库 . • 固定数据结构 • json-iterator/go • 不固定数据结构 • buger/jsonparser 减少反射, 减少对象创建 !!!
46. logrus json ( map vs struct ) struct > map cost 9.27s
47. logrus json ( map vs struct ) cost 4.09s
48. GC 耗时调优 压测环境下, GC占⽐比⾼高达16% GC的STW时间也会暴暴到⼏几秒 !!! GODEBUG=gctrace=1
49. GC 耗时调优 通过Heap确定, io.copyBuffer逻辑导致的问题 ! 业务代码是通过io.Copy来调⽤用的copyBuffer !
50. GC 耗时调优 当为nil的时候, copyBuffer会new⼀一个对象 使⽤用sync.pool来复⽤用[]byte对象
51. GC 耗时调优 已经压缩到 1%, gc的次数明显减少 !
52. GC 性能对⽐比 在我们的项⽬目⾥里里 go 1.10 > go 1.9 > go 1.8 sync.pool 效果明显
53. 主动介⼊入runtime调度调度 P M M M G G G G G P G G G G G P G G G G G Runtime Accept M recv • LockOSThread • 增加 P 队列列 • CPU 亲和性
54. • 压测⼯工具 • wrk > ab > golang • 不不要本地环路路测试 • 多源压⼒力力 • 注意带宽问题 • 注意内核的种种error • more… 压测 Mar 15 16:56:55 xxx kernel: nf_conntrack: table full, dropping packet. Mar 15 16:56:55 xxx kernel: nf_conntrack: table full, dropping packet. Mar 15 16:56:55 xxx kernel: nf_conntrack: table full, dropping packet. Mar 10 13:45:11 xxxx kernel: TCP: time wait bucket table overflow Mar 10 13:45:11 xxxx kernel: TCP: time wait bucket table overflow Mar 10 13:45:11 xxxx kernel: TCP: time wait bucket table overflow more ….
55. wrk -t8 -c800 -d100s -T10s --script=js --latency http://xxxxx:9181/v1/engine/task thread addr: xxxxx … 8 threads and 800 connections Thread Stats Avg Stdev Max +/- Stdev Latency 19.60ms 11.97ms 144.14ms 73.42% Req/Sec 23.73k 2784.29 29.85k 79.29% Latency Distribution 50% 17.90ms 75% 24.92ms 90% 34.63ms … Requests/sec: 23189.11 Transfer/sec: 5.06MB Wrk
56. 上线部署4
57. 打包⽅方法 Engine端 • build • pack • repo • salt • unpack • reload Proxy端, 封装RPM包上线 . go build -ldflags "-X main.Version=V0.1.10" main.go
58. EngingSrv SLBMGR EngingSrv Proxy Proxy 灰度平滑迁移-SLB层 • 通过SLB删除主机 • Engine不不接受新任务 • Engine正常处理理回报 • client需要有重试机制 权重及删除主机的区别 http rpc
59. 灰度平滑迁移-框架层 ChannelPoller Pusher push Push Pull Pull # 伪代码 recvAndSetExit(signal) publishSigWorerks() if globalExited && len(queue) == 0 && pushExit { return } wait(saveQueueExit) wait(eventBus) • 从调⽤用链最前端开始顺序关闭. • 持久化的数据要优先保证 • join住所有的业务协程.
60. 收集分布式⽇日志 Producter • 追踪所有调⽤用链 • 计算耗时 • 排查问题 Kafka tcp/udp inotify Rsyslog Producter File ElasticSearch comsumer
61. 如何报警通知 ChannelPoller Pusher push Push Pull AlertMan 服务本身主动去上报异常 ! 异步去上报 ! 异常抽样报警 !
62. 状态监控⻚页⾯面
63. 总结5
64. 性能演进 1w 1.5w 2w 3w + QPS
65. GC 还需优化
66. 协程太多
67. Q & A


下面是ppt的部分截图,  只是很可惜这次分享没有录制视频,等有机会给大家录制视频补上。



更多内容请到github上看。


END .


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc