品牌名稱
HelloTalk
企業(yè)規(guī)模
201-500人

HelloTalk:基于 OpenResty 和 Apache APISIX 的

671次閱讀

HelloTalk:技術(shù)上看是基于全球的 Tiny 版微信

HelloTalk 是全球最大的外語學(xué)習(xí)社交社區(qū),全球 1600 萬用戶通過 HelloTalk 和全球語伴學(xué)習(xí) 150 門外語、進行跨文化交流及交友。用戶廣泛分布中國、日本、韓國、美、歐、巴西等國家,其中海外用戶占 80%,從技術(shù)角度來看 HelloTalk 是一個基于全球的 Tiny 版微信。

 

2.jpg

 

HelloTalk 海外有很多 KOL 用戶在 YouTube、Instagram、Twitter 等平臺協(xié)助做推廣,知名度較高,產(chǎn)品兼顧聊天、改錯、翻譯等功能,用戶可以邊聊邊語音改文字。其中語音改文字和翻譯支持 100 多種語言。

 

從運營層面看,很多企業(yè)出海時不知道怎樣去做第一步,技術(shù)上也同樣面臨這個問題——如何做到出海,并且為全球用戶提供優(yōu)質(zhì)的服務(wù)。為了讓每個國家的用戶都能擁有更好的使用體驗,這里就要不得不提到 OpenResty 給我們帶來的幫助了。

 

3.jpg

 

如上圖所示,HelloTalk 的用戶分布很散,我們需要找到最佳的平衡點,以性價比最優(yōu)的方式部署連接節(jié)點。亞太區(qū)域如韓國、日本,中國長三角和珠三角等地用戶分布比較集中,比較好處理,但是在用戶高度分散的其他地區(qū)(如上圖的歐洲、非洲、中東),對提供穩(wěn)定可靠的服務(wù)提出了較高的挑戰(zhàn)。

為什么使用 OpenResty

早期 HelloTalk 使用 C++ 寫 IM 服務(wù),當(dāng)時是用到某大廠的高性能網(wǎng)絡(luò)框架,協(xié)議都是內(nèi)部擬定的,HTTP 協(xié)議都很少用。這對小公司而言成本很高,假設(shè)內(nèi)部寫服務(wù)要曝露給外部使用,還需要自己開發(fā) Proxy Server,而最新加的命令字要做適配,這就非常麻煩了。

 

所以從 2015 年開始,HelloTalk 開始引進 OpenResty,基于 OpenResty 在前面做代理,直接進行協(xié)議轉(zhuǎn)換傳到內(nèi)部服務(wù),減少了很多的成本。

 

此外,服務(wù)簡單暴露給外部使用,會需要 WAF 的功能。我們早期有些 API 是基于 PHP 實現(xiàn)的,經(jīng)常會因為框架原因被發(fā)現(xiàn)一些漏洞,導(dǎo)致一些黑客做各種注入和攻擊,其中主要的手法就是 POST 各種 PHP 的關(guān)鍵字,或者在 URL 里面攜帶 PHP 關(guān)鍵字。

 

當(dāng)時我們在 OpenResty 里添加很少的代碼(基于正則)后解決了這個問題,后來發(fā)現(xiàn)即使增加 WAF 功能,性能上也不會有太大的損失。

 

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

早期我們做 IM 開發(fā)都希望協(xié)議短小精悍,HelloTalk 的協(xié)議頭也比較精簡,全部是 TCP 的協(xié)議體。比較有意思的是通過前后加兩個特殊的字節(jié)符號,定義中間的內(nèi)容,即 0x09 + Header(20 bytes) + Body + 0x0A,基本上可以保證數(shù)據(jù)包不會出亂。如果沒有前后 0x09 和 0x0A 兩個包,其實還是有一定概率會產(chǎn)生錯包的。

 

4.jpg

 

  • 自定協(xié)議 -> HTTP 的研發(fā)成本,急需高效的 proxy 服務(wù)做協(xié)議轉(zhuǎn)換

 

5.jpg

 

早期 HelloTalk 采用 TLV + PB 的協(xié)議模式,當(dāng)時業(yè)務(wù)正快速發(fā)展,需要改成對外的 restful + JSON,第一步要做的是 PB 轉(zhuǎn) JSON。

 

而做協(xié)議解析遇到一個問題:OpenResty 使用的是云風(fēng)寫的 PBC 解析器,解析寫起來非常麻煩,必須要知道里層的結(jié)構(gòu)。假設(shè)結(jié)構(gòu)有三層,你得寫三層判斷代碼,一層一層地把它拋出來。但后來發(fā)現(xiàn) Apache APISIX 是基于 lua-protobuf,所以我們也改成了用 lua-protobuf 庫,它可以直接把一個 PB 對象直接轉(zhuǎn)成了 JSON,非常方便。

 

  • 基于 cosocket 的 TCP 協(xié)議安全解析

 

