米哈伊尔(Mikhail Salosin)。Golang聚会。在Watch +应用程序的后端中使用Go

Mikhail Salosin(以下简称MS): -大家好!我的名字叫麦克。我是MC2 Software的后端开发人员,我将讨论在Watch +移动应用程序的后端中使用Go的问题。



有人喜欢曲棍球吗?



那么这个程序是给你的。它适用于Android和iOS,用于在线和录制观看各种体育赛事的广播。同样在该应用程序中,有各种统计信息,文本广播,会议表格,锦标赛以及对粉丝有用的其他信息。



同样在应用程序中,还有视频时刻,也就是说,您可以看到比赛的激烈时刻(进球,打架,枪战等)。如果您不想观看整个广播,则只能观看最有趣的节目。

开发中使用了什么?


主体是用Go语言编写的。移动客户端与之通信的API是用Go编写的。另外,在Go上编写了一项服务,用于向手机发送推送通知。我们还必须编写自己的ORM,有一天我们可能会谈论它。好吧,一些小服务写在Go上:用于编辑器的大小调整和图像加载...

我们使用Postgres(PostgreSQL)作为数据库。编辑器的界面是使用ActiveAdmin gem在Ruby on Rails中编写的。从统计信息提供程序导入的统计信息也用Ruby编写。

对于系统API测试,我们使用了Python单元测试(Python)。Memcached用于限制API付款请求,Chef用于控制配置,Zabbix用于收集和监视内部系统统计信息。Graylog2-用于收集日志,Slate是客户端的API文档。



协议选择


我们遇到的第一个问题:我们必须基于以下几点来选择后端与移动客户端交互的协议...

  • 最重要的要求:必须实时更新客户端上的数据。也就是说,当前正在观看广播的每个人都应该几乎立即收到更新。
  • 为简单起见,我们接受不删除与客户端同步的数据,而是使用特殊标志将其隐藏。
  • 普通的GET请求会收到各种稀有请求(如统计信息,阵容,团队统计信息)。
  • 另外,该系统原本可以同时承受10万名用户。

基于此,我们有两个协议选项:
  1. Websockets。但是我们不需要从客户端到服务器的通道。我们仅需要将更新从服务器发送到客户端,因此Web套接字是多余的选择。
  2. 服务器发送事件(SSE)恰到好处!它非常简单,基本上可以满足我们的所有需求。

服务器发送的事件


关于这件事如何

工作的几句话... 它在http连接的顶部起作用。客户端发送一个请求,服务器以Content-Type响应:文本/事件流,并且不关闭与客户端的连接,而是继续向连接写入数据:



可以按照与客户端一致的格式发送数据。在本例中,我们以这种形式发送:在事件字段中发送了更改的结构的名称(人,玩家),在数据字段中-JSON包含了播放器的新更改字段。

现在介绍交互本身是如何工作的。
  • 首先,客户端确定何时与服务进行最后一次同步:它查看其本地数据库并确定从中记录的最后一次更改的日期。
  • 他以这个日期发送了一个请求。
  • 作为回应,我们向他发送了该日期发生的所有更新。
  • 之后,他建立了与直播频道的连接,并且直到需要以下更新后才关闭:



我们向他发送一份变更清单:如果有人进球了-我们更改了比赛的分数,受伤了-也会实时发送。因此,在比赛的事件流中,客户立即收到相关数据。为了使客户端了解服务器没有死机,没有发生任何事情,我们会每隔15秒钟定期发送一个时间戳,以便它知道一切正常,并且无需重新连接。

实时连接如何服务?


  • 首先,我们创建一个通道,缓冲区更新将进入该通道。
  • 之后,我们订阅该频道以接收更新。
  • 设置正确的标头,以便客户端知道一切正常。
  • ping. timestamp .
  • , . timestamp, , .



我们遇到的第一个问题如下:对于与客户端打开的每个连接,我们创建了一个计时器,该计时器每15秒计时一次-事实证明,如果我们在一台机器(一台API服务器)上建立了6000个连接,创建了6000个计时器。这导致机器没有承受必要的负载。这个问题对我们来说不是那么明显,但是他们帮助了我们一点,我们消除了它。

结果,现在我们从来自更新的同一通道发出ping命令。

因此,只有一个计时器每15秒滴答一次。

这里有一些帮助函数-发送标头,ping和结构本身。也就是说,表的名称(人,比赛,季节)和有关此记录的信息在此处传输:



发送更新的机制


现在介绍更改的来源。我们有几个人,编辑,可以实时观看广播。他们创建所有事件:有人被移走,有人受伤,某种替换...

