HelloTalk 基于 OpenResty 的全球化探索之路

2019 年 12 月 14 日,又拍云联合 Apache APISIX 社区举办 API 网关与高性能服务最佳实践丨Open Talk 广州站活动,HelloTalk, Inc. 后台技术负责人李凌作了题为《HelloTalk 基于 OpenResty 的全球化探索之路》的分享。html

李凌,HelloTalk,Inc. 后端技术负责人,专一在服务出海和基于 Golang/CPP 的 IM 服务及相关技术平台的架构,5 年基于 OpenResty 服务治理和使用经验,Apache APISIX Committer。nginx

如下是分享全文:web

你们好,我是来自 HelloTalk 的李凌,本次主要介绍 HelloTalk 作什么业务以及基于怎样的场景使用 OpenResty 和 Apache APISIX。算法

HelloTalk:技术上看是基于全球的 Tiny 版微信

HelloTalk 是全球最大的外语学习社交社区,全球 1600 万用户经过 HelloTalk 和全球语伴学习 150 门外语、进行跨文化交流及交友。用户普遍分布中国、日本、韩国、美、欧、巴西等国家,其中海外用户占 80%,从技术角度来看 HelloTalk 是一个基于全球的 Tiny 版微信。后端

HelloTalk 海外有不少 KOL 用户在 YouTube、Instagram、Twitter等平台协助作推广,知名度较高,产品兼顾聊天、改错、翻译等功能,用户能够边聊边语音改文字。其中语音改文字和翻译支持 100 多种语言。缓存

从运营层面看,不少企业出海时不知道怎样去作第一步,技术上也一样面临这个问题——如何作到出海,而且为全球用户提供优质的服务。为了让每一个国家的用户都能拥有更好的使用体验,这里就要不得不提到 OpenResty 给咱们带来的帮助了。安全

如上图所示,HelloTalk 的用户分布很散,咱们须要找到最佳的平衡点,以性价比最优的方式部署链接节点。亚太区域如韩国、日本,中国长三角和珠三角等地用户分布比较集中,比较好处理,可是在用户高度分散的其余地区(如上图的欧洲、非洲、中东),对提供稳定可靠的服务提出了较高的挑战。服务器

为何使用 OpenResty

早期 HelloTalk 使用 C++ 写 IM 服务,当时是用到某大厂的高性能网络框架,协议都是内部拟定的,HTTP 协议都不多用。这对小公司而言成本很高,假设内部写服务要曝露给外部使用,还须要本身开发 Proxy Server,而最新加的命令字要作适配,这就很是麻烦了。微信

因此从 2015 年开始,HelloTalk 开始引进 OpenResty,基于 OpenResty 在前面作代理,直接进行协议转换传到内部服务,减小了不少的成本。websocket

此外,服务简单暴露给外部使用,会须要 WAF 的功能。咱们早期有些API 是基于 PHP 实现的,常常会由于框架缘由被发现一些漏洞,致使一些黑客作各类注入和攻击,其中主要的手法就是 POST 各类 PHP 的关键字,或者在 URL 里面携带 PHP 关键字。

当时咱们在 OpenResty 里添加不多的代码(基于正则)后解决了这个问题,后来发现即便增长 WAF 功能,性能上也不会有太大的损失。

  • TLV:0x09+Header(20 bytes)+Body+0x0A

早期咱们作 IM 开发都但愿协议短小精悍,HelloTalk 的协议头也比较精简,所有是 TCP 的协议体。比较有意思的是经过先后加两个特殊的字节符号,定义中间的内容,即 0x09+Header(20 bytes)+Body+0x0A,基本上能够保证数据包不会出乱。若是没有先后 0x09 和 0x0A 两个包,其实仍是有必定几率会产生错包的。

  • 自定协议->HTTP 的研发成本,急需高效的 proxy 服务作协议转换

早期 HelloTalk 采用 TLV+PB 的协议模式,当时业务正快速发展,须要改为对外的reastful+JSON,第一步要作的是 PB 转 JSON。

