昨天看了一篇關於用幾行代碼實現RPC框架的博客[http://javatar.iteye.com/blog...](),收穫很大,因而我想在這篇博客的基礎上理一理思路,儘量的多加一點註釋,進一步下降學習RPC框架原理的門檻。java
先上一個原理圖,讀者可根據此圖來幫助理解後續的代碼。
服務器
RpcFramework核心類框架
import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.ServerSocket; import java.net.Socket; public class RpcFramework { /** * 暴露服務 * * @param service 服務實現 * @param port 服務端口 * @throws Exception */ public static void export(final Object service, int port) throws Exception { System.out.println("Export service " + service.getClass().getName() + " on port " + port); ServerSocket server = new ServerSocket(port); //一直輪詢,相比while(true),這種方式性能更佳 for(;;) { try { //此處阻塞一直等到有consumer請求過來 final Socket socket = server.accept(); //每來一個消費請求就開啓一個新的線程 new Thread(() -> { try { try { ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); try { //consumer會分三次發送所須要的方法信息,這裏的readUTF(),readObject()都會發生阻塞 String methodName = input.readUTF(); Class<?>[] parameterTypes = (Class<?>[])input.readObject(); Object[] arguments = (Object[])input.readObject(); ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); try { //獲取到目標方法 Method method = service.getClass().getMethod(methodName, parameterTypes); //經過反射執行目標方法並返回結果 Object result = method.invoke(service, arguments); //將執行結果返回給consumer output.writeObject(result); } catch (Throwable t) { output.writeObject(t); } finally { output.close(); } } finally { input.close(); } } finally { socket.close(); } } catch (Exception e) { e.printStackTrace(); } }).start(); } catch (Exception e) { e.printStackTrace(); } } } /** * 引用服務 * * @param <T> 接口泛型 * @param interfaceClass 接口類型 * @param host 服務器主機名 * @param port 服務器端口 * @return 遠程服務 * @throws Exception */ @SuppressWarnings("unchecked") public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception { System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port); //經過JDK動態代理的方式直接返回給調用refer方法的調用者一個被動態代理處理過的 對象 return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass}, new InvocationHandler() { @Override //調用該對象的每一個方法都會先去調用下面的邏輯 public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable { //當方法真實被調用的時候纔會發起RPC遠程請求provider執行服務 Socket socket = new Socket(host, port); try { ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); try { //分三次發送方法所須要的信息 output.writeUTF(method.getName()); output.writeObject(method.getParameterTypes()); output.writeObject(arguments); ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); try { //獲得服務執行的最終結果 Object result = input.readObject(); if (result instanceof Throwable) { throw (Throwable) result; } return result; } finally { input.close(); } } finally { output.close(); } } finally { socket.close(); } } }); } }
服務接口socket
public interface HelloService { String hello(String name); }
服務接口實現分佈式
public class HelloServiceImpl implements HelloService { @Override public String hello(String name) { System.out.println("被調用了"); return "Hello" + name; } }
provider引導類ide
public class RpcProvider { public static void main(String[] args) throws Exception { HelloService service = new HelloServiceImpl(); RpcFramework.export(service, 1234); } }
consumer引導類性能
public class RpcConsumer { public static void main(String[] args) throws Exception { //此時獲取到的service是被JDK動態代理包裝後的service,在調用方法的時候會進行遠程調用 HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1234); for (int i = 0; i < Integer.MAX_VALUE; i ++) { String hello = service.hello("World" + i); System.out.println(hello); Thread.sleep(1000); } } }
下圖是provider項目的類結構圖學習
下圖是consumer項目的類結構圖spa
簡單總結了一下簡易RPC框架,題主最近正在學習dubbo原理,故並無延展講太多分佈式內容,但願隨着學習的深刻之後能寫一篇關於分佈式的文章,共勉!.net