在CMS的帮助下,数据进入数据库。之后,使用侦听/通知机制的数据库将通知此事。API服务器已经将该信息发送给客户端。因此,实际上,我们只有少数几台服务器连接到数据库,并且数据库上没有任何特殊负载,因为客户端不会以任何方式直接与数据库进行交互:



PostgreSQL:监听/通知


Postgres中的侦听/通知机制使您可以将一些事件已更改的事件通知给订户-在数据库中已创建某种记录。为此,我们编写了一个简单的触发器和函数:



在插入或更改记录时,我们在data_updates通道上调用notify函数,将表名和已更改或插入的记录的标识符传递到该通道。

对于应该与客户端同步的所有表,我们定义了一个触发器,该触发器在更改/更新记录后将调用下面幻灯片中指示的函数。
API如何订阅这些更改?

创建扇出机制-将消息发送到客户端。他收集了所有客户渠道,并通过这些渠道发出了他收到的更新:



在这里,标准pq库连接到数据库,并说它想监听通道(data_updates),它检查连接是否打开并且一切正常。我省略了错误检查以节省空间(不要检查烦恼)。

接下来,我们异步设置股票行情指示器,它将每15秒发送一次ping,并开始收听我们订阅的频道。如果收到ping命令,我们将发布此ping命令。如果我们收到记录,那么我们会将记录发布给此Fanout的所有订阅者。

扇出如何工作?


在俄语中,这翻译为“拆分器”。我们有一个对象注册希望接收任何更新的订户。并且一旦对此对象的更新到达,它将把此更新传播给它拥有的所有订阅者。足够简单:



如何在Go上实现它:



有一个结构,可以使用Mutexes进行同步。它具有一个字段,用于保存与数据库的Fanout连接的状态,即在它监听并接收更新的那一刻,以及所有可用通道的列表-映射,其关键是值形式的通道和结构(实际上,它不以任何方式使用)。

有两种方法-Connected和Disconnected-允许您告诉Fanout我们已经连接到基座,它已经出现并且与基座的连接已断开。在第二种情况下,您需要断开所有客户端的连接,并告知它们不再能够收听任何内容,并且重新连接,因为与客户端的连接已关闭。

还有一个Subscribe方法可以向侦听器添加频道:



还有一个Unsubscribe方法,如果客户端断开连接,该方法将从侦听器中删除频道;还有一种Publish方法,可以将消息发送给所有订阅者。

问题: -通过此通道传输了什么?

MS: -传输的模型已更改或ping(本质上只是一个数字,整数)。

多发性硬化症:-您可以发送任何内容,发布任何结构,它都变成了JSON,就是这样。

MS: -我们收到来自Postgres的通知-它包含表名和标识符。通过获得的表名和标识符,我们可以获得所需的记录,并且已经发送了此结构以进行发布。

基础设施


就基础架构而言,它看起来像什么?我们有7台Iron服务器:其中一台完全专用于基础服务器,其余6台虚拟计算机正在旋转。该API有6个副本:具有该API的每个虚拟机都在单独的Iron服务器上运行-这是为了提高可靠性。



我们有两个安装了Keepalived的前端,以提高可访问性,以便在其中一个前端可以替代另一个前端的情况。 CMS的另外两个副本。

还有一个统计输入器。有一个数据库从站,可以从该数据库从站定期进行备份。有Pigeon Pusher(Pigeon Pusher)-该应用程序向用户发送小便,以及基础设施:Zabbix,Graylog2和Chef。

实际上,此基础架构是冗余的,因为可以用更少的服务器为10万个服务器提供服务。但是有铁-我们用了它(被告知有可能-为什么不这样做)。

Go的优点


在我们研究了此应用程序之后,Go的这些明显优势得以展现。
  • 酷的http库。使用它,您可以开箱即用地创建很多东西。
  • 另外,通过这些渠道,我们可以非常轻松地实现向客户发送通知的机制。
  • 出色的Race检测器使我们能够消除一些关键的错误(分段基础结构)。所有运行于分期的东西都在运行,并用Race键编译;因此,我们可以看到暂存基础架构存在哪些潜在问题。
  • 极简和简单的语言。




我们正在寻找开发人员!如果有人要-请。

问题


观众提出的问题(以下简称-B):-在我看来,您错过了有关扇出的重要一点。我正确理解,当您向客户发送回复时,如果客户不想阅读,您就会被阻止?

MS: -不,我们没有阻止。首先,我们将所有内容都保留在nginx后面,也就是说,慢客户端没有问题。其次,客户端有一个带有缓冲区的通道-实际上,我们可以在那里存储多达一百个更新...如果我们无法写入该通道,则它将删除它。如果我们发现该频道已被阻止,则只需关闭该频道即可,就是这样-如果有任何问题,客户端将重新连接。因此,原则上这里不会发生阻塞。

