標籤:cep 代碼 cto href 發送 bip 服務端 通過 proxy
轉載請註明出處:jiq?欽‘s technical Blog
RPC。遠端程序呼叫。就是調用遠端機器上的方法。
原理事實上非常easy。就是client上執行的程式在調用對象方法時,底層將針對該方法的調用轉換為TCP/HTTP請求,發送到遠端server,遠端server監聽固定port,收到這個TCP/HTTP請求後會解析出相關資訊,包含client想要調用哪個類的哪個方法,參數是什麼等,然後進行相應的調用,將調用結果再通過資料包發回就可以。
RPC中通常會有一些“契約”的概念。即client和服務端兩方約定好的介面,表明服務端實現了哪些介面,client能夠使用這些介面。以下以一個我實現的簡單的RPC架構為例說明。
一、client
client通常會建立一個服務訪問代理,事實上就是這個契約介面類型的對象,以下是一個建立服務代理的範例:
//建立代理,調用服務IHelloService proxy= (IHelloService)RpcProxyFactory.createProxy(IHelloService.class,"HelloService", 3000);System.out.println("服務調用結果:"+proxy.sayHello("季義欽"));
當中createProxy介面的實現例如以下:
/** * 建立遠程調用代理 * @paramobjClass 介面類對象 * @paramserviceName 服務名稱 * @paramURL 服務地址 * @paramtimeout 連線逾時時限 * @return * @throws Exception */publicstatic Object createProxy(Class<?> objClass, String serviceName, StringURL, int timeout) throws Exception{//解析調用地址String[]urls = URL.split(":");StringipAddress = urls[1];intport =Integer.parseInt(urls[2]); //建立InvocationHandlerCBIPInvocationHandlerhandler = new CBIPInvocationHandler(ipAddress, port, timeout);handler.setServiceName(serviceName); //返回代理returnProxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{objClass}, handler);}
非常明顯能夠看到藉助的是JavaInvocationHandler動態代理機制。通過Proxy靜態方法newProxyInstance返回服務代理。
當中CBIPInvocationHandler是client代碼的核心。是實現了InvocationHandler介面的Java動態代理,其invoke方法實現例如以下:
/** * 全部方法調用都通過invoke來運行 */publicObject invoke(Object proxy, Method method, Object[] args) throws Throwable{Objectresult = null;try{//擷取調用相關資訊StringbeanName = this.serviceName; //this.targrtObject.getClass().getSimpleName();StringmethodName = method.getName();String[]argTypes = createParamSignature(method.getParameterTypes()); //發起遠程方法同步調用,傳遞調用資訊 result = invokeSync(beanName,methodName, argTypes, args);}catch(Exceptionee){ ee.printStackTrace();returnnull;} returnresult;}
invokeSync方法作用就是將調用相關資訊通過Mina架構(JavaNIO架構,非同步事件驅動,提供TCP/UDP通訊的高層抽象API)發送出去。
二、服務端
通過Mina架構實現的服務端程式擷取來自client的方法調用的請求資料包後。解析出調用相關資訊。
/** * 收到來自client的訊息 */ @Override public void messageReceived(IoSessionsession, Object message) throws Exception { if(message instanceofCbipTcpRequest) { CbipTcpRequestrequest = (CbipTcpRequest)message; try { //讀取spring設定檔依據服務名稱擷取服務全限定名,然後反射出目標執行個體 ApplicationContextctx = new ClassPathXmlApplicationContext("dsf_config.xml"); ServiceConfigservice = (ServiceConfig)ctx.getBean(request.getBeanName()); Class<?>objClass = Class.forName(service.getServiceName()); Objectobj = objClass.newInstance(); //調用指定的方法 CBIPSkeletonskeleton = new CBIPSkeleton(obj, obj.getClass()); Objectresult = skeleton.invoke(request.getMethodName(), request.getArgs()); //回複 CbipTcpResponse response= new CbipTcpResponse(); response.setRequestID(request.getId()); // 請求ID response.setBeanName(request.getBeanName()); response.setMethodName(request.getMethodName()); response.setResult(result); session.write(response); //相似通訊端 }catch(Exceptionee) { ee.printStackTrace(); } } }
這個資料包接收函數主要三個進行步驟:
(1)通過收到的client請求知道調用哪個類,從設定檔裡讀取該類的全限定名載入到JVM。反射出該類執行個體;
(2)通過反射出來的執行個體進行方法調用;
(3)通過Mina架構返回調用結果;
Java基礎:動態代理在RPC架構中應用