原文:Going for Go and Sticking with SQL
作者: Poki Engineering
翻译:雁惊寒
责编:仲培艺
译者注:Poki是一家在线游戏发行商,在全球拥有3000多万用户。在Poki的官网上提供了数千款在线游戏,并可以在手机、平板和桌面电脑上运行。本文出自Poki工程师,讲述了在重构游戏平台过程中,有关编程语言和数据库选型的一些看法。
以下是译文。
在最初的时候,我们自认为应该使用自己熟悉的编程语言,因为我们是一个小团队,而且已经做了两项冒险的举动:将我们的大流量游戏平台切换到微服务和完全重建Web应用程序。
不过,最终,我们还是决定放弃PHP,转向Go。在这篇文章中,我们将解释为什么要这样做。我们还将分享一些微服务架构中有关数据库的观点。
我们熟悉的语言是PHP,它驱动着我们现有的应用程序,有两个模糊的理由支撑着我们使用PHP:
这些听起来都很正确,但是当我们清楚地认识到PHP真的不是我们这个案例的正确选择时,我们很快就放弃了这些想法。
我们正在向着微服务架构迁移,因为我们希望这些大流量的基础架构(每日200万活跃用户)具备可扩展性。从长远来看,随着我们向1000万甚至更多用户发展的时候,我们的基础设施也应该能相应地进行扩大。
PHP无法满足这些需求,因为:
PHP的启动成本很高。 PHP一开始是为短生命周期脚本的运行而设计的,因此持久性并不是其原生特性。这意味着对于每个请求、数据库连接和类都必须实例化,这增加了不必要的开销。当然,这也是有办法解决的,例如通过PHP-FPM或Apache来创建连接池,或者绑定C以获得与Redis的长连接。但是,由于我们需要追求高性能,所以这些依赖让我们开始质疑PHP对于这个系统来说是否是一个合适的工具。
容器化的PHP是一个雷区。 PHP需要借助Nginx和PHP-FPM(或类似的软件)来进行进程管理和连接池管理。这意味着对于部署的每个微服务来说,PHP-FPM和Nginx必须同时运行。这既浪费了资源,又降低了效率。对运行在服务器上的PHP实例进行优化也是相当困难的,因为你需要同时熟悉PHP、PHP-FPM和Nginx的配置。我们无法想象在弹性Kubernetes环境上配置多个PHP栈的痛苦,我们甚至不知道在这同一台机器上还运行了其他什么东西。
对微服务来说,其复杂性存在于架构中,因为你正在处理的是一个复杂的交互系统。既然我们已经确定采用微服务架构,那么因为错误的选择了编程语言导致的消耗显然就不值得。
招聘的要求是什么?我们发现这个所谓的要求对于我们现在这种情况是毫无意义的。像微服务一样,我们认为开发人员应该是编程语言无关的。我们宁愿聘请一位聪明的并愿意为了完成工作而学习新的编程语言的开发人员,而不是一位坚持己见的专家。因此,从这个意义上来说,放弃PHP对我们来说是一种解放。
我们主要偏向使用Node.js和Golang这两种语言。在做了一些研究之后,我们最后决定放弃Node,使用Go。
那么为什么要使用Go呢?
性能: Go的二进制文件会生成一个长时间运行的进程,这意味着每个请求和数据库连接的启动成本很低。这使得Go在处理大量的并发请求时能保证极快的速度,因为Go语言(goroutines模块)专为网络和多核计算而设计。
Go可以编译出一个小巧便携的二进制文件。这使得Go非常适合在Docker容器中使用。部署我们的Go容器只需几秒钟,因为它们的体积很小(大多数是4-5MB),并且由于是静态链接,因此在容器内不需要OS或运行时依赖。例如,当使用Node Alpine Linux镜像时,我们的前端容器大约为55MB。
Go是类型严格的。这让代码中的内部通信更为可靠,也有助于在构建期间捕获异常,而不是在运行期间。
Go的工具链的规模很大。虽然工具是很多编程语言关注的问题,但Google从一开始就解决了这个问题,他提供了大量常用的工具作为语言安装时的一部分。
我们也考虑到Go有这些缺点:
然而,我们必须接受这一点:用Go程序确实需要花上一些功夫,但它能提高代码质量,并让我们能够时刻知道代码实际是如何运行的。
这并不是说所有的代码我们都用Go来写。对于服务器端渲染,我们使用Node,因为它允许我们在前端和后端之间共用代码逻辑。我们也可以使用Java来解决特定的问题,因为它已经存在了很长时间,并且拥有大量的库。我们希望能使用最合适的工具,对于大多数情况而言,Go是我们的首选。
当我们开始使用Go语言来编写我们的第一个服务时,我们也开始考虑数据库的选择。我们习惯了过去为我们服务的MySQL,但它经常会成为性能的瓶颈。
在我们的传统架构中,我们使用了大量的Redis来进行缓存,它的性能非常棒,因为它有效地减少了昂贵的连接数量。所以当我们开始在我们的新架构中探索数据库时,我们要探索一下NoSQL,来看看是否可以完全避免这些连接。
我们试用了这两个数据库:
MongoDB:因为我们非常好奇对于存储包含大量元数据的游戏数据而言,文件存储是否是一个好的解决方案。但是,缺点是:我们必须在Google Cloud上管理它,而且根据社区所说,它根本不能很好地进行扩展。
Cassandra:因为它是一个大家熟知的可以扩展的数据库,并被大流量平台Netflix和Reddit所使用。它的优点是:速度非常快,能线性扩展。不过,我们发现它的内容管理太复杂了。如果你知道该如何查询数据,那么Cassandra是挺好的。它适用于包含大量数据的分析服务,但是在敏捷产品设计环境中,产品变化频繁,Cassandra就是一个强大的野兽,对于大多数情况而言它太笨重了。
我们倾向于构建小型而又独立的服务,这些服务可以完成指定的工作,并且在需要的时候可以很轻松地进行升级或更换。
这就是为什么我们决定坚持使用MySQL作为我们的默认数据库的原因。我们已经使用MySQL很多年了,知道如何设计高性能的数据库方案。虽然它不能线性扩展,但现在还好,得益于微服务架构的模块化特性,应用程序负载可以分布在不同机器的不同微服务上,并且每个微服务都可以访问自己的32核数据库机器和不同的数据库读副本(Read Replicas)。
让我们高兴的是,至今我们还没有过度设计。如果有某个服务确实需要Cassandra或其他数据库的话,那么没有什么可以阻止我们迁移这个服务。
那么为什么选用MySQL?主要是因为它可以在Google Cloud上进行管理,而在DevOps方面我们是务实的。我们想尝试试用Postgres,因为它是开源的,有一个强大的社区,并且已经改进了很多。因此,如果Google Cloud上有了Alpha版本,我们也会研究一下。