6.jpg

 

協(xié)議的解析過程基本上是不斷地讀 socket,讀到上圖中的包頭里的 Length 字段,再去讀 body 段,這里可以看出自己要實現(xiàn)協(xié)議解析比較麻煩,因為要對每個協(xié)議做適配。

 

  • 快速實現(xiàn)一個 Web IM

我們當(dāng)時做完 C++ 的 IM 通訊服務(wù)后,看到主流的 IM App 如 WhatsApp、微信都有 Web IM,我們很快的基于 OpenResty 對他們的協(xié)議進行兼容和改造,大概兩周時間,我們就從服務(wù)端快速實現(xiàn)了一個 WebIM 版本的 HelloTalk。

 

7.jpg

 

和微信網(wǎng)頁版本一樣掃描登錄聊天,基本不對協(xié)議做改動,只在中間添加一層 OpenResty 做 WebSocket 協(xié)議轉(zhuǎn)換。

 

  • 控制消息頻率

公共服務(wù)如果暴露出去,會有人頻繁地給所有的人發(fā)消息,因此我們需要做消息限流,這是直接基于 resty.limit.req 做的,當(dāng)然 API 頻率控制也是如此進行的。

 

  • WAF 保護 PHP 業(yè)務(wù)

 

8.jpg

 

做過 PHP 開發(fā)應(yīng)該知道,所有的入侵其實是各種注入 PHP 的函數(shù)名字、關(guān)鍵字。但當(dāng)我把所有的 PHP 的函數(shù)名全放在 WAF 后,我再也沒發(fā)現(xiàn)過被攻擊,但在日志里發(fā)現(xiàn)很多,這說明全部被攔截了,到不了 PHP 那邊。

 

9.jpg

 

三步走:

  1. 純 TCP 協(xié)議快速實現(xiàn);
  2. 基于 Openresty 的 HTTP 服務(wù)暴露;
  3. API網(wǎng)關(guān)(Apache APISIX) 加 Golang 微服務(wù)開發(fā)和治理。

國際化過程中的挑戰(zhàn)和問題

  • HelloTalk 用戶分布區(qū)域非常分散,需要想辦法解決用戶分布區(qū)域分散的問題;
  • HelloTalk 國內(nèi)大概有 20% 的用戶,面臨防火墻的問題;
  • 海外語言環(huán)境和網(wǎng)絡(luò)環(huán)境一樣復(fù)雜,語言適配問題難以處理。

 

怎樣提高用戶的全球接入質(zhì)量

我比較過市面上很多服務(wù)商提供的方案:

  1. 阿里云全球加速 (BGP + 專線),直接就是 4 層加速。
  2. 阿里云 DCDN 全站加速。
  3. AWS 的 Global Accelerator 方案。
  4. Ucloud 的 XPath 方案 。
  5. 專線服務(wù)(兩端 VPC,中間專線,邊緣卸載 https)
  6. Zenlayer。

但我們需要考慮兩個問題:成本,真正的服務(wù)質(zhì)量。

 

10.jpg

 

在解決跨境問題時,由于要考慮到國內(nèi) 20% 的用戶和公司總部地理位置,所以我們是基于阿里云全站加速展開,原本是全部用公網(wǎng)代理到香港阿里云,采用兩邊是 VPC、中間專線的形式,但有時候會遇到專線網(wǎng)絡(luò)抖動導(dǎo)致延時提高的問題,所以在深圳做了基于 OpenResty 的網(wǎng)關(guān)代理。而實際情況是:如果專線不通就選擇走公網(wǎng),公網(wǎng)延時大概 14ms,專線是 4ms。

 

這里會涉及到上游檢測,線路不通時需要快速的切換到另外一條線路,這部分問題是基于又拍云提供的 Resty 庫在解決。

 

香港阿里機房到香港騰訊騰訊機房感覺其實是在同一個區(qū)域,因為我們測試延時大概在 0.3ms~0.4ms。

 

對于海外其他用戶,基本全部是直接加速回到香港阿里,但直接加速會導(dǎo)致客戶端的網(wǎng)絡(luò)質(zhì)量受地域問題影響嚴重,所以我們設(shè)置了一些 failover 的機制來保障用戶的使用體驗。

 

接入線路控制和流量管理

  • 專線網(wǎng)絡(luò)的帶來的穩(wěn)定性,例如歐洲到香港,延時: 244 ms -> 150 ms;
  • 動態(tài) upstream 控制 (lua-resty-healthcheck),多服務(wù)商線路之間靈活切換,保證服務(wù)的可靠性;
  • 部分邏輯可以直接在邊緣處理,serverless(原理都是基于 pcall + loadstring 實現(xiàn)),serverless 這塊我們現(xiàn)在正則將其改造成 Apsche APISIX + ETCD。

 

