追踪 Rails 应用中的内存泄露

7ba062b59a726dd93c0774dce3e284ad?s=47 Yunzheng
September 23, 2016

追踪 Rails 应用中的内存泄露

RubyConf China 2016. presented by 42thcoder

7ba062b59a726dd93c0774dce3e284ad?s=128

Yunzheng

September 23, 2016
Tweet

Transcript

  1. 张运政 追踪 Rails 应用中 的内存泄漏

  2. 42thcoder ❖ 张运政 ❖ Ruby 准新人, Rails 熟手 ❖ 前端届吃瓜群众

    ❖@大搜车
  3. None
  4. None
  5. 2012年成立

  6. 2012年成立 团队500人

  7. D轮数千万美金 2012年成立 团队500人

  8. 项目介绍

  9. 拍卖

  10. 秒杀 App

  11. 秒杀 App

  12. ERP

  13. None
  14. None
  15. 15472

  16. 应用指标

  17. 应用指标 100+ 接口, 30+ 屏

  18. 应用指标 100+ 接口, 30+ 屏 平均 500 rpm, 峰值 6000

    rpm
  19. 应用指标 100+ 接口, 30+ 屏 平均 500 rpm, 峰值 6000

    rpm 3 台 ESC ( 4核 8 G) + RDS
  20. 上线啦

  21. 死机啦! 内存泄露啦!

  22. 怎么办?

  23. 下面我就聊一聊在拍卖项目中, 追踪内存泄露的经历

  24. 动手解决

  25. 工欲善其事必先利其器 — 孔子

  26. Linux 工具

  27. passenger-memory-stats passenger-status top && htop cat /proc/pid/status & cat /proc/[pid]/mem

  28. None
  29. None
  30. None
  31. None
  32. None
  33. None
  34. resident set size, the non- swapped physical memory that a

    task has used. RSS VSZ virtual memory size of the process in KiB. Device mappings are currently excluded; this is subject to change.
  35. 线程组 Tgid( Thread Group ID) 才是真正意义上的 进程 ID, 即 get_pid

    的结果
  36. APM

  37. None
  38. 动手解决问题吧

  39. Survive Address Fix Lesson

  40. 企业级应用, 需要企业级的稳定 Survive Address Fix Lesson

  41. 看门狗: 报警 passenger_killer: 完成 N 个请求后杀掉 oom_killer: 内存超过 N 后杀进程,

    passenger 自动重启 oob: 进程每处理 N 个请求, 自动 GC
  42. None
  43. None
  44. None
  45. 定位问题 Survive Address Fix Lesson Learned

  46. 插个话题

  47. Is it Memory Bloat?

  48. Memory Bloat VS Memory Leak

  49. 补充⼀一张 oneapm 看 vm 的截图

  50. Monitor

  51. 补⼀一张 scoutapp 看各个接⼝口内存分配的图; 补⼀一张 GC 执⾏行行时间的图

  52. None
  53. Profile

  54. Boot App => Hit with Request => Profile Memory

  55. Derailed Benchmarks https://github.com/schneems/derailed_benchmarks Go faster, off the Rails - Benchmarks

    for your whole Rails app
  56. Memory Profiler https://github.com/SamSaffron/memory_profiler memory_profiler for ruby

  57. 进程内存随请求数上涨 TEST_COUNT=10_000 PATH_TO_HIT=/ api/v1/home/counts bundle exec derailed exec perf:mem_over_time

  58. 单个请求, 内存分配 TEST_COUNT=100 PATH_TO_HIT=/api/v1/home/counts? token=5c78a9adeec3613b7a3ac0d734475e06 bundle exec derailed exec perf:objects

  59. 接口 X 会生成大量 Timeout 对象, 占用内存过多 总结 profile 要比 monitor

    目的性更强 内存泄露确实存在, 内存随时间不断上涨
  60. 修复问题 Survive Address Fix Lesson

  61. 接口 X 做了什么? 猜是没有⽤用的,我们继续跟

  62. Stackprof https://github.com/tmm1/stackprof a sampling call-stack profiler for ruby 2.1+

  63. config.middleware.use(StackProf::Middleware, enabled:true, mode: :wall, interval: 1000, save_every: 5)

  64. config.middleware.use(StackProf::Middleware, enabled:true, mode: :wall, interval: 1000, save_every: 5)

  65. None
  66. None
  67. None
  68. def write(*args) Timeout.timeout(@write_timeout, TimeoutError) { super } end

  69. Gocha! redis-rb 的锅, 不过还是要验证下

  70. #!/usr/bin/env ruby
 # encoding: utf-8
 
 require 'memory_profiler'
 
 gem

    'redis', ENV['RVERSION']
 require 'redis'
 
 puts Process.pid
 puts Redis::VERSION
 
 MemoryProfiler.report {
 r = Redis.new
 i=0
 100.times do
 r.set "key#{i}", "value#{i}"
 end
 }.pretty_print
  71. None
  72. None
  73. None
  74. 经验和教训 Survive Address Fix Lesson Learned

  75. 寻找内存热点 能否重现? 能否按接口跟踪? 调整是否有用? YES NO

  76. git diff 对照组

  77. Timeout is pure evil

  78. 每个人都可能出错

  79. None
  80. None
  81. None
  82. 可能其他用到的 Gem

  83. Rbkit & Rbkit Client https://github.com/code-mancers/rbkit A new profiler for Ruby.

    With a GUI http://rbkit.codemancers.com 2.3.x 下无法使用
  84. Oink https://github.com/noahd1/oink/ Log parser to identify actions which significantly increase

    VM heap size
  85. Memory Logic https://github.com/binarylogic/memorylogic Adds in proccess id and memory usage

    in your rails logs, great for tracking down memory leaks
  86. 参考资料 • Ruby Under a Microscope • 垃圾回收的算法与实现 • Ruby

    Performance Optimization
  87. THANKS

  88. Q & A