而作协议解析遇到一个问题:OpenResty 使用的是云风写的 PBC 解析器,解析写起来很是麻烦,必需要知道里层的结构。假设结构有三层,你得写三层判断代码,一层一层地把它抛出来。但后来发现 Apache APISIX 是基于 lua-protobuf,因此咱们也改为了用 lua-protobuf 库,它能够直接把一个 PB 对象直接转成了 JSON,很是方便。

  • 基于 cosocket 的 TCP 协议安全解析

协议的解析过程基本上是不断地读 socket,读到上图中的包头里的 Length 字段,再去读 body 段,这里能够看出本身要实现协议解析比较麻烦,由于要对每一个协议作适配。

  • 快速实现一个 Web IM

咱们当时作完 C++ 的 IM 通信服务后,看到主流的 IM App 如 WhatsApp、微信都有 Web IM,咱们很快的基于 OpenResty 对他们的协议进行兼容和改造,大概两周时间,咱们就从服务端快速实现了一个 WebIM 版本的 HelloTalk。

和微信网页版本同样扫描登陆聊天,基本不对协议作改动,只在中间添加一层 OpenResty 作 WebSocket 协议转换。

  • 控制消息频率

公共服务若是暴露出去,会有人频繁地给全部的人发消息,所以咱们须要作消息限流,这是直接基于 resty.limit.req 作的,固然 API 频率控制也是如此进行的。

  • WAF 保护 PHP 业务

作过 PHP 开发应该知道,全部的入侵实际上是各类注入 PHP 的函数名字、关键字。但当我把全部的 PHP 的函数名全放在 WAF 后,我再也没发现过被攻击,但在日志里发现不少,这说明所有被拦截了,到不了 PHP 那边。

三步走:

一、纯 TCP 协议快速实现;

二、基于 Openresty 的 HTTP 服务暴露;

三、API网关(Apache APISIX) 加 Golang 微服务开发和治理。

国际化过程当中的挑战和问题

  • HelloTalk 用户分布区域很是分散,须要想办法解决用户分布区域分散的问题;
  • HelloTalk 国内大概有 20% 的用户,面临防火墙的问题;
  • 海外语言环境和网络环境同样复杂,语言适配问题难以处理。

怎样提升用户的全球接入质量

我比较过市面上不少服务商提供的方案:

一、阿里云全球加速(BGP + 专线),直接就是 4 层加速。

二、阿里云 DCDN 全站加速。

三、AWS的 Global Accelerator 方案。

四、Ucloud的 XPath方案 。

五、专线服务(两端 VPC,中间专线,边缘卸载https)

六、Zenlayer。

但咱们须要考虑两个问题:成本,真正的服务质量。

在解决跨境问题时,因为要考虑到国内 20% 的用户和公司总部地理位置,因此咱们是基于阿里云全站加速展开,本来是所有用公网代理到香港阿里云,采用两边是 VPC、中间专线的形式,但有时候会遇到专线网络抖动致使延时提升的问题,因此在深圳作了基于 OpenResty 的网关代理。而实际状况是:若是专线不通就选择走公网,公网延时大概 14ms,专线是 4ms。

这里会涉及到上游检测,线路不通时须要快速的切换到另一条线路,这部分问题是基于又拍云提供的 Resty 库在解决。

香港阿里机房到香港腾讯腾讯机房感受实际上是在同一个区域,由于咱们测试延时大概在 0.3ms~0.4ms。

对于海外其余用户,基本所有是直接加速回到香港阿里,但直接加速会致使客户端的网络质量受地域问题影响严重,因此咱们设置了一些 failover 的机制来保障用户的使用体验。

接入线路控制和流量管理

  • 专线网络的带来的稳定性,例如欧洲到香港,延时:244 ms -> 150 ms;
  • 动态 upstream 控制 (lua-resty-healthcheck),多服务商线路之间灵活切换,保证服务的可靠性;
  • 部分逻辑能够直接在边缘处理,serverless (原理都是基于 pcall+loadstring 实现),serverless 这块咱们如今正则将其改形成 Apsche APISIX + ETCD。

接入节点和质量把控

