一輪電話面試,一個半小時,昨天晚上面試的,今早面試官打電話約了二面(為啥是一面面試官:)?
- 自我介紹
- 工作經歷,項目經歷
- 項目挑兩個介紹一下
- 這里介紹了一個偏技術的基于Mysql搭建的olap系統,數據量大概十幾億,但是業務邏輯比較簡單,olap邏輯主要體現在SQL上,重點說了一下分表和主從的搭建和原因;分表方便備份和管理;因為數據只是順序寫入和增量讀取,大表本質上不影響事務性能;
- 另外一個toB的業務系統,這里核心業務邏輯是TOB場景下的復雜權限管理,講了一下利用狀態機模式實現的思路,狀態機利用Mysql的自生成列加狀態位圖來實現;
- 第一個項目,數據庫主從備份的原理
- 三種模式,我使用的是異步+statement,為什么使用statement模式,其他兩種有什么優劣性,這里理解三種備份原理,就很容易明白怎么使用為什么使用--olap定時做批處理,對從庫的實時一致性要求不高,所以采用異步,事務只涉及到單行insert,且不包含SQL函數,所以使用statement,性能高;
- 分表--
- 我分表使用mycat主鍵范圍分片,一千萬一張表;主鍵在應用層自生成,服務重啟需要阻塞回查--mycat中間件壓測下自生成id有BUG;
- 如果多實例自增id怎么實現--雪花算法,redis原子自增命令,分布式鎖;
- 如果是業務數據庫怎么分表--冷熱分表,數據散列--數據散列的時候要注意數據配合查詢路由策略;
- 第二個項目
- 狀態機怎么實現的--自生成列,使用位圖,權限結果四字節,每個字節存儲的是狀態機中不同的業務對象的狀態,計算出來的就是權限結果
- 狀態機為什么用Mysql的自生成列,如果業務代碼中實現會怎么樣--回答避免了多次查詢,性能比較高
- 其實這里面試官想說的是強依賴與Mysql的功能,擴展性不夠好,另外Mysql的版本支持性,這里沒有意識到,面試官提了Mysql是否所有版本都支持自生成列才意識到,避免多次查詢來計算狀態結果可以用查詢時計算,這樣也可以保證狀態改變時為了計算出權限狀態而導致的多次查詢,但是這里其實有一個問題,就是查詢時計算如果查詢之后狀態被其他事務修改,也必須加鎖才能保證一致性,另外就是每次查詢都要計算權限值,所以性能開銷還是很大的,所以這個取舍還是有必要的--當時回答的是只考慮了性能;沒有考慮擴展性;
- 關于狀態機模型是否還在其他項目中使用過--工作過程中沒有,最近想做的分布式異步任務處理組件設計考慮到了狀態機模式,這種類似于zk,新建節點處于同步模式,數據同步完才進入worker模式,由于這個項目實際未在簡歷中體現,就大概提了一下,面試官也沒多問;
- 技術問題
- 是否使用過RPC,RPC的原理
- 回答,RPC其實后來因為公司單體架構加上vertx本身的eventbus單機部署用不上RPC,這點如實回答,大概有了解過fegin,回答http協議實現+集成ribbon負載均衡,原理就是對象的序列化+非代碼入侵式的接口調用;
- 如果自己設計實現一個RPC框架,怎么做--基于RPC的原理,服務發現,負載均衡,對象序列化和反序列化--當時只考慮到這么多,(后續還是要看一下springcloud的幾個核心組件的設計以及微服務架構中的一些核心架構思想;)
- 服務發現怎么實現--說了利用zk的watcher機制和注冊中心eurake,核心就是分布式+發布訂閱,分布式保證注冊中心不會出現單節點問題,發布訂閱就是服務發現的核心功能;
- 常用的LB算法--核心就是分流,之前配置過nginx的lb,先說了一下基于nginx的lb怎么配置的,很簡單,兩個業務服務器2:1 的流量分配,面試官問2:1是什么意思--答主服務器百分之七十的流量,從服務器百分之三十的流量;
- 然后問數據庫怎么主從設計的,面試官其實就想問數據一致性的問題了這里,回答了數據庫還是主庫單實例模式,主庫負責所有事務讀寫,發生故障轉移之后流量才轉發到到從庫上,之前的lb只是基于服務的lb;繼續回答LB算法,簡單輪訓,IP散列,這里說了LB對于數據一致性的影響,如果水平分庫分表的時候要保證同一用戶的多次請求最終轉發到同一服務實例上,最終保證了訪問到同一數據庫實例;
- Spring和Springboot的區別--
- 答Spring核心是Bean管理工廠,springboot核心是對spring開發配置優化,提供了很多組件的starter,實現了組件之間的版本依賴問題,另外springboot集成了Tomcat,可以直接部署war包一鍵運行,而Spring需要單獨部署,但是springboot的核心還是spring的bean管理---這里因為springboot很久沒有接觸了,回答的比較簡單,但是核心沒啥問題,springboot就是為了方便spring開發,在配置和部署方面做了優化,要具體說我感覺也沒啥好展開講的
- 如果實現一個限流框架怎么做--
- 先說了下限流實現--流量轉發或者服務降級
- 核心關注了怎么做流量統計,提到了服務自反饋和網關層代理,服務自反饋的時候監控JVM運行狀態,服務自己決定是否接受新的請求,提了下對JVM性能監控本身會影響到服務性能,所以時間會長一些,一般不考慮;核心還是網關層的限流
- 網關層的話做流量統計監控就可以,針對某個服務實例粒度的流量統計,一般需要更細粒度的實例的接口層面的流量統計;然后提到了怎么做到流量控制,這里首先想到的點是如何發現需要限流,就說了一下可以先做統計記錄某個實例的平均TPS,如果TPS變大的話就限流;
- 繼續問流量統計具體怎么實現,比如配置好某個實例的某個接口限流QPS小于100----回答了一個簡單的算法,每秒重置的計時器,對某個接口每秒接受的請求做計數,小于一百就發給該實例,大于一百就做限流--轉發或者降級;然后問計數器并發問題怎么實現--其實這里問流量統計的時候好像核心就是想問多線程并發問題,但是我下意識都是覺得既然使用了計數器,肯定是要保證線程安全的,emm,于是說了cas就可以實現了,
- 然后說了單節點問題,所以限流框架本身要做分布式集群,所以還需要集群協議來做集群管理,簡單的可以用zk來做集群管理;
- 問怎么實現分布式鏈路追蹤
- 答設計一個請求的全局標識來記錄請求鏈路;
- 問具體怎么實現全局標識,怎么實現服務間傳遞--可以在網關層維護一個全局唯一ID放在http請求頭里,然后服務內方法間傳遞使用ThreadLocal,服務間傳遞根據具體的調用方式,可以繼續放在請求頭里或者顯示傳參都行
- 問線程池里的ThreadLocal傳遞全局ID會有問題嗎--emmm,當時沒有意識到線程池使用ThreadLocal的一個坑,因為使用ThreadLocal本來代碼標準就是要使用完之后remove,我以為要問的是線程調用線程池能不能直接把ThreadLocal傳過去,或者線程被回收會不會丟失ID,就大概說了下ThreadLocal的原理,因為每個線程實例維護自己的ThreadLocalMap,所以傳遞給線程池里的線程的時候必須顯式set;面試完才看了一下線程池使用ThreadLocal的雷---線程池里的線程可能不會被回收,ThreadLocal可能存放的上一個請求的ID,但是顯式set是沒有問題的,面試官主要是想看能否考慮到數據安全性這一點~
- 問JVMGC
- 先說了判斷對象存活的算法--根搜索和引用計數
- 然后說了經典的GC算法--標記復制,標記清除,標記整理,分代,
- 然后說了CMS和G1的實現核心原理,詳細說了CMS的GC過程--這里面試好像沒注意聽了,因為一個多小時了已經,也是八股文,我就一直講;
- 然后問currenthashmap----我大概說了下JDK七之前使用分段鎖來提高并發度,JDK八以后取消的分段鎖,我記得是用cas做了優化,但是沒看過具體實現,就老實說沒看具體怎么實現的,大概使用Synchronized做了get和set的同步控制,然后數據結構和hashmap基本一致,然后面試官就沒怎么問了,直接開啟下一題------emmm這道題真的是,八股文背了七股-一股沒背,送分題讓我給丟了:),因為想的是后邊系統看一下juc下的源碼,就沒想為了應付面試看currenthashmap,說實話最近三四場面試都問到了currenthashmap,但是我都沒答上來hhhhh
- 問類加載機制----老重八問題了
最后:面試官說今天的面試也一個多小時了,就到這里先,也沒有反問環節就直接結束了,面完一個半多小時了,經歷的單次最長的面試,乘著熱乎記錄一下,可能不完全,但是大差不差;
總體來說面試內容比較多但是比較簡單,微服務架構尤其springcloud那一套都沒怎么詳細看過,最近一直在研究偏底層的技術細節,但是之前了解到的一些核心思想都可以講明白,前年大概看過eurake的代碼,但是看的不細,重要的是最近在分布式任務調度組件中設計中對線程并發,分布式一致性,分布式集群管理有了一些比較深刻的認識,雖然沒有去get太多的新技術點,但是就已經很夠了,另外看netty源碼,雖然沒有問到過,但是也學習到了很多,計算機技術很多都是相通的---因為最近才開始系統性的學一些底層的東西,所以自己沒啥把握完全通過面試,慢慢來吧;