1、背景java
最初遇到這個問題是去58面試。部門領導是原同事,因此面試比較水。水到什麼程度呢?mysql
面試就是走個形式而已,不會不過的。linux
一面面試官就問了一個問題:「一個請求過來都通過了什麼?」 剩下的全是閒聊。順便展現一下公司和部門的優點。期待加入的意思。面試
聲明sql
面試如此之鬆是基於兩點:數據庫
第一點,與原同事多年的共事已經展現了能力和綜合素質,比幾個小時的面試獲得的結論靠譜的多。緩存
第二點,原同事自己認人識人的能力獲得了其餘人的承認,因此你們放心他推薦的人。服務器
畢竟沒人願意讓一個不合適的人加入本身團隊拉低總體水準。網絡
2、問題考察點多線程
深度和廣度的綜合考察
3、靜兒的答案
創建虛擬場景
☆ 項目
容器調度服務(根據用戶傳入的機房、CPU、內存等信息給用戶建立所須要的機器)。
項目所用技術棧:SpringBoot、Thrift、緩存、mysql、k8s
☆ 需求
程序中須要用thrift(RPC調用)請求基礎服務取全部的機房信息。
☆ 設計
基礎服務提供了「傻瓜式」客戶端給調用方。客戶端只須要引入jar包,就能夠像調用本地接口同樣進行訪問。
客戶端實現方法
由於機房信息是低頻的、對變動必定時延不敏感的資源信息。因此客戶端首先RPC去遠程取結果放到本地JVM緩存中。每次調用直接返回JVM緩存信息。客戶端有定時任務定時將最新結果刷新到JVM緩存。
設計特色:
一、對thrift接口作封裝,封裝包括設定了遠程獲取異常的重試次數、重試間隔、遠程服務信息等。緣由:基礎服務提供方本身最清楚本身服務的響應時間、服務可用性等信息,本身設置更爲精確,避免對使用方形成困擾。
二、每次返回本地JVM存的機房信息。信息更新經過自帶定時任務。緣由:基礎服務提供方本身最清楚本身的資源被更新頻率如何,提供數據的實時性重要性如何。怎麼保持數據一致性。怎樣更高效。這些難題不該該拋給調用方。
三、懶加載處理。緣由:不能由於jar包被引用就直接佔用內存、CPU等資源。要按需提供。
服務端實現方法
服務端收到請求,考慮到這個是基礎服務,使用方多。爲了防止壓力直接做用於數據庫,上面加了一層集中式緩存。不穿透的狀況下,直接返回緩存信息。
數據庫中的全部機房信息是從k8s標籤中獲取過濾的。有變化直接刷緩存。即數據庫中存的是全部的標籤信息,其中一個小功能是從全部標籤中過濾機房標籤。這種設計是區別於有個機房管理系統錄入這種管理,機房信息永遠是實時準確的。(這塊涉及到內部系統的設計問題,若是看不懂直接忽略便可。)
☆ 請求過來通過了什麼
從程序設計上,大致通過的如上圖。限於篇幅(若是你想了解不限於篇幅是個什麼狀態,能夠閱讀下面一篇《一個請求過來都通過了什麼?(2017年http版)》),本篇不講JVM通過了怎樣的過程,主內存和工做內存交互,內存可能的分頁,numa綁核、定頻非定頻等等。深度上只介紹thrift調用這一層。
籠統的過程如上圖。大致分爲三個部分:
一、有IP直接訪問IP,沒有IP經過其餘信息獲取到IP。
在此部分中,第一次鏈接須要根據環境和服務標識獲取機器列表。客戶端通常會有本地進程代理。對於美團OCTO來講,就是Sg_Agent(服務治理代理)。代理會和OCTO服務器進行通訊,及時獲取最新信息。鏈接創建後,下次訪問就是直接IP訪問了。
二、經過IP訪問服務端,根據類名+方法簽名獲取方法。
三、經過攔截器認證的請求被服務端處理後返回結果。
Thrift內部的架構是分層次的。客戶端和服務端均可大致分爲代碼層、協議層、傳輸層、IO處理層四部分。客戶端和服務端的IO處理層直接交互。
代碼層:
代碼在美團OCTO體系中MtThrift中會直接由框架自動從java代碼轉成IDL文件,框架再根據IDL文件生成代碼實現數據的讀寫。
協議層(實現Tprotocol接口):
將數據編碼、解碼。最三種的三種傳輸格式支持是:二進制格式、壓縮格式、Json格式。
傳輸層(實現Ttransport接口):
提供從網絡等媒介讀取和寫入數據的方式。經常使用的有:向文件進行讀寫、從內存上讀寫、壓縮讀寫。
IO處理層:
這層所作的就是阻塞、非阻塞,單線程、多線程這些。通常像獲取全部機房信息這樣的讀操做用的是多線程阻塞方式。寫操做通常用多線程非阻塞方式。
再往下linux系統層的東西也是能夠講講的,內容有點多,你們本身作思考題吧。還有涉及網絡傳輸的信道、全雙工傳輸模式這些,你們能夠本身想想,查一查。
4、總結
本次公衆號文章共三篇《一個請求過來都通過了什麼?(Thrift版)》、《一個請求過來都通過了什麼?(2017年http版)》、《思惟發散的雙刃劍》。首篇是剛寫的,第二篇是17年寫的。最後一篇是一些總結思考。