有一个流媒体适配服务,出现了CPU开销很大的问题,一个服务把CPU资源占满了,导致其他服务无法正常工作。
下面来详细记录发现bug和解决的流程。

发现CPU开销很大

扫描发现,是垃圾回收导致 CPU 使用上升 :

Time: Mar 22, 2019 at 5:52pm (CST)
Duration: 1mins, Total samples = 1.43mins (142.57%)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) tree 
Showing nodes accounting for 83.13s, 97.11% of 85.60s total
Dropped 256 nodes (cum <= 0.43s)
----------------------------------------------------------+-------------
      flat  flat%   sum%        cum   cum%   calls calls% + context           
----------------------------------------------------------+-------------
                                            71.36s 99.86% |   runtime.gcDrain
                                             0.10s  0.14% |   runtime.systemstack
    48.56s 56.73% 56.73%     71.46s 83.48%                | runtime.scanobject
                                            11.86s 16.60% |   runtime.heapBitsForObject
                                            11.04s 15.45% |   runtime.greyobject
----------------------------------------------------------+-------------
                                            11.86s 99.92% |   runtime.scanobject
    11.87s 13.87% 70.60%     11.87s 13.87%                | runtime.heapBitsForObject
----------------------------------------------------------+-------------
                                            11.04s   100% |   runtime.scanobject
    11.02s 12.87% 83.47%     11.04s 12.90%                | runtime.greyobject
----------------------------------------------------------+-------------
                                             6.53s 95.05% |   runtime.gosweepone.func1
                                             0.34s  4.95% |   runtime.(*mheap).alloc
     4.34s  5.07% 88.54%      6.87s  8.03%                | runtime.sweepone
                                             2.53s 36.83% |   runtime.(*mspan).sweep
----------------------------------------------------------+-------------
                                            74.42s   100% |   runtime.gcBgMarkWorker.func2
     1.97s  2.30% 90.84%     74.42s 86.94%                | runtime.gcDrain
                                            71.36s 95.89% |   runtime.scanobject
                                             0.52s   0.7% |   runtime.pollWork
----------------------------------------------------------+-------------

准备工作

之后考虑使用 buffer pool,

    // 这里不再分配新的内存,而是从 buffer pool 里面 GET 
    databuf = make([]byte, 100000)

解决

参考go buffer pool

  • 先创建一个buffer pool

  • Get

  • 用完再Put回去

  • 注意,最好在GetPut时加锁。

是什么原因导致了CPU开销很大(重点)

当我们新建了一个有长度变量时,例如100byte的数组,那么它在操作系统内存中是这样展现的

因此,当我们新建一个变量时,操作系统会在自己的运行内存里开辟一块内存给这个变量存数据用。当我们不需要这个数据时,或者说要删除这个变量时,Golang会执行垃圾回收机制。

然而当Golang在执行垃圾回收时,操作系统会不断对这些有或者没有被引用的变量进行扫描,这中间涉及操作系统的算法,我们不用深究,但是,在执行这种算法时,会占用CPU的资源,如果新开辟的变量和内存过多,就会导致系统不停的检查是否有不需要引用的变量了,从而造成占用CPU资源过多。

解决办法

创建一个buffer pool

创建一个大的buffer pool,你需要内存时,向buffer pool获取一下Get,用完不需要时再还回去Put

这样做的好处是,操作系统每次检查内存时,都只有一个buffer pool在引用,不增不减,于是也就减少CPU资源的消耗了。

打个比方

比如说操作系统就是一个土豪,借东西再换回来不收利息。它有一个很大的内存,周围许多人都想找它去借(新声明的变量并初始化),刚开始借的人只有十几个,后面有上万个,于是它要每天记录谁借了多少内存出去,谁还没有归还,归还的直接从记录上把名字划掉(垃圾回收)。后来操作系统烦了,于是就建了一个很大的内存池,够所有人分批次借,只要借完及时归还就行,而它每次去看这个内存池有没有变小即可,省了不少精力。

而这个内存池就是go buffer pool的作用。

注意

建议给GetPut加锁,防止多个协程同时借阅,造成竞争冒险。
这个CPU占满问题涉及Golang的垃圾回收机制,这块是要点,一定要搞明白。

参考文章和资源

工作中记一次碰到内存泄漏的解决过程。

发现内存泄露

