每天十道面試題-20200328

題目

  • 1、Spring解決相互依賴
  • 2、zookeeper和Spring-Eureka的區別
  • 3、BeanFactory-ApplicationContext的區別
  • 4、棧溢出原因剖析
  • 5、Apollo-SpringCloudConfig-Nacos區別
  • 6、Tomcat架構
  • 7、數據庫分庫分表
  • 8、MySQL 分表分庫全局 ID
  • 9、併發編程三要素?
  • 10、介紹一下 Spring 的事務實現方式及瞭解?

解答

題目一
  • 題幹:Spring解決相互依賴
  • 分析:
  • 考察Spring循環依賴,首先我們要明確循環依賴和循環調用的區別,先說一下循環調用,比如遞歸調用,自己調用自己然後給出一個終結條件,再如:方法A調用方法B然後方法B內部調用方法A,也是需要給出終結條件纔會停止,如果循環調用沒有給出正確的終結條件,最終將會導致棧溢出;然後我們主要說一下循環依賴,也就是循環引用,就是BeanA內部需要注入BeanB的引用,BeanB內部需要注入BeanC 的引用,BeanC內部需要注入BeanA的引用,在談這個問題之前我們先補充一句就是我們需要知道Spring注入的幾種方式,手動注入一般是構造方法注入 setter注入 以及工廠注入,對於循環依賴來講存在於構造方法注入和setter注入中;
    構造器循環依賴:表示通過構造器注入構成的循環依賴,此依賴是無法解決的,只能拋出BeanCurrentlyInCreationException異常表示循環依賴;Spring容器將每一個正在創建的Bean 標識符放在一個“當前創建Bean池”中,Bean標識符在創建過程中將一直保持在這個池中,因此如果在創建Bean過程中發現自己已經在“當前創建Bean池”裏時將拋出BeanCurrentlyInCreationException異常表示循環依賴;而對於創建完畢的Bean將從“當前創建Bean池”中清除掉。
    setter循環依賴(singleton):表示通過setter注入方式構成的循環依賴。對於setter注入造成的依賴是通過Spring容器提前暴露剛完成構造器注入但未完成其他步驟(如setter注入)的Bean來完成的,而且只能解決單例作用域的Bean循環依賴。通過提前暴露一個單例工廠方法,從而使其他Bean能引用到該Bean。會對外暴露一個ObjectFactory類型的對象,這樣在去正在創建的Bean的緩存池裏獲取的時候由於已經暴露了ObjectFactory所以不會像構造器注入那樣拿不到Bean,這樣就解決了循環依賴.setter循環依賴我們可以通過設置參數來禁止和啓用循環依賴,setAllowCircularReferences(false);來禁用循環引用
    setter循環依賴(非singleton):對於prototype作用域Bean,Spring容器無法完成依賴注入,因爲prototype作用域的Bean,Spring容器不進行緩存,因此無法提前暴露一個創建中的Bean。
    思想:中間對象去解決循環依賴

  • 回答:
  • 1、首先我們一定要避免循環依賴,這是設計上的問題,杜絕循環調用因爲如果沒有跳出條件將陷入死循環。
    2、只有setter【singleton】循環依賴Spring可以解決,我們可以,因爲可以解決所以我們可以通過設置參數setAllowCircularReferences(false);來禁用循環引用。
    3、對於setter【非singleton】,由於我們不進行緩存,所以沒有辦法暴露出來已經創建的Bean,所以沒有辦法解決。
    4、構造器產生的循環依賴此依賴是無法解決的,只能拋出BeanCurrentlyInCreationException異常表示循環依賴。Spring容器將每一個正在創建的Bean 標識符放在一個“當前創建Bean池”中,Bean標識符在創建過程中將一直保持在這個池中,因此如果在創建Bean過程中發現自己已經在“當前創建Bean池”裏時將拋出BeanCurrentlyInCreationException異常表示循環依賴;而對於創建完畢的Bean將從“當前創建Bean池”中清除掉。
    5、Setter【singleton】可以解決主要是因爲提前暴露一個單例工廠方法暴露出來一個ObjectFactory。當找到已經創建的bean 的時候直接使用暴露出來的ObjectFactory,就不會像構造函數一樣去池子裏找導致異常拋出了。
    總結能夠循環依賴的條件:
    1、setter 注入(構造器注入不能循環依賴)
    2、singleton bean(非單例 bean 不支持)
    3、AbstractAutowireCapableBeanFactory#allowCircularReferences 這個 boolean 屬性控制是否可以循環,默認爲 true

