使用 Go 构建 Resilient Services - 技术会谈 已翻译 100%

oschina 投递于 2015/08/07 10:45 (共 26 段, 翻译完成于 01-05)
阅读 2999
收藏 55
Go
2
加载中

这是一篇在 GopherCon 2015 的技术会谈,主讲人 Blake Caldwell 曾是 Fog Creek 里 Kiln 团队的软件工程师,他将讲述如何使用 Go 来重写的我们的 SSH 反向代理, KilnProxy,达到了性能的提升。 一起来听听他怎样来重写服务的,在将git clone时间减半的同时, 保证了服务质量更加稳定和可靠。

Blake 在他的博客写一些关于 Go 语言和软件开发的文章。他已经开源了在会谈里提到的分析器 profiler,会谈的幻灯片也放在了他的GitHub上。

视频链接:https://youtu.be/PyBJQA4clf

关于 Fog Creek 技术会谈

在 Fog Creek 每周都会一个技术性的会谈,我们自己的员工上去讲或是邀请一些嘉宾。 通常都是一些较短的、不太正式的演讲, 内容都是软件开发工程们感兴趣的话题。我们会尽力分享更多给大家的。

h
hizhew
翻译于 2015/08/26 12:59
1

内容和时间点

  • 简介(0:00)

  • 项目背景(0:22)

  • 关于SSH反向代理KilnProxy(1:33)

  • 结果(3:35)

  • 错误处理(5:20)

  • Channels(7:17)

  • 异常处理(8:09)

  • 避免竞争(10:00)

  • 超时的实现(11:20)

  • 内存分析(13:44)

  • 日志生成(21:20)

内容抄录

简介

Blake:  大家好。 这又是一个代码重写的案例,进行地非常顺利,所以我想分享给每一个人。和其它的会谈差不多, 会谈到很多不同内容,但不会就每一个方面深入展开下去。我只能尽可能多的讲讲使用到的一些工具和技术了, 怎样来帮忙我实现第一个上线的 Go 实现的服务。

背景

先说说背景吧。我去年在位于 New York 的 Fog Creek 工作。你有听说过 Fog Creek 的话,就可能很熟悉 Kiwi 了,它是 FogBugz 的吉祥物。我之前工作的项目是 Kiln。 Kiln 是 Fog Creek 提供的 Git 和Mercurial 的源代码托管服务,和 GitHub 类似的一个东东,不过同时支持 Git 和 Mercurial。 我在那里工作之前,连 Mercurial 都没听说过, 但是现在已经非常熟悉了。我的大部分工作是使用 C# 和 Python,想对底层的一些东西总是非常感兴趣。

h
hizhew
翻译于 2015/08/26 22:46
1

去年我也很幸运的参见了 Google I/O。当时我完全不知道 Go 是什么,实际上我还没有听说过它。现在你在哪儿都可以看到它,它哪里都有。我不需要想你解释我为什么这么快就爱上它。只要有机会我就去参加所有的 Go 讲座,我去见一些 Go 的作者,非常爽。我知道如果有什么问题出现,总有一天会出现相应的解决方案。

我想用 Go 实现些神奇的东西。我想在业余时间用它做些兴趣相关的,有意思的事情。但是我想在工作中证明,Go 可以做些非常神奇的东西,让 Kiln 变的更好。我找了下有什么东西可以重写,你总可以找到些东西重写。最后决定重写我们的 SSH 反向代理。当我开始重写它的时候,我还不知道反向代理是个什么东东。

asdfsx
asdfsx
翻译于 2015/08/07 16:38
2

关于 SSH 反向代理 KilnProxy

还是先说说背景,看它到底是来做什么的。当你使用 Git 或者 Mercurial 时,有两种方式和远程服务器交互, 使用 HTTP 或者 SSH。 SSH 使用到了公私密钥对,相对的会更加安全一些。 假如你正在用 SSH 从Kiln 克隆代码仓库的话,那就肯定和 KilnProxy 打交道了。你是左边这部分人中的其中一个。 和KilnProxy 交互时, KilnProxy 需要认证你的 Key,来确定正在交互的这个人就是你,而不是其它人;然后它会查找你的代码到底放在哪里,我们的后台服务器太多了, 所以需要确定你的代码是在哪台服务器上的。它和后台服务器建立连接,同时也有一个到你那边的连接,那所做的工作就是把交互的数据传过来然后再传回去。

它已经能够很好的工作了,为什么还要重写?当时的情况是,使用 SSH 来克隆比 HTTP 要慢好多, 应该不慢才对。无论怎么看,使用 SSH 应该要更快。同时还遇到了一些稳定性的问题,我们的系统管理员必需要每天重启服务。可以想像一下,你有一个具大的代码仓库且你正在克隆着,20多分钟已经耗进去了,而这时恰好撞到了我们重启服务器,你耗的那些时间都白费了,全部得从头再来。这是很让人崩溃的。

h
hizhew
翻译于 2015/08/26 23:54
2

事实证明这个项目很适合用 Go 来实现 ,因为有太多太多的并发了。 大概讲下,以便大家有个概念。当你第一次连接的时候,会进入监听循环,开启了一个 Go routine。 这个 Go routine 会先负责验证你公私密钥对,或者是私钥;然后再和后服务器建立连接。一旦连接建立起来了,相应的 Go routine 还会代理标准输入、标准输出和标准错误输出 。

结果