写的一个定时删除文件的服务,结果无缘无故被系统杀死了。

于是我设置了参数,每隔30s启动一次。并用dmesg命令查看

[1700172.849656] Out of memory: Kill process 1195 (SliceDyson) score 730 or sacrifice child
[1700172.849665] Killed process 1195 (SliceDyson) total-vm:13314968kB, anon-rss:8693980kB, file-rss:116kB

确定是内存泄露造成的。

使用pprof分析造成内存泄露的原因

准备工作

One thing you can do is to compare 2 heap pprofs. You can do it like this:

    Extract a heap profile heap0.pprof

    Add some load to the application

    Extract another heap profile heap1.pprof

    Compare them with go tool pprof -base heap0.pprof <bin> heap1.pprof

This way you can see exactly what is increasing over time.

分析

  • 分析生成的.pprof文件  
$ go tool pprof 120.pprof 
Type: inuse_space
Time: Mar 6, 2019 at 9:27pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 603.20MB, 99.67% of 605.20MB total
Dropped 1 node (cum <= 3.03MB)
      flat  flat%   sum%        cum   cum%
  603.20MB 99.67% 99.67%   605.20MB   100%  main.main
         0     0% 99.67%   605.20MB   100%  runtime.main
(pprof) ^C


$ go tool pprof 240.pprof 
Type: inuse_space
Time: Mar 6, 2019 at 11:42pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) top
Showing nodes accounting for 2.81GB, 99.86% of 2.81GB total
Dropped 6 nodes (cum <= 0.01GB)
      flat  flat%   sum%        cum   cum%
    2.81GB 99.86% 99.86%     2.81GB   100%  main.main
         0     0% 99.86%     2.81GB   100%  runtime.main

  • 分析各个函数的占比情况
File: SliceDyson
Type: inuse_objects
Time: Mar 7, 2019 at 2:52pm (CST)
Entering interactive mode (type "help" for commands, "o" for options)
(pprof) web
(pprof) top
Showing nodes accounting for 6810, 100% of 6810 total
      flat  flat%   sum%        cum   cum%
      6554 96.24% 96.24%       6554 96.24%  main.GetXMLFiles
       256  3.76%   100%        256  3.76%  vendor/golang_org/x/net/http2/hpack.addDecoderNode
         0     0%   100%        256  3.76%  main.init
         0     0%   100%       6554 96.24%  main.main
         0     0%   100%        256  3.76%  net/http.init
         0     0%   100%       6810   100%  runtime.main
         0     0%   100%        256  3.76%  vendor/golang_org/x/net/http2/hpack.init
         0     0%   100%        256  3.76%  vendor/golang_org/x/net/http2/hpack.init.0

最后定位到造成内存泄露的原因,是GetXMLFiles函数造成的。

var xmlFilesPath []string

var LoopGetXmlFilesPath []string
var AllTasks []string
var tasks []*Task
for _, RootPath := range ParseStorageINI(StorageINIPath) {
    xmlFilesPath = GetXMLFiles(RootPath)
    for _, xmlFilesPath_str := range xmlFilesPath {
        LoopGetXmlFilesPath = append(LoopGetXmlFilesPath, xmlFilesPath_str)
    }
}

解决

xmlFilesPath放到for range循环里面声明初始化即可

是什么原因会导致内存泄露

什么时候需要垃圾回收(GC)

参考文章和资源

append(x,1)与x = append(x,1)的区别

今天有同事问我

x := make([]int,4,4)
x = append(x,1)
//append(x,1)

//两者有什么区别

我瞬间懵了,这不很明显吗?

x = append(x,1)是在x的原先基础上增加了一个元素,所以x中元素为5,而append(x,1)没有赋返回值,所以会报错!

同事说,这不是重点,我是问你append(x,1)x中的元素有几个。

于是,我想了下,同时也请教了他人,最终得出答案,x为4。

为什么会这样?

参考文章

(本文有感于五一,完稿于母亲节)

5月6号晚,在乌鲁木齐的地窝堡国际机场,坐在飞机上,广播里说着飞机即将起飞,请关闭手机或调整为飞行模式,我知道,我又要和这座城市告别了。

父亲开车送我去机场的路上,在穿过蜘蛛山隧道时,落日的余晖洒向了这座城市,我看到了这里林立的高楼,和四通八达的街道,以及刚刚亮起来的路灯。

