1、RPC技术介绍
RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程, 然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。
RPC 调用分类RPC 调用的分类方式有很多种。
从通信协议层面可以分为:
基于 HTTP 协议的 RPC;
基于二进制协议的 RPC;
基于 TCP 协议的 RPC。
从是否跨平台可分为:
单语言 RPC,如 RMI, Remoting;
跨平台 RPC,如 google protobuffer, restful json,http XML。
从调用过程来看, 可以分为同步通信RPC和异步通信RPC:
同步 RPC:指的是客户端发起调用后,必须等待调用执行完成并返回结果;
异步 RPC:指客户方调用后不关心执行结果返回,如果客户端需要结果,可用
通过提供异步 callback 回调获取返回信息。大部分 RPC 框架都同时支持这两种方式的
调用。
2、实现
客户端的中间代理类:
/**
* 客户端接口代理
* 当客户端接口方法被调用的时候,把方法名,方法参数作为参数。
* 传送给远程服务执行,然后获取返回值
*
* DynDemo $Proxy0 重新方法,方法里调用dynDemo.invoke
*/
public class RpcProxy implements InvocationHandler,
Serializable{
private String ip;
private int port;
// private Class<?> c;
private String class4Name;
private static final long serialVersionUID = 1L;
public RpcProxy(String ip, int port, String c) {
this.ip = ip;
this.port = port;
this.class4Name = c;
}
/**
* 动态代理类,当调用接口方法的时候转为调用此方法
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args
)throws Throwable {
// 用作返回值
Object o = null;
// 通过socket调用远程服务
Socket s = new Socket(ip, port);
// 组装为一个保留了要调用的类,方法名及参数的对象,然后序列化之后传给远程
RpcObject rpcObject = new RpcObject(class4Name, method.getName(), args);
ObjectOutputStream os = null;
ObjectInputStream is = null;
try{
//在socket输入流建立对象流
os = new ObjectOutputStream(s.getOutputStream());
os.writeObject(rpcObject);// 把对象序列化,发送到服务端;
os.flush();// 马上发送
// 从远程得到返回结果
//在socket输入流建立对象流
is = new ObjectInputStream(s.getInputStream());
o = is.readObject(); // 反序列化,获取结果
} catch (Exception e) {
e.printStackTrace();
} finally{
if(os!=null){
os.close();
}
if(is!=null){
is.close();
}
}
return o;
}
}
服务端线程:
public class RpcThread extends Thread {
private Socket s;
public RpcThread(Socket s) {
this.s = s;
}
@Override
public void run() {
ObjectInputStream is = null;
ObjectOutputStream os = null;
try {
//从TCP报文获取数据
is = new ObjectInputStream(s.getInputStream());
// 得到远程调用参数,包含了接口名,调用方法,方法参数
RpcObject rpcObject = (RpcObject) is.readObject();
System.out.println("Method:"+rpcObject.getMethodName());
// 构建接口的实现类,然后通过反射调用方法
Object o = getObject(rpcObject.getClass4Name());
System.out.println("class:"+rpcObject.getClass4Name());
Object reO = executeMethod(o, rpcObject.getMethodName(), rpcObject.getArgs());
// 输出返回值
os = new ObjectOutputStream(s.getOutputStream());
os.writeObject(reO);
os.flush();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
if(is!=null){
is.close();
}
if(os!=null){
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 通过反射技术执行方法,并返回返回值
* @param o // 方法作用在该对象上的
* @param methodName
* @param args
* @return
*/
private Object executeMethod(Object o, String methodName, Object[] args) {
Object objR = null;
Class<?>[] cs = new Class[args.length];
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
cs[i] = arg.getClass();
}
try {
//获取类的方法;
Method m = o.getClass().getMethod(methodName, cs);
//动态调用该方法
objR = m.invoke(o, args);
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return objR;
}
/**
* 根据接口名得到实例
* @param c
* @return
*/
private Object getObject(String c) {
Object o = null;
try {
Class clz=Class.forName(c);
o=clz.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}catch(Exception e)
{
e.printStackTrace();
}
return o;
}
}
3、总结
RPC技术就是远程调用函数。
客户端首先把需要调用的函数信息发送给代理类,代理类序列化封装参数,通过Socket网络传输发送给服务端,服务端接收到信息后反序列化,调用函数。
然后再将结果序列化,发送给客户端,客户端反序列化网络数据,返回方法调用结果。
客户端怎么进行工作的?
- 把客户端接口代理。
- 当客户端接口方法被调用的时候,把方法名,方法参数作为参数。
- 调用中间代理类的invoke函数,序列化。
- 传送给远程服务执行,然后获取返回值。
服务端怎么进行工作的?
- 接受到网络数据后,先反序列化得到远程调用参数(类的全限定名,调用方法,请求参数)。
- 通过类的全限定名构建接口的实现类,然后反射机制调用方法。
- 输出返回值。
4、负载均衡
使用集群时,当一个服务有多个请求地址的时候,会返回多个地址。
所以需要负载均衡来控制我们想要请求哪台机器来获得请求。