爲什麼有了HTTP還要使用RPC
- HTTP是被設計用來在桌面瀏覽器上的
- HTTP是無狀態的、每次傳輸都需要攜帶報文頭,會造成一部分額外的網絡開銷
- 適用於接口不多的情況下,簡單,直接;
- RPC會有註冊中心,有豐富的監控中心,提供軟負載均衡,動態擴展,還有安全等
最重要的一點,RPC是一套理論,使用HTTP協議也可以實現RPC,RPC是解耦的一種方式!!!
RPC需要解決的問題
- 服務調用問題(服務的註冊與發現)
- 遠程代理問題(jdk動態代理,屏蔽網絡問題)
- 通信問題(BIO)
- 序列化問題(jdk序列號)
- 註冊服務的實例化(反射)
我的簡單實現步驟是這樣的:
-
client通過代理拿到對象;
-
當實際訪問方法時,通過jdk動態代理拿到(接口名,方法名,參數類型,參數值);
-
然後將數據寫入socket中,發往server 端;
-
server端接受到以後使用同樣的順序解析數據,通過接口名拿到持有的實現類對象 ;
-
再通過反射調用對應的服務方法;
-
將方法結果發給client;
下面是代碼解釋:
1.Server接受客戶端請求,並處理請求
@Override
public void run() {
try (ObjectInputStream inputStream = new ObjectInputStream(client.getInputStream());
ObjectOutputStream outputStream = new ObjectOutputStream(client.getOutputStream())) {
// 接受客戶端請求
String serviceName = inputStream.readUTF();
String methodName = inputStream.readUTF();
Class<?>[] paraType = (Class<?>[]) inputStream.readObject();
Object[] args = (Object[]) inputStream.readObject();
// 進行業務處理,並返回結果
Class serviceClass = serviceHolder.get(serviceName);
if (null == serviceClass) {
throw new ClassNotFoundException(serviceClass + " not found!");
}
Method method = serviceClass.getMethod(methodName, paraType);
Object result = method.invoke(serviceClass.newInstance(), args);
outputStream.writeObject(result);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
2. 服務的持有容器使用HashMap(key=服務名,Class = 實際服務的Class類)
Map<String, Class> serviceHolder = new HashMap<>();
3. client 端調用實現
public class RPCClientFrame {
public static <T> T getRemoteProxyObj(final Class<?> serviceInterface, final InetSocketAddress address) {
return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[] { serviceInterface },
new DynProxy(serviceInterface, address));
}
private static class DynProxy implements InvocationHandler {
private final Class<?> serviceInterface;
private final InetSocketAddress address;
public DynProxy(Class<?> serviceInterface, InetSocketAddress address) {
this.serviceInterface = serviceInterface;
this.address = address;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = null;
ObjectOutputStream output = null;
ObjectInputStream input = null;
try{
socket = new Socket();
socket.connect(address);
output = new ObjectOutputStream(socket.getOutputStream());
//發送客戶端的調用請求
output.writeUTF(serviceInterface.getName()); //接口名
output.writeUTF(method.getName()); //調用接口
output.writeObject(method.getParameterTypes()); //參數類型
output.writeObject(args); //參數值
output.flush();
input = new ObjectInputStream(socket.getInputStream());
return input.readObject();
}finally {
if(output !=null) output.close();
if(input !=null) input.close();
if(socket !=null) socket.close();
}
}
}
}
4. 測試代碼
@Test
public void shouldAnswerWithTrue() {
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 9000);
Service service = RPCClientFrame.getRemoteProxyObj(Service.class, address);
String res = service.recivedParams("abc", 1000);
assertEquals("abc|1000", res);
}
5.Service的實現類
public class ServiceImpl implements Service {
@Override public String recivedParams(String a, long b) {
return "params:"+a +"|" +b;
}
}
說明:此實現是最簡單的一種實現(直連),未考慮其上層的內容,但對於其核心的實現原理基本解釋清楚了。
如有問題,可私信