為什么需要序列化?
序列化的分類與效率
為什么要有RPC?
RPC是怎么實(shí)現(xiàn)的?
前言
一年以來,工作中也用過很多關(guān)于分布式的framework,但從來沒有系統(tǒng)的歸納總結(jié)過。打算開一個(gè)分支來記錄這一年以來對分布式的積累與理解。礙于工作年限較短,無法從整體構(gòu)架來分析分布式系統(tǒng),因此打算從底層的序列化和rpc開啟篇章。
為什么需要序列化?
In computer science, in the context of data storage, serialization (or serialisation) is the process of translating data structures or object state into a format that can be stored (for example, in a file or memory buffer) or transmitted (for example, across a networkconnection link) and reconstructed later (possibly in a different computer environment).
序列化是將一個(gè)結(jié)構(gòu)或?qū)ο筠D(zhuǎn)換成一種可以被存儲或傳輸?shù)臓顟B(tài),且這種狀態(tài)可以被反向的重建這個(gè)結(jié)構(gòu)或?qū)ο蟆?/strong>
可以看到,序列化其實(shí)是一種事物表達(dá)形式的轉(zhuǎn)換,它可以把一個(gè)人類世界的復(fù)雜對象,轉(zhuǎn)換為計(jì)算機(jī)世界可識別的數(shù)據(jù)表現(xiàn)形式,并且可以逆向此過程。因此我對序列化的理解就是:序列化是復(fù)雜數(shù)據(jù)類型與二進(jìn)制數(shù)據(jù)的媒介。
無論是磁盤存儲還是網(wǎng)絡(luò)傳輸,我們的信息最終會被序列化為二進(jìn)制流。
序列化的分類與效率
目前常用的序列化格式有JSON、XML、Thrift、Protocol Buffer等。但從宏觀來看,主要分為用戶可讀和二進(jìn)制類兩種。這兩種方式各有利弊,JSON這類可讀型更用戶友好且更容易映射至一般語言的數(shù)據(jù)結(jié)構(gòu),適合web類研發(fā)與交互。PB這種二進(jìn)制編碼格式的序列化方式有著更緊湊的編碼方式和更高效的編解碼效率,適合大請求量的場景。因此針對業(yè)務(wù)選擇更合適的序列化協(xié)議。
PB協(xié)議編碼與原理 很好的解釋了問什么PB的效率如此的高,以及一些更好的應(yīng)用方式??傮w來講,PB就是通過數(shù)據(jù)的位移和簡單運(yùn)算實(shí)現(xiàn)緊湊編碼,此外合理的編碼直接去除了分隔符,進(jìn)一步減小了編碼后的數(shù)據(jù)量,使得其非常適合大請求數(shù)量的場景。
說完了序列化,就向上走一步說一說由序列化為底層實(shí)現(xiàn)的Remote Procedure Call.
為什么要有RPC?
隨著業(yè)務(wù)的發(fā)展,服務(wù)越來越多,不可避免的會將這些服務(wù)分布在不同的服務(wù)器甚至不同機(jī)房的服務(wù)器上,那么這時(shí)就需要一種遠(yuǎn)程交互手段來實(shí)現(xiàn)服務(wù)的請求與處理。
In distributed computing, a remote procedure call (RPC) is when a computer program causes a procedure (subroutine) to execute in a different address space (commonly on another computer on a shared network), which is coded as if it were a normal (local) procedure call, without the programmer explicitly coding the details for the remote interaction
RPC是能像本地調(diào)用函數(shù)一樣地調(diào)用一個(gè)處于不同地址空間的函數(shù)過程,且不需要去關(guān)心與遠(yuǎn)程調(diào)用相關(guān)的額外代碼。
從操作系統(tǒng)的角度來講,一個(gè)進(jìn)程(線程)在調(diào)用一個(gè)本地函數(shù)時(shí),會將當(dāng)前方法的狀態(tài)壓棧存入內(nèi)存,然后開啟一個(gè)新的棧幀來描述這個(gè)本地函數(shù)的狀態(tài)。但如果是不同進(jìn)程之間通信,那就需要用到IPC通信,使得不同地址空間的進(jìn)程交換數(shù)據(jù)。如果更復(fù)雜,讓不同計(jì)算機(jī)上的不同進(jìn)程進(jìn)行調(diào)用通信,那還要涉及到網(wǎng)絡(luò)連接與處理。而RPC就是希望可以透明化這些IPC及網(wǎng)絡(luò)調(diào)用,使得程序員只關(guān)心該有的業(yè)務(wù)邏輯,簡化開發(fā)的一種方式。此外部分RPC是自定義底層通信協(xié)議,會比HTTP這類通用協(xié)議更高效、更適合特定業(yè)務(wù)。
RPC是怎么實(shí)現(xiàn)的?

RPC調(diào)用過程
1)服務(wù)消費(fèi)方(client)調(diào)用以本地調(diào)用方式調(diào)用服務(wù);
2)client stub接收到調(diào)用后負(fù)責(zé)將方法、參數(shù)等組裝成能夠進(jìn)行網(wǎng)絡(luò)傳輸?shù)南Ⅲw;
3)client stub找到服務(wù)地址,并將消息發(fā)送到服務(wù)端;
4)server stub收到消息后進(jìn)行解碼;
5)server stub根據(jù)解碼結(jié)果調(diào)用本地的服務(wù);
6)本地服務(wù)執(zhí)行并將結(jié)果返回給server stub;
7)server stub將返回結(jié)果打包成消息并發(fā)送至消費(fèi)方;
8)client stub接收到消息,并進(jìn)行解碼;
9)服務(wù)消費(fèi)方得到最終結(jié)果
public class RPCProxyClient implements java.lang.reflect.InvocationHandler{
private Object obj;
public RPCProxyClient(Object obj){
this.obj=obj;
}
/**
* 得到被代理對象;
*/
public static Object getProxy(Object obj){
return java.lang.reflect.Proxy.newProxyInstance(obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(), new RPCProxyClient(obj));
}
/**
* 調(diào)用此方法執(zhí)行
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//結(jié)果參數(shù);
Object result = new Object();
// ...執(zhí)行通信相關(guān)邏輯
// ...
return result;
}
}
public class Test {
public static void main(String[] args) {
HelloWorldService helloWorldService = (HelloWorldService)RPCProxyClient.getProxy(HelloWorldService.class);
helloWorldService.sayHello("test");
}
}
在Java里,實(shí)現(xiàn)調(diào)用過程透明化的是動態(tài)代理,本地方法調(diào)用代理,代理完成編碼、網(wǎng)絡(luò)調(diào)用等額外作業(yè),這些作業(yè)對上層應(yīng)用透明。以此達(dá)到RPC調(diào)用的。Java原生 RPC的示例 用非常清晰示例說明了一個(gè)RPC的基本Java實(shí)現(xiàn)。至此,關(guān)于序列化和RPC的最核心內(nèi)容已經(jīng)寫完了,關(guān)于注冊中心、監(jiān)控等服務(wù)化治理內(nèi)容,將在后續(xù)文章中繼續(xù)。