动态代理RPC
转自:
https://www.jianshu.com/p/bb9beca7f7bc
进程间通信(IPC):是在多任务操作系统或联网的计算机之间运行的程序和进程所用的通信技术。有两种类型的进程间通信(IPC)。
- 本地过程调用(LPC):LPC用在多任务操作系统中,使得同时运行的任务能互相会话。这些任务共享内存空间使任务同步和互相发送信息。
- 远程过程调用(RPC):RPC类似于LPC,只是在网上工作RPC开始是出现在Sun微系统公司和HP公司的运行UNIX操作系统的计算机中。
现在互联网公司的系统都由许多大大小小的服务组成,各服务部署在不同的机器上,由不同的团队负责。这时就会遇到两个问题:
1)要搭建一个新服务,免不了需要依赖他人的服务,而现在他人的服务都在远端,怎么调用?
2)其它团队要使用我们的服务,我们的服务该怎么发布以便他人调用?
假如现在有这样一个接口,需要你实现后,提供给他人使用,如何提供给他呢?
package rpc;
/**
* 客户端和服务端公共的接口
* <p>
* 作者:余天然 on 2017/1/4 下午6:00
*/
public interface HelloService {
String sayHello(String msg);
}
我们希望可以这样
服务端实现接口:
package rpc;
/**
* 作者:余天然 on 2017/1/4 下午10:30
*/
public class Server {
/**
* 服务端对接口的具体实现
*/
public static class HelloServiceImpl implements HelloService {
然后客户端直接调用接口:
package rpc;
/**
* 作者:余天然 on 2017/1/4 下午10:30
*/
public class Client {
public static void main(String[] args) {
HelloService service = new Server.HelloServiceImpl();
service.sayHello("直接调用");
}
}
然而,这样客户端就直接依赖了服务端的HelloServiceImpl这个具体实现类,太耦合了!
客户端和服务端之间应该是依赖于接口,而不必依赖于具体的实现的。而且,客户端和服务端往往在不同的机器上,这样直接依赖肯定是不行的。那么,怎么办呢?
下面该我们的RPC登场了!
1、RPC服务端
package rpc;
import java.io.IOException;
/**
* 服务端
* <p>
* 作者:余天然 on 2017/1/4 下午6:27
*/
public class RpcServer {
public static void main(String[] args) {
try {
//暴露服务
HelloService service = new HelloServiceImpl();
RpcFramework.export(service, 8989);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 服务端对接口的具体实现
*/
private static class HelloServiceImpl implements HelloService {
2、RPC客户端
package rpc;
/**
* 客户端
* <p>
* 作者:余天然 on 2017/1/4 下午6:02
*/
public class RpcClient {
public static void main(String[] args) {
try {
//引用服务
HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 8989);
for (int i = 0; i < Integer.MAX_VALUE; i++) {
String hello = service.sayHello("rpc" + i);
System.out.println(hello);
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
3、运行结果
服务端输出:
客户端输出:
这里,客户端并没有依赖HelloServiceImpl这个类,为什么同样也可以输出“hello world rpc0”呢?
这就是我的这个简单的RpcFramework的功劳了。
4、简单的RPC框架
核心就是:动态代理+Socket
思路分析:
- 服务端暴露服务,绑定一个端口,利用Socket轮询,等待接受客户端的请求。
- 客户端引用服务,利用动态代理,隐藏掉每个接口方法的实际调用。
- 客户端将方法名、参数类型、方法所需参数传给服务端,服务端接受到客户端传过来的方法名、参数类型、方法所需参数之后,利用反射,执行具体的接口方法,然后将执行结果返回给客户端
package rpc;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 简单的RPC框架
* <p>
* 动态代理+Socket
* <p>
* 作者:余天然 on 2017/1/4 下午5:59
*/
public class RpcFramework {
private static ExecutorService executorService = Executors.newFixedThreadPool(20);
/**
* 服务端暴露服务
*
* @param service 服务实现
* @param port 服务端口
*/
public static void export(final Object service, int port) throws IOException {
if (service == null) {
throw new IllegalArgumentException("service instance == null");
}
if (port <= 0 || port > 65535) {
throw new IllegalArgumentException("Invalid port " + port);
}
System.out.println("Export service " + service.getClass().getName() + " on port " + port);
ServerSocket server = new ServerSocket(port);
while (true) {
final Socket socket = server.accept();
executorService.submit(new Runnable() {