1. 程式人生 > >從面試角度學完 Kafka

從面試角度學完 Kafka

Kafka 是一個優秀的分散式訊息中介軟體,許多系統中都會使用到 Kafka 來做訊息通訊。對分散式訊息系統的瞭解和使用幾乎成為一個後臺開發人員必備的技能。今天`碼哥位元組`就從常見的 Kafka 面試題入手,和大家聊聊 Kafka 的那些事兒。 ![思維導圖](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/mind.png) ## 講一講分散式訊息中介軟體 ### 問題 - 什麼是分散式訊息中介軟體? - 訊息中介軟體的作用是什麼? - 訊息中介軟體的使用場景是什麼? - 訊息中介軟體選型? ![訊息佇列](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/mq.png) 分散式訊息是一種通訊機制,和 RPC、HTTP、RMI 等不一樣,訊息中介軟體採用分散式中間代理的方式進行通訊。如圖所示,採用了訊息中介軟體之後,上游業務系統傳送訊息,先儲存在訊息中介軟體,然後由訊息中介軟體將訊息分發到對應的業務模組應用(分散式生產者 - 消費者模式)。這種非同步的方式,減少了服務之間的耦合程度。 ![架構](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/mq-service.png) **定義訊息中介軟體:** - 利用高效可靠的訊息傳遞機制進行平臺無關的資料交流 - 基於資料通訊,來進行分散式系統的整合 - 通過提供訊息傳遞和訊息排隊模型,可以在分散式環境下擴充套件程序間的通訊 在系統架構中引用額外的元件,必然提高系統的架構複雜度和運維的難度,那麼**在系統中使用分散式訊息中介軟體有什麼優勢呢?訊息中介軟體在系統中起的作用又是什麼呢?** - 解耦 - 冗餘(儲存) - 擴充套件性 - 削峰 - 可恢復性 - 順序保證 - 緩衝 - 非同步通訊 面試時,面試官經常會關心面試者對開源元件的選型能力,這既可以考驗面試者知識的廣度,也可以考驗面試者對某類系統的知識的認識深度,而且也可以看出面試者對系統整體把握和系統架構設計的能力。開源分散式訊息系統有很多,不同的訊息系統的特性也不一樣,選擇怎樣的訊息系統,不僅需要對各訊息系統有一定的瞭解,也需要對自身系統需求有清晰的認識。 **下面是常見的幾種分散式訊息系統的對比:** ![選擇](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/comp.jpeg) ### 答案關鍵字 - 什麼是分散式訊息中介軟體?通訊,佇列,分散式,生產消費者模式。 - 訊息中介軟體的作用是什麼? 解耦、峰值處理、非同步通訊、緩衝。 - 訊息中介軟體的使用場景是什麼? 非同步通訊,訊息儲存處理。 - 訊息中介軟體選型?語言,協議、HA、資料可靠性、效能、事務、生態、簡易、推拉模式。 ## Kafka 基本概念和架構 ### 問題 - 簡單講下 Kafka 的架構? - Kafka 是推模式還是拉模式,推拉的區別是什麼? - Kafka 如何廣播訊息? - Kafka 的訊息是否是有序的? - Kafka 是否支援讀寫分離? - Kafka 如何保證資料高可用? - Kafka 中 zookeeper 的作用? - 是否支援事務? - 分割槽數是否可以減少? **Kafka 架構中的一般概念:** ![架構](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/arch.png) - Producer:生產者,也就是傳送訊息的一方。生產者負責建立訊息,然後將其傳送到 Kafka。 - Consumer:消費者,也就是接受訊息的一方。消費者連線到 Kafka 上並接收訊息,進而進行相應的業務邏輯處理。 - Consumer Group:一個消費者組可以包含一個或多個消費者。使用多分割槽 + 多消費者方式可以極大提高資料下游的處理速度,同一消費組中的消費者不會重複消費訊息,同樣的,不同消費組中的消費者訊息訊息時互不影響。Kafka 就是通過消費組的方式來實現訊息 P2P 模式和廣播模式。 - Broker:服務代理節點。Broker 是 Kafka 的服務節點,即 Kafka 的伺服器。 - Topic:Kafka 中的訊息以 Topic 為單位進行劃分,生產者將訊息傳送到特定的 Topic,而消費者負責訂閱 Topic 的訊息並進行消費。 - Partition:Topic 是一個邏輯的概念,它可以細分為多個分割槽,每個分割槽只屬於單個主題。同一個主題下不同分割槽包含的訊息是不同的,分割槽在儲存層面可以看作一個可追加的日誌(Log)檔案,訊息在被追加到分割槽日誌檔案的時候都會分配一個特定的偏移量(offset)。 - Offset:offset 是訊息在分割槽中的唯一標識,Kafka 通過它來保證訊息在分割槽內的順序性,不過 offset 並不跨越分割槽,也就是說,Kafka 保證的是分割槽有序性而不是主題有序性。 - Replication:副本,是 Kafka 保證資料高可用的方式,Kafka 同一 Partition 的資料可以在多 Broker 上存在多個副本,通常只有主副本對外提供讀寫服務,當主副本所在 broker 崩潰或發生網路一場,Kafka 會在 Controller 的管理下會重新選擇新的 Leader 副本對外提供讀寫服務。 - Record: 實際寫入 Kafka 中並可以被讀取的訊息記錄。每個 record 包含了 key、value 和 timestamp。 **Kafka Topic Partitions Layout** ![主題](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/kafka-topic.jpeg) Kafka 將 Topic 進行分割槽,分割槽可以併發讀寫。 **Kafka Consumer Offset** ![consumer offset](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/offset.png) ### zookeeper ![zookeeper](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/zookeeper.png) - Broker 註冊:Broker 是分散式部署並且之間相互獨立,Zookeeper 用來管理註冊到叢集的所有 Broker 節點。 - Topic 註冊: 在 Kafka 中,同一個 Topic 的訊息會被分成多個分割槽並將其分佈在多個 Broker 上,這些分割槽資訊及與 Broker 的對應關係也都是由 Zookeeper 在維護 - 生產者負載均衡:由於同一個 Topic 訊息會被分割槽並將其分佈在多個 Broker 上,因此,生產者需要將訊息合理地傳送到這些分散式的 Broker 上。 - 消費者負載均衡:與生產者類似,Kafka 中的消費者同樣需要進行負載均衡來實現多個消費者合理地從對應的 Broker 伺服器上接收訊息,每個消費者分組包含若干消費者,每條訊息都只會傳送給分組中的一個消費者,不同的消費者分組消費自己特定的 Topic 下面的訊息,互不干擾。 ### 答案關鍵字 - 簡單講下 Kafka 的架構? > Producer、Consumer、Consumer Group、Topic、Partition - Kafka 是推模式還是拉模式,推拉的區別是什麼? > Kafka Producer 向 Broker 傳送訊息使用 Push 模式,Consumer 消費採用的 Pull 模式。拉取模式,讓 consumer 自己管理 offset,可以提供讀取效能 - Kafka 如何廣播訊息? > Consumer group - Kafka 的訊息是否是有序的? > Topic 級別無序,Partition 有序 - Kafka 是否支援讀寫分離? > 不支援,只有 Leader 對外提供讀寫服務 - Kafka 如何保證資料高可用? > 副本,ack,HW - Kafka 中 zookeeper 的作用? > 叢集管理,元資料管理 - 是否支援事務? > 0.11 後支援事務,可以實現”exactly once“ - 分割槽數是否可以減少? > 不可以,會丟失資料 ## Kafka 使用 ### 問題 - Kafka 有哪些命令列工具?你用過哪些? - Kafka Producer 的執行過程? - Kafka Producer 有哪些常見配置? - 如何讓 Kafka 的訊息有序? - Producer 如何保證資料傳送不丟失? - 如何提升 Producer 的效能? - 如果同一 group 下 consumer 的數量大於 part 的數量,kafka 如何處理? - Kafka Consumer 是否是執行緒安全的? - 講一下你使用 Kafka Consumer 消費訊息時的執行緒模型,為何如此設計? - Kafka Consumer 的常見配置? - Consumer 什麼時候會被踢出叢集? - 當有 Consumer 加入或退出時,Kafka 會作何反應? - 什麼是 Rebalance,何時會發生 Rebalance? ### 命令列工具 Kafka 的命令列工具在 Kafka 包的`/bin`目錄下,主要包括服務和叢集管理指令碼,配置指令碼,資訊檢視指令碼,Topic 指令碼,客戶端指令碼等。 - kafka-configs.sh: 配置管理指令碼 - kafka-console-consumer.sh: kafka 消費者控制檯 - kafka-console-producer.sh: kafka 生產者控制檯 - kafka-consumer-groups.sh: kafka 消費者組相關資訊 - kafka-delete-records.sh: 刪除低水位的日誌檔案 - kafka-log-dirs.sh:kafka 訊息日誌目錄資訊 - kafka-mirror-maker.sh: 不同資料中心 kafka 叢集複製工具 - kafka-preferred-replica-election.sh: 觸發 preferred replica 選舉 - kafka-producer-perf-test.sh:kafka 生產者效能測試指令碼 - kafka-reassign-partitions.sh: 分割槽重分配指令碼 - kafka-replica-verification.sh: 複製進度驗證指令碼 - kafka-server-start.sh: 啟動 kafka 服務 - kafka-server-stop.sh: 停止 kafka 服務 - kafka-topics.sh:topic 管理指令碼 - kafka-verifiable-consumer.sh: 可檢驗的 kafka 消費者 - kafka-verifiable-producer.sh: 可檢驗的 kafka 生產者 - zookeeper-server-start.sh: 啟動 zk 服務 - zookeeper-server-stop.sh: 停止 zk 服務 - zookeeper-shell.sh:zk 客戶端 我們通常可以使用`kafka-console-consumer.sh`和`kafka-console-producer.sh`指令碼來測試 Kafka 生產和消費,`kafka-consumer-groups.sh`可以檢視和管理叢集中的 Topic,`kafka-topics.sh`通常用於檢視 Kafka 的消費組情況。 ### Kafka Producer Kafka producer 的正常生產邏輯包含以下幾個步驟: 1. 配置生產者客戶端引數常見生產者例項。 2. 構建待發送的訊息。 3. 傳送訊息。 4. 關閉生產者例項。 Producer 傳送訊息的過程如下圖所示,需要經過`攔截器`,`序列化器`和`分割槽器`,最終由`累加器`批量傳送至 Broker。 ![producer](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/producer.png) Kafka Producer 需要以下必要引數: - bootstrap.server: 指定 Kafka 的 Broker 的地址 - key.serializer: key 序列化器 - value.serializer: value 序列化器 常見引數: - batch.num.messages > 預設值:200,每次批量訊息的數量,只對 asyc 起作用。 - request.required.acks > 預設值:0,0 表示 producer 毋須等待 leader 的確認,1 代表需要 leader 確認寫入它的本地 log 並立即確認,-1 代表所有的備份都完成後確認。 只對 async 模式起作用,這個引數的調整是資料不丟失和傳送效率的 tradeoff,如果對資料丟失不敏感而在乎效率的場景可以考慮設定為 0,這樣可以大大提高 producer 傳送資料的效率。 - request.timeout.ms > 預設值:10000,確認超時時間。 - partitioner.class > 預設值:kafka.producer.DefaultPartitioner,必須實現 kafka.producer.Partitioner,根據 Key 提供一個分割槽策略。_有時候我們需要相同型別的訊息必須順序處理,這樣我們就必須自定義分配策略,從而將相同型別的資料分配到同一個分割槽中。_ - producer.type > 預設值:sync,指定訊息傳送是同步還是非同步。非同步 asyc 成批發送用 kafka.producer.AyncProducer, 同步 sync 用 kafka.producer.SyncProducer。同步和非同步傳送也會影響訊息生產的效率。 - compression.topic > 預設值:none,訊息壓縮,預設不壓縮。其餘壓縮方式還有,"gzip"、"snappy"和"lz4"。對訊息的壓縮可以極大地減少網路傳輸量、降低網路 IO,從而提高整體效能。 - compressed.topics > 預設值:null,在設定了壓縮的情況下,可以指定特定的 topic 壓縮,未指定則全部壓縮。 - message.send.max.retries > 預設值:3,訊息傳送最大嘗試次數。 - retry.backoff.ms > 預設值:300,每次嘗試增加的額外的間隔時間。 - topic.metadata.refresh.interval.ms > 預設值:600000,定期的獲取元資料的時間。當分割槽丟失,leader 不可用時 producer 也會主動獲取元資料,如果為 0,則每次傳送完訊息就獲取元資料,不推薦。如果為負值,則只有在失敗的情況下獲取元資料。 - queue.buffering.max.ms > 預設值:5000,在 producer queue 的快取的資料最大時間,僅僅 for asyc。 - queue.buffering.max.message > 預設值:10000,producer 快取的訊息的最大數量,僅僅 for asyc。 - queue.enqueue.timeout.ms > 預設值:-1,0 當 queue 滿時丟掉,負值是 queue 滿時 block, 正值是 queue 滿時 block 相應的時間,僅僅 for asyc。 ### Kafka Consumer Kafka 有消費組的概念,每個消費者只能消費所分配到的分割槽的訊息,每一個分割槽只能被一個消費組中的一個消費者所消費,所以同一個消費組中消費者的數量如果超過了分割槽的數量,將會出現有些消費者分配不到消費的分割槽。消費組與消費者關係如下圖所示: ![consumer group](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/consumer-group.png) Kafka Consumer Client 消費訊息通常包含以下步驟: 1. 配置客戶端,建立消費者 2. 訂閱主題 3. 拉去訊息並消費 4. 提交消費位移 5. 關閉消費者例項 ![過程](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/consumer.png) 因為 Kafka 的 Consumer 客戶端是執行緒不安全的,為了保證執行緒安全,並提升消費效能,可以在 Consumer 端採用類似 Reactor 的執行緒模型來消費資料。 ![消費模型](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/consumer-thread.png) #### Kafka consumer 引數 - bootstrap.servers: 連線 broker 地址,`host:port` 格式。 - group.id: 消費者隸屬的消費組。 - key.deserializer: 與生產者的`key.serializer`對應,key 的反序列化方式。 - value.deserializer: 與生產者的`value.serializer`對應,value 的反序列化方式。 - session.timeout.ms: coordinator 檢測失敗的時間。預設 10s 該引數是 Consumer Group 主動檢測 (組內成員 comsummer) 崩潰的時間間隔,類似於心跳過期時間。 - auto.offset.reset: 該屬性指定了消費者在讀取一個沒有偏移量後者偏移量無效(消費者長時間失效當前的偏移量已經過時並且被刪除了)的分割槽的情況下,應該作何處理,預設值是 latest,也就是從最新記錄讀取資料(消費者啟動之後生成的記錄),另一個值是 earliest,意思是在偏移量無效的情況下,消費者從起始位置開始讀取資料。 - enable.auto.commit: 否自動提交位移,如果為`false`,則需要在程式中手動提交位移。對於精確到一次的語義,最好手動提交位移 - fetch.max.bytes: 單次拉取資料的最大位元組數量 - max.poll.records: 單次 poll 呼叫返回的最大訊息數,如果處理邏輯很輕量,可以適當提高該值。 但是`max.poll.records`條資料需要在在 session.timeout.ms 這個時間內處理完 。預設值為 500 - request.timeout.ms: 一次請求響應的最長等待時間。如果在超時時間內未得到響應,kafka 要麼重發這條訊息,要麼超過重試次數的情況下直接置為失敗。 #### Kafka Rebalance rebalance 本質上是一種協議,規定了一個 consumer group 下的所有 consumer 如何達成一致來分配訂閱 topic 的每個分割槽。比如某個 group 下有 20 個 consumer,它訂閱了一個具有 100 個分割槽的 topic。正常情況下,Kafka 平均會為每個 consumer 分配 5 個分割槽。這個分配的過程就叫 rebalance。 **什麼時候 rebalance?** 這也是經常被提及的一個問題。rebalance 的觸發條件有三種: - 組成員發生變更(新 consumer 加入組、已有 consumer 主動離開組或已有 consumer 崩潰了——這兩者的區別後面會談到) - 訂閱主題數發生變更 - 訂閱主題的分割槽數發生變更 **如何進行組內分割槽分配?** Kafka 預設提供了兩種分配策略:Range 和 Round-Robin。當然 Kafka 採用了可插拔式的分配策略,你可以建立自己的分配器以實現不同的分配策略。 ### 答案關鍵字 - Kafka 有哪些命令列工具?你用過哪些?`/bin`目錄,管理 kafka 叢集、管理 topic、生產和消費 kafka - Kafka Producer 的執行過程?攔截器,序列化器,分割槽器和累加器 - Kafka Producer 有哪些常見配置?broker 配置,ack 配置,網路和傳送引數,壓縮引數,ack 引數 - 如何讓 Kafka 的訊息有序?Kafka 在 Topic 級別本身是無序的,只有 partition 上才有序,所以為了保證處理順序,可以自定義分割槽器,將需順序處理的資料傳送到同一個 partition - Producer 如何保證資料傳送不丟失?ack 機制,重試機制 - 如何提升 Producer 的效能?批量,非同步,壓縮 - 如果同一 group 下 consumer 的數量大於 part 的數量,kafka 如何處理?多餘的 Part 將處於無用狀態,不消費資料 - Kafka Consumer 是否是執行緒安全的?不安全,單執行緒消費,多執行緒處理 - 講一下你使用 Kafka Consumer 消費訊息時的執行緒模型,為何如此設計?拉取和處理分離 - Kafka Consumer 的常見配置?broker, 網路和拉取引數,心跳引數 - Consumer 什麼時候會被踢出叢集?奔潰,網路異常,處理時間過長提交位移超時 - 當有 Consumer 加入或退出時,Kafka 會作何反應?進行 Rebalance - 什麼是 Rebalance,何時會發生 Rebalance?topic 變化,consumer 變化 ## 高可用和效能 ### 問題 - Kafka 如何保證高可用? - Kafka 的交付語義? - Replic 的作用? - 什麼事 AR,ISR? - Leader 和 Flower 是什麼? - Kafka 中的 HW、LEO、LSO、LW 等分別代表什麼? - Kafka 為保證優越的效能做了哪些處理? ### 分割槽與副本 ![分割槽副本](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/r-p.png) 在分散式資料系統中,通常使用分割槽來提高系統的處理能力,通過副本來保證資料的高可用性。多分割槽意味著併發處理的能力,這多個副本中,只有一個是 leader,而其他的都是 follower 副本。僅有 leader 副本可以對外提供服務。 多個 follower 副本通常存放在和 leader 副本不同的 broker 中。通過這樣的機制實現了高可用,當某臺機器掛掉後,其他 follower 副本也能迅速”轉正“,開始對外提供服務。 **為什麼 follower 副本不提供讀服務?** 這個問題本質上是對效能和一致性的取捨。試想一下,如果 follower 副本也對外提供服務那會怎麼樣呢?首先,效能是肯定會有所提升的。但同時,會出現一系列問題。類似資料庫事務中的幻讀,髒讀。 比如你現在寫入一條資料到 kafka 主題 a,消費者 b 從主題 a 消費資料,卻發現消費不到,因為消費者 b 去讀取的那個分割槽副本中,最新訊息還沒寫入。而這個時候,另一個消費者 c 卻可以消費到最新那條資料,因為它消費了 leader 副本。Kafka 通過 WH 和 Offset 的管理來決定 Consumer 可以消費哪些資料,已經當前寫入的資料。 ![watermark](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/kafka_high_watermark.png) **只有 Leader 可以對外提供讀服務,那如何選舉 Leader** kafka 會將與 leader 副本保持同步的副本放到 ISR 副本集合中。當然,leader 副本是一直存在於 ISR 副本集合中的,在某些特殊情況下,ISR 副本中甚至只有 leader 一個副本。 當 leader 掛掉時,kakfa 通過 zookeeper 感知到這一情況,在 ISR 副本中選取新的副本成為 leader,對外提供服務。 但這樣還有一個問題,前面提到過,有可能 ISR 副本集合中,只有 leader,當 leader 副本掛掉後,ISR 集合就為空,這時候怎麼辦呢?這時候如果設定 unclean.leader.election.enable 引數為 true,那麼 kafka 會在非同步,也就是不在 ISR 副本集合中的副本中,選取出副本成為 leader。 **副本的存在就會出現副本同步問題** Kafka 在所有分配的副本 (AR) 中維護一個可用的副本列表 (ISR),Producer 向 Broker 傳送訊息時會根據`ack`配置來確定需要等待幾個副本已經同步了訊息才相應成功,Broker 內部會`ReplicaManager`服務來管理 flower 與 leader 之間的資料同步。 ![sync](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/sync.png) ### 效能優化 - partition 併發 - 順序讀寫磁碟 - page cache:按頁讀寫 - 預讀:Kafka 會將將要消費的訊息提前讀入記憶體 - 高效能序列化(二進位制) - 記憶體對映 - 無鎖 offset 管理:提高併發能力 - Java NIO 模型 - 批量:批量讀寫 - 壓縮:訊息壓縮,儲存壓縮,減小網路和 IO 開銷 #### Partition 併發 一方面,由於不同 Partition 可位於不同機器,因此可以充分利用叢集優勢,實現機器間的並行處理。另一方面,由於 Partition 在物理上對應一個資料夾,即使多個 Partition 位於同一個節點,也可通過配置讓同一節點上的不同 Partition 置於不同的 disk drive 上,從而實現磁碟間的並行處理,充分發揮多磁碟的優勢。 #### 順序讀寫 Kafka 每一個 partition 目錄下的檔案被平均切割成大小相等(預設一個檔案是 500 兆,可以手動去設定)的資料檔案, 每一個數據檔案都被稱為一個段(segment file), 每個 segment 都採用 append 的方式追加資料。 ![追加資料](https://magebyte.oss-cn-shenzhen.aliyuncs.com/kafka/append.png) ### 答案關鍵字 - Kafka 如何保證高可用? > 通過副本來保證資料的高可用,producer ack、重試、自動 Leader 選舉,Consumer 自平衡 - Kafka 的交付語義? > 交付語義一般有`at least once`、`at most once`和`exactly once`。kafka 通過 ack 的配置來實現前兩種。 - Replic 的作用? > 實現資料的高可用 - 什麼是 AR,ISR? > AR:Assigned Replicas。AR 是主題被建立後,分割槽建立時被分配的副本集合,副本個 數由副本因子決定。 > ISR:In-Sync Replicas。Kafka 中特別重要的概念,指代的是 AR 中那些與 Leader 保 持同步的副本集合。在 AR 中的副本可能不在 ISR 中,但 Leader 副本天然就包含在 ISR 中。關於 ISR,還有一個常見的面試題目是如何判斷副本是否應該屬於 ISR。目前的判斷 依據是:Follower 副本的 LEO 落後 Leader LEO 的時間,是否超過了 Broker 端引數 replica.lag.time.max.ms 值。如果超過了,副本就會被從 ISR 中移除。 - Leader 和 Flower 是什麼? - Kafka 中的 HW 代表什麼? > 高水位值 (High watermark)。這是控制消費者可讀取訊息範圍的重要欄位。一 個普通消費者只能“看到”Leader 副本上介於 Log Start Offset 和 HW(不含)之間的 所有訊息。水位以上的訊息是對消費者不可見的。 - Kafka 為保證優越的效能做了哪些處理? > partition 併發、順序讀寫磁碟、page cache 壓縮、高效能序列化(二進位制)、記憶體對映 無鎖 offset 管理、Java NIO 模型 本文並沒有深入 Kafka 的實現細節和原始碼分析,但 Kafka 確實是一個 優秀的開源系統,很多優雅的架構設計和原始碼設計都值得我們學習,十分建議感興趣的同學更加深入的去了解一下這個開源系統,對於自身架構設計能力,編碼能力,效能優化都會有很大的幫助。 ## 推薦閱讀 以下幾篇文章閱讀量與讀者反饋都很好,推薦大家閱讀: - [資料庫系統設計概述](https://mp.weixin.qq.com/s/HoSul-GjX6FmulY6ugdmgQ) - [不可不知的軟體架構模式](https://mp.weixin.qq.com/s/77OIESNbJtF6jNkSLq2fyg) - [Tomcat 架構原理解析到架構設計借鑑](https://mp.weixin.qq.com/s/fU5Jj9tQvNTjRiT9grm6RA) - [Tomcat 高併發之道原理拆解與效能調優](https://mp.weixin.qq.com/s/0jj7QfQCxEIBS2PM58H4bw) ![MageByte](https://magebyte.oss-cn-shenzhen.aliyuncs.com/wechat/Snip20200314_5.png)