当前位置: 首页 > news >正文

一些面试问题的深入与思考

Bug排查

原问题:多个服务的bug你是怎么排查的。如果是内存泄漏这种情况看日志看不了怎么办。
题解:内存泄漏的问题往往不会直接从日志中体现,需要用更多手段来定位解决。如下:

1、使用 Go 自带的性能分析工具

(1) pprof 工具(性能剖析)

pprof 是 Go 内置的性能分析工具,能分析内存、CPU 和 goroutine 使用等。如:heap:内存分配情况;goroutine:当前 goroutine 使用情况;cpu:CPU 使用情况
启动服务后,访问 http://localhost:6060/debug/pprof/ 即可查看不同类型的剖析数据,

import (_ "net/http/pprof" // 引入 pprof 包"net/http""log"
)func main() {// 在 6060 端口暴露 pprof 信息go func() {log.Println(http.ListenAndServe("localhost:6060", nil))}()// 其他业务逻辑代码
}

获取内存信息:用以下命令收集内存堆信息:这将打开一个交互式界面,显示内存使用情况。可以用 top 命令查看哪些函数占用了最多内存,或者用 list <func_name> 查看某个函数的详细内存分配情况。

go tool pprof http://localhost:6060/debug/pprof/heap

分析 goroutine 堆栈:如果怀疑程序中存在 goroutine 泄漏,可以用以下命令查看 goroutine 的堆栈:查看是否有不必要的 goroutine 一直存在,未能及时退出,导致内存泄漏。

go tool pprof http://localhost:6060/debug/pprof/goroutine

(2) Go Trace(跟踪)

go tool trace 提供了更深入的跟踪功能,帮助观察程序的调度情况、goroutine 的执行轨迹等。识别是否有 goroutine 持续占用内存,或者是否存在调度瓶颈等问题。
启用 pprof 后,访问 http://localhost:6060/debug/pprof/trace 来获取追踪数据。

go tool trace http://localhost:6060/debug/pprof/trace?seconds=30 # 收集 30 秒的追踪数据
2. 监控和日志结合

对于内存泄漏问题,日志本身可能不提供明确的异常信息,但可结合内存监控和日志来看。

(1) 使用 Prometheus + Grafana 监控内存使用情况

集成 Prometheus 客户端,定期暴露内存使用情况,用 Grafana 可视化展示。实时监控服务的内存消耗变化,及时发现内存泄漏。

暴露内存指标:在 Go 程序中使用 runtime.ReadMemStats 获取内存统计数据,并通过 Prometheus 暴露这些指标:如果内存使用不断增长而没有释放,就可能意味着存在内存泄漏。

import ("github.com/prometheus/client_golang/prometheus""github.com/prometheus/client_golang/prometheus/promhttp""net/http""runtime""log"
)var (memoryStats = prometheus.NewGaugeVec(prometheus.GaugeOpts{Name: "go_memory_stats",Help: "Go memory statistics",},[]string{"stat"},)
)func init() {prometheus.MustRegister(memoryStats)
}func recordMemoryStats() {var m runtime.MemStatsruntime.ReadMemStats(&m)memoryStats.WithLabelValues("heap_alloc").Set(float64(m.HeapAlloc))memoryStats.WithLabelValues("heap_sys").Set(float64(m.HeapSys))memoryStats.WithLabelValues("heap_idle").Set(float64(m.HeapIdle))memoryStats.WithLabelValues("heap_inuse").Set(float64(m.HeapInuse))
}func main() {go func() {for {recordMemoryStats()time.Sleep(10 * time.Second)}}()http.Handle("/metrics", promhttp.Handler())log.Fatal(http.ListenAndServe(":8080", nil))
}

(2) 定期记录内存使用信息

如果不使用 Prometheus,也可以定期记录程序的内存使用信息,并通过日志查看内存变化。例如:通过日志定期记录内存使用情况,可检测内存是否存在异常增长,尤其是在特定的负载下。

import ("log""runtime"
)func logMemoryUsage() {var m runtime.MemStatsruntime.ReadMemStats(&m)log.Printf("Alloc = %v MiB", bToMb(m.Alloc))log.Printf("TotalAlloc = %v MiB", bToMb(m.TotalAlloc))log.Printf("Sys = %v MiB", bToMb(m.Sys))log.Printf("NumGC = %v", m.NumGC)
}func bToMb(b uint64) uint64 {return b / 1024 / 1024
}
3、代码审查和手动检查

检查不必要的引用:确保没有无意中持有不再需要的引用,尤其是大对象(如 map、slice 等)。
检查 goroutine 的生命周期:确保所有启动的 goroutine 在任务完成后能够正确退出,避免因 goroutine 泄漏导致内存问题。
用 defer 确保资源释放:在会发生内存泄漏的地方,确保通过 defer 释放资源(例如关闭文件、数据库连接等)。

