RPC场景和过程
在微服务环境下,存在大量的跨 JVM 进行方法调用的场景,如下图:
具体到某一个调用来说,希望 A 机器能通过网络,调用 B 机器内的某个服 务方法,并得到返回值:
RPC 的实现切入口
从本质上来讲,某个 jvm 内的对象方法,是无法在 jvm 外部被调用的
好在 java 中的对象方法的调用,还有反射模式的调用:
public class InvokeUtils {
public static Object call(Object target,String methodName,Class [] argTypes,Object[]args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Method method = target.getClass().getMethod(methodName, argTypes);
return method.invoke(target,args);
}
public static Object call(Map<String,String> info, ApplicationContext ctx){
String targetStr = info.get("target");
String methodName = info.get("methodName");
String arg = info.get("arg");
try {
return call(ctx.getBean(targetStr),methodName,new Class[]{String.class},new Object[]{arg});
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ProviderConfig.class);
ctx.start();
Map<String,String> info = new HashMap<>();
info.put("target","orderServiceImpl");
info.put("methodName","getDetail");
info.put("arg","1");
Object result = InvokeUtils.call(info, ctx);
System.out.println("测试InvokeUtils.call调用功能,调用结果"+result);
}
只要谁告诉我了反射需要信息,target/method/arg,我就能调用本地的任何对象方法
网络通信传递反射信息
网络通信的方法很多,如 http/rmi/webservice 等等,我们只需要选用任 意一种即可,为简便起见,我们选用 jdk 的 rmi 方式,其使用方式如下:
服务端
只需要将实例绑定注册到指定的 URL 和 port 上,远程即可调用此实例
public interface InfoService extends Remote {
String RMI_URL = "rmi://127.0.0.1:9080/InfoService";
int port = 9080;
Object passInfo(Map<String,String> info) throws RemoteException;
}
public class InfoServiceImpl extends UnicastRemoteObject implements InfoService {
static AnnotationConfigApplicationContext ctx;
static {
ctx = new AnnotationConfigApplicationContext(ProviderConfig.class);
ctx.start();
}
public InfoServiceImpl() throws RemoteException {
super();
}
@Override
public Object passInfo(Map<String, String> info) {
//反射调用
Object result = InvokeUtils.call(info, ctx);
return result;
}
}
public static void main(String[] args) {
try {
//注冊通讯端口
InfoService infoService = new InfoServiceImpl();
LocateRegistry.createRegistry(InfoService.port);
Naming.bind(InfoService.RMI_URL, infoService);
System.out.println("初始化rmi绑定");
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
} catch (AlreadyBoundException e) {
e.printStackTrace();
}
}
客户端
RMI 远程通过 URL 连接并调用
public static void main(String[] args) {
InfoService infoService = null;
try {
infoService = (InfoService) Naming.lookup(InfoService.RMI_URL);
OrderService service = getService(infoService);
OrderEntity result = service.getDetail("1");
System.out.println(result);
} catch (NotBoundException e) {
e.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (RemoteException e) {
e.printStackTrace();
}
}
public static OrderService getService(InfoService infoService){
OrderService service = new OrderServiceImpl(){
@Override
public OrderEntity getDetail(String id) {
Map<String,String> info = new HashMap();
info.put("target","orderServiceImpl");
info.put("methodName","getDetail");
info.put("arg",id);
OrderEntity result = null; try {
result = (OrderEntity)infoService.passInfo(info); } catch (RemoteException e) {
e.printStackTrace(); }
return result;
}
};
return service;
}
至此,已经实现了通过 RMI 跨机器传递 target/method/arg