当今分布式应用盛行,rpc框架也就随之出现,今天用java写一个小的demo。
首先看一下什么事远程调用
远程调用涉及到了网络io,基本过程是客户端向服务端发送消息,表明自己要调用的方法的名字以及包含的参数,然后服务端调用函数,再向客户端返回结果。所以涉及了两次网络io。
这样的话,客户端的代码写起来比较费劲,每一个方法底层都是网络io,但是过程都是一样的,传递参数,得到结果,所以这里肯定可以优化,比如在java中可以使用动态代理机制。只需要使用一个服务端的接口类就,让Proxy动态生成一个代理类。服务端就是一个socket的常规服务器,可以使用线程池或者多路复用这样的技术,然后通过反射来调用实际类。还有注册服务的功能,所谓注册就是让接口和真正的实现类对接,最后就可以调用到实现类的方法。
所以远程调用其实就是网络io,在客户端和服务端下面都有消息的封装和解析,以及网络io。
下面是一个简单的例子:
接口
public interface HelloService{
public String hello(String name);
}
实现类:
public class HelloServiceImpl implements HelloService{
@Override
public String hello(String name) {
return "Hello, " + name;
}
}
sever:
public class RPCServer {
private ExecutorService threadPool;
private static final int DEFAULT_THREAD_NUM = 10;
public RPCServer(){
threadPool = Executors.newFixedThreadPool(DEFAULT_THREAD_NUM);
}
public void register(Object service, int port){
try {
System.out.println("server starts...");
ServerSocket server = new ServerSocket(port);
Socket socket = null;
while((socket = server.accept()) != null){
System.out.println("client connected...");
threadPool.execute(new Processor(socket, service));
}
} catch (IOException e) {
e.printStackTrace();
}
}
class Processor implements Runnable{
Socket socket;
Object service;
public Processor(Socket socket, Object service){
this.socket = socket;
this.service = service;
}
public void process(){
}
@Override
public void run() {
try {
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
String methodName = in.readUTF();
Class<?>[] parameterTypes = (Class<?>[]) in.readObject();
Object[] parameters = (Object[]) in.readObject();
Method method = service.getClass().getMethod(methodName, parameterTypes);
try {
Object result = method.invoke(service, parameters);
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeObject(result);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
}
}
}
}
启动:
public class Main {
public static void main(String args[]){
HelloService helloService = new HelloServiceImpl();
RPCServer server = new RPCServer();
server.register(helloService, 50001);
}
}
public class RPCClient {
public static void main(String args[]){
HelloService helloService = getClient(HelloService.class, "127.0.0.1", 50001);
System.out.println(helloService.hello("liyao1"));
}
@SuppressWarnings("unchecked")
public static <T> T getClient(Class<T> clazz, String ip, int port){
return (T) Proxy.newProxyInstance(RPCClient.class.getClassLoader(), new Class<?>[]{clazz}, new InvocationHandler() {
@Override
public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
Socket socket = new Socket(ip, port);
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
out.writeUTF(arg1.getName());
out.writeObject(arg1.getParameterTypes());
out.writeObject(arg2);
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
return in.readObject();
}
});
}
}
这就是远程调用的一个小例子。
可以看到,随便实现一个仅仅是返回字符串的逻辑都是如此的复杂,反射,动态代理,网络io,消息解析等等。。。头疼。。。。
那么什么是rpc呢?既然是框架,那么必然是帮我们做了一下上面的事情的。rpc帮助我们生成了客户端和服务端的消息和io等公共的代码,这样客户端可以直接调用代理类的方法,感知不到远程调用,服务端也只需要注册服务即可,也就是上面的io,反射,代理等等复杂的代码rpc框架都给我们生成了。
使用了rpc框架,我们只需要定义一个接口,然后让rpc框架为我们生成客户端的代理类,同时生成服务端的serversocket类,我们只需要在服务端编写真正的实现类,然后注册在服务器上就可以。其余的事情都是框架做的。
再说一遍,我们只需要:
(1)定义接口;
(2)用接口生成服务端和客户端代理的代码;
(3)编写接口实现;
(4)注册服务;
这下是不是简单了很多,这就是rpc框架的魅力。
下一节我会给出一个thrift的小例子,就可以感受使用框架的便捷。