Disqus用Go取代Python来开发实时系统

翻译自:Update On Disqus: It’s Still About Realtime, But Go Demolishes Python

disqus realtime system
disqus realtime system

我们上一篇关于Disqus的文章:“如何让Disqus在2秒的延迟内,能够每秒处理16.5万条消息”,已经过时了,当时Disqus的码农们曾为忙于开发,不说话,所以他们现在在干什么我们知道的不多,不过我们在 C1MM and NGINX 上由John Watson做了个小的分享,还有一篇文章Trying out this Go thing

 

现在Disqus有了一点提升:

  • 13亿的UV
  • 100亿PV
  • 5亿用户在线用户
  • 300万个社区
  • 2500万条评论

这些都和实时性相关,但在他们的实时系统中,Go语言取代了Python:

  • 最初的实时后端是用轻量级的Python+gevent写的
  • 实时服务混合了CPU密集型任务和大量网络IO。Gevent处理网络IO没有什么问题,只是CPU竞争比较高,成为瓶颈
  • 换成Go语言解决了CPU竞争的这个主要问题
  • 仍然在5台Nginx机器上运行
    • 使用NginxPushStream,它支持EventSource、WebSockt、Long Polling和Forever Iframe
    • 所有的用户都链接到这写机器上
    • 通常一天每台机器承载每秒3200个连接,100万连接,每秒传送15万数据包并接收13万数据包,数据发送速度150mbits/s,数据接收速度80mbits/s,端到端的延迟在15ms内(比javascript选人一条评论还要快)
    • 有许多资源消耗问题。Nginx和操作系统的配置缓解了这些问题,优化了一个场景:连接很多但传输的数据很少
  • 在别的优化前,先把网络带宽用完
    • 用10G的网卡帮助很大
    • 启用gzip压缩作用很大,不过在gzip模式下,Nginx为每个连接预分配不少内存,而评论大都很小,这导致内存消耗更厉害了。因为减少Nginx的缓存大小可避免内存溢出的问题。
  • 随着消息速度的增加,高峰期每秒需处理1万多消息,到了机器的极限,端到端的延迟可能会达到几秒甚至几分钟
  • 切换到Go语言
    • 使用Go是因为它的的性能、天生就支持并发、Python码农易上手
    • 在一周时间里,Go语言版的系统就完成了,并拿到了很好的效果:
      • 端到端延迟平均少于10ms
      • 目前CPU消耗大概在10-20%,降低了很多
    • 没有选择Node是因为Node不适合处理CPU密集型的任务
    • Go不直接访问数据库。它从RabbitMQ队列中消费事件并发布到Nginx前端。
    • 没有使用Go语言相关的框架。这只是一个很小的部分,其他部分仍然是使用Django
  • 要的是把资源利用得更好,而不是堆机器:
    • 做了这么多的工作,但我们不是横向的扩展机器,因为堆很多机器并不总是解决问题最好方案。最后,速度更快的产品也能体现其价值。

 

函数式编程——把函数作为参数

函数式编程
函数式编程

网上不少函数式编程的概念都说得好抽象,看不懂,还不如码点代码来得清晰。本文以go语言为例,实践一个函数式编程中小点:把函数作为参数。这也是函数式语言与其他语言的一个很重要的区别,可以显著减少代码量。

实践:把函数作为参数

先看段段代码:

上面的代码,每个err都要判空,还要根据不同情况打印不同级别的日志。代码显得很冗长,难看。怎么办?把判空和输出日志的部分抽成方法,代码应该会清爽很多,改造后如下:

上面的代码,把冗余逻辑抽成单独的方法,主体代码逻辑就简化了,代码清爽了许多。但问题是原来的一个main函数,变成了4个函数,代码行数明显增加了。

好吧,那把三个抽出来的函数合成一个,这样判空的代码就只需要写一份了。看看这样吧:

嗯,这样确实方法数少了,代码行数少了点,但是新增了3个常量。好吧,来看看函数式编程会怎样吧:

上面的代码把函数作为参数,瞬间将代码行数降到最低,逻辑仍然非常清晰。用什么输出日志都由调用方来决定。

