高效的數(shù)據序列化框架
實現(xiàn)RPC框架的分布式協(xié)議允許程序在不同計算機上進行交互,而無需為此編寫額外的代碼。RPC的目標是讓構建分布式應用更加容易,同時保持本地調用的簡潔性。在代碼層面上,一個RPC框架需要實現(xiàn)將數(shù)據轉化為字
實現(xiàn)RPC框架的分布式協(xié)議允許程序在不同計算機上進行交互,而無需為此編寫額外的代碼。RPC的目標是讓構建分布式應用更加容易,同時保持本地調用的簡潔性。在代碼層面上,一個RPC框架需要實現(xiàn)將數(shù)據轉化為字節(jié)數(shù)組以及字節(jié)數(shù)組與數(shù)據之間的相互轉換。雖然Java已經提供了默認的序列化方式,但在高并發(fā)場景下可能會遇到性能瓶頸。因此,出現(xiàn)了許多開源的、高效的序列化框架,例如Kryo、fastjson和Protobuf等。buddha目前支持Kryo和fastjson兩種序列化框架。
TCP拆包與粘包處理
TCP關注的是字節(jié)流,不了解上層數(shù)據的格式。當客戶端應用層一次要發(fā)送的數(shù)據過大時,TCP會將數(shù)據進行分解傳輸,因此服務端需要進行粘包處理(由TCP保證數(shù)據的有序性)。另一方面,如果客戶端一次要發(fā)送的數(shù)據量很小,TCP則不會立即發(fā)送數(shù)據,而是將其存儲在緩沖區(qū)中,當達到某個閾值時再發(fā)送。這就需要在服務端進行拆包的工作。為解決這類問題,我們可以向數(shù)據包添加邊界信息,在發(fā)送端給每個數(shù)據包添加包首部,其至少包含了數(shù)據包的長度。接收端在接收到數(shù)據時,通過讀取首部的長度信息來獲取有效數(shù)據的長度。此外,發(fā)送端還可以將每個數(shù)據包封裝為固定長度(多余的位置用0填充),接收端則根據約定好的固定長度讀取每個數(shù)據包的數(shù)據。另一種方法是使用特殊符號將每個數(shù)據包區(qū)分開來,接收端也可以通過該特殊符號來劃分數(shù)據包的邊界。buddha采用添加包首部的方式來解決TCP拆包和粘包的問題。
多線程與NIO優(yōu)化
傳統(tǒng)的BIO(同步阻塞)模型中,像accept()、read()和write()等函數(shù)都是同步阻塞的。這意味著當應用程序以單線程進行IO操作時,如果線程被阻塞,應用程序就會進入掛死狀態(tài),而CPU卻處于空閑狀態(tài)。通過開啟多線程,可以讓CPU為更多的線程提供服務,提高CPU的利用率。然而,線程切換帶來的開銷仍然存在。因此,在高并發(fā)場景下,傳統(tǒng)的BIO模型無法滿足需求。相比之下,NIO(非阻塞IO)模型具有重要特點:讀、寫、注冊和接收函數(shù)在等待就緒階段都是非阻塞的,可以立即返回。這允許我們在不使用多線程的情況下,充分利用CPU資源。如果一個連接不能讀寫,可以將該事件記錄下來,并切換到其他就緒的連接進行數(shù)據讀寫。在buddha中,我們使用Netty來編寫結構更加清晰的NIO程序。
服務注冊與發(fā)現(xiàn)
為了保證RPC服務的穩(wěn)定性和可靠性,RPC服務提供者通常需要使用集群。因此,我們需要實現(xiàn)一個服務注冊中心,服務提供者將當前可用的服務地址信息注冊到注冊中心。當客戶端進行遠程調用時,首先通過服務注冊中心獲取當前可用的服務列表,然后獲取具體服務提供者的地址信息(可以進行負載均衡),并向服務提供者發(fā)起調用。這種服務注冊與發(fā)現(xiàn)的機制能夠有效地管理和調度分布式系統(tǒng)中的服務。