1. 認識RPC
RPC(Remote Procedure Call)是指遠程過程調用,在微服務架構中,各個微服務之間的通信一般使用RPC,比如用戶微服務和訂單微服務,需求是在用戶微服務中查詢當前用戶的訂單,此時用戶服務是RPC的請求端,訂單微服務是RPC的服務端。
2. RPC原理
2.1 要實現RPC有3個難點問題:
①如何讓客戶端知道服務端提供的RPC接口以及方法?
方法一:客戶端和服務端在pom文件中同時依賴定義了接口的依賴,服務端需要實現這些接口,提供服務
方法二:比如Spring Cloud Feign是將請求通過動態代理加工成HTTP請求,返回時再將Response反加工過程
②客戶端和服務端如何通信?
方法一:Java可以通過NIO/BIO
③客戶端如何將參數傳遞到服務端?
將數據序列化獲得二進制數據進行傳輸 序列化的技術也很多java默認序列化/Json/XML/Protobuf/Hessian 序列化和反序列化效率對比
3. 手寫RPC框架
3.1 RPC Client
main線程
public static void main( String[] args )
{
IOrderService orderService = null;
RpcProxyClient rpcProxyClient = new RpcProxyClient();
//代理類的實現
orderService = rpcProxyClient.clientProxy(IOrderService.class,"localhost",8080);
System.out.println(orderService.getOrderList());
}
代理類
public class RpcProxyClient {
public <T>T clientProxy(final Class<T> interfaceCls, String host, int port){
//爲了更簡潔,創建一個繼承InvocationHandler的子類實現具體邏輯
return (T)Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class<?>[]{interfaceCls}, new RpcInvocationHandler(host, port));
}
}
InvocationHandler的子類
public class RpcInvocationHandler implements InvocationHandler {
private String host;
private int port;
public RpcInvocationHandler(String host, int port) {
this.host = host;
this.port = port;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通過socket創建連接
RpcNetTransport rpcNetTransport = new RpcNetTransport(host,port);
rpcNetTransport.createSocket();
//傳輸請求的數據 請求接口名稱,方法名稱,參數等
RpcRequest request = new RpcRequest();
request.setArgs(args);
request.setMethodName(method.getName());
request.setTypes(method.getParameterTypes());
request.setClassName(method.getDeclaringClass().getName());
return rpcNetTransport.send(request);
}
}
3.2 服務端
main線程
public static void main( String[] args ) throws IOException {
IOrderService service = new OrderImpl();
RpcProxyServer rpcProxyServer = new RpcProxyServer();
rpcProxyServer.publisher(service,8080);
}
RpcProxyServer接收客戶端請求並處理
public class RpcProxyServer {
//使用多線程防止阻塞
private final ExecutorService executorService = Executors.newCachedThreadPool();
public void publisher(Object service, int port){
ServerSocket serverSocket = null;
Socket socket = null;
try {
serverSocket = new ServerSocket(port);
while (true){
socket = serverSocket.accept();
//具體的處理類
executorService.submit(new ProcessHandler(socket, service));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
ProcessHandler 反序列化處理以及反射調用目標方法
public class ProcessHandler implements Runnable {
private Socket socket;
private Object service;
public ProcessHandler(Socket socket, Object service) {
this.socket = socket;
this.service = service;
}
@Override
public void run() {
ObjectInputStream objectInputStream = null;
ObjectOutputStream objectOutputStream = null;
InputStream inputStream = null;
try {
inputStream = socket.getInputStream();
objectInputStream = new ObjectInputStream(inputStream);
RpcRequest request = (RpcRequest)objectInputStream.readObject();
//反射調用目標方法
Object rs = invoke(request);
System.out.println("服務端處理執行的結果" + rs);
objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(rs);
objectOutputStream.flush();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} finally {
//TODO 關閉流
}
}
private Object invoke(RpcRequest request) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = Class.forName(request.getClassName());
Method method = clazz.getMethod(request.getMethodName(),request.getTypes());
return method.invoke(service, request.getArgs());
}
}
4. 改進的方向
- 只支持java語言,耦合性比較強。可以採用json或者xml進行數據傳輸
- Java NIO/BIO效率比較低,可以採用Netty BIO傳輸
- Java序列化數據量大,可以考慮其他方式Hessian/Protobuf/Avro
- 使用註解方式標識需要RPC的服務端和客戶端
- 考慮如何做成中間件的形式
5. 現有的RPC框架
- Hessian
- Thrift
- Dubbo
- Motan
- gRPC
代碼github鏈接:RPC V1