RPC框架中通常都有3個角色:服務提供者、服務消費者和註冊中心。服務提供者將服務註冊到註冊中心,服務消費者從註冊中心拉取服務的地址,並根據服務地址向服務提供者發起RPC調用。動態代理在這個RPC調用的過程當中有什麼做用?對於服務消費者,通常只會依賴服務接口,而服務的具體實現是在服務提供者這一端的,服務消費者和服務提供者分別部署在不一樣的機器上,服務消費者調用接口中的方法時怎麼可以獲得結果呢?JDK的動態代理就派上用場了。服務消費者使用JDK的動態代理技術,能夠建立接口的代理對象,並在回調函數中將本身要調用的接口名稱、方法簽名信息經過http或者tcp的方式發送給服務提供者,服務提供者再經過反射的方式調用本地的服務,最後將結果經過http或tcp的方式返回給消費者。java
下面是一個簡單的演示程序:架構
1 import java.lang.reflect.InvocationHandler; 2 import java.lang.reflect.Method; 3 import java.lang.reflect.Proxy; 4 import java.util.HashMap; 5 import java.util.Map; 6 7 /** 8 * 動態代理在RPC中的使用 9 * 10 * @author syj 11 */ 12 public class JDKProxyTest { 13 14 // --------------------------- 模擬RPC客戶端 --------------------------- 15 16 // 客戶端依賴服務端的接口(實現類在服務端) 17 private static IUserService userService; 18 19 public static void main(String[] args) { 20 // 根據接口建立代理對象 21 userService = (IUserService) Proxy.newProxyInstance( 22 JDKProxyTest.class.getClassLoader(), 23 new Class[]{IUserService.class}, 24 new InvocationHandler() { 25 // 在回調方法模擬進行RPC調用(經過http或者tcp與服務端通訊) 26 @Override 27 public Object invoke(Object proxy, Method method, Object[] params) throws Throwable { 28 return rpcInvoke(IUserService.class.getSimpleName(), method.getName(), method.getParameterTypes(), params); 29 } 30 } 31 ); 32 // 本地調用 33 String result = userService.sayHello("hello"); 34 System.out.println(">>>> result =" + result); 35 } 36 37 38 // --------------------------- 模擬RPC服務端 --------------------------- 39 40 /** 41 * 反射調用 42 * 43 * @param methodName 方法名稱 44 * @param parameterTypes 方法參數類型 45 * @param parameters 方法參數 46 * @return 47 */ 48 private static Object rpcInvoke(String interfaceName, String methodName, Class<?>[] parameterTypes, Object[] parameters) { 49 Object result = null; 50 try { 51 // 根據接口名稱從Bean容器中獲取Bean實例 52 Object serviceBean = beanMap.get(interfaceName); 53 // 反射調用 54 Class<?> serviceClass = serviceBean.getClass(); 55 Method method = serviceClass.getMethod(methodName, parameterTypes); 56 method.setAccessible(true); 57 // 獲得調用結果 58 result = method.invoke(serviceBean, parameters); 59 } catch (Exception e) { 60 e.printStackTrace(); 61 } 62 return result; 63 } 64 65 // 模擬Bean容器, key爲接口名稱, value爲bean實例 66 private static Map<String, Object> beanMap = new HashMap<String, Object>() {{ 67 put("IUserService", new UserServiceImpl()); 68 }}; 69 }
接口(服務提供者和服務的消費者都會依賴該接口):框架
1 public interface IUserService { 2 String sayHello(String content); 3 }
服務提供者實現類:tcp
public class UserServiceImpl implements IUserService { @Override public String sayHello(String content) { return content + "::" + System.currentTimeMillis(); } }
其實,JDK的動態代理技術,不只能夠應用在RPC框架中,也能夠應用在全部基於客戶端和服務端通訊的架構中,好比微服務架構中的註冊中心、配置中心、消息中心等。ide