再說RPC之前,先說一下同類的通信方式。
從單機走向分佈式,產生了很多分佈式的通信方式。
- 最古老也是最有效,且永遠不過時的,TCP/UDP的二進制傳輸。事實上所有的通信方式歸根結底都是TCP/UDP。
- CORBA Common Object Request Broker Architecture。古老而複雜,支持面向對象的通信協議
- Web Service(SOA SOAP RDDI WSDL...協議):基於http + xml的標準化Web API。
XML指的是接口文檔以XML的形式進行展現,http指的是通信協議。
- RestFul(Representational State Transfer):迴歸簡單化本源的Web API的事實標準 http://mashibing.com/product/java
通信協議是http,以json方式進行數據傳出。
- RMI Remote Method Invocation:java內部的分佈式通信協議,就是Java內部封裝的RPC,不過名稱叫做RMI擺了。
- JMS Java Message Service:JavaEE中的消息框架標準,爲很多MQ所支持。
- RPC(Remote Procedure Call):遠程過程調用,就是調用遠程的方法。
遠程方法調用,這只是一個統稱,重點在於方法調用(不支持對象的概念),具體實現甚至可以用RMI RestFul等去實現,但一般不用,因爲RMI不能跨語言,而RestFul傳輸效率過低。
RPC多用於服務器集羣間的通信,因此常使用更加有效、短小精悍的傳出模式以提高效率,如Thrift,Hessian,gooogle protobuf。
RPC根據序列化方式的不同,出現了多樣的RPC序列化框架
注意RPC序列化框架並不是RPC框架,RPC框架還包括服務發現,服務註冊、服務治理等。。。
- java.io.Serializable
java原生序列化,不好,序列化時間長,且序列化後的字節也非常大。
- Hessian
- google protobuf
- facebook Thrift
- kyro
- fst
- json序列化框架
1 jackson
2 google gson
3 Ali FastJson
- xmlrpc(stream)
RPC通信協議
- http
- http2.0(gRPC)
- TCP
同步/異步/阻塞/非阻塞
- WebService
根據序列化方式和RPC通信協議方式的組合,可以產生多樣的RPC使用方式。
分佈式通信最基本最核心的部分:兩臺/多臺服務器之間的二進制數據的傳出。
以下是單機的二進制數據傳送與接收實例。
公共部分代碼:
public interface IUserService {
public User findUserById(Integer id);
}
public class User implements Serializable {
private static final long serialVersionUID = 1;
private Integer id;
private String name;
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
服務端代碼:
public class Server {
private static boolean running = true;
public static void main(String[] args) throws Exception{
ServerSocket ss = new ServerSocket(8888);
while(running){
Socket s = ss.accept();
process(s);
s.close();
}
ss.close();
}
private static void process(Socket s)throws Exception{
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
DataInputStream dis = new DataInputStream(in);
DataOutputStream dos = new DataOutputStream(out);
/*
* 獲取客戶端的數據
* */
int id = dis.readInt();
IUserService service = new UserServiceImpl();
/*
* 如何將對象寫入到客戶端呢?
* 把對象的基本類型寫到客戶端。
* */
User user = service.findUserById(id);
dos.writeInt(user.getId());
dos.writeUTF(user.getName());
dos.flush();
}
}
User實現類
public class UserServiceImpl implements IUserService {
@Override
public User findUserById(Integer id) {
return new User(id,"hello world");
}
}
客戶端代碼:
/*
向外寫的過程:
客戶端的數據需要轉換成二進制的過程,
通過DataOutputStream的寫入操作將123數值轉成二進制的表示,然後
通過調用toByteArray方式轉成字節數組.
*/
public class client {
public static void main(String[] args) throws Exception {
Socket s = new Socket("127.0.0.1",8888);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(123);
s.getOutputStream().write(baos.toByteArray());
s.getOutputStream().flush();
/*上方的操作進行寫出操作,下方讀取服務器返回的數據,然後
* 將基本類型進行拼接成User對象*/
DataInputStream dis = new DataInputStream(s.getInputStream());
int id = dis.readInt();
String name = dis.readUTF();
User user = new User(id,name);
System.out.println(user);
dos.close();
dis.close();
}
}
筆記參考:Java講解RPC的基本實現
這裏僅是粗淺理解性,對於RPC而言,其中動態代理扮演了比較重要的角色。