面試哈啰,差點要了狗命~

磊哥課程 2024-06-27 10:29:32

這幾天面試哈啰,本來以爲小小哈啰可以輕松拿捏,但沒成想,問的還挺深,差點要了狗命,一起來看看吧~

1. 創建一個線程時底層發生了什麽?

在 Java 中,創建一個線程時,底層發生了以下幾個主要步驟:

分配線程棧:線程對象被創建後,Java 虛擬機會爲該線程分配一個獨立的線程棧(Thread Stack),用于存儲該線程的方法調用、局部變量等信息。初始化線程屬性:設置線程的屬性,例如優先級、守護線程標志等。調用線程的 start() 方法:當調用線程對象的 start() 方法時,會觸發 Java 虛擬機調用 run() 方法,並啓動線程的執行。啓動線程執行:Java 虛擬機會在後台創建並啓動一個操作系統線程來執行 Java 線程,每個 Java 線程對應一個底層的操作系統線程。線程的調度和執行:操作系統負責線程的調度,按照線程優先級、時間片輪轉等算法來確定線程的執行順序,線程開始執行其業務方法。線程執行結束:當線程的業務方法執行完畢或線程抛出異常導致執行終止時,線程進入終止狀態。2. 說說線程的生命周期?

線程生命周期總共有以下 6 種:

NEW(新建狀態):new Thread() 時線程的狀態。RUNNABLE(可運行/運行狀態):調用 start() 方法後的狀態。BLOCKED(阻塞狀態):調用了 synchronized 加鎖之後的狀態。獲得鎖之後就從 BLOCKED 狀態變成了 RUNNABLE 狀態。WAITING(無時限等待狀態):調用了 wait() 方法之後會進入此狀態。TIMED_WAITING(有時限等待狀態):調用了 sleep(long millis) 方法之後會進入此狀態。TERMINATED(終止狀態):線程任務執行完成之後就變成此狀態。

線程狀態的轉換如下圖所示:

3. 使用線程池時核心線程數和最大線程數如何設計?

核心線程數設計分爲以下兩種情況:

計算型任務:根據實際業務場景設置,參考值爲:CPU 核數+1。IO 型任務:根據實際業務場景設置,參考值爲:2*CPU 核數+1。

PS:爲什麽要 CPU 核數+1,而不是 CPU 核數?多出來的一個線程可用于更加平滑的線程替補,例如有現成執行完,或休眠、中斷等,多出來的一個線程就可以盡快的進行補充,避免了 CPU 空閑。

如果,任務量波動不大,可以將最大線程數設置和核心線程數一樣的數量,避免臨時線程創建和銷毀的性能開銷,如果波動比較大,可根據實際業務場景設置,參考值爲 2 倍的核心線程數。

4. 說說你都用過哪些微服務組件?

微服務常見組件如下:

Nacos:注冊中心和配置中心。注冊中心:負責維護微服務實例的注冊信息,包括服務地址和服務狀態。服務啓動時向注冊中心注冊自身,服務消費者通過注冊中心發現服務提供者的地址,實現服務間的動態發現與路由。配置中心:集中管理微服務應用的配置信息,支持動態配置更新,使得微服務可以在運行時獲取或更新配置,便于配置的統一管理和版本控制。Spring Cloud OpenFeign:RESTful(基于標准的 HTTP 協議和 URI(Uniform Resource Identifier)的一組約束和原則)通訊,負責微服務之間的調用。Spring Cloud LoadBalancer:負載均衡器(客戶端)負責微服務調用時節點的選取。Spring Cloud Gateway:網關,微服務架構的入口,提供統一的接口訪問入口,處理所有進來的請求,包括路由轉發、協議轉換、安全認證(如OAuth)、速率限制、負載均衡等,對外屏蔽微服務細節。Sentinel:限流、熔斷降級,防止服務雪崩效應,增強系統的穩定性和韌性。Seata:分布式事務,保證微服務間一組執行方法的原子性(要麽一起執行成功,要麽一起執行失敗)。Skywalking:分布式鏈路追蹤,記錄微服務調用日志,監控系統和方便排查問題。5. 講一下Dubbo運行原理?

Dubbo 是一款高性能、輕量級的開源 RPC(遠程過程調用)框架,主要用于構建分布式服務和微服務架構。

要說 Dubbo 運行流程就不得不先來了解一下 Dubbo 的核心組件了,因爲 Dubbo 的交互流程是和核心組件息息相關的。