目前 HelloTalk 的接入节点主要分布在:美国东部,法兰克福,新加坡,东京,香港。美国直接到香港有可能会不通,此时会按照既定机制经转德国再回到香港,日本和韩国也是回到香港。巴西也有不少用户,但巴西云厂商只有 AWS 在作,基本上所有是连到美国,若是连不通也会多个线路之间作选择。这个环节实际上是云厂商或者是 CDN 厂商完成,但实际发现总有一些地区作的并很差,因此为了保证用户体验不受损,咱们得有些 failover 机制保证多个服务商之间切换,保证用户的服务是可靠的。

7 层和 4 层加速的选择

不少服务商会提供 7 层加速和 4 层加速,但也会有一些问题须要解决。

  • 4层加速:SSL握手时间过长,容易失败,得不到客户端的 IP,不方便作节点质量统计。

4 层加速得不到客户端的 IP,(注:有些云厂商是支持的但须要在服务器上打个补丁),它在 TCP 的包里提供了此功能,也不是很友好,若是打补丁出了问题,谁来负这个责任呢?

此外,监控质量也成了问题,咱们须要知道哪条线路行、哪条线路不行,虽然有切换机制,但咱们要知道它真实的通信路线。事实上咱们在每一个流量层代理时都会把真实 IP 带着跑,若是采用阿里云,那阿里云会帮咱们填到一个头里面去,不断地把客户端的真实 IP 带给下一个节点。

  • 7 层加速:不能保证 IM 服务须要长链接保持消息的可靠到达

7 层加速的问题在于使得 IM 服务机制变成了 long polling 或者是短链接轮循机制,但在实际过程当中咱们发现它比较耗流量,并且 IM 服务须要长链接保持消息的可靠和及时到达,但大部分 7 层加速厂商不支持 WebSocket,个别支持 WebSocket 的厂商边缘卸载 HTTPS 又很贵的,尤为是国外的像 AWS 挺贵的。此外,若是云厂商边缘节点宕机,会对用户形成比较差的影响,所以咱们就在多个云厂商之间的客户端作了不少 failover 逻辑设计(内置 IP 机制),一旦故障可以切实保障切换到另一个节点,保证链接质量。

多云环境下的全球接入的管理方案

  • 支持 websocket 的 7 层加速。(云服务+自建)
  • 自建低速率的 VPC+专线通道。(性价比考虑,IM 自身流量并很少,只作通知消息下发)
  • 长短链接混合收发消息:websocket+long polling+httpdns + 内置 IP failover 机制

固然内置哪一个 IP 到客户端也是一个问题,好比对于欧洲用户,其实确定是要分配欧洲的 IP,那么首先咱们服务端要把欧洲的服务端 IP 存起来,怎么存?何时存?这里咱们是经过腾讯云的 httpdns + openresty timer 机制分配、缓存、更新的,上图中的 IP 就是用户的真实 IP,这个时候 httpdns 服务商就会根据 IP 参数作域名的 IP 解析。

从自建 API Gateway 到深刻体验 Apache APISIX

自建 API Gateway 实现假装动态化

咱们早期是直接改 nginx.conf,我本身以为裸的 nginx 性能确定是最高的。但问题是不少人不必定记得 Location 配制的优先级顺序规则,咱们也常常会改错。并且咱们的需求比较固定:动态更新 SSL 证书、Location、upstream,当时的作法相似如今的 K8S 的 ingress 更新机制,即经过模本生成:nginx_template.conf+JSON -> PHP -> nginx.conf -> PHP-cli> Reload 来实现动态化。但这个方案在遇到 Apache APISIX 以后能够考虑替换掉了。

Apache APISIX 成为 HelloTalk 的选择:

  • 自身需求比较简单,依赖 RDMS 以为过重,带来额外的维护成本;
  • 代码极致简单易懂,在我的能力范围内,能够理解;
  • 基于 ETCD,节省维护成本;
  • 项目主要维护者几乎实时在线支持,QQ 群、邮件响应及时。
推荐阅读

从 0 到 1:Apache APISIX 的 Apache 之路

阿里巴巴王发康:阿里七层流量入口负载均衡算法演变之路

相关文章
相关标签/搜索