一、RPC簡介
分佈式系統的成員之一,解決服務之間的調用問題。遠程調用時,RPC實現了調用遠程服務能夠像本地調用一樣方便,讓調用者感知不到遠程調用的邏輯。
二、RPC組成成員
- 動態代理
- 序列化
- 網絡傳輸
- 調用服務API接口
本次RPC搭建使用的接口代理模式、Java默認Serializable序列化以及BIO網絡傳輸方式
三、源碼實現
- 動態代理模塊封裝
package com.ruider.Invoker;
/**
* 動態代理工廠,對相應的class生產其對應代理類
*/
import com.ruider.common.ServiceInformation;
import com.ruider.networkCommunication.BIOService;
import com.ruider.networkCommunication.NetIO;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class InvokerUtils implements InvocationHandler {
/**
* 調用服務類
*/
private Class clazz;
/**
* 調用服務管理
*/
private ServiceInformation serviceInformation;
/**
* 獲取代理對象
* @param T
* @param url
* @param port
* @return
*/
public Object getBean (Class T, String url, int port) {
this.clazz = T;
ServiceInformation serviceInformation = new ServiceInformation();
serviceInformation.setServiceURL(url);
serviceInformation.setPort(port);
serviceInformation.setClassName(T.getName());
this.serviceInformation = serviceInformation;
return Proxy.newProxyInstance(T.getClassLoader(), new Class[]{T}, this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
NetIO BIO = new BIOService();
this.serviceInformation.setMethod(method);
this.serviceInformation.setArgs(args);
return BIO.send(serviceInformation);
}
}
- 網絡傳輸模塊封裝
package com.ruider.networkCommunication;
import com.ruider.common.ServiceInformation;
/**
* BIO服務
*/
public interface NetIO {
/**
* 客戶端發送請求到其他服務端
* @param serviceInformation
* @return
*/
Object send (ServiceInformation serviceInformation);
/**
* 本機作爲服務端接受請求並且處理請求
* @param port
*/
void recv (int port);
}
package com.ruider.networkCommunication;
import com.ruider.common.ServiceInformation;
import com.ruider.common.UserApi;
import com.ruider.server.UserService;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 使用BIO通信
*/
public class BIOService implements NetIO {
/**
* 主機IP
*/
private String url;
/**
* 端口
*/
private int port;
/**
* 客戶端socket
*/
private Socket clientSocket;
/**
* 服務端socket
*/
private ServerSocket serverSocket;
public BIOService() {}
public BIOService(String url, int port) {
this.url = url;
this.port = port;
}
/**
* 客戶端採用BIO通信方式發送請求信息
* @param serviceInformation
* @return
*/
@Override
public Object send(ServiceInformation serviceInformation) {
ObjectOutputStream objectOutputStream = null;
ObjectInputStream objectInputStream = null;
try {
this.clientSocket = new Socket(serviceInformation.getServiceURL(), serviceInformation.getPort());
String className = serviceInformation.getClassName();
String methodName = serviceInformation.getMethod().getName();
Class<?>[] parameterTypes = serviceInformation.getMethod().getParameterTypes();
Object[] args = serviceInformation.getArgs();
objectOutputStream = new ObjectOutputStream(this.clientSocket.getOutputStream());
objectOutputStream.writeUTF(className);
objectOutputStream.writeUTF(methodName);
objectOutputStream.writeObject(parameterTypes);
objectOutputStream.writeObject(args);
objectInputStream = new ObjectInputStream(this.clientSocket.getInputStream());
Object o = objectInputStream.readObject();
System.out.println("Object = " + o);
return o;
}
catch (Exception e) {
if (this.clientSocket != null) {
try {
this.clientSocket.close();
}catch (IOException e1) {
e1.printStackTrace();
}
}
if (objectOutputStream != null) {
try {
objectOutputStream.close();
}catch (IOException e1) {
e1.printStackTrace();
}
}
if (objectInputStream != null) {
try {
objectInputStream.close();
}catch (IOException e1) {
e1.printStackTrace();
}
}
return null;
}
finally {
if (this.clientSocket != null) {
try {
this.clientSocket.close();
}catch (IOException e1) {
e1.printStackTrace();
}
}
if (objectOutputStream != null) {
try {
objectOutputStream.close();
}catch (IOException e1) {
e1.printStackTrace();
}
}
if (objectInputStream != null) {
try {
objectInputStream.close();
}catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
/**
* 服務端採用BIO通信獲取數據
* @return
*/
@Override
public void recv (int port) {
ObjectInputStream objectInputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
this.serverSocket = new ServerSocket(port);
System.out.println("啓動遠程服務監聽...");
//監聽客戶端發來消息
while (true){
Socket socket = serverSocket.accept();
objectInputStream = new ObjectInputStream(socket.getInputStream());
//客戶端傳輸類名
String className = objectInputStream.readUTF();
String methodName = objectInputStream.readUTF();
Class<?>[] parameterTypes = (Class<?>[])objectInputStream.readObject();
Object[] arguments = (Object[]) objectInputStream.readObject();
Class clazz = null;
//服務匹配
if(className.equals(UserApi.class.getName())){
clazz = UserService.class;
}
//clazz = UserService.class;
Method method = clazz.getMethod(methodName,parameterTypes);
Object result = method.invoke(clazz.newInstance(),arguments);
objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(result);
objectOutputStream.flush();
socket.close();
}
} catch (Exception e) {
try {
if (objectOutputStream != null) {
objectOutputStream.close();
}
if (objectInputStream != null) {
objectInputStream.close();
}
if (this.serverSocket != null) {
this.serverSocket.close();
}
}
catch (IOException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
finally {
try {
if (objectOutputStream != null) {
objectOutputStream.close();
}
if (objectInputStream != null) {
objectInputStream.close();
}
if (this.serverSocket != null) {
this.serverSocket.close();
}
}
catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
- 調用服務API接口
package com.ruider.common;
/**
* RPC服務調用公用接口API
*/
public interface UserApi {
User getUser(Integer id);
}
- 調用服務信息管理類
package com.ruider.common;
/**
* 被調用服務信息
*/
import java.io.Serializable;
import java.lang.reflect.Method;
public class ServiceInformation implements Serializable {
/**
* 被調用服務的主機ip
*/
private String serviceURL;
/**
* 端口
*/
private int port;
/**
* 類名
*/
private String className;
/**
* 方法名
*/
private Method method;
/**
* 方法參數
*/
private Object[] args;
public String getServiceURL() {
return serviceURL;
}
public void setServiceURL(String serviceURL) {
this.serviceURL = serviceURL;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
}
四、功能測試
- 客戶端
package com.ruider.client;
import com.ruider.RPC.RPC;
import com.ruider.common.User;
import com.ruider.common.UserApi;
public class Client {
//本地調用遠程服務
public static void main(String args[]){
UserApi userApi = (UserApi) RPC.rpc(UserApi.class, "127.0.0.1", 9000);
User user = userApi.getUser(1);
System.out.println("本地輸出遠程調用結果:" + user.toString());
}
}
- 服務端
package com.ruider.server;
import com.ruider.networkCommunication.BIOService;
import com.ruider.networkCommunication.NetIO;
public class Server {
public static void main(String[] args) {
NetIO BIOService = new BIOService();
BIOService.recv(9000);
}
}
package com.ruider.server;
import com.ruider.common.User;
import com.ruider.common.UserApi;
public class UserService implements UserApi {
@Override
public User getUser(Integer id) {
User user=new User();
user.setName("RuiDer");
user.setId(id);
System.out.println("user = " + user.toString());
return user;
}
}
- 測試結果
五、GitHub源碼地址
https://github.com/RuiDer/RuiDer_RPC