是的,这是我生活了二十二年的城市。

它完整的记录了我从孩童成长为工作者的心路历程。

就让我再穿越回过去,回忆这些转瞬即逝又弥足珍贵的记忆。

与杨搭城堡

时间:小学四年级。

地点:小巷子。

人物:童年玩伴杨

结束了一天在学校的学习,作业太少了,提前做完了,就算不会第二天去早点抄,玩耍才是最重要的!

我约了杨和我一起去沙堆上搭城堡(其实现在看来像是在打洞),我和他挖的正兴奋时,父亲怒气冲冲的过来把我像提小鸡一样提起来,告诫我不要玩沙子,把手弄脏了。

我给杨递了个眼色,于是给父亲保证再不会碰这个了。

等父亲走后,我又跑过去和他动工了。忙活到了晚上十点多,终于把城堡搭完了,筷子用断了好几根,脸也脏了,不过还是很开心呢。

虽然这些艺术品在大人看来一文不值,明天估计就要被毁了,但我知道那时的我看到自己辛苦搭建出来的东西后,充满了成就感。

新华书店窃读记

时间:小学五年级

地点:乌鲁木齐市南门新华书店

人物:童年玩伴杨

因为非常喜欢看冒险小虎队系列的侦探书,无奈杨用零花钱买的几本书已经被我看了两遍了,于是我和他计划,周末去书店看。

于是和父母说了一声后,周末吃过早饭和杨坐着61路公交车去书店,心里默念:今天我要看十本书!

抵达书店,上二楼后厅,拿书,找空地坐下来,动作一气呵成。

那一天,我记得是我生命中最充实最开心的时光,因为我把我的意识完全沉浸在侦探的世界里。

街边小巷你追我打的欢快

时间:小学六年级

地点:街头小巷

人物:一群小屁孩

作业写完,叫上好朋友,一起去玩捉迷藏,真假孙悟空…反正就是各种跑。

有时候玩得过头了,十几个小娃娃追着我,我就东躲西藏,他们说抓住我有重赏。

就这样,间接锻炼了我奔跑的耐力和随机应变的决断力,也收获了一下午的喜悦。

插卡游戏机街头霸王车轮战

时间:初一、二年级

地点:韩家蛋糕盒厂

人物:韩,杨等游戏高手

初一认识了韩,因为他家有插卡游戏机,所以我经常跑他家里打游戏。

还记得当时我最爱玩街头霸王,最喜欢用一个黄头发发冲击波的格斗家和会放电的野兽。为了发出绝招,我把游戏手柄的方向键搓掉了,又把自己的大拇指搓肿了。经常玩到父母上门来找才肯结束。

除了街头霸王,还有四代魂斗罗,超级赛亚人等。

地下游戏厅称霸记

时间:初一

地点:家对面的地下游戏厅

人物:游戏爱好者

家对面开了家游戏厅,里面有台球桌,还有两台PS2,在一个小隔间里还有四五台电脑,于是这里成了我放学和周末的天堂。

我充分发挥了自己善于钻研和学习的天分,不到两个月,没花一分钱,成了这家游戏厅里的游戏专家,没事给打三国无双的玩家指点一二,或者去和其他人联机CS1.6场场爆头。

后来因为白玩的次数太多了,被游戏厅老板拉入了黑名单。

懵懂无知的好感

时间:初二

地点:某初中

人物:a gril

第一次对一个女孩产生好感应该就是这个时候吧。

想想也真是奇葩,产生好感那就表现的好一点就是,然而偏偏把自己最坏的一面表现了出来。

那种抑制不住的脸红和怦怦的心跳声,大概就是初恋的滋味吧,也是我再也回不去的青春。

虽然我已经记不清她的模样,但也是因为她的出现和离开,让我学会如何去用心喜欢上一个人。

与赵的原始书信

时间:初三

地点:无

人物:学习的榜样老赵

面临中考,每当这时候会与相互打气,还记得那时候没有手机,不能登QQ,于是就以最原始的方式保持通信—写信。

将信写好,装入那种放自动笔芯的圆筒内,然后交给顺路的人帮忙捎带给他,想想也是够机智的。

这种方式一直持续到高考结束,然后就戛然而止。

影响我一生的良师益友