題目二
  • 題幹:zookeeper和Spring-Eureka的區別
  • 分析:
  • 在以Dubbo進行構建的微服務中,可以使用zk作爲註冊中心,而在SpringCloud進行構建的微服務中,SpringEureka作爲註冊中心的角色,他們的區別最基本的來講,在基於CAP理論來講,zk以CP進行構建的,而Eureka是以AP進行構建的,而我們要知道在微服務中一般我們都需要高可用,而對於數據一致性只要保證最終一致性就可以了,而zk是因爲在進行選舉的時候有一段不可用的時間,所以一般建議使用Eureka作爲註冊中心。

  • 回答:
  • 參考上面分析部分 .

題目三
  • 題幹:BeanFactory-ApplicationContext的區別
  • 分析:
  • BeanFactory是spring中比較原始的Factory,本身是無法支持如:AOP功能 Web引用等,而ApplicationContext是由BeanFactory派生出來的,ApplicationContext通過擴展了MessageResource ApplicationEventPublisher ResourcePatternResolver 等接口提供了:1MessageSource, 提供國際化的消息訪問 2 資源訪問,如URL和文件 3 事件傳播 4 載入多個(有繼承關係)上下文 ,使得每一個上下文都專注於一個特定的層次,比如應用的web層,此外Bean Factory是使用懶加載形式來注入Bean,而這樣的行爲雖然節省了空間但是很難發現錯誤的Bean,如果存在錯誤的Bean只有在getBean使用的時候纔會發現Bean配置錯誤的情況,而Application Context則是在容器啓動的時候一次性創建了所有的Bean,這樣在容器啓動階段我們就可以發現Bean中存在的配置錯誤,最後二者都是可以支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但兩者之間的區別是:BeanFactory需要手動註冊,而ApplicationContext則是自動註冊。

  • 回答:
  • 1 ApplicationContext作爲BeanFatory的派生類提供了一些其他的功能aMessageSource, 提供國際化的消息訪問 b 資源訪問,如URL和文件 c 事件傳播 d 載入多個(有繼承關係)上下文 ,使得每一個上下文都專注於一個特定的層次,比如應用的web層
    2 Bean的注入形式不同Bean Factory使用懶加載的形式,而ApplicationContext容器啓動就加載所有Bean
    3 對於BeanPostProcessor、BeanFactoryPostProcessor的使用,但兩者之間的區別是:BeanFactory需要手動註冊,而ApplicationContext則是自動註冊。

題目四
  • 題幹:棧溢出原因剖析?
  • 分析:
  • JVM中StackOverFlowError的分析;首先Java中的棧是一塊線程私有的內存空間,線程執行的基本行爲是函數調用。每次函數調用的數據都是通過Java棧傳遞的,Java棧和數據結構的棧一樣都是後進先出的數據結構,只支持入棧和出棧的操作,Java棧中保存的內容主要是棧幀,每一次函數調用都會有一個對應的棧幀壓入棧,每一個函數調用結束,都會有一個棧幀被彈出棧。函數返回棧幀會被從棧中彈出,Java 有兩種函數返回的方式,一種是正常的函數返回,一種是異常拋出的返回,但是無論哪種最終都會導致棧幀被彈出。由於函數每次調用都會生成對應的棧幀,從而佔用一定的棧空間,因此如果棧空間不足,那麼函數調用自然無法進行下去,當請求的棧深度大於最大可用棧深度,系統會拋出StackOverFlowError這個棧溢出的錯誤。
    擴展:
    1-Xss 是Java中指定線程的最大內存空間的參數。這個參數也決定了函數調用的最大深度。
    2棧幀: 局部變量表、操作數棧、幀數據。
    局部變量表 - 保存函數的參數以及局部變量
    操作數棧 - 主要用於保存計算過程中的中間結果,同時作爲計算過程中變量臨時的存儲空間
    幀數據 - 除了局部變量表、和操作數棧外,Java棧幀還需要一些數據來支持常量池解析、正常方法返回和異常處理等。大部分Java字節碼指令都需要進行常量池訪問,在幀數據區中保存着訪問常量池的指針,方便程序訪問常量池。
    此外當函數返回或者調用異常的時候,虛擬機必須恢復調用者函數的棧幀,並讓調用者函數繼續執行下去,對於異常處理,虛擬機必須有一個異常處理表,方便在發生異常的時候找到處理異常的代碼,因此異常處理表也是幀數據的重要一部分。

  • 回答:
  • 在死循環和遞歸無法正確跳出的情況下會導致棧溢出錯誤,歸根結底是由於方法的不停調用導致棧上分配的棧幀過多超過了我們配置的Xss參數的大小。