Dubbo 核心組件有以下幾個:

服務提供者(Provider):暴露服務的應用,通過 Dubbo 框架將自身的服務接口及實現注冊到注冊中心。服務消費者(Consumer):調用遠程服務的應用,從注冊中心訂閱所需的服務,然後通過遠程調用消費服務。注冊中心(Registry):集中管理服務的地址信息,服務提供者和服務消費者均在此注冊或訂閱服務信息。常見的注冊中心有 ZooKeeper、Nacos 等。

Dubbo 運行流程如下圖所示:

它的執行流程如下:

服務提供者會將實例(URL 地址)注冊到注冊中心,注冊中心負責對數據進行聚合(健康檢測)。消費者從注冊中心讀取地址列表並訂閱變更,每當地址列表發生變化,注冊中心將最新的列表通知到所有訂閱的消費者實例。消費者得到服務實例之後,通過 Dubbo 內置的負載均衡策略,選擇其中的一個節點,之後使用 RPC 的方式與服務提供者建立連接,並進行通訊和服務調用。

更詳細的調用流程如下:

a. Dubbo通訊協議

Dubbo 框架提供了自定義的高性能 RPC 通信協議:基于 HTTP/2 的 Triple 協議和基于 TCP 的 Dubbo2 協議。除此之外,Dubbo 框架支持任意第三方通信協議,如官方支持的 gRPC、Thrift、REST、JsonRPC、Hessian2 等,更多協議可以通過自定義擴展實現。這對于微服務實踐中經常要處理的多協議通信場景非常有用。

Dubbo 框架不綁定任何通信協議,在實現上 Dubbo 對多協議的支持也非常靈活,它可以讓你在一個應用內發布多個使用不同協議的服務,並且支持用同一個 port 端口對外發布所有協議。

通過 Dubbo 框架的多協議支持,你可以做到:

將任意通信協議無縫地接入 Dubbo 服務治理體系。Dubbo 體系下的所有通信協議,都可以享受到 Dubbo 的編程模型、服務發現、流量管控等優勢。比如 gRPC over Dubbo 的模式,服務治理、編程 API 都能夠零成本接入 Dubbo 體系。兼容不同技術棧,業務系統混合使用不同的服務框架、RPC 框架。比如有些服務使用 gRPC 或者 Spring Cloud 開發,有些服務使用 Dubbo 框架開發,通過 Dubbo 的多協議支持可以很好的實現互通。讓協議遷移變的更簡單。通過多協議、注冊中心的協調,可以快速滿足公司內協議遷移的需求。比如如從自研協議升級到 Dubbo 協議,Dubbo 協議自身升級,從 Dubbo 協議遷移到 gRPC,從 HTTP 遷移到 Dubbo 協議等。b. Dubbo負載均衡策略

目前 Dubbo(3.X)內置了如下負載均衡算法如下:

Weighted Random LoadBalance(加權隨機):默認負載均衡算法,默認權重相同。按權重設置隨機概率。缺點:存在慢的提供者累積請求的問題,比如:第二台機器很慢,但沒挂,當請求調到第二台時就卡在那,久而久之,所有請求都卡在調到第二台上。RoundRobin LoadBalance(加權輪詢):借鑒于 Nginx 的平滑加權輪詢算法,默認權重相同,按公約後的權重設置輪詢比率,循環調用節點。缺點:同樣存在慢的提供者累積請求的問題。LeastActive LoadBalance(最少活躍優先+加權隨機):背後是能者多勞的思想,活躍數越低,越優先調用,相同活躍數的進行加權隨機。活躍數指調用前後計數差(針對特定提供者:請求發送數 - 響應返回數),表示特定提供者的任務堆積量,活躍數越低,代表該提供者處理能力越強。使慢的提供者收到更少請求,因爲越慢的提供者的調用前後計數差會越大;相對的,處理能力越強的節點,處理更多的請求。Shortest-Response LoadBalance(最短響應優先+加權隨機):更加關注響應速度,在最近一個滑動窗口中,響應時間越短,越優先調用。相同響應時間的進行加權隨機。使得響應時間越快的提供者,處理更多的請求。缺點:可能會造成流量過于集中于高性能節點的問題。ConsistentHash LoadBalance(一致性哈希):確定的入參,確定的提供者,適用于有狀態請求。當某一台提供者挂時,原本發往該提供者的請求,基于虛擬節點,平攤到其它提供者,不會引起劇烈變動。P2C LoadBalance(隨機選擇兩個節點+連接數較小):隨機選擇兩個節點後,繼續選擇“連接數”較小的那個節點。對于每次調用,從可用的 provider 列表中做兩次隨機選擇,選出兩個節點 providerA 和 providerB,比較 providerA 和 providerB 兩個節點,選擇其“當前正在處理的連接數”較小的那個節點。Adaptive LoadBalance(自適應負載均衡):在 P2C 算法基礎上,選擇二者中 load 最小的那個節點,是一種能根據後端實例負載自動調整流量分布的算法實現,它總是嘗試將請求轉發到負載最小的節點。6. RPC和HTTP有什麽區別?