时间:高三

地点:高中

人物:张,沈,杨,赵

高三,多么神圣的阶段,也许你的一分,就能干掉上千人,这是当时最流行的宣传语。

然而我又何尝不想拼搏努力呢。

只是,当我把自己想象成一台做题机器时,总会有人帮我找回本来的模样。

我想起,每当吃午饭的时候,我都会给凡儿和魁拔,讲述我自编的喜剧小说《高考命题组专家的故事》;我会在补习班内和杨还有赵吐槽这该死的高考题;我会咨询张老师是不是我太笨了为什么我一天睡五个小时,做题十个小时还是年级还不进步呢…

多年后当我再来看这些问题时,会觉得当时的我多么幼稚。

大学毕业之后才懂

  • 原来挣钱真的不容易
  • 保持终生学习的观念,这是一个长跑
  • 有空常回家看看
  • 学习靠主动,工作靠自觉
  • 有些人走着走着就会散了,你可以沉默一时,但不能一直活在这个阴影里
  • 生命第一,生活第二,工作第三

再见乌鲁木齐,再见新疆

这里有我忘不掉的人和景。

我想起和家人一起去阜康天池坐船看瀑布;一起去吐鲁番欣赏交河故城的荒凉,维吾尔族村寨,坎儿井的避暑好去处,火焰山的金箍棒温度计,葡萄沟的葡萄;一起去天山野生动物园里看狮子老虎大象长颈鹿,去植物园看各种花花草草,去游乐场玩碰碰车、旋转椅子和疯狂老鼠;一起自驾游去昌吉的杜氏旅游村;一起去石人沟骑马,我在湖边行走时右脚踏入沼泽地硬是左腿发力挣脱了出来;

我感谢家人给予我资金和精神上的鼓励,在我大二暑假时同意让我一个人走出去看看,这一走就是一个多月,走了十三座城市,路上遇到了许多社会上各个阶级的人,使我长了见识;感谢他们一如既往的支持我继续读研和深造,而不是逼着我赶紧就业,虽然最后没有成功考上。

我会想起和大学同学一起骑行一百二十公里去阿克苏的惊险刺激;和朋友去库车旅行,在沙漠公路上遭遇沙尘暴,方圆能见度不足三米的时候一辆越野车及时出现化解了危机;百里徒步去沙漠路上的艰辛,以及看到屹立不倒的胡杨,放佛沙漠中守护的卫士;

我会想起和女友夏天去的博斯腾湖,夜晚欣赏那静谧的孔雀河;冬天在公园里散步,差点把自己的耳朵冻掉;

我会想起和学校同学夏天扔水球,泼水冲凉,冬天打雪仗的开心;和慎在学校内散步,讨论物理化学题;和涛咨询人生困惑;和杨推着购物车在七一酱园,好家乡,友好里面乱逛;和好朋友去火焰山吃自助火锅烧烤,去八音和里唱歌,去人民电影院和和平都会踩点找最合适的场次;高三毕业去南山牧场吃烧烤,骑马。

仔细梳理,原来我留在这的回忆有这么多。

五一回家,看着周围熟悉的街道已经拆的差不多了,想想自己以后也是要一年才回一次家,不知明年回来时,这些记忆是否还能记住一半?

从我决定离开这座城市到一座新城市发展的时候,我知道我以后需要独自面临许多未知的阻碍。我要随机应变适应新的环境。

  • 我要更加坚强,来应对突如其来的质疑和压力;
  • 我要更加机警,防止被套路;
  • 我要更加勇敢,接受新挑战;
  • 我要更加感恩,记住在这样一个陌生的城市里还愿意帮助我的人;
  • 我要更加珍惜学习的机会;
  • 我要学会接纳自己的不足。

今天,我不认为一年前我放弃校招选择社招离开自己熟悉的环境而感到遗憾,并且我从没有如此神清气爽过,我放佛看到了重生,因为从此以后,我将不受束缚。

为什么要写这样一篇文章

零点整,和远在三千公里外的女友互道晚安后,继续刷我在极客时间上订阅的关于操作系统的专栏,无意间看到微信群里有人谈到焦虑,我想是时候写一篇文章,来聆听下自己的心声,因为不知道下次又等到什么时候去了。

我的知识焦虑