这算是一个函数式编程的实践案例吧。尽管用java面向对象的方式也能实现类似的效果,但是想想写出来的代码行数一定不少。话说代码越短,阅读代码的人接受的信息越集中,越容易看懂代码。有时候用函数式的思维来解决问题,你会发现代码好短。

Reference

函数式编程思想

Goproxy代理-灵活的反向代理和静态资源代理

 

goproxy代理
goproxy代理

goproxy代理可以实时的让hosts文件的修改生效,而且对hosts文件的配置功能进行了增强,用起来会很有意思,(因为是用go语言写的,所以取名为goproxy)。先说说这个软件的来由:

做web开发的同学难免会碰到线上、线下、预发布、其他环境相互切换的场景,目前有两个方案:

  1. 方案一:直接在浏览器中把url中的域名改成指定环境的ip:port,回车直接访问。不过这个办法是有缺陷的,比如:你的web应用和样式都需要使用线下的环境时,你就不能通过修改浏览器的url来实现了,因为浏览器的url只能指定web应用的环境,html中的js和css还是使用的域名,当然,如果你不嫌麻烦,也可以修改web应用的源码来使js、css资源的域名改成ip:port。这个方式对于频繁切换环境的人来说无疑很痛苦。而且为了测试而修改了源码,在提交代码时,有会把测试代码提交到线上的风险。
  2. 方案二:在hosts文件中配置几套环境,用switchhosts软件来做几个环境的切换,配合firefox的dns flusher插件(点一下这个插件,可以让firefox立刻重新加载hosts文件),可以比较快速的切换环境。而且dns flusher可以实时查看当前的域名访问的ip是哪个。这个方案明显比第一个方案好很多。但是我使用这种方案很长时间,发现firefox打开页面的速度确实不如chrome,对于每天要刷新页面千百次的我,我甚至快忍受不了firefox的那么一点点慢。但是chrome有个致命的弱点,就是没有一个类似dns flusher的软件,让修改后的hosts文件立刻对浏览器生效。这导致喜欢chrome的同学,每次用hosts切换软件切换了域名绑定后,都只能把chrome关掉,然后重新打开,这样才能让修改后的hosts文件立即生效,好在chrome关闭和打开的速度很算过得去(firefox,估计想屎的心都有了)。

