作者归档:mzh

某项目上线及事故总结

EW4终于在中国iOS顺利上线了,虽然我已经被调到其他项目组,但是也经历了上线加班调试到4点的情况。所用的技术栈其实在招聘页面都有,所以不算泄密。

事故总是由看似无关紧要的小错误累积而成的。

整个系统架构上是一般的二代网游架构,大问题没啥,但是登入服务赶新潮地依赖了DynamoDB这个SLA号称在线率三个9的玩意。可是偏偏在正式上线前三天us
east1的数据中心写故障!这时IT团队试图从us east1迁到us
east2,顺利的迁移完了,结果再次中了彩票,east2也故障了!这简直是可以买彩票的节奏!!反正数据还没迁完,IT团队就再次修改了配置指向east1,由于此前迁移没有进行过演练,所有配置的更改都是手动操作的,所以有台负责处理连接的节点没有更新,埋下了祸根:这个节点不停地对登入节点上报错误的地址,但由于ELB的关系,这个错误在人工测试的阶段都没有察觉到。
这时,悄然开服的消息已经传遍了骨灰玩家圈,玩家纷纷开始登入注册。

大概是下午开始,客服陆续收到玩家投诉说丢档了!但错误收集sentry并没有报告什么异常,顿时我就纳闷了,难道是DynamoDB又出了问题?但,这次,并不是,只好继续排查代码上的问题。一波未平一波又起,之前给各个安卓运营商上线之后一直正常,但是现在某运营商那边的玩家投诉无法登入,美国上线的负责人也表示自己无法登入,这时整个团队都非常沮丧,因为错误收集系统压根就没有错误报告。IT团队开始一遍又一遍地切换DynamoDB的数据库、登入服务的配置来查错,这时已经是凌晨4点了,估计因为在线人数下降了,ELB自动分配到了一台机子上,所有人又能正常登入了。王总提出让一部分人休息,明天好继续排错,所以我回家休息了。
结果第二天来时,美国团队的人通过debug
客户端,发现本来该指向US的节点,竟然给出了中国区的服务器地址!大家顿时恍然大悟,有台机子在上报错误的地址!!于是IT团队用昨晚写好的部署程序,重新部署了所有大区服务,登入问题就都解决了。
接下来就是无法下载的问题,客户端的同事发现是更新用的服务下载到了不同的数据更新包导致的,不过所幸只是一个小国的部分用户……
这次事故,其实说实话是完全可以避免的。

  • DynamoDB出故障是意外,但是我们服务器团队没有跟IT部门交代清楚配置,导致上报的密钥还是开发时的TEST_KEY是根本原因
  • 服务器并没有完整的校验机制、机器人、最后线上bug还得手动测试,浪费了很多宝贵的时间
  • 对于代码,各种需求变更过快、没有足够的人手做完UT,导致排查代码也花费了大量的时间
  • IT团队也没有及时地做好自动化部属工作、加上2日的操劳、同事们出错也是在所难免
  • 客户端团队对于数据更新包的需求也没有弄清楚就急忙开发,导致下载到了不同的数据更新包。

p.s.通过这次排查代码,我发现系统里还有不少因为加新功能而加的补丁,重构看来也是不可避免了。

如何使用Wireshark对远程服务器数据进行分析

抓包对于后端工程师来说就是家常便饭的技巧,包分析软件里最牛B的是Wireshark,可是Wireshark只能在有X环境的机子上运行,当我们想知道服务器上的情况,又想用Wireshark该怎么办呢?
@MatheMatrix指出用管道就好了

ssh username@domain "sudo tcpdump -s 0 -U -n -i eth0 not port 22 -w -" | wireshark -k -i -

-–原文章—-

1. 在本地运行 mkfifo /tmp/remote 这样就创建了一个先进先出的文件

2. 在本地运行

ssh username@domain "sudo tcpdump -s 0 -U -n -w - -i eth0 not port 22" > /tmp/remote

将远端服务器的数据通过管道输送到刚才我们建立的文件中 tcpdump的规则里将22端口去掉了,因为我们传输的方法是ssh

3. 在本地运行 wireshark -k -i /tmp/remote 开启wireshark!

如何让Go程序更快

本文为部分翻译、整理。 原文为Go的开发者之一的Dave Cheney所做的 Five things that make go
fast

清晰赋值类型

例如,有一个绝对不会超过uint32的数值,就不要用int var gocon uint32 = 2015 这样gocon这个值只会占用4个字节
为啥呢? 因为如下图,CPU的处理速度已经远超内存的总线速度了 Gocon-2014-10 所以数值能用小的用小的,尽量让数值留在CPU
cache,而不是速度更慢的内存里

函数调用有overhead,为了内联,尽量消除编译器无法侦测的dead code

当函数调用时,始终是由overhead(额外开销)的,比如保存调用栈,CPU切出。
因此编译器会尝试进行内联,将小函数直接复制并编译。
举个栗子:

func Max(a,b int) int {
if a > b {
return a
}
return b
}
func DoubleMax(a, b) int {
return 2 * Max(a,b)
}

-m 查看内联状态 $go build -gcflags=-m main.go # utils src/utils/max.go:4: can inline Max src/utils/max.go:11: inlining call to Max
这样做的代价是可执行的二进制文件更大了,但由于内联,并不是函数调用,性能自然是更好了。 但有些函数,是不能内联的,比如下面这个

func Test() bool {return False}
func Expensive() {
if test(){ //接下来的Expensive没办法内联
// Expensive....
}
}

