善仁,螞蟻金服通用搜索產品負責人,通用搜索目前擁有上萬億文檔,服務了上百個業務方,是螞蟻內部最大的搜索產品。其所在的螞蟻中間件搜索團隊專一於構建簡單可信的搜索產品,是阿里經濟體中最大的搜索服務提供商。目前專一於抽象各類複雜場景下的搜索解決方案,力求讓搜索人人可用,人人會用。 前端
本文根據他在 2018 Elastic 中國開發者大會的分享整理nginx
你們好,我是來自螞蟻金服中間件團隊的善仁,目前是螞蟻通用搜索產品的負責人。今天給你們分享的主題是《Elasticsearch 在螞蟻金服的中臺實踐經驗》git
基於 Elasticsearch 的通用搜索是螞蟻內部最大的搜索產品,目前擁有上萬億文檔,服務了上百個業務方。而通用搜索的發展主要分爲兩個階段:平臺化和中臺化。github
今天我將分別介紹下咱們在這兩個階段的發展中爲業務解決了哪些痛點以及咱們是如何去解決這些痛點的。web
和大多數大型企業同樣,螞蟻內部也有一套自研的搜索系統,咱們稱之爲主搜。數據庫
可是因爲這種系統可定製性高,因此通常業務接入比較複雜,週期比較長。而對於大量新興的中小業務而言,迭代速度尤其關鍵,所以難以用主搜去知足。後端
主搜不能知足,業務又實際要用,怎麼辦呢?那就只能自建了。在前幾年螞蟻內部有不少小的搜索系統,有 ES,也有 solr,甚至還有本身用 lucene 的。api
然而業務因爲自身迭代速度很快,去運維這些搜索系統成本很大。就像 ES,雖然搭建一套非常簡單,可是用在真實生產環境中仍是須要不少專業知識的。做爲業務部門很難去投入人力去運維維護。緩存
而且因爲螞蟻自身的業務特性,不少業務都是須要高可用保證的。而咱們都知道ES自己的高可用目前只能跨機房部署了,先不談跨機房部署時的分配策略,光是就近訪問一點,業務都很難去完成。架構
由於這些緣由,致使這類場景基本都沒有高可用,業務層寧願寫兩套代碼,準備一套兜底方案。以爲容災時直接降級也比高可用簡單。
從總體架構層面看,各個業務自行搭建搜索引擎形成了煙囪林立,各類重複建設,而且這種中小業務通常數據量都比較小,每每一個業務一套三節點集羣只有幾萬條數據,形成總體資源利用率很低,並且因爲搜索引擎選用的版本,部署的方式都不一致,也難以保證質量。在架構層面只能當作不存在搜索能力。
基於以上痛點,咱們產生了構建一套標準搜索平臺的想法,將業務從運維中解放出來,也從架構層面統一基礎設施。提供一種簡單可信的搜索服務。
如何作『低成本,高可用,少運維』呢?
咱們先來一塊兒看一下總體架構,如圖
首先說明一下咱們這兩個框框表明兩個機房,咱們總體就是一種多機房的架構,用來保證高可用
最上層是用戶接入層,有 API,Kibana,Console 三種方式,用戶和使用 ES 原生的 API 同樣能夠直接使用咱們的產品;
中間爲路由層(Router),負責將用戶請求真實發送到對應集羣中,負責一些干預處理邏輯;
下面每一個機房中都有隊列(Queue),負責削峯填谷和容災多寫。
每一個機房中有多個 ES 集羣,用戶的數據最終落在一個真實的集羣中,或者一組對等的高可用集羣中;
右邊紅色的是 Meta,負責全部組件的一站式自動化運維和元數據管理;
最下面是 kubernetes, 咱們全部的組件均是以容器跑在k8s上的,這解放了咱們不少物理機運維操做,使得滾動重啓這些變得很是方便;
看完了總體,下面就逐點介紹下咱們是怎麼作的,第一個目標是低成本。
在架構層面,成本優化是個每一年必談的話題。那麼下降成本是什麼意思?實際上就是提升資源利用率。提升資源利用率方法有不少,好比提升壓縮比,下降查詢開銷。可是在平臺上作簡單有效的方式則是多租戶。
今天我就主要介紹下咱們的多租戶方案:
多租戶的關鍵就是租戶隔離,租戶隔離分爲邏輯隔離和物理隔離。
首先介紹下咱們的邏輯隔離方案,邏輯隔離就是讓業務仍是和以前本身搭 ES 同樣的用法,也就是透明訪問,可是實際上訪問的只是真實集羣中屬於本身的一部分數據,而看不到其餘人的數據,也就是保證水平權限。而 ES 有一點很適合用來作邏輯隔離,ES 的訪問實際上都是按照 index 的。所以咱們邏輯隔離的問題就轉化爲如何讓用戶只能看到本身的表了。
咱們是經過 console 保存用戶和表的映射關係,而後在訪問時經過 router,也就是前面介紹的路由層進行干預,使得用戶只能訪問本身的 index。具體而言,咱們路由層採用 OpenResty+Lua 實現,將請求過程分爲了右圖的四步,Dispatch,Filter,Router,Reprocess
在 Dispatch 階段咱們將請求結構化,抽出其用戶,app,index,action 數據
而後進入 Filter 階段,進行寫過濾和改寫,filter 又分爲三步
Access 進行限流和驗權這類的准入性攔截,
Action 對具體的操做進行攔截處理,好比說 DDL ,也就是建表,刪表,修改結構這些操做,咱們將其轉發到 Console 進行處理,一方面方便記錄其 index 和 app 的對應信息,另外一方面因爲建刪表這種仍是很影響集羣性能的,咱們經過轉發給 console 能夠對用戶進行進一步限制,防止惡意行爲對系統的影響。
Params 則是請求改寫,在這一步咱們會根據具體的 index 和 action 進行相應的改寫。好比去掉用戶沒有權限的 index,好比對於 kibana 索引將其改成用戶本身的惟一 kibana 索引以實現 kibana 的多租戶,好比對ES不一樣版本的簡單兼容。在這一步咱們能夠作不少,不過須要注意的有兩點,一是儘可能不要解析 body, 解 body是一種很是影響性能的行爲,除了特殊的改寫外應該盡力避免,好比 index 就應該讓用戶寫在 url 上,並利用ES自己的參數關閉 body 中指定 index 的功能,這樣改寫速度能夠快不少。 二是對於_all 和 getMapping 這種對全部 index 進行訪問的,若是咱們替換爲用戶全部的索引會形成 url 過長,咱們採用的是建立一個和應用名同名的別名,而後將其改寫成這個別名
進行完 Filter 就到了真實的 router 層,這一層就是根據 filter 的結果作真實的路由請求,多是轉發到真實集羣也能是轉發到咱們其餘的微服務中。
最後是 Reprocess ,這是拿到業務響應後的最終處理,咱們在這邊會對一些結果進行改寫,而且異步記錄日誌
上面這四步就是咱們路由層的大體邏輯,經過 app 和 index 的權限關係控制水平權限,經過 index 改寫路由進行共享集羣。
作完了邏輯隔離,咱們能夠保證業務的水平權限了,那麼是否就能夠了呢?顯然不是的,實際中不一樣業務訪問差別仍是很大的,只作邏輯隔離每每會形成業務間相互影響。這時候就須要物理隔離了。不過物理隔離咱們目前也沒有找到很是好的方案,這邊給你們分享下咱們的一些嘗試
首當其衝,咱們採用的方法是服務分層,也就是將不一樣用途,不一樣重要性的業務分開,對於關鍵性的主鏈路業務甚至能夠獨佔集羣。對於其餘的,咱們主要分爲兩類,寫多查少的日誌型和查多寫少的檢索型業務,按照其不一樣的要求和流量預估將其分配在咱們預設的集羣中。不過須要注意的是申報的和實際的總會有差別的,因此咱們還有按期巡檢機制,會將已上線業務按照其真實流量進行集羣遷移
作完了服務分層,咱們基本能夠解決了低重要性業務影響高重要性業務的場景,可是在同級業務中依舊會有些業務由於好比說作營銷活動這種形成突發流量。對於這種問題怎麼辦?
通常而言就是全侷限流,可是因爲咱們的訪問都是長鏈接,因此限流並很差作。如右圖所示,用戶經過一個 LVS 訪問了咱們多個 Router,而後咱們又經過了 LVS 訪問了多個 ES 節點,咱們要作限流,也就是要保證全部 Router 上的令牌總數。通常而言全侷限流有兩種方案,一是以限流維度將全部請求打在同一實例上,也就是將同一表的全部訪問打在一臺機器上,可是在 ES 訪問量這麼高的場景下,這種並不合適,而且因爲咱們前面已經有了一層lvs 作負載均衡,再作一層路由會顯得過於複雜。
第二種方案就是均分令牌,可是因爲長鏈接的問題,會形成有些節點早已被限流,可是其餘節點卻沒有什麼流量。
那麼怎麼辦呢?
既然是令牌使用不均衡,那麼咱們就讓其分配也不均衡就行了唄。因此咱們採用了一種基於反饋的全侷限流方案,什麼叫基於反饋呢?就是咱們用巡檢去定時去定時採集用量,用的多就多給一些,用的少就少給你一點。那麼多給一些少給一點究竟是什麼樣的標準呢?這時咱們就須要決策單元來處理了,目前咱們採起的方案是簡單的按比例分配。這邊須要注意的一點是當有新機器接入時,不是一開始就達到終態的,而是漸進的過程。因此須要對這個收斂期設置一些策略,目前由於咱們機器性能比較好,不怕突發毛刺,因此咱們設置的是所有放行,到穩定後再進行限流。
這裏說到長鏈接就順便提一個 nginx 的小參數,keepalive_timeout, 用過 nginx 的同窗應該都見過,表示長鏈接超時時間,默認有75s,可是這個參數實際上還有一個可選配置,表示寫在響應頭裏的超時時間,若是這個參數沒寫的話就會出如今服務端釋放的瞬間客戶端正好複用了這個鏈接,形成 Connection Reset或者 NoHttpResponse 的問題。出現頻率不高,可是真實影響用戶體驗,由於隨機低頻出現,咱們以前一直覺得是客戶端問題,後來才發現是原來是這個釋放順序的問題。
至此服務分層,全侷限流都已經完成了,是否是能夠睡個好覺了呢? 很遺憾,仍是不行,由於ES語法很是靈活,而且有許多大代價的操做,好比上千億條數據作聚合,或者是用通配符作箇中綴查詢,寫一個複雜的script都有可能形成拖垮咱們整個集羣,那麼對於這種狀況怎麼辦呢? 咱們目前也是處於探索階段,目前看比較有用的一種方式是過後補救,也就是咱們經過巡檢去發現一些耗時大的 Task,而後對其應用的後繼操做進行懲罰,好比降級,甚至熔斷。這樣就能夠避免持續性的影響整個集羣。可是一瞬間的rt上升仍是不可避免的,所以咱們也在嘗試事前攔截,不過這個比較複雜,感興趣的同窗能夠一塊兒線下交流一下
講完了低成本,那麼就來到了咱們第二個目標,高可用。
正如我以前提到那樣,ES 自己其實提供了跨機房部署的方案,經過打標就能夠進行跨機房部署,而後經過preference能夠保證業務就近查詢。我這裏就再也不詳細說了。可是這種方案須要兩地三中心, 而咱們不少對外輸出的場景出於成本考慮,並無三中心,只有兩地兩中心,所以雙機房如何保證高可用就是咱們遇到的一個挑戰。下面我主要就給你們分享下咱們基於對等多機房的高可用方案,咱們提供了兩種類型,共三種方案分別適用於不一樣的業務場景。
咱們有單寫多讀和多寫多讀兩種類型
單寫多讀咱們採用的是跨集羣複製的方案,經過修改 ES,咱們增長了利用 translog 將主集羣數據推送給備的能力。就和 6.5 的 ccr 相似,可是咱們採用的是推模式,而不是拉模式,由於咱們以前作過測試,對於海量數據寫入,推比拉的性能好了很多。容災時進行主備互換,而後恢復後再補上在途數據。由上層來保證單寫,多讀和容災切換邏輯。這種方案經過 ES 自己的t ranslog 同步,部署結構簡單,數據也很準確,相似與數據庫的備庫,比較適合對寫入 rt 沒有太高要求的高可用場景。
多寫多讀,咱們提供了兩種方案
第一種方案比較取巧,就是由於不少關鍵鏈路的業務場景都是從DB同步到搜索中的,所以咱們打通了數據通道,能夠自動化的從DB寫入到搜索,用戶無需關心。那麼對於這類用戶的高可用,咱們採用的就是利用DB的高可用,搭建兩條數據管道,分別寫入不一樣的集羣。這樣就能夠實現高可用了,而且還能夠絕對保證最終一致性。
第二種方案則是在對寫入rt有強要求,有沒有數據源的狀況下,咱們會採用中間層的多寫來實現高可用。咱們利用消息隊列做爲中間層,來實現雙寫。就是用戶寫的時候,寫成功後保證隊列也寫成功了才返回成功,若是一個不成功就總體失敗。而後由隊列去保證推送到另外一個對等集羣中。用外部版本號去保證一致性。可是因爲中間層,對於Delete by Query的一致性保證就有些無能爲力了。因此也僅適合特定的業務場景。
最後,在高可用上我還想說的一點是對於平臺產品而言,技術方案有哪些,怎麼實現的業務其實並不關心,業務關心的僅僅是他們能不能就近訪問下降rt,和容災時自動切換保證可用。所以咱們在平臺上屏蔽了這些複雜的高可用類型和這些適用的場景,徹底交由咱們的後端去判斷,讓用戶能夠輕鬆自助接入。而且在交互上也將讀寫控制,容災操做移到了咱們本身系統內,對用戶無感知。只有用戶能夠這樣透明擁有高可用能力了,咱們的平臺才真正成爲了高可用的搜索平臺。
最後一個目標,少運維
今天運維的話題已經分享了不少了,我這邊就不在贅述了。就簡單介紹一下咱們在總體運維繫統搭建過程當中沉澱出的四個原則。自包含,組件化,一站到底,自動化。
自包含ES作的就很不錯了,一個jar就能夠啓動,而咱們的整套系統也都應該和單個ES同樣,一條很簡單的命令就能啓動,沒有什麼外部依賴,這樣就很好去輸出。
組件化是指咱們每一個模塊都應該能夠插拔,來適應不一樣的業務場景,好比有的不須要多租戶,有的不須要削峯填谷。
一站究竟是指咱們的全部組件,router,queue,es,還有不少微服務的管控都應該在一個系統中去管控,萬萬不能一個組件一套本身的管控。
自動化就不說了,你們都懂
右邊就是咱們的一個大盤頁面,展示了router,es和queue的訪問狀況。固然,這是mock的數據
至此咱們已經擁有了一套『低成本,高可用,少運維』的 Elasticsearch 平臺了,也解決了以前談到的業務痛點,那麼用戶用的是否就爽了呢?咱們花了大半個月的時間,對咱們的業務進行了走訪調研,發現業務雖然已經從運維中解放了出來,可是身上仍是有很多搜索的枷鎖。
咱們主要分爲兩類用戶,數據分析和全文檢索的。
數據分析主要以爲配置太複雜,他只是想導入一個日誌數據,要學一堆的字段配置,並且好久纔會用到一次,每次學完就忘,用到再重學,很耽誤事情。其次,無關邏輯重,由於數據分析類的通常都是保留多天的數據,過時的數據就能夠刪除了,爲了實現這一個功能,數據分析的同窗要寫不少代碼,還要控制不一樣的別名,非常麻煩。
而全文檢索類的同窗主要痛點有三個,一是分詞配置複雜,二是難以修改字段,reindex 太複雜,還要本身先建立別名,再控制無縫切換。第三點是Debug艱難,雖然如今有 explain,可是用過的同窗應該都懂,想要總體梳理出具體的算分緣由仍是須要本身在腦中開闢很大的一塊緩存的。對於不熟悉 ES 的同窗就太痛苦了。
整理一下,這些痛點歸類起來就兩個痛點,學習成本高和接口過於原子。
學習成本高和接口過於原子,雖然是業務的痛點,可是對ES自己而言卻反而是優勢,爲何學習成本高呢?由於功能豐富。而爲何接口原子呢,爲了讓上層能夠靈活使用。
這些對於專家用戶而言,很是不錯,可是對於業務而言,的確非常麻煩。
所以咱們開始了咱們第二個階段,搜索中臺。什麼叫中臺呢,就是把一些通用的業務邏輯下移,來減小業務的邏輯,讓業務專一於業務自己。
而爲何業務不能作這些呢?固然也能作。可是俗話說『天下武功,惟快不破』,前臺越輕,越能適應這變化極快的業務訴求。
所以咱們的搜索中臺的主要目標就是兩點:
一是下降業務學習成本,加快上手速度。咱們此次介紹的主要是如何下降對於配置類這種低頻操做的學習成本;
二是抽象複雜邏輯來加速業務迭代,咱們此次主要會介紹抽象了哪兩種業務邏輯。
下降學習成本,這個怎麼作呢?衆所周知,黑屏變白屏,也就是白屏化。可是不少的白屏化就是把命令放在了 web 上,回車變按鈕。 這樣真的能夠下降用戶學習成本麼? 我想毋庸置疑,這樣是不行的。
咱們在可視化上嘗試了許多方案,也走了許多彎路,最後發現要想真正下降用戶學習成本,須要把握三個要點
用戶分層,區分出小白用戶和專家用戶,不要讓專家用戶的意見影響總體產品的極簡設計,對於小白用戶必定是越少越好,選擇越少,路徑越短,反饋越及時,效果越好。正如所謂的沉默的大多數,不少小白用戶並不會去主動發聲,只會隨着複雜的配置而放棄使用。 下圖就是咱們對於專家用戶和小白用戶在配置表結構時不一樣的頁面,對於專家用戶,基本就是ES全部的功能可視化,加快使用速度。對於小白用戶而言,則是徹底屏蔽這些功能點,讓其能夠直接使用。
引導式配置,引導式配置其實也就是加上限制,經過對用戶的上一步輸入決定下一步的可選。要避免一個頁面打開一堆配置項,這樣用戶就會無從下手,更不要談學習成本了。經過引導式配置來減小用戶的選擇,下降用戶的記憶成本。限制不必定就意味着約束用戶,合適的限制更能夠下降用戶的理解成本。好比右圖就是咱們的一個分詞器配置,很簡單的引導,用戶選擇了中文字典後才能夠選擇相應的詞典
第三點,深層次結構打平,什麼叫深層次結構打平,就是指像如今的分詞器,類似度這些都是在index級別下的,咱們將其抽象出來,變爲全局的。用戶能夠自行建立全局的分詞器,類似度,而且還能夠共享給其餘人,就像一個資源同樣。而後在index中則是引用這個分詞器。雖然這邊作的僅僅是將分詞器從index級別變爲了全局,可是缺真正的減小了不少業務操做,由於在一個業務場景中,每每存在多張表,而多張表每每會使用同一套分詞器。經過這種全局性的分詞器用戶僅需修改一處便可做用於全部位置。
好的,說完了白屏化的一些經驗,這邊給你們分享咱們對於複雜邏輯的抽象封裝的兩種新型表結構。這兩種分別是數據分析類場景,咱們抽象出了日誌型表,另外一種是全文檢索類場景,咱們抽象出了別名型表。
日誌型表的做用顧名思義就是存日誌,也就是以前說的對於數據分析類業務,每每只保留幾天,好比咱們如今有個業務場景,有張es的日誌表,只想保留3天,因而咱們就給他按天建立索引,而後寫入索引掛載到今天,查詢索引掛載全部的,用router去自動改寫別名,用戶仍是傳入es,可是執行寫入操做時實際就是在es_write執行,查詢就是在es_read執行。固然實際中咱們並非按天建的索引,咱們會利用Rollover建立不少的索引來保證海量寫入下的速度。可是總體邏輯仍是和這個是同樣的
而對於全文檢索類場景,主要的痛點就是表結構的變動和分詞器,字典類的變動,須要重建索引。因此咱們則抽象了一個叫別名表的表結構,用戶建立一張表es,實際建立的是一個es的別名,咱們會把他和咱們真實的index一一對應上。這樣利用這個別名,咱們就能夠自動幫用戶完成索引重建的操做。而索引重建,咱們有兩種方式,一是用戶配置了數據源的,咱們會直接從數據源進行重建,重建完成後直接切換。另外對於沒有數據源,直接api寫入的,目前咱們是利用了 ES 的 reindex 再配合咱們消息隊列的消息回放實現的。具體而言,咱們就是首先提交 reindex,同時數據開始進 queue 轉發,而後待 reindex 完成後,queue 再從 reindex 開始時進行回放,追平時切別名便可
總結
總結一下此次分享的內容,咱們首先構建了一個『低成本,高可用,少運維』的ES平臺將業務從運維中解脫出來,而後又進一步構建了搜索中臺,經過下降業務學習成本,下沉通用業務邏輯來加速業務迭代,賦能業務。 固然,這裏介紹的搜索中臺只是最基礎的中臺能力,咱們還在進一步探索些複雜場景下如何抽象來下降業務成本,也就是垂直化的搜索產品,有興趣的同窗歡迎加入咱們,一同構建,讓天下沒有難用的搜索。咱們各種崗位持續招聘中,產品,設計,前端,後端,運維都歡迎加入。 郵箱: xinyu.jxy@antfin.com
完整 PPT 地址:
http://www.sofastack.tech/posts/2018-11-12-01
或點擊文字底部「閱讀全文」直接訪問
長按關注,獲取分佈式架構乾貨
歡迎你們共同打造 SOFAStack https://github.com/alipay