1 / 45

Go 语言高并发实战

Go 语言高并发实战. 构建千万级在线的实时消息推送服务 2012.12.23. 关于我. 张景埕,网名 diogin( /’daɪədrɪn/ ) programmer@360 Go 语言追随者与实践者 新浪微博: http://weibo.com/ diogin diogin@gmail.com. 摘要. 一、为什么选用 Go 二、探究 Go 语言的实现细节 三、实时消息推送服务的特点 四、系统架构及组件细节 五、数据、经验和教训. 一、为什么选用 Go. 1. 高并发.

skule
Télécharger la présentation

Go 语言高并发实战

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Go 语言高并发实战 构建千万级在线的实时消息推送服务 2012.12.23

  2. 关于我 张景埕,网名 diogin(/’daɪədrɪn/) programmer@360 Go 语言追随者与实践者 新浪微博:http://weibo.com/diogin diogin@gmail.com

  3. 摘要 一、为什么选用 Go 二、探究 Go 语言的实现细节 三、实时消息推送服务的特点 四、系统架构及组件细节 五、数据、经验和教训

  4. 一、为什么选用 Go

  5. 1. 高并发 一个 Go 进程可以轻易支撑几十万上百万并发运行的 Go 例程(只要你内存足够大) O(1) 的调度 5KiB/goroutine 的内存开销 net 包:pollServer (epoll/kqueue/iocp) 支持大量并发连接 fd 的事件通知,同时还支持多核并行 (目前为 8 核) select/channel 提供卓越的例程间通信能力

  6. 2. 高性能 编译为本地机器码 静态链接 (CGO_ENABLED=0) 轻量级的 runtime 实现 热点代码可以直接编写 C 或 ASM,同样静态链接进目标文件

  7. 3. 垃圾收集 不想为内存泄露问题焦头烂额 朴素的实现:停顿式、标记 & 清除 还有很大的提升空间 资源泄露还得自己解决

  8. 4. 低成本 学习成本低:源自 C 系,大量的 C 系程序员可以很快上手(这很重要) 运维成本低:不需要安装各种让人头疼的依赖,不需要搭建各种运行环境,部署非常方便

  9. 5. 够用的标准库 net encoding/gob strings, bytes, errors, strconv regexp os, time syscall sync ......

  10. 6. 高移植性 多种操作系统:Linux, FreeBSD, Darwin, NetBSD, OpenBSD, Windows 多种体系结构:386, amd64, arm(v5,v6,v7) 多种目标文件格式:elf/pe/macho

  11. 7. 优秀的背景 Google, BSD License Ken Thompson, Rob Pike, Robert Griesemer, Russ Cox, Brad Fitzpatrick... 拥有强烈的 Plan 9 血统 C, Python 的风格

  12. Alternatives? Erlang Node.js D Rust

  13. 二、探究 Go 语言的实现细节

  14. WHY? 产品级的应用,不是玩具 在国内(甚至国外),仍处于吃螃蟹阶段 我们需要能 hold 住可能出现的各种问题 提升团队成员技术水平 回击各种针对 Go 的恶意 FUD 和各种 PK 回馈 Go 本身,积极为其做贡献

  15. 摘要 代码结构 逻辑结构 实现结构 程序启动 运行视角 深入 runtime

  16. 1. 代码结构 工具 + 标准库 include/:Go 基本工具依赖的库的头文件 src/:Go 基本工具、标准库的实现 src/cmd/:Go 基本工具及其实现 src/lib*/:Go 基本工具依赖的库的实现 src/pkg/:Go 标准库的实现

  17. 2. 逻辑结构 gc(Go 编译器)、cc (Plan 9 C 编译器)、as (Plan 9 汇编器)、ld (Plan 9 链接器)、pack (Plan 9 目标文件归档工具) 工具链一条龙 支持多种操作系统、体系结构和可执行文件格式 程序 = N 个包的合并、组合 包 = const、var、type、func dist:引导程序,负责构建 Go 基本工具 go:管理 Go 的各项功能

  18. 3. 实现结构 每个包里可以有 .go、.c、.s 文件,分别由 gc、cc、as 编译,最后统一用 ld/pack 链接并打包成静态链接库(遵循一致的 ABI) 每个 Go 程序都包含最底层的 runtime 包,runtime 实现并封装了程序的运行时环境 syscall 包封装了操作系统调用,同时与 runtime 协作进行 goroutine 调度 sync 提供了基本的并发同步原语(工具包) reflect 为应用层代码提供运行时自省能力

  19. 4. 程序启动 基本概念:M—内核线程,G—goroutine Go 使用符号“·”分隔包与包内的成员名字 第一个 M 叫 m0,每个 M 的调度例程叫 g0 准备 runtime·m0/runtime·g0 -> 准备 argc, argv -> 调度器初始化(内存初始化 -> m 初始化 -> 注册 args, envs -> 设置并行参数) -> 创建并运行 runtime·main 例程(启动垃圾收集器 -> main·init() -> main·main())

  20. 5. 运行视角 程序由多个包构成,入口为 main 包,每个包在程序启动时按依赖顺序逐一初始化 程序内有多个 goroutine 并发运行,goroutine 通过 channel 进行同步/异步通信 自动化内存管理,不区分堆和栈

  21. 6. 深入 runtime mgc0.c:垃圾收集器 proc.c:goroutine 管理及调度 hashmap.c:Go map chan.c:channel 实现 malloc.goc:内存分配器 asm_GOARCH.s:体系结构相关功能 iface.c:Go interface symtab.c:符号表 cpuprof.c/mprof.goc:Profiling sys_GOOS.s:操作系统相关功能 thread_GOOS:内核线程统一抽象 panic:panic管理

  22. 三、实时消息推送服务的特点

  23. 摘要 长连接(多种接入协议:HTTP、TCP) 高并发(>= 10,000,000) 多种发送方式(支持单播、多播、广播) 持久/非持久 准实时(200ms ~ 2s) 客户端多样性(手机端、PC 端) 同一账号多客户端同时接入 接入网络频繁变化(电信、联通……)

  24. 1. 长连接 Server PUSH 的基础 HTTP Long Polling(Keep-Alive) 基于 TCP 的自定义通信协议 采用心跳来侦测对方是否还在线

  25. 2. 高并发 C10K? out 了,现在的标准是 C1000K 用户众多:各接入产品实时在线人数都在10,000,000 以上 Linux Kernel >= 2.6.32, x86-64 sysctl -w fs.file-max=12000000 sysctl -w fs.nr_open=11000000 limit.conf: nofile=10000000 TCP/IP 协议栈参数调优

  26. 3. 多种发送方式 点对点聊天(单播) 定点推送(多播) 全网推送(广播)

  27. 4. 持久/非持久 持久消息必须保证不丢失 (需要离线存储) 非持久消息仅发给当前在线用户

  28. 5. 准实时 发一条消息,另一个人必须能很快收到 200ms ~ 2s GC 卡顿会造成巨大麻烦

  29. 6. 客户端多样性 手机、PC、平板电脑 不同的产品有不同的需求 提供机制,而非策略

  30. 7. 同一账号多客户端接入 iMessage:iPhone, iPad, MacOSX QQ:PC、手机、平板、微信 互斥(早期 QQ)& 共存 策略:如何控制?

  31. 8. 接入网络频繁变化 白天在电信,晚上在联通 一会儿在公司 wifi,一会儿 3G,一会儿 2G 断线,重连,断线,重连……

  32. 四、系统架构及实现细节

  33. 摘要 逻辑架构:简单至上 组件:room 组件:register 组件:saver 组件:idgenerator 组件:center 存储:redis

  34. 1. 逻辑架构:简单至上

  35. 2. 组件:room 客户端所连接的进程,类似于一个聊天室 每个客户端一个 server goroutine 进行下推 每个 server 有一个 channel 存消息队列 room 内有 book 记录 user 与 server 映射 一个 http server 负责收消息并将消息路由到接收人所在的 room 和 server manager 负责掌控 room 的服务:内部单播、多播、广播 admin 负责 room 进程的管理

  36. 3. 组件:register 由于 room 的分布式与全对称设计,需要有一个地方记录用户当前连到了哪个 room,register 实现该角色 同时需要记录在线时长等信息 (业务需求) 本质上就是一个 key -> value 的 map value 是个 struct hash 算法定位 register 进程 可以直接用 redis,但自己实现可以方便地添加业务逻辑

  37. 4. 组件:saver 分布式全对称设计 提供存储接口,封装后端的分布式存储 接口采用 encoding/gob 编码格式的 rpc

  38. 5. 组件:idgenerator 全局消息 id 生成器,int64 分布式,每个进程负责一块 id 区域 保证不重叠 后台 goroutine 每隔一秒写一次磁盘,记录当前 id 启动时跳过一段 id,防止一秒内未写入磁盘的 id 重复生成

  39. 6. 组件:center 提供消息操纵接口给应用服务器调用 运营人员后台发消息 -> 应用服务器 -> center -> room -> 客户端 RESTful API 有些任务(比如广播)需要一段时间,运营人员需要追踪发送进度,可能要临时停止,因此需要有“任务”概念,并可管理 提供统计接口

  40. 7. 存储:redis 核心数据 db_users:ZSET,存各产品的用户集合 db_slots:LIST,存用户离线消息队列 db_buckets:DICT,存消息 id -> 消息体

  41. 五、数据、经验和教训

  42. 数据 16台机器,标配24个硬件线程,64GB内存 Linux Kernel 2.6.32 x86_64 单机80万并发连接,load 0.2~0.4,CPU 总使用率 7%~10%,内存占用20GB (res) 目前接入的产品约1280万在线用户 2分钟一次GC,停顿2秒 (1.0.3 的 GC 不给力,直接升级到 tip,再次吃螃蟹) 15亿个心跳包/天,占大多数 持续运行一个月无异常,稳定

  43. 经验 敢于尝试新东西,勇于面对新挑战 拿代码说话,拿数据说话 不要凭感觉,要实际去测 对一项新技术要切实了解其优势和劣势 对于感觉模糊的地方,追查其实现,做到有备无患

  44. 教训 实时系统对 GC 非常敏感,幸好 tip 上提供了并行 GC,否则这就是一次惨痛教训,需要做架构方面的大调整 (将大进程拆分成小进程)

  45. ThanksQ & A 张景埕 diogin@gmail.com http://weibo.com/diogin

More Related