问: -您能立即发送“监听/通知”记录而不是标识符表吗?

多发性硬化症:-监听/通知发送的每个预加载限制为8 000个字节。原则上,如果我们要处理少量数据,则可以发送,但是在我看来[我们所做的]方法更可靠。局限性在于Postgres本身。

问: -客户是否收到他们不感兴趣的比赛的更新?

多发性硬化症:-一般。通常,并行进行2-3场比赛,然后很少。如果客户正在看某物,那么通常他正在看正在进行的比赛。然后,在客户端上有一个本地数据库,所有这些更新加到其中,即使没有Internet连接,客户端也可以看到他具有更新的所有过去的匹配项。实际上,我们将服务器上的数据库与客户端的本地数据库同步,以便它可以脱机工作。

问: -为什么要做ORM?

Alexey(“ Watch +”的开发者之一):-当时(一年前)ORM比现在少了,当时有很多。在大多数现有的ORM中,我最不喜欢的是它们大多数都在空接口上工作。也就是说,这些ORM中准备使用的方法可以处理任何事情:结构,结构指针,数字,一点无关紧要的东西。

我们的ORM会基于数据模型生成结构。本身。因此,所有方法都是具体的,不使用反射等。它们接受结构并期望使用出现的结构。

问: -有多少人参加?

MS: -在最初阶段,有两个人参加。我们从六月的某个地方开始,八月的主要部分已经准备好(第一个版本)。 9月发布了一个版本。

在:-在描述SSE的地方,不使用超时。这是为什么?

MS: -老实说,SSE仍然是html5协议:据我所知,SSE标准旨在与浏览器进行通信。它具有其他功能,因此浏览器可以重新连接(依此类推),但是我们不需要它们,因为我们拥有可以实现连接和接收信息的任何逻辑的客户端。我们更可能不是上证所,而是类似上证所。这不是协议本身。
没必要据我了解,客户端是从头开始实现连接机制的。原则上,他们不在乎。

问: -您还使用了哪些其他实用程序?

多发性硬化症:-最活跃的我们使用govet和golint,因此样式和gofmt都统一了。他们什么也没用。

问: -用什么调试?

MS: -总的来说,调试是使用测试完成的。没有调试器,我们没有使用GOP。

问: -您可以返回实现了“发布”功能的幻灯片吗?单字母变量名不打扰您吗?

MS: -不。它们的范围相当狭窄。它们在其他任何地方(此处除外)都没有使用(此类的内部除外),它非常紧凑-仅占用7行。

问: -仍然不直观...

MS:-不,这是真实的密码!这与风格无关。就是这样一个功利主义者,很小的一类-该类中只有3个字段...



MS: -总的来说,与客户同步的所有数据(季节性比赛,球员)都不会改变。粗略地说,如果我们要进行另一种需要更改比赛的运动,我们将只考虑新版本客户端中的所有内容,而旧版本客户端将被禁止。

问: -是否有任何第三方软件包用于依赖性管理?

MS: -我们使用了go dep。

问: -报告主题中的视频有些问题,但报告中的视频却没有。

MS: -不,我没有关于视频的话题。它称为“ Look +”-这是应用程序的名称。

在:-您说您要向客户流媒体吗?..

MS: -我们没有流媒体视频。这完全由扩音器完成。是的,我没有说应用程序是扩音器。

MS: -执行-发送所有数据-按比分,比赛事件,统计信息...执行-这是应用程序的整个后端。客户必须从某处找出要用于播放器的链接,以便用户可以观看比赛。我们有指向准备好的视频和流的链接。


一点广告:)


感谢您与我们在一起。你喜欢我们的文章吗?想看更多有趣的资料吗?通过下订单或向您的朋友推荐给开发人员的基于云的VPS,最低价格为4.99美元这是我们为您发明的入门级服务器 独特类似物:关于VPS(KVM)E5-2697 v3(6核)的全部真相10GB DDR4 480GB SSD 1Gbps从$ 19还是如何划分服务器?(RAID1和RAID10提供选件,最多24个内核和最大40GB DDR4)。

阿姆斯特丹的Equinix Tier IV数据中心的戴尔R730xd便宜2倍吗?在荷兰,我们有2台Intel TetraDeca-Core Xeon 2x E5-2697v3 2.6GHz 14C 64GB DDR4 4x960GB SSD 1Gbps 100电视戴尔R420-2x E5-2430 2.2Ghz 6C 128GB DDR3 2x960GB SSD 1Gbps 100TB-$ 99起!阅读有关如何构建基础设施大厦的信息。使用Dell R730xd E5-2650 v4服务器花费一欧元9000欧元的c类?

All Articles