題目五
  • 題幹:Apollo-SpringCloudConfig-Nacos區別?
  • 分析:
  • 三者都是配置管理中心,SpringCloudConfig是在SpringCloud全家桶裏的一份子,它主要是和git通過webhook來做到感知配置更新的,並通知服務端獲取,Apollo是攜程開源的配置管理框架,需要結合mysql數據庫,性能也比較優異,Nacos是阿里開源的一款配置管理軟件,它除了能夠作爲配置管理中心還可以作爲服務發現的註冊部分,本身相較於Eureka要由又是因爲zk作爲註冊中心只保證了cp而SpringCloudEureka保證了Ap這個是符合分佈式註冊中心的初衷的,而nacos 是基於CP+AP它根據服務註冊選擇臨時和永久來決定走AP模式還是CP模式.

  • 回答:
  • Apollo參考:文檔
    Nacos參考:文檔
    SpringCloudConfig參考:參考Spring官網

題目六
  • 題幹:Tomcat架構
  • 分析:
  • Tomcat中最頂層的容器是Server,代表着整個服務器,一個Server可以包含至少一個Service,用於具體提供服務。
    Service主要包含兩個部分:Connector和Container。他們的作用如下:
    1、Connector用於處理連接相關的事情,並提供Socket與Request和Response相關的轉化;
    2、Container用於封裝和管理Servlet,以及具體處理Request請求;
    一個Tomcat中只有一個Server,一個Server可以包含多個Service,一個Service只有一個Container,但是可以有多個Connectors,這是因爲一個服務可以有多個連接,如同時提供Http和Https鏈接,多個 Connector 和一個 Container 就形成了一個 Service,有了 Service 就可以對外提供服務了,但是 Service 還要一個生存的環境,那就是 Server了!所以整個 Tomcat 的生命週期由 Server 控制。上述的包含關係或者說是父子關係,都可以在tomcat的conf目錄下的server.xml配置文件中看出來.
    頂層結構:
    (1)Tomcat中只有一個Server,一個Server可以有多個Service,一個Service可以有多個Connector和一個Container;
    (2) Server掌管着整個Tomcat的生死大權;
    (4)Service 是對外提供服務的;
    (5)Connector用於接受請求並將請求封裝成Request和Response來具體處理;
    (6)Container用於封裝和管理Servlet,以及具體處理request請求;
    Connector和Container:
    一個請求發送到Tomcat之後,首先經過Service然後會交給我們的Connector,Connector用於接收請求並將接收的請求封裝爲Request和Response來具體處理,Request和Response封裝完之後再交由Container進行處理,Container處理完請求之後再返回給Connector,最後在由Connector通過Socket將處理的結果返回給客戶端,這樣整個請求的就處理完了!
    Connector最底層使用的是Socket來進行連接的,Request和Response是按照HTTP協議來封裝的,所以Connector同時需要實現TCP/IP協議和HTTP協議!
    Connector架構分析:
    Connector用於接受請求並將請求封裝成Request和Response,然後交給Container進行處理,Container處理完之後在交給Connector返回給客戶端。
    Container架構分析:
    4個子容器的作用分別是:
    (1)Engine:引擎,用來管理多個站點,一個Service最多隻能有一個Engine; (2)Host:代表一個站點,也可以叫虛擬主機,通過配置Host就可以添加站點; (3)Context:代表一個應用程序,對應着平時開發的一套程序,或者一個WEB-INF目錄以及下面的web.xml文件; (4)Wrapper:每一Wrapper封裝着一個Servlet;

  • 回答:
  • Tomcat中最頂層的容器是Server,代表着整個服務器,一個Server可以包含至少一個Service,用於具體提供服務。
    Service主要包含兩個部分:Connector和Container。他們的作用如下:
    1、Connector用於處理連接相關的事情,並提供Socket與Request和Response相關的轉化;
    2、Container用於封裝和管理Servlet,以及具體處理Request請求;
    一個Tomcat中只有一個Server,一個Server可以包含多個Service,一個Service只有一個Container,但是可以有多個Connectors,這是因爲一個服務可以有多個連接,如同時提供Http和Https鏈接,多個 Connector 和一個 Container 就形成了一個 Service,有了 Service 就可以對外提供服務了,但是 Service 還要一個生存的環境,那就是 Server了!所以整個 Tomcat 的生命週期由 Server 控制。上述的包含關係或者說是父子關係,都可以在tomcat的conf目錄下的server.xml配置文件中看出來.

