首頁>技術>

本文以極簡的Java程式碼演示RPC框架的基本原理。

完整教程請訪問:https://bigbird.blog.csdn.net/

需求描述:

1.客戶端呼叫遠端服務ProductService、UserSerivce的介面,並列印結果

2.服務端提供具體的服務實現類,接受客戶端請求,並返回響應

3.客戶端像使用本地方法一樣呼叫遠端介面,對網路通訊、序列化等細節無感知

廢話少說,直接上程式碼!

### 遠端介面定義

```

public interface IProductService {

public Product findProductById(Integer id);

}

```

```

public interface IUserSerivce {

public User findUserById(Integer id);

}

```

```

public class Product implements Serializable {

private static final long serialVersionUID = 1L;

private Integer id;

private String name;

public Product(Integer id, String name) {

this.name = name;

this.id = id;

}

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 "Product{" +

"id=" + id +

", name='" + name + '\'' +

'}';

}

}

```

```

public class User implements Serializable {

private static final long serialVersionUID = 2L;

private Integer id;

private String name;

public User(Integer id, String name) {

this.name = name;

this.id = id;

}

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 + '\'' +

'}';

}

}

```

### 客戶端程式碼

服務呼叫者( RPC客戶端)獲取服務提供方(RPC服務端)提供的服務,並像呼叫本地介面一樣呼叫遠端方法。

```

public class Client {

public static void main(String[] args) {

//客戶端指定需要呼叫的遠端服務(客戶端可以注入服務提供方的服務)、呼叫服務的介面方法

IProductService productService = (IProductService) Stub.getstub(IProductService.class);

System.out.println(productService.findProductById(1));

IUserSerivce userSerivce = (IUserSerivce) Stub.getstub(IUserSerivce.class);

System.out.println(userSerivce.findUserById(123));

}

}

```

```

/**

* Stub用於代理服務端的方法,對服務呼叫者(客戶端)遮蔽socket連線、序列化等細節

*/

public class Stub {

public static Object getstub(Class clazz) {

InvocationHandler handler = new InvocationHandler() {

@Override

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

//模擬服務發現,直接socket連線到服務提供者的機器上,把要呼叫的服務名和方法名、方法引數傳到服務端,服務端收到後解析請求並執行方法呼叫返回資料

Socket socket = new Socket("127.0.0.1", 9999);

ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());

String clazzName = clazz.getName();

String methodName = method.getName();

Class<?>[] parameterTypes = method.getParameterTypes();

oos.writeUTF(clazzName);

oos.writeUTF(methodName);

oos.writeObject(parameterTypes);

oos.writeObject(args);

oos.flush();

ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());

Object o = ois.readObject();

oos.close();

socket.close();

return o;

}

};

Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, handler);

return o;

}

}

```

### 服務端程式碼

```

//具體的服務實現類

public class ProductServiceImpl implements IProductService {

@Override

public Product findProductById(Integer id) {

return new Product(id, "productA");

}

}

public class UserServiceImpl implements IUserSerivce {

@Override

public User findUserById(Integer id) {

return new User(id, "UserA");

}

}

```

```

/**

* 服務端不斷接受客戶端請求,按照雙方約定的格式(協議)反序列化服務端發來的訊息、完成服務端本地介面呼叫、返回序列化資料

*/

public class Server {

private static boolean running = true;

public static void main(String[] args) throws Exception {

ServerSocket serverSocket = new ServerSocket(9999);

while (running) {

Socket socket = serverSocket.accept();

process(socket);

socket.close();

}

serverSocket.close();

}

private static void process(Socket socket) throws Exception {

InputStream inputStream = socket.getInputStream();

OutputStream outputStream = socket.getOutputStream();

ObjectInputStream ois = new ObjectInputStream(inputStream);

//按照服務端寫入的格式來反序列化資料

String clazzName = ois.readUTF();

String methodName = ois.readUTF();

Class<?>[] parameterTypes = (Class<?>[]) ois.readObject();

Object[] args = (Object[]) ois.readObject();

//模擬從服務登錄檔查詢到具體的服務實現類

Class clazz = findService(clazzName);

Method method = clazz.getMethod(methodName, parameterTypes);

Object o = method.invoke(clazz.newInstance(), args);

ObjectOutputStream oos = new ObjectOutputStream(outputStream);

oos.writeObject(o);

oos.flush();

}

private static Class findService(String clazzName) throws Exception {

if (IProductService.class.getName().equals(clazzName)) {

return ProductServiceImpl.class;

} else if (IUserSerivce.class.getName().equals(clazzName)) {

return UserServiceImpl.class;

} else {

throw new Exception("no service found");

}

}

}

```

### 執行演示

先執行Server、再執行Client

一次完整的RPC呼叫流程如下:

1)服務消費方(client)呼叫以本地呼叫方式呼叫服務;

2)client stub接收到呼叫後負責將方法、引數等組裝成能夠進行網路傳輸的訊息實體;

3)client stub找到服務地址,並將訊息傳送到服務方;

4)服務提供方server 端收到訊息後進行解碼;

5)server 端根據解碼結果呼叫本地介面;

6)server 端將本地介面呼叫結果組裝成訊息併發送至消費方;

7)client stub接收到訊息,並進行解碼、反序列化;

8)服務消費方得到最終結果。

9
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 設計模式之 迭代器模式(附ArrayList原始碼分析)