RPC 实现的简单雏形
基本介绍
RPC,远程过程调用,顾名思义,在远方有个服务,你该怎么调用它?
这个问题的解决核心是先解决网络通讯问题,我得能访问到,其次解决调用问题。
网络通讯的方案
这么多年以来,网络通讯的实现方案有很多,简单列举如下:
- 最古老也是最有效,并且永不过时的,TCP/UDP的二进制传输。事实上所有的通信方式归根结底都是 TCP/UDP
- CORBA (Common Object Request Broker Architecture)。古老而复杂的,支持面向对象的通信协议
- Web Service (SOA SOAP RDDI WSDL •••) 基于http + xml的标准化Web API
- RestFul (Representational State Transfer) 回归简单化本源的Web API的事实标准 http + json
- RMI (Remote Method Invocation )Java内部的分布式通信协议
- JMS (Java Message Service) JavaEE中的消息框架标准,为很多MQ所支持
- RPC (Remote Procedure Call) 远程过程调用,这只是一个统称,重点在于方法调用(不支持对象的概念).具体实现甚至可以用RMI RestFul 等去实现,但一般不用,因为RMI不能跨语言,而RestFul效率太低。 多用力服务器集群间的通信,因此常使用更加高效短小精悍的传输模式以提高效率。
目前来说,主流的还是RestFul 和 RPC,各有各的优缺点,比如能否跨越防火墙,性能高低啊,当然TCP虽然写着麻烦,但是优点多多
序列化框架
网络通讯势必避免不了序列化问题,那么常见的序列化框架如下:
- java.io.Serializable
- Hessian
- google protobuf
- facebook Thrift
- kyro
- fst
- json序列化框架
- Jackson
- google Gson
- Ali FastJson
- xmlrpc(xstream)
基于性能体积等考虑,比较推荐的是google protobuf和facebook Thrift
简单的案例演示
下面是一个最简单的RPC实现,里面的序列化方案使用最原始的JDK的序列化,实际使用过程中更换
//Server
public class Server {
public static boolean running = true;
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(9000);
while(running){
Socket s = ss.accept();
process(s);
s.close();
}
ss.close();
}
private static void process(Socket s) throws Exception {
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
ObjectInputStream ois = new ObjectInputStream(in);
ObjectOutputStream oos = new ObjectOutputStream(out);
//接收客户端发送过来的方法详情,通过反射调用相关方法,并返回客户端数据
String className = ois.readUTF();
String methodName = ois.readUTF();
Class[] parameterTypes = (Class[]) ois.readObject();
Object[] args = (Object[]) ois.readObject();
//根据接口类名找到接口同目录下实现该接口的所有类,这里也可以通过Spring @Autowired 去注入一个具体类对象
Class clazz = Class.forName(className);
List<Class<?>> allClassByInterface = ClassUtils.getAllClassByInterface(clazz);
Class clazz2 = allClassByInterface.get(0);
Method method = clazz2.getMethod(methodName, parameterTypes);
Object o = method.invoke(clazz2.newInstance(), args);
oos.writeObject(o);
oos.flush();
}
}
//client
public class Client {
public static void main(String[] args) throws IOException {
System.out.println("我是demo06 的client");
IUserService service = (IUserService)Stub.getStub(IUserService.class);
System.out.println(service.findUserById(123));
}
}
//stub
public class Stub {
public static Object getStub(final Class clazz){
/*
proxy: 代理的对象
method: 代理的对象正在执行的方法
args: 方法的参数
*/
InvocationHandler h = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//1.建立连接
Socket socket = new Socket("localhost", 9000);
//2.往Server端写数据
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
String clazzName = clazz.getName();
String methodName = method.getName();
//为了避免方法重载,把方法的参数类型传递给server
Class<?>[] parameterTypes = method.getParameterTypes();
oos.writeUTF(clazzName);
oos.writeUTF(methodName);
oos.writeObject(parameterTypes);
oos.writeObject(args);
oos.flush();
//3.从Server端读数据
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object o = ois.readObject();
oos.close();
socket.close();
return o;
}
};
/*
Proxy.newProxyInstance :new出来一个代理类对象
IUserService.class.getClassLoader() :产生代理类的class loader
new Class[]{IUserService.class} : 这个代理类实现了哪些接口
h;调用动态代理类方法的处理器,h 实例化的时候(上面代码),invoke方法在动态代理类的所有方法执行的时候都会执行
*/
Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, h);
//通过反射可以看看这个代理类的相关信息
System.out.println(o.getClass().getName());
System.out.println(o.getClass().getInterfaces()[0]);
return o;
}
}