4、压力测试和负载测试

通过模拟实际业务场景的高并发或长时间运行,来进行压力测试。可以使用如 wrk、hey 等工具进行负载测试,观察在高负载下服务的内存使用情况,看看是否存在内存泄漏。

压测

1、hey
一个轻量级、易用的 HTTP 性能测试工具,适合对中小规模应用进行负载测试。配置简单,适合快速进行简单的压力测试。不支持脚本。
举例:

hey -n 1000 -c 100 http://example.com
#-n 1000:发送 1000 个请求
#-c 100:使用 100 个并发连接

输出:

Summary:Total:	2.1234 secsSlowest:	0.2890 secsFastest:	0.0123 secsAverage:	0.0456 secsRequests/sec:	470.31Total data:	113.8 MBResponse time histogram:0.012 [1]	|0.045 [934]	|0.100 [65]	|0.150 [0]	|

2、wrk
适用于大规模、高并发的性能测试。通过多线程和 Lua 脚本提供更高的灵活性和定制能力,适合需要进行详细性能分析的复杂场景。

压测效果影响因素:1、硬件资源:服务器的CPU、内存和网络带宽;2、并发量;3、请求类型;4、代码质量,数据库性能,缓存机制; 5、网络延迟

举例与输出:

wrk -t12 -c400 -d30s http://example.com
#-t12:使用 12 个线程进行测试
#-c400:使用 400 个连接
#-d30s:进行 30 秒的测试
Running 30s test @ http://example.com12 threads and 400 connectionsThread Stats   Avg      Stdev     Max   +/- StdevLatency     31.77ms   22.45ms  98.56ms   84.55%Req/Sec    1405.72     65.85   1552.00    83.43%506837 requests in 30.08s, 63.25MB read
Requests/sec: 16807.51
Transfer/sec:    2.10MB

性能优化方向 思考

1、 缓存
l1、l2、l3 级 cache、浏览器缓存、Redia、

缓存相关的问题:雪崩、穿透、击穿、热点 key监控再分散、缓存淘汰、数据一致性三类方法、

2、 并行化处理
后来的Redis 多线程、主从数据库

3、 批量化处理
AOF 缓冲区、Redis的pipeline 、

4、 数据压缩处理
AOF 文件对同一key的多次写、pb、

5、 无锁化
sync.Map、GM到GMP、mvcc 、消息队列

6、 分片化
Redis集群、

7、 避免请求
bufferpool 、

8、 池化
对象池:sync.pool
协程池:gopool
数据库连接池

9、 异步处理
回调函数实现、bgsave、Redis用unlink 异步删除key、

10、顺序写
MySQL 自增主键、写undolog 和 binlog 日志、

总结:做服务性能优化,要从自身服务架构出发,分析服务调用链耗时分布跟 CPU 消耗,优化有问题的 RPC 调用和函数。
多学中间件,上面几种方法很多在常用中间件中有体现。具体学“如何用;底层优秀的设计思想;理解为什么要这样设计;这种设计有什么好处;”


http://www.mrgr.cn/news/78546.html

相关文章:

  • 浅尝Selenium自动化框架
  • RK3568-ubuntu旋转显示和触摸
  • Nginx:认证与授权
  • 采用标准化的方式开展设计-研发中运用设计模式
  • L27.【LeetCode笔记】2 的幂(五种解法)
  • Linux: 关于 mount 的一些细节
  • JAVA篇07 —— 异常
  • 68000汇编实战01-编程基础
  • Zariski交换代数经典教材Commutative Algebra系列(pdf可复制版)
  • Java进阶七-网络编程,反射
  • ElasticSearch7.x入门教程之全文搜索(五)
  • QT知识整理
  • 快速理解微服务中Ribbon的概念
  • C7.【C++ Cont】范围for的使用和auto关键字
  • 深度学习基础3
  • Android Studio安装TalkX AI编程助手
  • 性能监控系统Prometheus、Node-exporter与Grafana部署详解搭建
  • 网络原理(一)—— http
  • 虚拟机ubuntu-20.04.6-live-server搭建OpenStack:Victoria(三:安装服务-controller node)
  • OceanBase 大数据量导入(obloader)
  • React第十节组件之间传值之context
  • SpringBoot实战(三十二)集成 ofdrw,实现 PDF 和 OFD 的转换、SM2 签署OFD
  • 【Electron学习笔记(二)】基于Electron开发应用程序
  • 算法基础 - 求解非线性方程(二分迭代法)
  • 超级详细讲解转义字符,\? \‘ \f \0 \t等等!!!
  • 【科研】9如何高效阅读和理解学术论文