我最开始感到知识焦虑,是在我上大三的时候,当时下载了一个得到app,看到我尊敬的老师在里面建了专栏发文章,于是我咬牙拿出我半个月的伙食费,订阅了为期一年的专栏,于是我坚持早起打开app刷一篇文章,觉得不错还在下面进行留言。

讲真,我到现在都没有记住里面的内容,只记得当时讲的各种人生经验、理论、方法都很高大上,让我这小白大开眼界。

后来知识付费不知不觉就流行了起来,从得到app,到知识星球和小专栏,再到极客时间,于是美其名曰充分利用碎片化时间进行学习。

为什么要知识付费

说说我为什么会对一些专栏进行付费的原因

  • 得到某一领域技能的提点,从而提升在某领域的核心竞争力(主因)
  • 与大佬近距离接触,沟通,不是常说“近朱者赤,近墨者黑”嘛
  • 拓宽人脉,结识优秀的人
  • 督促自己主动学习(既然花钱了,肯定想要回本啊)

那为什么会有知识焦虑呢

在这里让我想起了木心的一首诗

记得早先少年时 大家诚诚恳恳 说一句是一句

清早上火车站 长街黑暗无行人 卖豆浆的小店冒着热气

从前的日色变得慢 车、马、邮件都慢 一生只够爱一个人

从前的锁也好看 钥匙精美有样子 你锁了 人家就懂了

从前的交通很慢,父母要从陕西到乌鲁木齐,要坐七天七夜的绿皮车;从前的网速邮件很慢,话费很贵,见字如面,惜字如金,只要能半个月写封信或通个电话那头的人就已经心满意足了;从前的人想法很简单,只要学一个技能,能混个饭饱饿不死就行。

然而那是二十年前的日子,现在是高速信息化时代,一切都在追寻快。

交通如果慢一点,就着急跺脚;视频通话稍微卡顿,就说网速太差;一个技能还没有吃透,又想着吃另一个,然而精力时间有限。

我一直在找寻产生知识焦虑的原因,如果让我追本溯源的话,我想,那就是当我们的野心还配不上我们的行动时,现实与理想的落差而造成的。

IT,一个特殊的行业

IT,我觉得它与传统行业的差别在于,更新迭代速度非常快,稍微慢一点,你就会感觉到掉队。

比如新框架替换老框架,就像人体的新城代谢似的。

所以,在我们入了这个行业的时候,就要抱着终身学习的方向去努力。

我曾如此焦虑过

当我早上醒来时,去操场跑步,去食堂吃早饭,在校园的林荫小道上背半小时的新概念,看一会我订阅的专栏的更新,然后去教师上课,这时候视情况而定,如果老师在吹牛逼,就屏蔽掉,同时拿出自己到图书馆借阅的算法书或一些文学小说看;中午午休,下午没课去图书馆或实验室看书刷题写博客;晚饭过后继续做看书学习的循环。

这样的日子持续了很长时间,因为我不知道未来我会遇到什么样的竞争对手,不知道我遇到的同事是否比我强,如果比我强,拖后腿就难受了。
未知是恐惧的,所有的这一切促使我紧张而又焦虑的度过生活的每一天。

战胜焦虑的过程

这一切的改变源于一个人的出现。

直到她的出现我的生活中,让我知道原来学校外还有一些好吃的餐馆,原来秋天学校里通往教学楼的路是如此美丽,原来梨花开的那一刻竟是如此浪漫,原来和喜欢的人安安静静的坐在小水池旁边,一人各抱着半个西瓜啃是如此美好。

她说:生命苦短,及时行乐

适当焦虑

现在的我,有时会因为新出现的需求和bug而忙前忙后,和各个岗位负责人做沟通,但我依旧会挤出晚上宝贵的睡前半小时,静下心来写一写日记,听几首喜欢的音乐。培养着和艺术,音乐,书法方面的爱好。

焦虑每一个人都会有,我也不例外。也许我们会为了自己道路远方的光而不断奔跑,但请别忘了适当停下来回首自己走过的路,因为那是属于你独一无二,不可替代的曾经。

今天是2019年4月25日,距离她毕业离校到来成都,还有一个月的时间。

放佛是越到最后越按捺不住自己的情绪。感情这东西,真是有趣。

