面試官不講碼德,欺負我一個年輕的開發工程師,問如果是你怎么設計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物件,他會幫你去找到服務端實作類,實作遠程呼叫
package rpc.socket;
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;
}
});
}
}
這是服務端介面
package rpc.socket;
import java.io.IOException;
public interface Server {
void Handler() throws IOException;
}
這是我實作的服務端rpc,就是采用一個nio接受客服端資料,然后去呼叫服務端的方法
package rpc.socket;
import rpc.socket.service.GODService;
import rpc.socket.service.GODServiceImpl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.net.Socket;
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);
}
@Override
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) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}).start();
}
}
}
這里我們來建立一個服務端
首先定義一個介面
package rpc.socket.service;
public interface GODService {
String getGod(String lockMessage,String offerings);
}
實作類
package rpc.socket.service;
public class GODServiceImpl implements GODService {
@Override
public String getGod(String lockMessage, String offerings) {
System.out.println("的供品:"+ offerings);
System.out.println("你的愿望:"+lockMessage);
return "年輕人還是少做夢!";
}
}
寫一個服務端的啟動類
package rpc.socket.demo;
import rpc.socket.RpcServer;
import java.io.IOException;
public class ServiceStart {
public static void main(String[] args) throws IOException {
new RpcServer().Handler();
}
}
然后是客服端去呼叫
package rpc.socket.demo;
import rpc.socket.Clinet;
import rpc.socket.RpcClint;
import rpc.socket.service.GODService;
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);
}
}
我們來看兩遍的結果
客服端結果

我來看服務端結果
’

很顯然呼叫成功了
現在我們來畫圖模擬一下呼叫程序
你學廢了嗎
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/229154.html
標籤:java
上一篇:Spring框架及七大模塊
