开源中国

我们不支持 IE 10 及以下版本浏览器

It appears you’re using an unsupported browser

为了获得更好的浏览体验,我们强烈建议您使用较新版本的 Chrome、 Firefox、 Safari 等,或者升级到最新版本的IE浏览器。 如果您使用的是 IE 11 或以上版本,请关闭“兼容性视图”。
使用 Go 构建 Resilient Services - 技术会谈 - 技术翻译 - 开源中国社区

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

oschina 推荐于 2年前 (共 26 段, 翻译完成于 01-05) 评论 2
收藏  
55
推荐标签: Go 待读

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

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

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

关于 Fog Creek 技术会谈

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

hizhew
 翻译得不错哦!

内容和时间点

  • 简介(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,想对底层的一些东西总是非常感兴趣。

hizhew
 翻译得不错哦!

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

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

asdfsx
 翻译得不错哦!

关于 SSH 反向代理 KilnProxy

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

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

hizhew
 翻译得不错哦!

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

结果

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

hizhew
 翻译得不错哦!

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

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

hizhew
 翻译得不错哦!

错误处理

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

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

卖女孩儿的小酱油
 翻译得不错哦!

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

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

智恒
 翻译得不错哦!

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

Channels

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

drkaka
 翻译得不错哦!

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

处理 Panic

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

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

drkaka
 翻译得不错哦!
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们
评论(2)
Ctrl/CMD+Enter

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