最近情绪不怎么稳定,运维上出现了两个bug已经拖了四天还没有解决,五一回家,想和女友聚一波去耍,无奈新疆太大,从她出学校到见我,还有坐两个小时的班车和十三个小时的火车(速度比较快的)。真是很惆怅。

我对她说,你要来,我肯定会给你把吃住安排好,带你去看美景,但是我也就在家呆六天,然后就要飞回成都工作。

感情这东西,真是神奇,长年累月,让两个素不相识的人在一起,成为伴侣,这样的感觉很美妙,然而,当两个人分开时,却要承受分开时思念的痛苦。

我希望这是我最后一次品尝,不管是不是最后一次,请不要再分开这么长的时间了。

而我,也绝对绝对绝对不会再让我爱的人离开我,也不会让她再受到半点委屈。

也许这就是爱情所赋予平凡人的力量吧。

起因

之前在知识星球里星主让用SMTP协议实现邮件发送功能,看了Max Li的设计,并和他交流了一小时,在此将设计经验记录下来。

设计过程

设计一个项目的结构,有时候就像写文章一样,得分清层级。下面我将把讨论出的设计经验记录下来。

  • main.go文件,当做项目的入口,里面可以写明版本号,读取配置文件,以及日志的初始化;

  • gomod文件,包管理

  • makefile文件,如果启动程序较多,可以写入makefile文件中,到时运行直接make即可;

  • pkg文件夹,存放项目的主要源代码

  • cmd文件夹,存放项目生成的可执行文件

  • config文件夹,存放配置文件,包含项目的基本配置信息

  • vendor文件夹,将依赖的包放入此目录中

  • util文件夹,项目中所写的小工具,例如进制转换,查错,类型转换,时间转时间戳等放入该文件件

存在的问题

1,设计中难免会出现明文密码显示等相关信息,如何做到保密呢?

2,如何做到在项目build同时自己更新tag号?

参考govvv

3,怎样设计出一个框架,支持易扩展可维护呢?

我能想到的是,web服务设计接口,在接口中添加操作。

参考文章

Server Project Layout

写在最后

demo的时候,还是忍不住将所有的程序写进一个main函数里,又或者是新建一个server.go文件,然后一股脑的在里面做文章。

今天早上看星球里的朋友提出了一个问题,我觉得很对,在此摘录如下

提高编程技能是很不容易的,一个人一旦习惯了某种编程思维就很难再突破。就说说我自己,一个业务用一般方法能实现的,我很难再去想到用接口,函数类型,闭包,反射等。虽然业务实现了,但总感觉代码写的很烂,自己看了都恶心。我相信大家一定有同感,教程和视频看了很多,也知道很多概念,但等到自己用的时候,又回到了旧的思维习惯。我觉得老大在布道go时,更应关注这方面的培养,知识的细节和技巧可以慢慢积累,但思维定势不突破,永远也不可能提高。

今天和Max Li的沟通,还有在做项目时不断的以工程思维冲击着以前遗留下来的旧的思维习惯,我只想说,越早做出好的改变,成长的空间的也就越大。

共勉!

前天,也就是星期五,我终于把我负责的流媒体收录系统上的bug和需求都清空了,成功打了tag号。

可以暂时休息下,思考一下前方的路该如何走?

有时候很好奇,大家都是24小时的时间,为什么有的人风生水起,有的人碌碌无为呢?

昨天去现场咨询了一下成都落户,本科以上学历+无房,落的是集体户口。嗯,觉得这块得好好了解下。不然以后再落会比较麻烦。

接下来要做的事情,似乎还有很多:

  • 流媒体收录关于管道,并发,以及nginx的服务还要细看下;

  • 英语流利说的会员到期日还有80天,要抓紧时间了;

  • 极客专栏上有关Linux操作系统内核,网络协议和数据库原理的的内容也需要更新;

  • 给伶说好的画画,要在她五月底来成都前画完,虽然时间还很充裕,但不能再拖了。

入职半年多,现在才真正感觉是在跟着大家一块做项目。

加油,努力!

注:本文内容源自polaris在知识星球Go项目实战中的直播,已获得本人许可。

第一站:穿梭于Internet

第二站:协议

重点掌握

  • TCP/UDP
  • HTTP

常考面试题

  • TCP的三次握手过程
  • TCP的四次挥手过程

推荐书籍

  • 《图解http协议》
  • 《图解TCP协议》