題目七
  • 題幹:數據庫分庫分表
  • 分析:
  • 首先對於這個問題我們要知道爲什麼要分庫分表,原因如下:當系統併發量很高的時候會訪問個的表會很多,而對於MySQL數據庫由於數據是存儲在操作系統的文件中被稱之爲表空間的區域,所以數據訪問的時候需要進行IO將磁盤中的數據放到Buffer Pool中進行緩存,而Buffer Pool是有大小限制的,而使用Buffer Pool的目的就是來減少IO訪問次數的,當訪問的表過多就會多次讀取磁盤IO,所以就會產生IO瓶頸,而由於業務的增長也會導致單表數據過大,很可能由於某個SQL比較複雜會導致CPU需要計算很多複雜的數據,這樣CPU佔用時間很長隨着併發的升高,和單表的數據量大,會導致CPU的瓶頸.故此我們需要進行分庫分表.
    分庫分表:主要分爲水平分庫/分表 垂直分庫/分表.
    水平分庫:
    應用場景:當併發量很高的時候,而分表已經提升不了性能,無法解決問題,而且也沒有明顯的業務劃分,無法垂直分庫,所以此時需要進行水平分庫.
    操作:以字段爲依據,按照一定策略(hash等),將一個庫中的數據拆分到多個庫中
    分庫後:
    每個庫的結構都一樣;
    每個庫的數據都不一樣,沒有交集;
    所有庫的並集是全量數據;
    分析:通過增加數據庫(就像加服務器一個道理),io和cpu的壓力自然可以成倍緩解。
    水平分表:
    應用場景:水平分表的誘因不是高併發量,而是由於單表數據量過大,這樣,這樣SQL的效率就不高,從而加重了CPU的負擔主要是來解決CPU瓶頸的…
    操作:以字段爲依據,按照一定策略(hash等),將一個表中的數據拆分到多個表中。
    分表後:
    每個表的結構都一樣;
    每個表的數據都不一樣,沒有交集;
    所有表的並集是全量數據;
    分析:表多了,單表數據量小了,單次SQL效率就高了,CPU負擔就降下來了。
    垂直分庫:
    應用場景:系統併發量上來了,業務劃分清晰,我們可以根據業務劃分,比如訂單庫,用戶庫等…
    操作:以字段爲依據,按照一定策略(hash等),將一個表中的數據拆分到多個表中。
    分庫後:
    每個庫的結構都不一樣;
    每個庫的數據也不一樣,沒有交集;
    所有庫的並集是全量數據;
    分析:進行微服務,根據服務拆分,一個服務一個庫。
    垂直分表:
    應用場景:系統絕對併發量並沒有上來,表的記錄並不多,但是字段多,並且熱點數據和非熱點數據在一起,單行數據所需的存儲空間較大。以至於數據庫緩存的數據行減少,查詢時會去讀磁盤數據產生大量的隨機讀IO,產生IO瓶頸。
    操作:以字段爲依據,按照字段的活躍性,將表中字段拆到不同的表(主表和擴展表)中。
    分庫後:
    每個表的結構都不一樣;
    每個表的數據也不一樣,一般來說,每個表的字段至少有一列交集,一般是主鍵,用於關聯數據;
    所有表的並集是全量數據;
    分析:可以用列表頁和詳情頁來幫助理解。垂直分表的拆分原則是將熱點數據(可能會冗餘經常一起查詢的數據)放在一起作爲主表,非熱點數據放在一起作爲擴展表。這樣更多的熱點數據就能被緩存下來,進而減少了隨機讀IO。拆了之後,要想獲得全部數據就需要關聯兩個表來取數據。[別join可以先查出來在處理]
    總結一下:無論是水分還是垂直分表基本上不是由於高併發的問題導致的,而無論是水平還是垂直分庫都是當併發量很高的情況下.
    分庫分表步驟:
    我們可以計算一下:比如拿學習單詞的場景來講,總共英文單詞大概是兩萬個,當用戶學習的時候需要記住用戶的錯詞,假設用戶全部學完20000萬個單詞,那麼有1000個用戶就是2千萬數據,拿對於單表來講數據量其實蠻大的,如果按照單表不超過500萬的標準我們可以進行水平分表,用userid hash後/4然後分爲4個表單表500萬,如果用戶量繼續增大我們也可以考慮分個100表.
    對於非partition key的查詢問題我們使用映射表來解決.

  • 回答:
  • 回答參考分析部分.