RPC(Remote Procedure Call,遠程過程調用)和 HTTP(Hypertext Transfer Protocol,超文本傳輸協議)都是用于服務間通訊的,它們主要區別如下:

概念和使用場景不同:RPC:RPC 是一種通信模式,允許一個程序在另一個地址空間上執行遠程計算過程,使得客戶端調用遠程服務就像調用本地方法一樣。HTTP:HTTP 是一個應用層協議,用于在客戶端和服務器之間傳輸文本、圖像、視頻等超媒體資源,通常用于 Web 應用之間的通信。傳輸數據不同:RPC:RPC 通常基于二進制數據傳輸,可以使用更高效的序列化方式(如 Protobuf、Thrift)進行數據交換。HTTP:HTTP 使用文本協議,請求和響應數據通常是基于文本格式(如 JSON、XML)進行傳輸。傳輸效率與性能不同:RPC:因爲 RPC 通常使用更高效的二進制序列化(如 Protobuf、Thrift),減少了數據傳輸的體積,且由于其針對性的設計,往往在性能上更爲優越,特別是在大量小數據包的傳輸場景。HTTP:傳統上使用文本格式如 JSON 進行數據交換,這可能導致更大的數據包和更多的序列化/反序列化開銷,但在 HTTP/2 中引入了頭部壓縮和多路複用,提升了效率。7. 什麽是序列化?

序列化是將對象轉換爲字節流的過程,可以用于數據持久化、數據傳輸等場景。序列化的主要目的是將對象在內存中的狀態轉換爲可存儲或傳輸的形式。

8. 讓你設計一個RPC框架,如何考慮數據序列化問題?

數據序列化需要考慮的以下問題:

性能問題:選擇高性能的序列化庫至關重要。二進制序列化(如 Protocol Buffers, Apache Thrift, FlatBuffers)通常比文本格式(如 JSON、XML)更高效,因爲它們占用的空間小,序列化和反序列化的速度更快。對于高性能要求的場景,應優先考慮這些二進制格式。安全性:在序列化過程中,應考慮數據的安全性,避免敏感信息的泄露。可以采用加密序列化內容、過濾敏感字段或使用安全的傳輸層協議(如 TLS/SSL)來增加安全性。兼容性:良好的版本兼容性是長期維護 RPC 框架的關鍵。設計時要考慮向前和向後兼容,即新老版本的序列化庫應能互相理解和處理對方生成的數據格式。可以采用預留字段、版本標識符等機制來支持這一點。跨語言支持:RPC 框架往往需要支持多種編程語言,因此選擇一種跨語言的序列化方案是必要的。Protocol Buffers、Apache Thrift、Avro 等都是很好的選擇,它們提供了多種語言的編解碼庫。可擴展性:設計時應考慮到未來可能增加的數據結構和字段,序列化方案應易于擴展,支持動態字段、自定義類型等特性。可配置性:允許用戶根據實際需求選擇或切換序列化策略。例如,對于對性能要求極高的場景,用戶可以選擇最高效的序列化方式;而對于調試或日志記錄,可能會偏好人類可讀性更好的格式。異常處理:在序列化或反序列化過程中可能會遇到錯誤(如數據損壞、不兼容的版本等)。框架應能優雅地處理這些異常,並提供清晰的錯誤信息,幫助開發者診斷問題。9. 說說索引的底層實現?

MySQL 默認的數據庫引擎 InnoDB 主要使用的是 B+ 樹實現的,它的特點是:

非葉子節點不存儲數據:僅存儲鍵值和指向子節點的指針。葉子節點存儲數據:所有實際的數據記錄或者指向記錄的指針都存放在葉子節點中,並且葉子節點通過指針相連,形成了一個有序鏈表,便于範圍查詢。高度平衡:通過分裂和合並保持樹的高度平衡,從而保證查詢效率穩定。高效率的磁盤I/O:由于樹的高度較低,即使在磁盤 I/O 操作中也能保持較高的查詢效率。10. 爲什麽用B+樹?