建议用wireshark抓包软件进行分析。

第三站:Unix/Linux操作系统

掌握对Unix/Linux操作系统的常用操作指令操作,例如

  • shell脚本
  • vim编辑器的使用
  • sed
  • awk

多练习,多总结

网络编程方面需掌握

  • Socket、Unix Domain Socket
  • 进程间通讯

推荐书籍

  • 《UNIX网络编程卷一:套接字》
  • 《UNIX环境高级编程》
  • 《Linux/Unix设计思想》

第四站:Nginx、Apache、Caddy、等Web Server和核心功能

介绍

Web Server核心功能

  • HTTP解析
  • HTTPS支持
  • 虚拟主机(一个端口,多个域名)
  • 静态资源
  • URL重写
  • gzip压缩
  • 作为反向代理,和其他进程常用通讯协议的支持

推荐学习了解下Caddy

第五站:常用的支持Web开发的语言

  • PHP(LAMP、LNMP)
  • Java
  • Python
  • Ruby
  • Go
  • C#(.NET)

另外常用的数据结构和算法要有了解

第六站:数据库

  • 关系数据库:SQLite,MySQL,Postgresql,SQL Server,Oracle
  • NoSQL:Redis,Mongodb,Cassandra,HBase
  • 缓存:Redis,Memcached
  • 其他:Go BoltDB,dgraph,CockroachDB

入门数据库推荐使用MySQL

推荐书籍

  • 《高性能MySQL》
  • 《MySQL技术内幕InnoDB引擎》
  • 《Redis实战》

第七站:架构

  • 高可用,高性能,可扩展
  • 分布式,CAP理论
  • 分布式消息队列:Redis,ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ等
  • 常用的应用场景:异步处理,应用解耦,流量削峰和消息通讯
  • 微服务

写在最后

直播后有个答疑环节,我将此次答疑的收货整理如下:

关于服务端面试

面试考语言的分量比较小,因为服务端涉及的面比较广,需要学习的东西很多,有时候原理比语言更重要。

关于承压

在项目研发过程中,无论是测试还是上线跑,服务端会经常背锅,所以在压力上要及时调整好心态,做好和其他岗位人员的及时有效沟通。

关于未来发展

努力去大公司,做一些大项目,最好流量是千万级别以上的。

学无止境,一起努力!

起因

最近Github上有一个叫996.ICU的项目很火,原因是它公开抵制国内的996工作制,这严重违反了劳动法。工作996,住院ICU。

于是我身边有朋友问我,996到底是什么意思,那么我先说下我个人的亲身经历吧。

回忆

去年六月大学本科毕业后,幸运的入职一家上市游戏公司,从事游戏服务端开发,刚开始对新公司,新工作充满好奇,想着下班回去也没什么事,就多待会,熟悉公司的业务流程。于是在公司内,我算是比较活跃的一类。

游戏公司与其他互联网公司最大的不同在于,活动非常多,各种节日庆祝,生日庆祝,下午茶,大家其乐融融,嬉戏打闹,品尝美食,氛围很融洽…

2018年8月,隔壁一工作室出了一款暗黑类手游,IOS端零点开服,于是我看着他们工作室从早忙到深夜,要么服务器崩了,要么数据有误急需修改,有些策划兼当游戏客服,而我看到一小伙子有时还披着被子工作,莫不是凌晨都睡在公司了吧。

后来,我也要开始加班了,虽然我也并不知道为什么加班,明明可以六点半走的,莫不是为了八点的餐补,还是九点的滴滴打车报销?

再后来,我经常晚上回来,同租的老哥每次看我拖着疲惫的身子回来,一脸倦容,就知道我又加班了。

八月底,做了两个梦

  • 第一个梦是让我两天把《Java编程思想》看完,我被吓醒了;
  • 第二个梦是半醒中,突然摸到了一双手在我后脑勺处,我吓的大叫了一声,后来才发现是我那双已经被压麻的手,因为太累,躺床上还没有调整好睡姿就已经睡着了。

八月三十一号,我记得很清楚,那是我离猝死最近的距离…

那天,我坐在工位前,一直在看服务端主程用Lua语言写的球球大作战源码,虽然看不懂,但我还是坚持把这四千行的代码都过了一遍,吃晚饭的时候一激动,起身起的太快,感到心脏隐隐作痛,头昏脑涨,恍恍惚惚间已经不知道自己在哪里,在干什么。旁边的客户端同事告诉我赶紧休息下,不能再工作了。我想也是。

