定義:RPC(Remote Procedure Call Protocol)——遠程過程調用協議 ,RPC協議假定某些傳輸協議的存在,如TCP或UDP,爲通訊程序之間攜帶信息數據。在OSI網絡通訊模型中,RPC跨越了傳輸層和應用層 ,RPC使得開發包括網絡分佈式多程序在內的應用程序更加容易。java
個人理解:與其說把RPC 看做是一種協議,倒不如把 它看做是一種 客戶機/服務器交互的模式,可是 RPC必定是基於 TCP 或者 其餘 通訊協議的 git
下面咱們來看一下一個RPC調用的流程涉及哪些通訊細節:github
RPC的目標就是要2~8這些步驟都封裝起來,讓用戶對這些細節透明。緩存
public interface IRpcService extends Serializable{ }
public interface IHelloService extends IRpcService{ String sayHi(String name,String message); }
public class HelloServiceImpl implements IHelloService{ private static final long serialVersionUID = 146468468464364698L; @Override public String sayHi(String name, String message) { return new StringBuilder().append("hi~!").append(",").append(message).toString(); } }
注:這個地方 我沒有采用dom4j 解析配置文件的形式 進行接口註冊 有時間的朋友能夠多加一層服務器
public interface Server { //Socket端口 int PORT = 8080; //啓動服務端 void start() throws IOException; //中止服務端 void stop(); /** * 服務註冊 * -- serviceInterface 對外暴露接口 * -- 內部實現類 */ void regist(Class<? extends IRpcService> serviceInterface,Class<? extends IRpcService> impl); }
public class ServerCenter implements Server{ /**線程池 接收客戶端調用**/ private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 20, 200, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(10)); /**服務註冊緩存**/ public static final Map<String,Class<?>> serviceRegistry = new HashMap<>(); /** * 啓動服務 */ @Override public void start() throws IOException { ServerSocket server = new ServerSocket(); server.bind(new InetSocketAddress(PORT)); try { while(true){ executor.execute(new ServiceTask(server.accept())); } } finally { server.close(); } } /** * 中止服務 */ @Override public void stop() { executor.shutdown(); } /** * 註冊服務 */ @Override public void regist(Class<? extends IRpcService> serviceInterface, Class<? extends IRpcService> impl) { serviceRegistry.put(serviceInterface.getName(), impl); } private static class ServiceTask implements Runnable{ Socket client = null; public ServiceTask(Socket client) { this.client = client; } @Override public void run() { ObjectInputStream input = null; ObjectOutputStream output = null; try { input = new ObjectInputStream(client.getInputStream()); String serviceName = input.readUTF(); String methodName = input.readUTF(); Class<?>[] parameterTypes = (Class<?>[]) input.readObject(); Object[] arguments = (Object[]) input.readObject(); Class<?> serviceClass = serviceRegistry.get(serviceName); if(serviceClass == null){ throw new ClassNotFoundException(serviceName + "not found"); } Method method = serviceClass.getMethod(methodName, parameterTypes); Object result = method.invoke(serviceClass.newInstance(), arguments); //將執行結果反序列化 經過socket返給客戶端 output = new ObjectOutputStream(client.getOutputStream()); output.writeObject(result); } catch (Exception e) { e.printStackTrace(); } finally { if(input != null){ try { input.close(); } catch (IOException e) { e.printStackTrace(); } } if(output != null){ try { output.close(); } catch (IOException e) { e.printStackTrace(); } } if(client != null){ try { client.close(); } catch (IOException e) { e.printStackTrace(); } } } } } public static void main(String[] args) throws Exception { ServerCenter center = new ServerCenter(); center.regist(IHelloService.class,new HelloServiceImpl().getClass()); center.start(); } }
public class Client { @SuppressWarnings("unchecked") public static <T extends IRpcService>T getRemoteProxyObj(final Class<? extends IRpcService> serviceInterface,final InetSocketAddress addr){ return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Socket socket = null; ObjectOutputStream output = null; ObjectInputStream input = null; try { //1.建立Socket客戶端,根據指定地址鏈接遠程服務提供者 socket = new Socket(); socket.connect(addr); //2.將遠程服務調用所需的接口類、方法名、參數列表等編碼後發送給服務提供者 output = new ObjectOutputStream(socket.getOutputStream()); output.writeUTF(serviceInterface.getName()); output.writeUTF(method.getName()); output.writeObject(method.getParameterTypes()); output.writeObject(args); //3.同步阻塞等待服務器返回應答 獲取應答後返回 input = new ObjectInputStream(socket.getInputStream()); return input.readObject(); } finally{ if(socket != null){ socket.close(); } if(output != null){ output.close(); } if(input != null){ input.close(); } } } }); } }
注:測試以前 須要開啓服務端網絡
public class RpcTest { public static void main(String[] args) throws IOException { IHelloService service = Client.getRemoteProxyObj(IHelloService.class, new InetSocketAddress(8080)); System.out.println(service.sayHi("張三", "新年快樂!")); } }
就這樣咱們實現了一個簡陋的RPC併發
本文意在經過實現簡單的RPC,去真正意義上對RPC框架的實現原理有初步的瞭解,而不是人云亦云。app
此RPC實現有諸多缺點,可是 咱們只要明白RPC的基座 其餘的RPC框架只是完善基座以及擴展而已 。框架