面試官不講碼德,欺負我一個年輕的開發工程師,問如果是你怎麼設計RPC?
RPC也不是很難啊,教你如何使用socket加動態代理與反射實現Rpc
先來解釋解釋一下rpc,首先很多人以為rpc是一種協議,其實這個就是出錯誤的,rpc:是遠程過程調用;
看他的全程英文Remote Position Control 他其實是一種設計思想而已,解決分布式各個系統之間的調用關係。
我們今天就用socket方式實現一套rpc調用框架,不多說上代碼
package rpc.socket;
//先定義一個clinet接口
public interface Clinet<T> {
T getService(Class<T> tClass);
}
這是我個人寫的一個實現類,給位大牛可以嘗試實現
這裡是採用動態代理。把調用大的過程交給代理對象,這樣就可以屏蔽掉底層的網絡和整個調用過程,
對於客服而言只用給一個接口的class對象,他會幫你去找到服務端實現類,實現遠程調用
import java.io.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import java.util.Properties;
public class RpcClint implements Clinet {
final static String RPC_SERVER_HOST="RPC_SERVER_HOST";
final static String RPC_SERVER_PORT="RPC_SERVER_PORT";
@Override
public Object getService(Class aClass) {
return Proxy.newProxyInstance(aClass.getClassLoader(), new Class[]{aClass}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//讀取配置文件
Properties properties = new Properties();
InputStream resourceAsStream = aClass.getClassLoader().getResourceAsStream("rpc/RpcConfig.properties");
properties.load(resourceAsStream);
String host = properties.getProperty(RPC_SERVER_HOST);
Integer port =new Integer((String) properties.get(RPC_SERVER_PORT));
//RPC註冊過程
Socket socket = new Socket(host, port);
OutputStream in= socket.getOutputStream();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(in);
//告訴服務端調用的是哪個類
objectOutputStream.writeObject(aClass.getName());
//告訴服務端調用的是哪個方法
objectOutputStream.writeObject(method.getName());
//告訴服務端調用方法傳入的參數
objectOutputStream.writeObject(args);
//告訴服務端調用方法的參數類型
objectOutputStream.writeObject(method.getParameterTypes());
//完成序列化,刷新
objectOutputStream.flush();
//接受服務端響應結果
ObjectInputStream returnObj=new ObjectInputStream(socket.getInputStream());
Object o = returnObj.readObject();
//關閉流
objectOutputStream.close();
returnObj.close();
resourceAsStream.close();
socket.close();
return o;
}
});
}
這是服務端接口
import java.io.IOException;
public interface Server {
void Handler() throws IOException;
這是我實現的服務端rpc,就是採用一個nio接受客服端數據,然後去調用服務端的方法
import rpc.socket.service.GODService;
import rpc.socket.service.GODServiceImpl;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class RpcServer implements Server {
//裝用實現類的bena
static Map<String,Class> RESmap;
//加載所有需要註冊的額服務
static {
//這裡可以用Aop 加註解的方式來把需要暴露的服務,註冊到服務列表裡面去
RESmap=new ConcurrentHashMap<>();
//把服務註冊到註冊表
RESmap.put(GODService.class.getName(),GODServiceImpl.class);
public void Handler() throws IOException {
ServerSocket serverSocket = new ServerSocket(8080);
//採取傳統的bio處理
while (true){
//等待客服端連結
Socket accept = serverSocket.accept();
new Thread(()->{
try {
//等待客戶端輸入
ObjectInputStream in= new ObjectInputStream(accept.getInputStream());
//獲取客戶端傳過來的類名稱
String className = (String) in.readObject();
//獲取客戶端客服端傳過來的方法名稱
String methodName=(String) in.readObject();
//獲取客戶端傳過來的參數
Object[] args = (Object[]) in.readObject();
//獲取客服的端傳過來的參數類型
Class[] argsType = (Class[]) in.readObject();
//從註冊表中獲取服務的字節碼
Class aClass = RESmap.get(className);
//通過字節碼對象獲取構造器
Constructor constructor = aClass.getConstructor();
constructor.setAccessible(true);
//通過反射的方式創建對象並且執行對象的方法
Object invoke = aClass.getMethod(methodName,argsType).invoke(constructor.newInstance(), args);
//把返回結果寫回給客戶端
ObjectOutputStream returnObject=new ObjectOutputStream(accept.getOutputStream());
returnObject.writeObject(invoke);
//關閉流
in.close();
returnObject.close();
accept.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
} catch (IllegalAccessException e) {
} catch (InstantiationException e) {
} catch (NoSuchMethodException e) {
} catch (InvocationTargetException e) {
}
}).start();
}
這裡我們來建立一個服務端
首先定義一個接口
package rpc.socket.service;
public interface GODService {
String getGod(String lockMessage,String offerings);
實現類
public class GODServiceImpl implements GODService {
public String getGod(String lockMessage, String offerings) {
System.out.println("的供品:"+ offerings);
System.out.println("你的願望:"+lockMessage);
return "年輕人還是少做夢!";
寫一個服務端的啟動類
package rpc.socket.demo;
import rpc.socket.RpcServer;
public class ServiceStart {
public static void main(String[] args) throws IOException {
new RpcServer().Handler();
然後是客服端去調用
import rpc.socket.Clinet;
import rpc.socket.RpcClint;
public class clintDemo {
public static void main(String[] args) {
Clinet Clint = new RpcClint();
GODService service = (GODService)Clint.getService(GODService.class);
String lockReturn = service.getGod("請給讓我中彩票吧", "獻祭我老闆的二十年壽命!");
System.out.println(lockReturn);
我們來看兩遍的結果
客服端結果
我來看服務端結果
很顯然調用成功了
現在我們來畫圖模擬一下調用過程