在回去的路上,我将我今天的症状对学西医的朋友描述的一遍,他说你这离猝死不远了,得好好休息。

我慌了,我再也不敢透支身体了!

九月初,转正答辩,虽然我列举了我这三个月的改变,和完成的事情,但当他们一脸不屑,以及对我提出的问题,我就能猜出我能否通过转正了,虽然我每天都在拼命加班,但产出在他们看来微乎其微,公司不养闲人,于是我光荣的提了离职申请。

九月十一号,我办好离职手续,出了公司,成了一名工作经验仅只有三个月的社会人士。

我很想找个没人的地方哭一次,但我知道,塞翁失马,焉知非福。如果这就是我所了解到的游戏生涯,那我的游戏开发的梦也可以就此宣告结束了。

工作996,生病ICU

我觉得这个比喻真的很好,说的更直白点,就是有命赚钱,没命花钱。

曾经我以为996就是累点而已,多学点东西也是极好的。

现在想想错了。

996在消耗你的体力的同时,也在消耗你的自由支配时间。在你本应该下班好好玩耍或吃顿美食开始看书学习的时候,你却要在嘈杂的环境下持续输出。可想而知,在精力和体力都枯竭的情况下,怎么能高产出呢?

然后负责人觉得你每天工作十二个小时,产出还这么低,看来你能力不行嘛,明天可以走人了,我再招一批年轻的实习生进来。

于是最终吃亏的还是自己。

关于996,我想说

  • 跟对leader很重要。在我看来,一个好leader可以成为自己学习的榜样。虽然我无法全面定义一个好leader到底是什么样子,但是有一点是可以证明的:就是不会总是在他人面前炫耀自己曾经获得过什么荣誉。毕竟那都是过去的事情,一个人总沉浸在过去,会影响现在以及对未来的判断。

  • 对不应该加的班说不。工作重要的一点是能力,另一点就是沟通。对于不是自己负责的任务,及时说明原因,让负责人定夺,而不是一股脑的全都接下。

  • 抓紧一切时间去学习,去提升自我。我相信那些大厂年薪过百万的人,即使不加班领导也不会拿他怎样,毕竟这种人是无法短期被替代的,同时掌握着公司内最核心的技术。

  • 加班多工资多,加班少工作也低,放佛总不能找到一个完美的选择,那么就请平衡好自己的能力和野心。

  • 提升工作效率是关键。比如设立番茄钟,遇到不懂的赶紧问。

写在最后

转眼间我已经工作快十一个月了,有时还会把自己想象是在校大学生。

从996到955,从睡眠不足到睡到自然醒精神的去上班,我知道我找到了适合自己的工作方式。

曾经看着红的蓝的绿的共享单车如三条颜色的河流缓缓的流向软件园,中间有分流到xx大厦,xx集团,我曾想过上班的意义是什么?

直到现在我也没彻底想明白,但我知道,上班绝不是去医院住ICU。

希望996这样的工作制能成为个例,也希望以后的程序员们可以早点下班。

Good Luck.

func main() {
    //转换为字符串后追加到字节数组
    slice := make([]byte, 1024)
    slice = strconv.AppendBool(slice, true)
    //第二个数为要追加的数,第三个数为指定10进制方式追加
    slice = strconv.AppendInt(slice, 1234, 10)
    slice = strconv.AppendQuote(slice, "abcgohello")

    fmt.Println("slice = ", string(slice))

    //其它类型转换为字符串
    var str string
    str = strconv.FormatBool(false)
    fmt.Println("str = ", str)
    //'f'指打印格式,以小数形式,-1指小数点位数,64以float64处理
    str = strconv.FormatFloat(3.14, 'f', -1, 64)
    fmt.Println("str = ", str)

    //整型转字符型
    str = strconv.Itoa(6666)
    fmt.Println("str = ", str)

    //字符串转其它类型
    flag, err := strconv.ParseBool("true")
    if err != nil {
        fmt.Println("err = ", err)
    }
    fmt.Println("flag = ", flag)

    //字符串转换为整型
    a, _ := strconv.Atoi("56479")
    fmt.Println("a = ", a)

}

输出