作者归档:mzh

2015年终总结

终于又到了年末自我审查的时间了……今年就不填表了,我觉得我已经不需要这种东西来督促我学习了,毕竟目标很明确了。

今年在游戏公司算是扎下了根,带了一小段时间的后端团队(虽然只有一个成员),学到了怎么给他人做职业规划,教学,发展;也学到了不少职场上的事情。Golang也算是能信手拈来的程度了,但是对于高性能系统还是有些不知所措,比如内存池规划,TCP连接优化。架构上至少接触了点分布式的东西,但是还没有上手的感觉,毕竟需求并不是那么强烈,对于CAP、Paxos都只是停留在大致知道上,明年务必要实现过一次Paxos和raft才行。算法/数据结构上,今年简直就是LRU年,之前在Game
Server里有用到,写了一个,然后跑到新团队发现,又缺一个,想着LRU反正不复杂,又写了一个……LeetCode重新捡了起来,刷了40多道而已,简单地学了些图论的东西,但是边上班边生活(其实都是些琐事,比如小区换出入证什么的)真的业余时间几乎就没有了,加上还要学车,简直快要把我给累垮了。幸好女友经常安慰,疏导我,其实对我来说,能有个人说说今天的烦恼,舒缓一下压力就已经足够,然而她给予我的更多,真的要感谢她。

今年只看了一本书--《网络游戏核心技术与实战》,讲得真得很细,对于架构上也是面面俱到,可惜,大裁员时顺手把书送给了同事,要不然,还得继续精读。

东西还是造了不少,可惜没有时间整理、总结:

  • 一个基于Onehop的去中心、分布式的KV数据库
  • 花了两周业余写了这个博客程序 bla
  • ucloud sdk
  • justrpc --jsonrpc v1 的python 实现

参加了不少分享会,不过学到的并不多,以后看来得参加更加高端点的分享会。或者报个算法班来系统补补课?

#年终总结

Bla — another static blog app

After 2 weeks working on my pet project, I’m glad to say that Bla is ready at
the stage of MVP(minimum viable product).

  • Fully static file serving
  • WYSIWYG (No markdown or offline editing)
  • Tag/Relation finding
  • Golang blog style template
  • Easy deploy with no php, no Mysql (only golang)

Other highlights:

  • automatically reload on content/template/config change
  • support TLS

Simple Tutorial

go get -u github.com/mengzhuo/bla/cmd/bla

bla new

edit your configure file “bla.config.json” and just run bla

Github repo:

github.com/mengzhuo/bla

某项目上线及事故总结

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,性能调优的时候再认真地梳理其实也来得及