第二版 (5/14/2019更新)
React component example
<LanguageResource key = checkout.cfmMoney>Confirm Amount</LanguageResource>
數(shù)據(jù)庫設(shè)計
Language Resource Table Schema
Key, OriginContent, LastUpdated, ContentLangId1, LastUpdatedLangId1, ContentLangId2, LastUpdatedLangId2, ...
Key一定是要有比較規(guī)范的name space, 避免出現(xiàn)同一個key被不同的developper使用修改。
Task Table Schema
Key, OriginContent, LastUpdated, LanguageId, TranslatorId, status, HtmlFileLink, prioritiy
一種做法: 網(wǎng)頁修改完畢不等翻譯完成就上線。
網(wǎng)頁文件修改之后一種做法是直接deploy,用戶load頁面的時候會產(chǎn)生翻譯請求。
這種做法的考慮基于是很多時候某個頁面在某種語言很少會出現(xiàn),沒必要等全部翻譯完再上線。
根據(jù)用戶load的時候如果某個key找不到,就會自動產(chǎn)生一個translation request, 同時用英語做為fall back。 一個key被Request的次數(shù)越多他的優(yōu)先級越高,
API:
GetTranlantion(key, originContent, url)
this API call may make internal call: RequestTranslatorService(key, originContent, url)
單詞內(nèi)容有變化? OriginalContent修改的情況
如果這個key的英文改了: 在translation的時候就會發(fā)現(xiàn)送過去的originalLanguageContent的數(shù)據(jù)庫里的不一致!這種情況要做三件事情: 1。 更新數(shù)據(jù)庫里的original content, 2。 更新originalContent 的last update time。 3. send update translation request.
如果翻譯還沒更新又來訪問這個request了, 雖然內(nèi)容一致,但翻譯和原文的timestamp順序相反,這時仍然使用舊的翻譯,同時再發(fā)一次translation request.
updateTranslationAsOriginalContentUpdated(key, previousContent, newContent)
如果嫌慢(因?yàn)橐獙懞芏鄠€任務(wù))可以丟到一個message Queue里面去異步執(zhí)行。
key , originalcontent, translantion.
如何翻譯
getTranslation(key, OriginalLanguageContent, languageId)
如果找到,originalContent一致并且翻譯的timestamp比數(shù)據(jù)庫里originalContent的時間stamp要晚,則直接返回。如果找到但內(nèi)容不一致/timestamp不對 則refer to original content修改的情況。
如果找不到就fall back為英語,同時增加一個翻譯請求。這個翻譯請求如果已經(jīng)在task list里了,就更新優(yōu)先級, 如果不在, 就新建。
如何給翻譯員分配任務(wù)
根據(jù)翻譯員的語言種類,從當(dāng)前任務(wù)列表里先擇優(yōu)先級最高的一組翻譯,最好是據(jù)有相同key前綴的(一般優(yōu)先級也會相同, 因?yàn)橐粋€頁面里的key是同時更新優(yōu)先級的)。同時更新Task table把當(dāng)前任務(wù)設(shè)為pending,翻譯員id也設(shè)置上. 這樣別的翻譯員就不會去拿同一個task。 可以再增加一個field記一下任務(wù)是什么時候開始的。如果一段時間任務(wù)并沒有完成,則自動重新變成available.
getTranslationTask(languageId, amount, translatorId);
翻譯員如何更新翻譯
Api: enterTranslation(key, languageId, translated content, translatetimeStamp, originContentTimeStamp)
在更新數(shù)據(jù)庫時候先check一下數(shù)據(jù)庫里original content的time stamp, 如果翻譯人員提交的original content的timestamp比較老, 則拒絕這次更新,返回outdated originalContent Exception.
如果更新成功, 則把更新的信息broadcast給所有存有cache的web服務(wù)器。也可以定期推送。
另外一種做法翻譯完再上線。
另外一種做法是網(wǎng)頁修改之后,直接extract新的key和修改過的key value pair. 這些key不翻譯完新版網(wǎng)頁就不上線。
Api: ExtractForTranslation(fileName) return a jobId
Using this jobId, the developper can see how much content has been already translated
全部翻譯完成后翻譯官把整個翻譯的package發(fā)過來。發(fā)過來之后由系統(tǒng)統(tǒng)一deploy。但這樣會delay上線速度。 此時翻譯官也不會直接把翻譯輸入數(shù)據(jù)庫。因?yàn)樾戮W(wǎng)頁還沒上線, 翻譯先上線會造成數(shù)據(jù)不一致。
Scale
可以把兩張表都放到數(shù)據(jù)庫里面。 Lanaguage Resource Table直接在每個webserver上做cache。如果數(shù)據(jù)庫有更新,數(shù)據(jù)庫服務(wù)器就會notify 其他所有的webserver更新cache??梢悦渴昼娮鲆淮?。
如何Sharding:
由于數(shù)據(jù)量不大,本身不一定需要sharding??梢园凑Z言和對應(yīng)的location sharding。
Indexing
LanguageResource直接按key查+就可以了, 本身就有index, 不需要再建index。
對于task Table要按 languageId + 狀態(tài) 建 index,
task Table的primary index是 key + languageId,
每天半夜12點(diǎn)可以更新一下優(yōu)先級,讓所有的優(yōu)先級衰減。刪掉那些低于某些threhold的值。
-----------------------------我寫的翻譯系統(tǒng)第二版到這里結(jié)束-------------------------------------------------------------
參考文獻(xiàn):
相關(guān)的官方技術(shù)博客
https://medium.com/airbnb-engineering/launching-airbnb-jp-in-record-time-52f8b0af965d
技術(shù)文檔 https://airbnb.io/polyglot.js/
其他 https://www.jiuzhang.com/qa/5054/ https://github.com/donnemartin/system-design-primer
4S 分析大法
Scenario:
前端人員使用一種語言(比如英語)設(shè)計前端。
menu label, 描述,免責(zé)條款,
前端人員release 英文版網(wǎng)頁或內(nèi)容 ==》 觸發(fā)翻譯系統(tǒng)extract 翻譯事件 :翻譯系統(tǒng)分析英文版文件,對比哪些句子/內(nèi)容已經(jīng)翻譯過了,哪些句子/內(nèi)容需要新翻譯。==》 extract出新句子分發(fā)給翻譯員。==》翻譯員完成翻譯任務(wù)之后返回給翻譯系統(tǒng)。翻譯系統(tǒng)輸出多種語言包。
系統(tǒng)可以分為兩種實(shí)現(xiàn)形式:
如果是網(wǎng)頁更新比較慢而且是一組靜態(tài)網(wǎng)頁的話。
1。前端人員 2。 網(wǎng)頁分析服務(wù)。 3數(shù)據(jù)庫 4。 Combine 服務(wù) 5。翻譯員。
這樣的話,所有的內(nèi)容在創(chuàng)建的時候都立馬翻譯好,可能有幾個小時到一兩天的delay。
這種的缺點(diǎn)是所有語言版本的發(fā)布會比較慢,因?yàn)樗袃?nèi)容都要翻譯。
如果是像Airbnb這樣的動態(tài)生成的網(wǎng)頁(數(shù)據(jù)都是數(shù)據(jù)庫里調(diào)出來的)
前端人員修改網(wǎng)頁的時候,可以在修改完網(wǎng)頁時自動發(fā)一個翻譯的request 通知系統(tǒng)去翻譯,但可能網(wǎng)頁還沒翻譯完就上線了。
用戶輸入airbnb.cn網(wǎng)站地址,根據(jù)用戶的地理語言信息,webserver在生成網(wǎng)頁的時候發(fā)一個translate request. 把網(wǎng)頁里對應(yīng)的標(biāo)簽轉(zhuǎn)換成相應(yīng)的語言再返回給用戶。相當(dāng)于一個實(shí)時的翻譯系統(tǒng)。不需要等待頁面全部翻譯完成再上線。如果一個詞在本地cache里,先查本地cache, 如果不在本地cache,則可以跨過網(wǎng)絡(luò)去查數(shù)據(jù)庫的cache,如果數(shù)據(jù)庫的cache里面也沒有,則去數(shù)據(jù)庫里找,找不到就用默認(rèn)語言并且去待翻譯數(shù)據(jù)庫里面給該key增加一下優(yōu)先級。 一個key被request的次數(shù)越多,它的優(yōu)先級越高。
翻譯人員從“待翻譯數(shù)據(jù)庫” 里拿出當(dāng)前優(yōu)先級最高的詞, 翻譯,輸入系統(tǒng)。
API
GetTranslate(key, default, locale)
EnterTranslatedVersion(key, default, value, locale)
如果要改key 和default的話, 可以增加一個api, updateTranslate(key, new default, locale)
需要設(shè)計多牛的系統(tǒng)
怎么估算size??
還真不知道。 算一下有多少網(wǎng)頁:每個網(wǎng)頁有多少單詞??還得再想想
Service
Translate Service
Translator task service
Client --> webServer
|
v
Front End -> Translate Service -> DataBase
^
|
v
Translator <--> Translate Task Service
Storage
Data model
Translation Table
用什么數(shù)據(jù)庫? Redis (內(nèi)存型), Cassandra, SQL + Memcached
Redis是純粹的key -value pair: 如果用Redis
Key:Key + Locale, Value : content in language of locale + time stamp
如果用Cassandra, 三層結(jié)構(gòu)。
Schema:
Row Key : Key ;
Column Key: Locale
Value: content in some language + time stamp
讀取的時候要用Key + locale去讀。
也可以用SQL,反正是讀多寫少,大不了用cache優(yōu)化一下。
SQL也行。。。因?yàn)槟膬耗拿炊嘣L問量
Task Table
這個量級很小,一個SQL就足夠了。
Schema如下
Key,Original Language, To Language,優(yōu)先級, status,
Working solution
前端人員把 key, English
優(yōu)化:
按優(yōu)先級排序,如果一個key被call的次數(shù)越多,優(yōu)先級越高。 如果一個key四天都沒有被call過,則自動清除出隊列。
加速 優(yōu)化perf
在Web Server端放一些Cache,有問題先查本地的cache,查不到再找DB。
建index, 只需要根據(jù)key 和locale建一個composite primary index就可以了。
bloom filter : 如果一個key不在bloom filter里面,則肯定不用找了。
關(guān)鍵問題: 如果DB更新了,如何第一時間更新那么多server:
DB主動發(fā)送push notification給Web Server.
需要設(shè)計TTL嗎? 最好這樣,不然如果某些push notification某種原因沒有收到,就跪了。
Sharding
如果需要Sharding,則可以按語言按距離按國家Sharding。
非要按Hash code sharding的話,sharding key就是我們的key。其實(shí)并不需要sharding,一臺機(jī)器是可以搞定的。
Consistent hashing 的概念
按String做為sharding key
Cache
Cache thru 和Cache aside都可以。但重要的是在服務(wù)器端直接cache, 就不用每次訪問數(shù)據(jù)庫了。
前端程序員不受后端設(shè)計的影響。
前端用key,和 value寫在html里面。
translator有另一個portal輸入翻譯而且能馬上看到效果
把對應(yīng)的html存下來,直接調(diào)存下來的html顯示。
再來就是有時候translator翻譯錯了修改之后怎么樣才能更快的讓大家看見(如果你有cache的話)
Broadcast,Server和DB 保持一個長鏈接,有新的變化立馬通知。