结果怎么呢?非常的好,出人意料。我只是想让它能正常工作,它却运行的非常好。那到底有多好。在几分钟的时间内,我们就可以克隆一个小的代码仓库,跟踪它所使用的时间。就在这点是我们从 python 实现转换到了 Go 的实现。 先让你们猜下是哪个点, 就是粉色的标记过去一点点。这是我们重写后刚上线的点,从此以后, SSH 和 HTTP 同样的快了。

h
hizhew
翻译于 2015/08/27 00:12
1

可能你以前就注意到了,1MB 的仓库之前花费的时间是 1.5s, 重写后只需要 0.75s 了。速度差不多是以前的两倍了。当仓库变得越得越来越大时,效果就更加显示了。同时遇到问题也少了。现在线上运行的就是一个更快、更稳定、很少出问题的服务了。

作为我的第一个用 Go 实现的服务,必须讲讲怎么用 Go 来实现一个稳定的服务了,也分享一些 Tips。现在讲的主题是弹性吧?就先要来看看弹性服务的要求,不能崩溃,也不需要每天重启;没有内存泄漏,不会挂起,也不能卡住不动了。很显示,这无疑将是个很长的流程,没有银弹。这个流程将覆盖从在上线之前的开发、调试剖析,以及上线之后的服务监控。

h
hizhew
翻译于 2015/08/27 10:21
1

错误处理

现在开始讨论错误处理,还好,没有太多的演讲者说这个…不得不说我们必须要处理每一个错误。我感觉在座的每一位都知道要处理每个出现的错误。我是用 java 的,在 java 里只用代码中用极简短的异常代码来处理错误,但我并不关心它们(异常) 被抛出,也不关心怎么处理它们。在 java 里我们根本不需要关心这些。

我们都明白这是一种模式。你从一个函数中获取一个资源或者一个返回值,还有对应的错误信息,并且有错误的话你不能继续使用返回值。之后我们需要检查这个错误是否发生,如果有错误我们就跳出这个函数。并且需要执行清理或关闭资源。有一件我非常确定的事就是,在视觉上不把这一小块代码写在新的一行里。我想看到的是这块代码是一个完整的单元,是不可分解的。

卖女孩儿的小酱油
卖女孩儿的小酱油
翻译于 2015/08/26 09:29
1

看起来这里有处理所有的错误,所以没有问题。众所周知,有些时候我们应该检查下空值(Nil)。回到我们的例子,假如 OpenResourceA 能够返回空值,并且这不是一个出错状态?也许这是一种不常见的情况:试图打开某个资源的时候,由于某些原因,掉线了。从技术上来说,这并不是一个错误。在 defer 语句中,可能就要慌了。

不过倒也未必,我们当然有办法避免这个,一种做法就是使用一个内联函数、一个匿名函数。我们可以在那里完成我们的小检查,判读不是空值,再关闭。这种方法的一个问题就是它很拙劣。我不是很喜欢这种做法。是否所有人都能看得清楚这个?我不知道这是否足够大了。

智恒
智恒
翻译于 2015/11/02 23:48
1

我喜欢用 defer 语句来处理需要推迟处理的方法,我喜欢简洁的 defer 语句,希望它能简单、整洁。有一件情况我最近差点忘记,如果一个 struct 方法需要传入一个 struct 指针,但是如果这个指针为空,此方法仍然会接受这个指针并且被调用。这种情况下,我通常采用 defer 语句,并且在语句中清理资源。我通常会检查空指针,回到前面提到的 DeferResourceA.close 方法,那么它实际上就是空指针的情况。我发现这种实践非常好。

Channels

让我们来聊聊 channels。我们熟悉 channels 并且知道它能给我带来很多乐趣,它很棒,但是如果你不知道正在用它做什么,就很有可能给你带来麻烦。我不准备在这里深入的探讨 channels,因为往深了讲需要半天的时间,但是我可以在这里给大家一个参考,那就是 Dave Cheney 的博客,它指引我度过一段很艰难的时间。每次我遇到 channel,我都会去看看这个叫“Channel 原理”的文章。我还把它记在了别处,因为我要确保不弄错 channels。

drkaka
drkaka
翻译于 2015/12/28 15:54
1

有三点我想在这里着重说一说。如果对一个空 channel 进行写入或者读取操作,它将会永远阻塞;当你在一个 go-routine 下遇到永远阻塞,那么这个 go-routine 就不会退出,相应的资源(比如生成的局部变量等)也就得不到回收;当你对一个已经关闭的 channel 进行操作,你就会被 panic。

处理 Panic

让我们来聊聊 panic。它们通常或者说大部分是由于编程错误引起的,如果遇到 panic,程序将会中断服务。如果我说有时我甚至喜欢从 panic 当中处理恢复,这有可能会让大概在座的一半的人感到困惑。这个问题富有争议,人们会说“这个程序员的错误,如果你有一个程序错误,那就应该让程序终止,并修复它”。

说的没错,但是我确实会犯错,而且错误也会在生产环境下发生,我希望能够尽可能的减轻它带来的影响。不需要过多的深究问题所在,你就能从 panic 中恢复。你不应该把它当做异常,这就是我的处理方式。当我建立那个函数的时候,你们看到了出错的地方,但是我会保证在函数返回之前,所有资源都会被妥善清理,采用这种方式,就算 panic 出现,所有清理工作也会得到进行。

drkaka
drkaka
翻译于 2015/12/28 16:15
1
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(2)

wwek
wwek
感谢翻译. 很棒的实践
gowk
gowk
翻译的很棒,非常好的Go语言资料,谢谢分享
返回顶部
顶部