好吧,话归正题,我其实是想用chrome和switchhosts软件做到快速的切换环境,于是用golang开发了goproxy。(项目是开源的代码很简单,大家觉得比较麻烦的代码用golang两行就能搞定,详细使用文档请见项目主页:https://bitbucket.org/weager/goproxy)。

为什么要用代理?代理相当于是浏览器和最终的服务器之间的中转站,浏览器如果使用了代理,你就可以控制浏览器打开的所有页面的,既然chrome不能实时的读取hosts文件,那我可以让goproxy实时的读取hosts文件(其实是每次url访问经过goproxy时去检查一下hosts文件是否修改,修改了就reload一下)。这样每次需要切换环境只需要用switchhosts选择一套环境的hosts绑定,然后F5刷新chrome就可以了。其操作步骤比firefox+dns flusher+switchhosts还要少(dns flusher需要点击触发reload hosts文件)。

这只是一个基本的goproxy代理功能,其实goproxy还有更有意思的功能。下面来一一解读:

hosts文件中配置的映射一般都是把多个域名映射到一个ip上,如果把url前面的“http://”去掉的话,hosts里面的映射,其实就是前缀的替换,比如这个hosts配置:

127.0.0.1  www.1688.com

如果一个url去掉http://后为www.1688.com/index.html,那么映射后的url就为127.0.0.1/index.html。试想,如果我本地的环境端口号不是80,而是8080,怎么办?你能在hosts文件中配置成127.0.0.1:8080 www.1688.com吗?如果你用goproxy就可以这么配了。因为goproxy的原理就是一个前缀的替换,他的配置格式和hosts文件本身的格式非常类似,只是做了一点扩展。所以你可以这样配置:

127.0.0.1:8080  www.1688.com   search.china.alibaba.com   list.china.alibaba.com

127.0.0.1:8080/aaa/ddd   www.1688.com   search.china.alibaba.com/ccc   list.china.alibaba.com/bbb

(注意:路径的斜杠是向左的斜杠,不是windows里面的向右的斜杠。)

继续在url上做文章,上面的两行配置都是反向代理,那是不是也可以把静态资源给代理起来呢?goproxy也做到了,你甚至可以这样配置:

D:/workspace/work/style   style.china.alibaba.com/app/search

看到这个简单的配置,你会意识到你装的apache、varnish可以卸掉了吧

既然代理的功能可以自己定制,那功能应该可以再多一点,比如:对某个前缀的url添加或者删除参数。比如下面的配置:

127.0.0.1:8080    www.1688.com     +debug=true   +begin=2    -n=y  -version

110.20.30.41:8080    www.1688.com     +debug=true   +begin=2    -n=y  -version=2

第一行配置的意思是:如果一个url的前缀是“www.1688.com”,那么就给这个url的参数中添加debug=true和begin=2(如果已经有了begin=1,则会被覆盖成begin=2,debug参数也同理),并移除n=y和version参数(无论version的值是什么,被移除的参数在url中时才移除,不在url中则不作修改)

第二行配置与第一行配置的不同是,移除version参数的条件必须是version的值等于2。

这种些看起来很奇特的配置在hosts中显得很另类,好在现在的浏览器对这种他们看不懂的映射配置直接忽略,只读取他们认识的配置,所以你可以在hosts文件中配置正常的和不正常的配置,正常的配置对没有使用代理的浏览器依然有效,不正常的配置则不会影响浏览器的工作。

目前goproxy我自己一直在使用,用得很爽,也有其他人在用,评价是很轻量级,如果你偏爱goproxy的简单强大的配置功能,那么做web开发切换环境时,可以考虑试试goproxy。当然goproxy不仅仅用于chrome,任何浏览器都可以可以配置代理指向goproxy所在机器的ip和端口号(默认是8000端口),欢迎交流。

【go语言】修改URL中的query失效的原因

用go语言来修改URL的query部分后,得到的URL尽然没有变化,也就是说修改URL中的query失效了,追查go的URL实现源码才知道原因,这和go语言对URL的实现机制有关。

go语言的net/url包中有如下的api:

二话不说,先上段代码看看效果:

这段代码运行的结果是:

显然结果中并没有把参数kkk=vvv加到url中,直接从api看,应该会加进去的啊,那为啥?我开始对着api做了很多尝试,比如传值和指针的对比、试试编码解码等,最终结果都一样,修改后的URL没有变化。最后没招了,只能看go的源代码了。

从这里看到query在URL对象中不是以一个map的形式存储的,而是一个原生的字符串

从这里可以看到ParseQuery的参数为URL.RawQuery字段,根据参数的值传递机制,ParseQuery函数的返回值v是一个与u.RawQuery无关的一个Values对象,此时对Values对象进行AddSetDel的操作都不会改变u中的RawQuery。所以要想把修改URL对象,就需要把修改后的Values转成字符串,再赋值给u.RawQuery字段。看下修改后的代码:

这样运行结果就正确了:

有时还是需要去研读源码和官方文档的,精华的东西都在源码中,这才是学习,而不是看着api,拿过来就用。

windows下用eclipse+goclipse插件+gdb搭建go语言开发调试环境

目前go语言在window或者linux操作系统上,最好的go语言开发调试环境都是由eclipse+goclipse插件+gdb搭建的。如果你还没有搭建好go语言的开发环境,请参考这篇博文《windows下Go语言的安装和开发环境搭建》

一般大家用eclipse都是开发java,如果要开发go,那就得安装goclipse插件了,这样才能有代码高亮、自动编译、联想提示、跳转到函数定义等丰富功能;如果还想要调试的功能,就还得有gdb;如果你还想引入github上的开源库,那还需要git客户端。下面一一介绍如果安装和使用:

安装goclipse

在安装goclipse前,需要事先安装好jdk8。goclipse下载地址:http://goclipse.github.io/releases/ ,由于我朝的高墙存在,可能这个地址访问不稳定,甚至有的人根本访问不了,建议参考goclipse官网的文档,把releases目录下的内容下载到本地后解压,使用本地的路径作为url来安装,比如:file:///D:/goclipse.github.io-master/releases/,搞java的安装eclipse插件应该比较熟悉,安装过程也比较简单,直接点下一步,接受,yes,重启eclipse就可以了(可能由于国内网速的原因,安装过程可能比较长)。如果安装成功,可以在Window–>Preferences里面左边的树形菜单里看到Go,点Go,然后在右边设置GOROOT的路径为go的安装目录即可,下面的几个Go tool的路径会被自动识别到。此时应该看到如下的效果:

浏览到go的安装目录

下面创建一个go项目来检验一下,在菜单栏如下操作File–>New–>Other–>选择Go–>选择Go Project–>Next–>给项目取个名字(比如gotest)–>Finish,创建成功,然后在项目的src目录下创建一个带main入口函数的源文件test。

配置gbd
配置gbd
创建带main函数的源文件
创建带main函数的源文件

然后编辑代码看看高亮和代码提示的效果

golang代码联想
golang代码联想

上面的效果不错吧,goclipse现在已经在方法提示、autocomplete和查看方法变量声明等功能方面支持得很好了,而且这些功能不用额外配置,因为goclipse中包含了一个gocode(用于Go语言的自动补全工具),在安装goclipse的时候会自动安装上gocode(比如:我的gocode在D:\software\green_software\eclipse-j2ee\eclipse\plugins\com.googlecode.goclipse.gocode_0.7.6.v439\tools\windows_amd64),所以gocode一般不用额外的安装和配置,只需要勾选让其在eclipse启动时自动启动即可,配置路径为:Window->Perferences->Go->Gocode,这样eclipse在启动后,你可以在Window资源管理器的进程列表中看到有gocode这个进程。如下图:

goclipse的gocode配置
goclipse的gocode配置

如果你没有gocode,或者gocode没有启动,或者你想用最新的gocode,那么你可以在启动eclipse之前使用如下的命令手动启动gocode:

gocode.exe -s -sock=tcp

这样你就可以在eclipse中使用点号来联想方法和字段,以及用alt+/来自动补全方法和字段。

运行这个程序有两个办法,一个是cd到这个工程的bin目录下,执行下面自动编链接好的exe文件,另一个是在eclipse上右键main函数所在的源文件–>Run As–>Run Go Application,即可。

我平时在使用过程中,发现一个奇怪的问题,就是自己的代码不能自动编译也不能运行了,研究一番才发现是工程的src目录配置不对,理论上这个配置不需要自己手动配的,但是如果你也碰到这种情况,你可以看看这个src目录是否配置正确了,见图你就知道了:

新引入的go项目后检查项目配置
新引入的go项目后检查项目配置

安装GDB

想一想,如果写代码不能调试,那就只能通过fmt.Println(“xxx”)这种方式,那是多么痛苦啊,让自己的开发环境可以动态的调试是很有必要的。下面介绍下,如何安装和配置GDB,让eclipse支持go语言代码的挑食的。

由于go编译器编译出来的可执行程序是按照gdb的标准的,所以目前调试go语言代码必须要有gdb,需要在windows上装一个,linux上装gdb很容易,但是windows上比较麻烦一下,比较常用的做法是装MinGW,然后用里面的gdb。但是我推荐另一个做法:

下载另一个自带了gdb的go语言集成开发软件liteide(绿色软件,解压即可),里面的bin目录下有gdb.exe和gdb64.exe,这两文件前者用于32位操作系统,后者用于64位操作系统。

然后把你操作系统对应的那个gdb配置到goclipse插件里面,操作如下:Window–>Preferences–>打开Go节点–>选择Debug,然后设置GDB,我的操作是64位的,所以选择了gdb64.exe。如图:

配置gdb
配置gdb

这样就ok了,现在可以去试试,在代码里设断点看效果了。(当然现在这个debug功能还没有那么完善,但是大部分的情况都是可以动态查看变量的值的,某未及之处只能通过输出的方式来做了。)

配置GOPATH变量关联go的SDK源码

如果不使用eclipse+goclipse,而使用记事本写代码的话,就必须要配置gopath,尽管使用goclipse后可以不设置GOPATH环境变量,但是那样就不能在eclipse里面直接关联查看go的源码了。如果想让自己的代码中选择一个函数,然后按F3(或者按住ctrl点某个函数)就能看到源码的话(eclipse常用的关联代码的功能),就需要给goclipse配置GOPATH,配置好了以后你可以按住ctrl点某个函数,就可以跳到go的源码中直接查看源码的实现,这个对学习go很有帮助,强烈推荐大家使用。配置效果如图:

在eclipse中设置gopath
在eclipse中设置gopath

配置完了以后,就可以看到Project Explorer里面多出了一个GOROOT节点,这个节点下的都是go的源码,没事多看看很有好处!

用git来下载第三方库

由于现在github非常火,很多开源爱好者都把自己的代码托管到了github、bitbucket、google code上,go语言本身也是开源。想学go语言的同学难免会去这些地方看其他人写的好的代码,不过最好是把这些代码下载自己把玩一番。要下载这些代码就需要使用git客户端,它可以让你用简单的命令就可以把代码下载下来,并打包好。这样你就可以方便的使用了。下面介绍下git客户端的下载和安装:

git的windows客户端下载地址为:http://code.google.com/p/msysgit/downloads/list?q=full+installer+official+git

安装过程中有一个步骤需要注意的,这一步可以让你在dos命令行中使用git命令,这样比较方便一点,如图:

git安装,让你的git命令可以在windows控制台里使用
git安装,让你的git命令可以在windows控制台里使用

这时候你就可以在windows命令行中使用git命令了,现在试试看看好用不,我在bitbucket上有个开源的工具包,以这个作为例子试试看~~

先cd到你的工程的src目录下,然后键入这个命令:go get -u bitbucket.org/weager/utils

这个命令中的-u可以在你曾经下载过这个包时,自动更新这个包。此时,应该能在src目录下看到bitbucket.org目录,这目录里面有一个weager目录,这个目录下就是utils包的代码了。

然后运行打包命令:go install bitbucket.org/weager/utils

此时会在pkg目录下产生于src相同的目录,在utils目录下会有一个utils.a文件,这就是打包后生成的文件。见图:

go项目目录结构以及下载的包
go项目目录结构以及下载的包

下载了包以后,就可以在你的工程中使用这个包了,比如:

go语言代码中引入第三方库代码演示
go语言代码中引入第三方库代码演示

 

 

到此为止,go语言的开发环境完成了,可以开始开发了,good luck 🙂

Reference

eclipse及其他IDE的go语言开发环境搭建

goclipse安装

Go语言出中文书籍啦!

现在比较靠谱的中文的Go语言书籍有3本:

1. 首先推荐一下:《Go语言编程》——由一个做云存储的公司“七牛”的两位大牛:许世伟和吕桂花 编著

这本书我第一时间买了,很适合阅读,书不厚,但是讲得很全面,很精练,适合有一点编程基础的同学阅读,而且这本书还可以作为手册来参考。

2. 还有一本开源的免费书籍:《Go Web 编程》——由一位乐于分享知识的朋友写的,目前还在编写阶段,但是已经可以直接在线阅读了,也可以把书下载下来,生成html来阅读。非常欣赏这位gofans的精神,乐于分享,共同进步,同时也希望有更多的人能参与到这本书的编写中来,不论你是熟悉go的还是不熟悉的go的。

这本书主要是从web开发的角度来讲解go语言的,内容讲得很基础,适合初学者学习,想用go来做web开发的同学看看这本书是很不错的。

3. 第三本书是:《GO语言 云动力》,这本书是最早的中文go语言书籍,但是正因为出来得较早,所以书的质量可能不是最好的。

如何创建、编译、打包go语言的源代码和工程进行go语言开发

go语言编译环境为Go1,下面用具体的实例来演示如何创建、编译、打包go语言的源代码和工程,如何用go的命令来进行go语言开发。

步骤如下:

  • 设置GOPATH,这个环境变量指向你的projectDir(工程目录),形如:GOPATH=/home/user/ext:/home/user/projectDir (可以设置多个工程目录,linux下用冒号分隔,windows下用分号分隔)
  • 创建工程文件夹projectDir
  • 在projectDir下创建src目录
  • 在src下创建区分包的文件夹myDir
  • 在myDir下创建包pkgDir
  • 在pkgDir下创建package source源代码文件,这些文件的package都是pkgDir,比如创建一个文件test.go,代码如下:

  • 写完源代码以后在src目录下运行go install myDir/pkgDir命令把包pkgDir打包成.a文件(会在projectDir/pkg目录下生成pkgDir.a的目标文件)
  • 在myDir下创建command source文件夹,取名myCommand
  • 在myCommand文件夹下创建带有main函数的源代码文件hello.go,代码如下:

  • 在src下运行go install myDir/myCommand (会创建projectDir/bin目录,并生成以myCommand?为文件名的可执行文件)。需要注意的是要生成可知性文件的话,go install后的文件夹下一定要有一个带有package main的go源文件。鉴于手动操作的复杂性,推荐大家使用eclipse来写go的代码,在eclipse上配置了go环境后,可执行文件和.a文件都是自动产生的。

最后projectDir目录下的结构类似如下的形式:

如何编译、打包go语言(golang)的源代码详见:http://golang.org/doc/code.html

windows下Go语言开发环境搭建

Golang是Google的第二门编程语言,但是Golang与simple和Dart不同的是,Golang是一门系统级编程语言,也就是说他和C++、Java是同类型的语言。学习Golang是我自己的爱好和兴趣,我个人对这个语言比较看好,毕竟这个语言是在已经有了C++、Java、Javascript、Python等强大并极其流行的语言下诞生的,他就是为了补偿现有编程语言的不足,提取他们的精髓。就此创造出了一门能适应当代快速开发和迭代、拥抱变化、大数据、多核高并发的场景。该语言的被号称是:互联网时代的C。这就意味着:1.具有互联网web开发所需的特点,即开发高效;2.具有C语言一样的运行速度,即运行高效。相信这两个特点会让你找到归宿了吧。希望Golang能流行起来,让大家见到它的魅力。下面介绍下Go语言开发环境搭建的步骤:

1. 安装

Windows上的安装步骤如下:

  • 下载Go开发环境:http://code.google.com/p/go/downloads/list
  • 如果你下载的是exe或者msi的安装包,就直接安装了,不需要配置环境变量
  • 如果下载的zip,就将其解压;配置环境变量
    • GOROOT = D:Program Files\go
    • GOBIN = %GOROOT%\bin (现在的go1.5已经不需要配置这个环境变量了)
    • GOARCH = 386                   (如果你的系统是32位则是386,如果是64位则是amd64(现在的go1.5已经不需要配置这个环境变量了)
    • GOOS = windows                (如果你的操作系统是windows,如果是linux请写linux)(现在的go1.5已经不需要配置这个环境变量了)
    • path:如果末尾如有没有分号,则添加之,然后再末尾添加%GOROOT%\bin
  • 安装好了后,在cmd中运行go,如果出现go命令的帮助信息,则go开发环境安装正确。

Linux、FreeBSD、OS X上的安装请看http://ioio.name/golang-install.html或者官网的介绍:http://golang.org/doc/install.html

2. 测试(Windows下)

在任何地方创建一个文件:goTest.go

写入如下代码后保存:

在cmd中cd到该文件的路径下,用一下命令进行编译、链接和执行

go run goTest.go

如果输出hello world!表示开发环境安装成功

Go 1的性能也很喜人,比较新的一个对Go 1和Node.js的性能对比测试:http://www.cnblogs.com/QLeelulu/archive/2012/08/12/2635261.html 

3. 开发工具

Golang(Go语言)中的defer的使用和机制

defer在Golang中可以被用来代替c++、java、C#中的try-catch-finally语句。

也可以一定程度上用来避免c、c++中的malloc-free和new-delete的未成对出现的风险。

defer是将需要执行的语句放到函数体的其他步骤执行后再执行,如果一个函数中有多个defer操作,那么这些defer操作会类似于放入栈中一样,先被defer的操作后执行。