每次面完騰訊,都是一把汗......
大家好,我是小林。
今天給大家分享一位 Java 后端同學的騰訊面經,問的問題還是比較多的,接近 30 個問題,再加上寫算法,一場面試下來,時長有 1 小時+。
面試的強度還是很大,很多同學跟我反饋,每次面完騰訊都一把汗,甚至有同學被騰訊面了 2 小時+。
考察的知識還是比較多的,我這里簡單給在大家列了一下:
- 操作系統:進程&線程、進程隔離性
- 數據結構:排序算法、排序穩定性、歸并排序、快速排序
- MySQL:存儲引擎、聚簇索引、B+樹、索引失效、事務隔離級別、臟讀、幻讀
- Redis:數據類型、String 底層實現、熱 key
- Java:ArrayList、Vector、HashMap
- MQ:消息隊列選型、消息可靠性、消息確認機制、Kafka 、RocketMQ
總體考察的范圍,就是編程語言+計算機基礎+后端組件。
操作系統
進程和線程的區別
圖片
- 本質區別:進程是操作系統資源分配的基本單位,而線程是任務調度和執行的基本單位
- 在開銷方面:每個進程都有獨立的代碼和數據空間(程序上下文),程序之間的切換會有較大的開銷;線程可以看做輕量級的進程,同一類線程共享代碼和數據空間,每個線程都有自己獨立的運行棧和程序計數器(PC),線程之間切換的開銷小
- 穩定性方面:進程中某個線程如果崩潰了,可能會導致整個進程都崩潰。而進程中的子進程崩潰,并不會影響其他進程。
- 內存分配方面:系統在運行的時候會為每個進程分配不同的內存空間;而對線程而言,除了CPU外,系統不會為線程分配內存(線程所使用的資源來自其所屬進程的資源),線程組之間只能共享資源
- 包含關系:沒有線程的進程可以看做是單線程的,如果一個進程內有多個線程,則執行過程不是一條線的,而是多條線(線程)共同完成的;線程是進程的一部分,所以線程也被稱為輕權進程或者輕量級進程
為什么進程崩潰不會對其他進程產生很大影響
主要是因為:
- 進程隔離性:每個進程都有自己獨立的內存空間,當一個進程崩潰時,其內存空間會被操作系統回收,不會影響其他進程的內存空間。這種進程間的隔離性保證了一個進程崩潰不會直接影響其他進程的執行。
- 進程獨立性:每個進程都是獨立運行的,它們之間不會共享資源,如文件、網絡連接等。因此,一個進程的崩潰通常不會對其他進程的資源產生影響。
數據結構
知道哪些排序算法,時間復雜度
圖片
- 冒泡排序:通過相鄰元素的比較和交換,每次將最大(或最小)的元素逐步“冒泡”到最后(或最前)。時間復雜度:最好情況下O(n),最壞情況下O(n^2),平均情況下O(n^2)。,空間復雜度:O(1)。
- 插入排序:將待排序元素逐個插入到已排序序列的合適位置,形成有序序列。時間復雜度:最好情況下O(n),最壞情況下O(n^2),平均情況下O(n^2),空間復雜度:O(1)。
- 選擇排序:通過不斷選擇未排序部分的最小(或最大)元素,并將其放置在已排序部分的末尾(或開頭)。時間復雜度:最好情況下O(n^2),最壞情況下O(n^2),平均情況下O(n^2)。空間復雜度:O(1)。
- 快速排序:通過選擇一個基準元素,將數組劃分為兩個子數組,使得左子數組的元素都小于(或等于)基準元素,右子數組的元素都大于(或等于)基準元素,然后對子數組進行遞歸排序。時間復雜度:最好情況下O(nlogn),最壞情況下O(n^2),平均情況下O(nlogn)。空間復雜度:最好情況下O(logn),最壞情況下O(n)。
- 歸并排序:將數組不斷分割為更小的子數組,然后將子數組進行合并,合并過程中進行排序。時間復雜度:最好情況下O(nlogn),最壞情況下O(nlogn),平均情況下O(nlogn)。空間復雜度:O(n)。
- 堆排序:通過將待排序元素構建成一個最大堆(或最小堆),然后將堆頂元素與末尾元素交換,再重新調整堆,重復該過程直到排序完成。時間復雜度:最好情況下O(nlogn),最壞情況下O(nlogn),平均情況下O(nlogn)。空間復雜度:O(1)。
歸并排序和快速排序的使用場景
- 歸并排序是穩定排序算法,適合排序穩定的場景;
- 快速排序是不穩定排序算法,不適合排序穩定的場景,快速排序是目前基于比較的內部排序中被認為是最好的方法,當待排序的關鍵字是隨機分布時,快速排序的平均時間最短;
排序穩定是什么意思?
圖片
排序穩定指的是在排序過程中,對于具有相同排序關鍵字的元素,在排序后它們的相對位置保持不變。
換句話說,如果在排序前兩個元素 A 和 B 的值相等,并且 A 在 B 的前面,那么在排序后 A 仍然在 B 的前面,這樣的排序就是穩定排序。穩定排序保持了相同元素之間的順序關系,適用于需要保持原始順序的場景。
穩定和不穩定排序算法有什么特點?
穩定排序算法的特點:
- 相同元素的相對位置不會改變,排序后仍然保持原始順序。
- 適用于需要保持元素間相對順序關系的場景,如按照年齡排序后按姓名排序。
不穩定排序算法的特點:
- 相同元素的相對位置可能會改變,排序后不保證原始順序。
- 可能會更快,但不適用于需要保持元素間相對順序關系的場景。
MySQL
MySQL 的存儲引擎有哪些?為什么常用InnoDB?
MySQL 的存儲引擎常用的主要有 3 個:
- InnoDB存儲引擎:支持事務處理,支持外鍵,支持崩潰修復能力和并發控制。如果需要對事務的完整性要求比較高(比如銀行),要求實現并發控制(比如售票),那選擇InnoDB有很大的優勢。如果需要頻繁的更新、刪除操作的數據庫,也可以選擇InnoDB,因為支持事務的提交(commit)和回滾(rollback)。
- MyISAM存儲引擎:插入數據快,空間和內存使用比較低。如果表主要是用于插入新記錄和讀出記錄,那么選擇MyISAM能實現處理高效率。如果應用的完整性、并發性要求比 較低,也可以使用。如果數據表主要用來插入和查詢記錄,則MyISAM引擎能提供較高的處理效率
- MEMORY存儲引擎:所有的數據都在內存中,數據的處理速度快,但是安全性不高。如果需要很快的讀寫速度,對數據的安全性要求較低,可以選擇MEMOEY。它對表的大小有要求,不能建立太大的表。所以,這類數據庫只使用在相對較小的數據庫表。如果只是臨時存放數據,數據量不大,并且不需要較高的數據安全性,可以選擇將數據保存在內存中的Memory引擎,MySQL中使用該引擎作為臨時表,存放查詢的中間結果
常用InnoDB的原因是支持事務,且最小鎖的粒度是行級鎖。
B+ 樹和 B 樹的比較
B 樹和 B+ 都是通過多叉樹的方式,會將樹的高度變矮,所以這兩個數據結構非常適合檢索存于磁盤中的數據。
但是 MySQL 默認的存儲引擎 InnoDB 采用的是 B+ 作為索引的數據結構,原因有:
- B+ 樹的非葉子節點不存放實際的記錄數據,僅存放索引,因此數據量相同的情況下,相比存儲即存索引又存記錄的 B 樹,B+樹的非葉子節點可以存放更多的索引,因此 B+ 樹可以比 B 樹更「矮胖」,查詢底層節點的磁盤 I/O次數會更少。
- B+ 樹有大量的冗余節點(所有非葉子節點都是冗余索引),這些冗余索引讓 B+ 樹在插入、刪除的效率都更高,比如刪除根節點的時候,不會像 B 樹那樣會發生復雜的樹的變化;
- B+ 樹葉子節點之間用鏈表連接了起來,有利于范圍查詢,而 B 樹要實現范圍查詢,因此只能通過樹的遍歷來完成范圍查詢,這會涉及多個節點的磁盤 I/O 操作,范圍查詢效率不如 B+ 樹。
除了聚簇索引,還有什么索引?
還有二級索引、聯合索引、前綴索引、唯一索引等。
二級索引存放的有哪些數據?
索引又可以分成聚簇索引和非聚簇索引(二級索引),它們區別就在于葉子節點存放的是什么數據:
- 聚簇索引的葉子節點存放的是實際數據,所有完整的用戶記錄都存放在聚簇索引的葉子節點;
- 二級索引的葉子節點存放的是主鍵值,而不是實際數據。
索引失效的情況
索引失效的情況:
- 當我們使用左或者左右模糊匹配的時候,也就是 like %xx 或者 like %xx%這兩種方式都會造成索引失效;
- 當我們在查詢條件中對索引列使用函數,就會導致索引失效。
- 當我們在查詢條件中對索引列進行表達式計算,也是無法走索引的。
- MySQL 在遇到字符串和數字比較的時候,會自動把字符串轉為數字,然后再進行比較。如果字符串是索引列,而條件語句中的輸入參數是數字的話,那么索引列會發生隱式類型轉換,由于隱式類型轉換是通過 CAST 函數實現的,等同于對索引列使用了函數,所以就會導致索引失效。
- 聯合索引要能正確使用需要遵循最左匹配原則,也就是按照最左優先的方式進行索引的匹配,否則就會導致索引失效。
事務隔離級別有哪些?
四個隔離級別如下:
- 讀未提交,指一個事務還沒提交時,它做的變更就能被其他事務看到;
- 讀提交,指一個事務提交之后,它做的變更才能被其他事務看到;
- 可重復讀,指一個事務執行過程中看到的數據,一直跟這個事務啟動時看到的數據是一致的,MySQL InnoDB 引擎的默認隔離級別;
- 串行化;會對記錄加上讀寫鎖,在多個事務對這條記錄進行讀寫操作時,如果發生了讀寫沖突的時候,后訪問的事務必須等前一個事務執行完成,才能繼續執行;
按隔離水平高低排序如下:
圖片
什么情況下會出現幻讀?
在一個事務內多次查詢某個符合查詢條件的「記錄數量」,如果出現前后兩次查詢到的記錄數量不一樣的情況,就意味著發生了「幻讀」現象。
舉個栗子。
假設有 A 和 B 這兩個事務同時在處理,事務 A 先開始從數據庫查詢賬戶余額大于 100 萬的記錄,發現共有 5 條,然后事務 B 也按相同的搜索條件也是查詢出了 5 條記錄。
圖片
接下來,事務 A 插入了一條余額超過 100 萬的賬號,并提交了事務,此時數據庫超過 100 萬余額的賬號個數就變為 6。
然后事務 B 再次查詢賬戶余額大于 100 萬的記錄,此時查詢到的記錄數量有 6 條,發現和前一次讀到的記錄數量不一樣了,就感覺發生了幻覺一樣,這種現象就被稱為幻讀。
事務的 MVCC 是怎么實現的?
對于「讀提交」和「可重復讀」隔離級別的事務來說,它們是通過 Read View 來實現的,它們的區別在于創建 Read View 的時機不同:
- 「讀提交」隔離級別是在每個 select 都會生成一個新的 Read View,也意味著,事務期間的多次讀取同一條數據,前后兩次讀的數據可能會出現不一致,因為可能這期間另外一個事務修改了該記錄,并提交了事務。
- 「可重復讀」隔離級別是啟動事務時生成一個 Read View,然后整個事務期間都在用這個 Read View,這樣就保證了在事務期間讀到的數據都是事務啟動前的記錄。
這兩個隔離級別實現是通過「事務的 Read View 里的字段」和「記錄中的兩個隱藏列」的比對,來控制并發事務訪問同一個記錄時的行為,這就叫 MVCC(多版本并發控制)。
在創建 Read View 后,我們可以將記錄中的 trx_id 劃分這三種情況:
圖片
一個事務去訪問記錄的時候,除了自己的更新記錄總是可見之外,還有這幾種情況:
- 如果記錄的 trx_id 值小于 Read View 中的 min_trx_id 值,表示這個版本的記錄是在創建 Read View 前已經提交的事務生成的,所以該版本的記錄對當前事務可見。
- 如果記錄的 trx_id 值大于等于 Read View 中的 max_trx_id 值,表示這個版本的記錄是在創建 Read View 后才啟動的事務生成的,所以該版本的記錄對當前事務不可見。
- 如果記錄的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之間,需要判斷 trx_id 是否在 m_ids 列表中:
- 如果記錄的 trx_id 在 m_ids 列表中,表示生成該版本記錄的活躍事務依然活躍著(還沒提交事務),所以該版本的記錄對當前事務不可見。
- 如果記錄的 trx_id 不在 m_ids列表中,表示生成該版本記錄的活躍事務已經被提交,所以該版本的記錄對當前事務可見。
這種通過「版本鏈」來控制并發事務訪問同一個記錄時的行為就叫 MVCC(多版本并發控制)。
事務之間怎么避免臟讀的?
針對不同的隔離級別,并發事務時可能發生的現象也會不同。
圖片
也就是說:
- 在「讀未提交」隔離級別下,可能發生臟讀、不可重復讀和幻讀現象;
- 在「讀提交」隔離級別下,可能發生不可重復讀和幻讀現象,但是不可能發生臟讀現象;
- 在「可重復讀」隔離級別下,可能發生幻讀現象,但是不可能臟讀和不可重復讀現象;
- 在「串行化」隔離級別下,臟讀、不可重復讀和幻讀現象都不可能會發生。
所以,要解決臟讀現象,就要升級到「讀提交」以上的隔離級別,這樣事務只能讀到其他事務已經提交完成的數據,而不會讀到未提交事務的數據,就避免臟讀的問題。
Redis
Redis 支持哪幾種數據類型?
Redis 提供了豐富的數據類型,常見的有五種數據類型:String(字符串),Hash(哈希),List(列表),Set(集合)、Zset(有序集合)。
圖片
圖片
隨著 Redis 版本的更新,后面又支持了四種數據類型:BitMap(2.2 版新增)、HyperLogLog(2.8 版新增)、GEO(3.2 版新增)、Stream(5.0 版新增)。Redis 五種數據類型的應用場景:
- String 類型的應用場景:緩存對象、常規計數、分布式鎖、共享 session 信息等。
- List 類型的應用場景:消息隊列(但是有兩個問題:1. 生產者需要自行實現全局唯一 ID;2. 不能以消費組形式消費數據)等。
- Hash 類型:緩存對象、購物車等。
- Set 類型:聚合計算(并集、交集、差集)場景,比如點贊、共同關注、抽獎活動等。
- Zset 類型:排序場景,比如排行榜、電話和姓名排序等。
Redis 后續版本又支持四種數據類型,它們的應用場景如下:
- BitMap(2.2 版新增):二值狀態統計的場景,比如簽到、判斷用戶登陸狀態、連續簽到用戶總數等;
- HyperLogLog(2.8 版新增):海量數據基數統計的場景,比如百萬級網頁 UV 計數等;
- GEO(3.2 版新增):存儲地理位置信息的場景,比如滴滴叫車;
- Stream(5.0 版新增):消息隊列,相比于基于 List 類型實現的消息隊列,有這兩個特有的特性:自動生成全局唯一消息ID,支持以消費組形式消費數據。
熱 key 是什么?怎么解決?
Redis熱key是指被頻繁訪問的key,可能會導致單個key的訪問量過大,影響系統性能。解決方法包括:
- 開啟內存淘汰機制,并選擇使用LRU算法來淘汰不常用的key,保證內存中存儲的是最熱門的數據。
- 設置key的過期時間,確保key在一段時間后自動刪除,防止長時間占用內存。
- 對熱點key進行分片,將數據分散存儲在不同的節點上,減輕單個key的壓力。
String 是使用什么存儲的?為什么不用 c 語言中的字符串?
Redis 的 String 字符串是用 SDS 數據結構存儲的。
下圖就是 Redis 5.0 的 SDS 的數據結構:
圖片
結構中的每個成員變量分別介紹下:
- len,記錄了字符串長度。這樣獲取字符串長度的時候,只需要返回這個成員變量值就行,時間復雜度只需要 O(1)。
- alloc,分配給字符數組的空間長度。這樣在修改字符串的時候,可以通過 alloc - len 計算出剩余的空間大小,可以用來判斷空間是否滿足修改需求,如果不滿足的話,就會自動將 SDS 的空間擴展至執行修改所需的大小,然后才執行實際的修改操作,所以使用 SDS 既不需要手動修改 SDS 的空間大小,也不會出現前面所說的緩沖區溢出的問題。
- flags,用來表示不同類型的 SDS。一共設計了 5 種類型,分別是 sdshdr5、sdshdr8、sdshdr16、sdshdr32 和 sdshdr64,后面在說明區別之處。
- buf[],字符數組,用來保存實際數據。不僅可以保存字符串,也可以保存二進制數據。
總的來說,Redis 的 SDS 結構在原本字符數組之上,增加了三個元數據:len、alloc、flags,用來解決 C 語言字符串的缺陷。
O(1)復雜度獲取字符串長度
C 語言的字符串長度獲取 strlen 函數,需要通過遍歷的方式來統計字符串長度,時間復雜度是 O(N)。
而 Redis 的 SDS 結構因為加入了 len 成員變量,那么獲取字符串長度的時候,直接返回這個成員變量的值就行,所以復雜度只有 O(1)。
二進制安全
因為 SDS 不需要用 “\0” 字符來標識字符串結尾了,而是有個專門的 len 成員變量來記錄長度,所以可存儲包含 “\0” 的數據。但是 SDS 為了兼容部分 C 語言標準庫的函數, SDS 字符串結尾還是會加上 “\0” 字符。
因此, SDS 的 API 都是以處理二進制的方式來處理 SDS 存放在 buf[] 里的數據,程序不會對其中的數據做任何限制,數據寫入的時候時什么樣的,它被讀取時就是什么樣的。
通過使用二進制安全的 SDS,而不是 C 字符串,使得 Redis 不僅可以保存文本數據,也可以保存任意格式的二進制數據。
不會發生緩沖區溢出
C 語言的字符串標準庫提供的字符串操作函數,大多數(比如 strcat 追加字符串函數)都是不安全的,因為這些函數把緩沖區大小是否滿足操作需求的工作交由開發者來保證,程序內部并不會判斷緩沖區大小是否足夠用,當發生了緩沖區溢出就有可能造成程序異常結束。
所以,Redis 的 SDS 結構里引入了 alloc 和 len 成員變量,這樣 SDS API 通過 alloc - len 計算,可以算出剩余可用的空間大小,這樣在對字符串做修改操作的時候,就可以由程序內部判斷緩沖區大小是否足夠用。
而且,當判斷出緩沖區大小不夠用時,Redis 會自動將擴大 SDS 的空間大小,以滿足修改所需的大小。
Java
編譯型語言和解釋型語言的區別?
編譯型語言和解釋型語言的區別在于:
- 編譯型語言:在程序執行之前,整個源代碼會被編譯成機器碼或者字節碼,生成可執行文件。執行時直接運行編譯后的代碼,速度快,但跨平臺性較差。
- 解釋型語言:在程序執行時,逐行解釋執行源代碼,不生成獨立的可執行文件。通常由解釋器動態解釋并執行代碼,跨平臺性好,但執行速度相對較慢。
- 典型的編譯型語言如C、C++,典型的解釋型語言如Python、JavaScript。
動態數組的實現有哪些?
ArrayList和Vector都支持動態擴容,都屬于動態數組。
ArrayList 和 Vector 的比較
- 線程安全性:Vector是線程安全的,ArrayList不是線程安全的。
- 擴容策略:ArrayList在底層數組不夠用時在原來的基礎上擴展0.5倍,Vector是擴展1倍。
HashMap 的擴容條件是什么?
Java7 的 HashMap 擴容必須滿足兩個條件:
- 當前數據存儲的數量(即size())大小必須大于等于閾值
- 當前加入的數據是否發生了hash沖突
因為上面這兩個條件,所以存在下面這些情況:
- 第一種情況,就是hashmap在存值的時候(默認大小為16,負載因子0.75,閾值12),可能達到最后存滿16個值的時候,再存入第17個值才會發生擴容現象,因為前16個值,每個值在底層數組中分別占據一個位置,并沒有發生hash碰撞。
- 第二種情況,有可能存儲更多值(超多16個值,最多可以存26個值)都還沒有擴容。原理:前11個值全部hash碰撞,存到數組的同一個位置(雖然hash沖突,但是這時元素個數小于閾值12,并沒有同時滿足擴容的兩個條件。所以不會擴容),后面所有存入的15個值全部分散到數組剩下的15個位置(這時元素個數大于等于閾值,但是每次存入的元素并沒有發生hash碰撞,也沒有同時滿足擴容的兩個條件,所以也不會擴容),前面11+15=26,所以在存入第27個值的時候才同時滿足上面兩個條件,這時候才會發生擴容現象。
Java8 不再像 Java7中那樣需要滿足兩個條件,Java8中擴容只需要滿足一個條件:
- 當前存放新值的時候已有元素的個數大于等于閾值
MQ
用的什么消息隊列,消息隊列怎么選型的?
項目用的是 RocketMQ 消息隊列。選擇RocketMQ的原因是:
- 開發語言優勢。RocketMQ 使用 Java 語言開發,比起使用 Erlang 開發的 RabbitMQ 來說,有著更容易上手的閱讀體驗和受眾。在遇到 RocketMQ 較為底層的問題時,大部分熟悉 Java 的同學都可以深入閱讀其源碼,分析、排查問題。
- 社區氛圍活躍。RocketMQ 是阿里巴巴開源且內部在大量使用的消息隊列,說明 RocketMQ 是的確經得起殘酷的生產環境考驗的,并且能夠針對線上環境復雜的需求場景提供相應的解決方案。
- 特性豐富。根據 RocketMQ 官方文檔的列舉,其高級特性達到了 12 種,例如順序消息、事務消息、消息過濾、定時消息等。順序消息、事務消息、消息過濾、定時消息。RocketMQ 豐富的特性,能夠為我們在復雜的業務場景下盡可能多地提供思路及解決方案。
有沒有消息積壓的問題
導致消息積壓突然增加,最粗粒度的原因,只有兩種:要么是發送變快了,要么是消費變慢了。
要解決積壓的問題,可以通過擴容消費端的實例數來提升總體的消費能力。
如果短時間內沒有足夠的服務器資源進行擴容,沒辦法的辦法是,將系統降級,通過關閉一些不重要的業務,減少發送方發送的數據量,最低限度讓系統還能正常運轉,服務一些重要業務。
RocketMQ 消息可靠性怎么保證?
使用一個消息隊列,其實就分為三大塊:生產者、中間件、消費者,所以要保證消息就是保證三個環節都不能丟失數據。
圖片
- 消息生產階段:生產者會不會丟消息,取決于生產者對于異常情況的處理是否合理。從消息被生產出來,然后提交給 MQ 的過程中,只要能正常收到 ( MQ 中間件) 的 ack 確認響應,就表示發送成功,所以只要處理好返回值和異常,如果返回異常則進行消息重發,那么這個階段是不會出現消息丟失的。
- 消息存儲階段:RabbitMQ 或 Kafka 這類專業的隊列中間件,在使用時是部署一個集群,生產者在發布消息時,隊列中間件通常會寫「多個節點」,也就是有多個副本,這樣一來,即便其中一個節點掛了,也能保證集群的數據不丟失。
- 消息消費階段:消費者接收消息+消息處理之后,才回復 ack 的話,那么消息階段的消息不會丟失。不能收到消息就回 ack,否則可能消息處理中途掛掉了,消息就丟失了。
Kafka 和 RocketMQ 消息確認機制有什么不同?
Kafka的消息確認機制有三種:0,1,-1:
- ACK=0:這是最不可靠的模式。生產者在發送消息后不會等待來自服務器的確認。這意味著消息可能會在發送之后丟失,而生產者將無法知道它是否成功到達服務器。
- ACK=1:這是默認模式,也是一種折衷方式。在這種模式下,生產者會在消息發送后等待來自分區領導者(leader)的確認,但不會等待所有副本(replicas)的確認。這意味著只要消息被寫入分區領導者,生產者就會收到確認。如果分區領導者成功寫入消息,但在同步到所有副本之前宕機,消息可能會丟失。
- ACK=-1:這是最可靠的模式。在這種模式下,生產者會在消息發送后等待所有副本的確認。只有在所有副本都成功寫入消息后,生產者才會收到確認。這確保了消息的可靠性,但會導致更長的延遲。
RocketMQ 提供了三種消息發送方式:同步發送、異步發送和單向發送:
- 同步發送:是指消息發送方發出一條消息后,會在收到服務端同步響應之后才發下一條消息的通訊方式。應用場景非常廣泛,例如重要通知郵件、報名短信通知、營銷短信系統等。
- 異步發送:是指發送方發出一條消息后,不等服務端返回響應,接著發送下一條消息的通訊方式,但是需要實現異步發送回調接口(SendCallback)。消息發送方在發送了一條消息后,不需要等待服務端響應即可發送第二條消息。發送方通過回調接口接收服務端響應,并處理響應結果。適用于鏈路耗時較長,對響應時間較為敏感的業務場景,例如,視頻上傳后通知啟動轉碼服務,轉碼完成后通知推送轉碼結果等。
- 單向發送:發送方只負責發送消息,不等待服務端返回響應且沒有回調函數觸發,即只發送請求不等待應答。此方式發送消息的過程耗時非常短,一般在微秒級別。適用于某些耗時非常短,但對可靠性要求并不高的場景,例如日志收集。
Kafka 和 RocketMQ 的 broker 架構有什么區別
- Kafka 的 broker 架構:Kafka 的 broker 架構采用了分布式的設計,每個 Kafka broker 是一個獨立的服務實例,負責存儲和處理一部分消息數據。Kafka 的 topic 被分區存儲在不同的 broker 上,實現了水平擴展和高可用性。
- RocketMQ 的 broker 架構:RocketMQ 的 broker 架構也是分布式的,但是每個 RocketMQ broker 有主從之分,一個主節點和多個從節點組成一個 broker 集群。主節點負責消息的寫入和消費者的拉取,從節點負責消息的復制和消費者的負載均衡,提高了消息的可靠性和可用性。
其他
- 手撕:字符串乘法,輸出 2 的 1000 次方
- 反問:流程,業務