【译】从PHP(Laravel)迁移到Go
今年早些时候,我做出了一个糟糕的商业决定,我决定用Go重写Laravel应用Boxzilla,虽然没有遗憾,最后效果惊人。
仅仅几个星期后我就部署了Go应用程序。构建它是我几个月来最有趣的事情,我学到了很多东西,最终结果是对旧应用程序的巨大改进、更好的性能、更容易的部署和更高的测试覆盖率。
该应用程序是一个相当简单的数据库驱动的API和包含帐户管理等应用程序,用户可以在登录以后下载产品,查看发票或更新付款方式。
Stripe和Braintree用于接受订阅付款。发票使用MoneyBird处理,一些事务性的电子邮件使用Mailgun发送。
虽然Laravel在这方面做得足够好,但有些事情总是让我感到过于复杂。每隔几个月发布一个新的“主要”版本,如果新的版本包含重大改进,我心里也会觉得这没什么,但是很多时候,感觉都是一些非常简单的改动,例如命名和目录结构进行了更改。
Why Go?
去年,我已经将几种服务转移到了Go上,所以我对这种语言并不是完全陌生。作为销售基于WordPress的产品的开发人员,我的一部分工作是在一个古老的技术堆栈中工作,该堆栈主要关注最终用户。
如果我不是自雇人士,我会简单地申请一份新工作来弥补这种缺乏性感技术的现象。作为我自己的老板,我有责任使自己的日常工作变得有趣,而不仅仅是追逐更多的即时资金。如果收入允许(并且确实如此),为什么不找点乐子呢?
编写Go代码很高兴,工具很棒,不仅开发速度快,而且最终结果通常也很快。刚读完Go项目的目的(https://golang.org/doc/faq#What_is_the_purpose_of_the_project),就使我对语言产生了浓厚的兴趣。
我认为我们会在未来几年内看到大量人从动态类型的语言(如PHP,Python和JavaScript)切换到Go。
移植代码库
将代码迁移到Go中主要包括正确地进行数据库交互以及将Blade模板移植到我们可以在Go中使用的东西。
ORM总是会阻碍我的一件事,因此我选择了可模拟的数据访问层和简单的SQL查询。使用Meddler摆脱了一些烦恼:将查询结果映射到结构体中。
为了支持分层模板和局部,我开源了grender(https://github.com/dannyvankooten/grender),这是基于Go的标准库html / template包的一个小包装。这使我可以相对轻松地将Blade模板文件移植到Go中,因为我可以使用相同的层次结构和部分模板。
为了与Stripe集成,有官方的Stripe -Go(https://github.com/stripe/stripe-go)软件包。对于Braintree,有一个非正式的braintree-go(https://github.com/lionelbarrow/braintree-go)软件包,该软件包被忽略了一会儿,但最近又受到了新的关注。由于到目前为止,在Moneybird中还没有Go软件包来管理发票,因此我构建并开源了moneybird-go(https://github.com/dannyvankooten/moneybird-go)。
对比分析
由于Go是一种编译语言,具有比PHP更好的标准库,因此像我将要比较的那样比较这两种语言并不是很公平。就是说,我认为分享一些数字会很有趣。
性能
wrk用于为两个返回登录页面HTML的应用程序执行一些简单的HTTP基准测试。
并发 | 平均 潜伏 | 要求/秒 | 传输/秒 | |
---|---|---|---|---|
Laravel | 1 | 3.87ms | 261.48 | 1.27MB |
Laravel | 100 | 108.86ms | 917.27 | 6.04MB |
Go | 1 | 325.72μs | 7365.48 | 34.27MB |
Go | 100 | 11.63ms | 19967.31 | 92.91MB |
Go | 200 | 37.68ms | 22653.22 | 105.41MB |
不幸的是,一旦我将并发“用户”的数量增加到100以上,Laravel应用程序(或PHP-FPM套接字)就一直崩溃。
NetData提供了以下图表,以查看服务器在所有这些负载下的承受能力。
100个并发连接的Go程序
100个并发连接的Laravel 程序
请注意,我在运行应用程序的同一台计算机上运行了基准测试,因此这会严重影响两个图表。
代码行数
让我们比较两个应用程序中的代码行,包括所有的依赖项。
$ find . -name '*.php' | xargs wc -l
156289 total
Laravel版本包含超过156.000行代码。这还不包括要运行Laravel测试等相关的开发依赖包。
$ find . -name '*.go' | xargs wc -l
33624 total
另一方面,Go版本包含33.000行代码。这是完全相同功能的代码的五分之一。
让我们在Laravel应用程序中排除外部依赖关系,以便知道我实际写了多少行php。
$ find . -name '*.php' -not -path "./vendor/*" | xargs wc -l
13921 total
多少行Go
$ find . -name '*.go' -not -path "./vendor/*" | xargs wc -l
6750 total
即使仅查看托管代码行,结果也要稍微多一些。尽管如此,它还是只使用了原来完全相同的应用程序的一半代码。
测试范围
测试是Go中的头等公民,测试文件紧邻实际源文件存在。
license.go
license_test.go
subscription.go
subscription_test.go
这使得应用测试驱动的开发变得异常方便。
在我们的Laravel应用程序中,我们主要进行了集成测试,以检查请求处理程序是否返回正确的响应。总体测试覆盖率很低,主要是由于紧密耦合,而这又主要是我的错。再次编写相同的应用程序确实也有帮助。
TLDR
做了一些您不应该做的事情:用另一种语言重写应用程序,因为我感觉很喜欢。获得了很多乐趣,并且得到了更小,更快的应用程序。