改成下面这样就可以内联了

const TEST = False
func Expensive() {
if TEST{
// Expensive....
}
}

逃逸检查

首先,要理解一个概念,stack 和 heap Gocon-2014-24
stack作用域是本地的(locals),在函数执行完之后会自动收回,CPU控制,效率高 而heap则需要由程序来管理,效率低 具体有篇文章讲这个:
Memory stack vs
heap

因此,就算有GC,也应该把不需要传出的参数尽量控制在函数内。 例如下图的程序
Gocon-2014-26 因为numbers 只在
Sum中,编译器也会自动分配100个int空间在stack中,而不是heap中。 正因为在stack 中,所以不需要GC参与,自动收回。
但这不意味着不能用指针引用,见第二个栗子: Gocon-2014-27
尽管变量c是通过new函数生成的,但是因为在center外没有c的引用,所以c也会被存储在stack上。 逃逸检查实例:
Gocon-2014-28

Goroutine

第四个,我觉得是提示吧: Goroutine是比进程、线程都小的执行单元 Goroutine中的会被调度器(scheduler)切出的操作:

  1. chan 收发
  2. go 语句调用函数
  3. 阻塞的syscall
  4. gc

一图胜千言,下图表示了调度器是如何在goroutine之间切换的。 Gocon-2014-35
第五个算是对Go1.3以后的stack分配机制的总结,我就不翻译了,有兴趣的同学自己可以看看原文:)

总结

  1. 我想补充的是: 逃逸对于channel也是成立的,因此,在channel之间,最好传递的也是对象,而不是引用。这个问题上我栽过一次了哈哈哈
  2. 之前我并不知道内联是啥,Golang、pypy这样的JIT、或者cpython真的是减轻了我的心智负担,当然,了解一下也是很不错的。
  3. 清晰的赋值个人感觉不是很必要,因为Go的int类型最大是2**31-1,性能调优的时候再认真地梳理其实也来得及

如何压缩Golang 编译出的可执行文件大小

先给结论:可以减少到原来的29%

最近在写一个TLScat小工具
Github.com/mengzhuo/tlscat
源文件仅仅2KB不到,但是用 go build tlscat.go 编译出来的有4.6MB!

屏幕快照 2015-05-04 23.39.08

贴纸

后来发现这个Golang的1.5才会解决的问题 Issue #6853 all: binaries too big and
growing
可是,我就不信这个邪,于是搜索到了go
build的一些用法 go build -ldflags "-s -w" ‘-s’ 相当于strip掉符号表,
但是以后就没办法在gdb里查看行号和文件了。 ‘-w’ flag to the linker to omit the debug information
告知连接器放弃所有debug信息 这样一来就只有3MB了

贴纸

然后发现在Mac平台下,还有upx这样神一般的存在。

UPX achieves an excellent compression ratio and offers very fast
decompression.

简而言之,upx就是对可执行文件进行压缩,然后可以已极快的速度解压并运行

可以用brew快速安装upx brew install upx upx 可执行文件 屏幕快照 2015-05-0523.41.40

参加某开发比赛后记

前些日子参加了某云的开发大赛,结果出来了~ 屏幕快照 2015-05-04 16.19.27 仅仅拿了一个三等奖,略桑心。
参赛过程具体见《[如何一下午写3000行?记某云的Golang API
SDK生产过程](/%e5%a6%82%e4%bd%95%e4%b8%80%e4%b8%8b%e5%8d%88%e5%86%993000%e8%a1%8c%ef%bc%9f%e8%ae%b0%e6%9f%90%e4%ba%91%e7%9a
%84golang-api-sdk%e7%94%9f%e4%ba%a7%e8%bf%87%e7%a8%8b)》

不过伤心之余,我理性一把,看了看前4的分别都是啥: 特等奖是移动客户端的解决方案:mu
MU (Mobile UCloud) 是一个基于 UCloud API 的手机版管理工具……
成熟度已经和产品可以媲美了,也解决了在移动端大潮之下,官方对移动端支持几乎是空白的痛点,所以只需要签个协议就可以用的产品,拿奖不奇怪。
而三个一等奖里,有另一个产品: ESS_For_Ucloud
这货就相当于AWS里的ELB,同样是改改就能投入线上使用的产品。 解决了……呃……官方有LB,但是需要人工调整这样"不云"的尴尬。 第二个一等奖是:
Ucloud CLI命令行接口
回想我司的运维、SA们用来控制AWS的工具,不是web控制端,不是各种写好的SDK,而是AWS CLI,因为他们最熟悉的就是各种脚本。
这也正是比赛举办方所缺少的! 虽然命令覆盖得并不是很完善,但也算是补上了个自动化运维的坑。 最后一个一等奖是: Python
SDK

在我吐槽官方的SDK用了**params这样的动态语言特性时,聪明的人自然会想到,举办方自然希望修补官方的SDK也是比赛的侧重方向了嘛~

虽然,我粗浅地看下去,这个SDK也仅仅是修补了官方SDK调用时不严谨的尴尬,而且由于API这么多,自然覆盖得不全,但是也算抓住了举办方的心了。
公布之后在交流群里,不少人也吐槽自己UT覆盖率比得上面的都高,为啥只能拿二等甚至是三等。反过来想,真的算是程序员典型的思维了:自己做的程序、工具很"牛B",但是为啥没有人用,甚至是不受欢迎。
都是因为: 没有抓住痛点!没有抓住痛点!!没有抓住痛点!!! 所以,下次做东西的时候,得多问问自己:

需求是啥!?