接入節(jié)點和質(zhì)量把控

 

11.jpg

 

目前 HelloTalk 的接入節(jié)點主要分布在:美國東部,法蘭克福,新加坡,東京,香港。美國直接到香港有可能會不通,此時會按照既定機制經(jīng)轉(zhuǎn)德國再回到香港,日本和韓國也是回到香港。巴西也有很多用戶,但巴西云廠商只有 AWS 在做,基本上全部是連到美國,如果連不通也會多個線路之間做選擇。這個環(huán)節(jié)其實是云廠商或者是 CDN 廠商完成,但實際發(fā)現(xiàn)總有一些地區(qū)做的并不好,所以為了保證用戶體驗不受損,我們得有些 failover 機制保證多個服務(wù)商之間切換,保證用戶的服務(wù)是可靠的。

 

7 層和 4 層加速的選擇

很多服務(wù)商會提供 7 層加速和 4 層加速,但也會有一些問題需要解決。

 

  • 4 層加速:SSL 握手時間過長,容易失敗,得不到客戶端的 IP,不方便做節(jié)點質(zhì)量統(tǒng)計。

 

12.jpg

 

4 層加速得不到客戶端的 IP,(注:有些云廠商是支持的但需要在服務(wù)器上打個補丁),它在 TCP 的包里提供了此功能,也不是很友好,如果打補丁出了問題,誰來負這個責(zé)任呢?

 

此外,監(jiān)控質(zhì)量也成了問題,我們需要知道哪條線路行、哪條線路不行,雖然有切換機制,但我們要知道它真實的通訊路線。事實上我們在每個流量層代理時都會把真實 IP 帶著跑,如果采用阿里云,那阿里云會幫我們填到一個頭里面去,不斷地把客戶端的真實 IP 帶給下一個節(jié)點。

 

  • 7 層加速:不能保證 IM 服務(wù)需要長連接保持消息的可靠到達

7 層加速的問題在于使得 IM 服務(wù)機制變成了 long polling 或者是短連接輪循機制,但在實際過程中我們發(fā)現(xiàn)它比較耗流量,而且 IM 服務(wù)需要長連接保持消息的可靠和及時到達,但大部分 7 層加速廠商不支持 WebSocket,個別支持 WebSocket 的廠商邊緣卸載 HTTPS 又很貴的,尤其是國外的像 AWS 挺貴的。此外,如果云廠商邊緣節(jié)點宕機,會對用戶造成比較差的影響,因此我們就在多個云廠商之間的客戶端做了很多 failover 邏輯設(shè)計(內(nèi)置 IP 機制),一旦故障能夠切實保障切換到另外一個節(jié)點,保證連接質(zhì)量。

 

多云環(huán)境下的全球接入的管理方案

  • 支持 websocket 的 7 層加速。(云服務(wù)+自建)
  • 自建低速率的 VPC + 專線通道。(性價比考慮,IM 自身流量并不多,只做通知消息下發(fā))
  • 長短連接混合收發(fā)消息:websocket + long polling + httpdns + 內(nèi)置 IP failover 機制

 

13.jpg

 

當(dāng)然內(nèi)置哪個 IP 到客戶端也是一個問題,比如對于歐洲用戶,其實肯定是要分配歐洲的 IP,那么首先我們服務(wù)端要把歐洲的服務(wù)端 IP 存起來,怎么存?什么時候存?這里我們是通過騰訊云的 httpdns + openresty timer 機制分配、緩存、更新的,上圖中的 IP 就是用戶的真實 IP,這個時候 httpdns 服務(wù)商就會根據(jù) IP 參數(shù)做域名的 IP 解析。

 

從自建 API Gateway 到深入體驗 Apache APISIX

自建 API Gateway 實現(xiàn)偽裝動態(tài)化

我們早期是直接改 nginx.conf,我自己覺得裸的 nginx 性能肯定是最高的。但問題是很多人不一定記得 Location 配制的優(yōu)先級順序規(guī)則,我們也經(jīng)常會改錯。而且我們的需求比較固定:動態(tài)更新 SSL 證書、Location、upstream,當(dāng)時的做法類似現(xiàn)在的 K8S 的 ingress 更新機制,即通過模本生成:nginx_template.conf + JSON -> PHP -> nginx.conf -> PHP-cli -> Reload 來實現(xiàn)動態(tài)化。但這個方案在遇到 Apache APISIX 之后可以考慮替換掉了。

 

Apache APISIX 成為 HelloTalk 的選擇

  • 自身需求比較簡單,依賴 RDMS 覺得太重,帶來額外的維護成本;
  • 代碼極致簡單易懂,在個人能力范圍內(nèi),可以理解;
  • 基于 ETCD,節(jié)省維護成本;
  • 項目主要維護者幾乎實時在線支持,QQ 群、郵件響應(yīng)及時。