題目八
  • 題幹:MySQL 分表分庫全局 ID
  • 分析:
  • 全局ID,我們考慮使用Snowflake,這個我在項目中有用到,底層就是使用zk做的分佈式鎖防止同一數據中心內多個實例共用同一個 worker id,每個服務指定一個datacenterId數據中心ID(0~31).

  • 回答:
  • 來自Flicker的數據庫自增ID解決方案由於(auto_increment + replace into + MyISAM),需要使用MyIsAm引擎而我們大多數的服務都是使用的InnoDB引擎,所以可以使用Snowflake.

題目九
  • 題幹:併發編程三要素?
  • 分析:
  • 原子性 可見性 有序性
    原子性,就是不可分割的操作,我們可以使用synchronized 和cas保證原子性.
    可見性:就是之前的修改要對之後的修改可見,可是使用synchronized和volatile關鍵字保證可見性.
    有序性:程序執行的順序按照代碼的先後順序執行有時候會進行重排序.

  • 回答:
  • 分析

題目十
  • 題幹:介紹一下 Spring 的事務實現方式及瞭解?

  • 分析:

  • 編程式事務管理使用TransactionTemplate或者直接使用底層的PlatformTransactionManager。對於編程式事務管理,spring推薦使用TransactionTemplate。
    聲明式事務管理建立在AOP之上的。其本質是對方法前後進行攔截,然後在目標方法開始之前創建或者加入一個事務,在執行完目標方法之後根據執行情況提交或者回滾事務。聲明式事務最大的優點就是不需要通過編程的方式管理事務,這樣就不需要在業務邏輯代碼中摻雜事務管理的代碼,只需在配置文件中做相關的事務規則聲明(或通過基於@Transactional註解的方式),便可以將事務規則應用到業務邏輯中。
    顯然聲明式事務管理要優於編程式事務管理,這正是spring倡導的非侵入式的開發方式。聲明式事務管理使業務代碼不受污染,一個普通的POJO對象,只要加上註解就可以獲得完全的事務支持。和編程式事務相比,聲明式事務唯一不足地方是,後者的最細粒度只能作用到方法級別,無法做到像編程式事務那樣可以作用到代碼塊級別。但是即便有這樣的需求,也存在很多變通的方法,比如,可以將需要進行事務管理的代碼塊獨立爲方法等等。
    聲明式事務管理也有兩種常用的方式,一種是基於tx和aop名字空間的xml配置文件,另一種就是基於@Transactional註解。顯然基於註解的方式更簡單易用

  • 回答:

  • 根據分析大致說一下,再就是談談ACID,再就是隔離級別了可以根據隔離級別問問MySQL的RR RC。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章