索引使用 B+ 樹的主要原因包括以下幾點:

高效的查找和範圍查詢:B+ 樹是一種多路平衡查找樹,具有良好的有序性和平衡性,可以快速定位目標數據並支持高效的範圍查詢。B+ 樹通過多級索引結構,能夠在保持有序性的同時,減少樹的深度,降低查找的時間複雜度,提高了查詢效率。高效的插入和刪除操作:B+ 樹的平衡性保證了樹的高度不會過深,插入和刪除操作的代價是比較穩定的,不會由于樹的不平衡而導致性能下降。通過調整分裂和合並操作,B+ 樹可以保持平衡並具有高效的插入和刪除性能。11. 一個表有索引說說它的查詢過程?

查詢過程大致步驟如下:

查詢分析與優化:解析查詢語句:首先,數據庫管理系統會對 SQL 查詢語句進行語法分析和語義分析,理解查詢的目的。查詢優化器:查詢優化器會評估多種執行計劃,決定最佳的查詢方法。如果表上有相關索引,優化器會考慮使用索引來加速查詢。索引查找:數據庫會根據查詢條件從根節點開始,沿著 B+ 樹的分支節點逐層定位到葉子節點,找到滿足查詢條件的索引記錄。回表(如果需要):索引通常只包含索引列和指向表中實際數據行的指針(或 ROWID)。如果查詢需要的列不在索引中(即覆蓋索引未被滿足),數據庫需要根據索引中的 ROWID 或指針回到原表中獲取其他列的數據,這個過程稱爲“回表”查詢。數據返回:篩選與排序:對于符合條件的行,數據庫引擎可能還需要進行進一步的篩選(比如 WHERE 子句中的其他條件),以及按照 ORDER BY、GROUP BY 等進行排序操作。結果集生成:最終,數據庫將處理後的數據組織成查詢結果集返回給用戶。12. 如果要操作1千萬條數據要注意什麽問題?

操作 1 千萬條數據時,需要性能問題和系統穩定性和安全問題,主要體現在以下幾點:

性能優化:索引優化:確保對經常查詢的列建立合適的索引,以加速查詢速度。但同時要避免過度索引,因爲索引也會占用存儲空間並影響寫入性能。分批處理:避免一次性加載或操作所有數據,可以將數據分成小批次進行處理,減少內存消耗和避免阻塞系統。避免全表掃描:盡量避免使用會導致全表掃描的查詢,如使用SELECT *或在沒有索引的列上進行查詢。資源管理:內存管理:監控和控制程序的內存使用,避免內存溢出。在處理大量數據時,合理分配內存,特別是進行排序、分組等操作時。磁盤I/O優化:考慮到數據庫操作可能引起的大量磁盤讀寫,優化磁盤I/O性能,比如使用SSD存儲,調整文件系統緩存設置等。數據庫設計:分區表:根據業務需求對大表進行水平或垂直分區,將數據分布在不同的物理位置,提升查詢效率。歸檔舊數據:定期歸檔或刪除不再需要的曆史數據,保持活躍數據集在一個可控的範圍內。查詢優化:避免複雜的JOIN操作:盡量減少或避免複雜的表連接,特別是對于千萬級別的數據表,考慮是否可以通過預計算或使用彙總表來簡化查詢。使用覆蓋索引:確保查詢只需要索引中的列,這樣數據庫可以直接從索引中返回數據而無需回表,提高查詢速度。事務管理:合理使用事務:對于大量數據的插入、更新操作,適當使用事務以確保數據一致性,但要避免過大的事務,以免長時間鎖定資源。批量提交:在插入大量數據時,使用批量插入而不是單條插入,並在適當的時候提交事務,減少提交次數。備份與恢複:在進行大規模數據操作之前,確保有完整的數據備份,以防操作失誤導致數據丟失。課後思考

最後一個問題是“如何進行JVM調優?”,各位大佬覺得應該如何回答這個問題?

本文已收錄到我的面試小站 [www.javacn.site](https://www.javacn.site),其中包含的內容有:Redis、JVM、並發、並發、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、設計模式、消息隊列等模塊。

0 阅读:1

磊哥課程

簡介:感謝大家的關注