分布式:

RPC可以提高系统稳定性,比如说,我们的订单服务程序更新出BUG,导致内存溢出,是这台服务器宕机了,但是它只会影响的整个系统的订单业务部分,对于用户注册登录等业务没有影响,同样对于系统的日志记录也没有影响。

RPC:

注册中心:服务端会把它的服务注册到注册中心中,包括服务名称、服务调用的ip地址、端口、协议、还有调用路径等等。

RMI:

RMI概述 RMI(remote method invocation),可以认为是RPC的java版本,允许运行在一个java 虚拟机的对象调用运行在另一个java虚拟机上对象的方法。

实现原理 RMI使用的是JRMP(Java Remote Messageing Protocol)协议, JRMP是专门为java定制的通信协议,所以是纯java的分布式解决方案

实现一个RMI程序

1.创建一个远程接口,并继承java.rmi.Remote接口

2.实现远程接口,并且继承UnicastRemoteObject

3.创建服务器程序,同时使用createRegistry方法注册远程接口对象

4.创建客户端程序,通过Naming类的lookup方法来远程调用接口中的方法

基于分布式思想下的RPC解决方案(RMI,手写RPC框架)_客户端

public interface IOrder extends Remote {

public String pay(String id ) throws Exception;

}
public class OrderImpl extends UnicastRemoteObject implements IOrder  {
protected OrderImpl() throws RemoteException {
super();
// TODO Auto-generated constructor stub
}
@Override
public String pay(String id) throws Exception {
// TODO Auto-generated method stub
return "支付成功!商品订单号:"+id;
}

}
public class Server {
public static void main(String[] args) throws Exception {
IOrder iorder = new OrderImpl();

LocateRegistry.createRegistry(6666);
//把刚才的实例绑定到本地端口上的一个路径上
Naming.bind("rmi://localhost:6666/order",iorder);
System.out.println("服务器已经启动了!");
}

}
public class Client {
public static void main(String[] args)throws Exception {
//通过RMI发现服务并且转成一个对象
IOrder iOrder = (IOrder)Naming.lookup("rmi://localhost:6666/order");
//远程调用下
System.out.println(iOrder.pay("168888"));
}

}

手写一个RPC框架

1.服务端定义接口和服务实现类并且注册服务

2.客户端查询出服务

3.客户端使用动态代理调用服务(动态代理)

4.客户端代理把调用对象、方法、参数序列化成数据

5.客户端与服务端通过Socket通讯传输数据

6.服务端反序列化数据成对象、方法、参数。

7.服务端代理拿到这些对象和参数后通过反射的机制调用服务的实例。

基于分布式思想下的RPC解决方案(RMI,手写RPC框架)_动态代理_02

RPC实现核心——动态代理+网络通讯增强

RPC中的远程调用:给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用;

目的:屏蔽掉具体实现的细节——通过网络通讯远程调用的增强

RPC实现细节——序列化和反射

序列化与反序列化

发送方将对象名称、方法名称、参数等对象信息变为二进制的01串、

接收方将二进制的01串变为本来的对象名称、方法名称、参数等对象。

基于分布式思想下的RPC解决方案(RMI,手写RPC框架)_动态代理_03

具体实现方式:

服务端

注册中心

public class RegisterCenter {
//线程池
private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
//定义注册中心的静态对象
private static final HashMap<String, Class> serviceRegistry = new HashMap<String, Class>();

private static boolean isRunning = false;

private static int port;

public RegisterCenter(int port) {

this.port = port;
}

//服务注册中心启动
public void start() throws IOException {
//服务器监听
ServerSocket server = new ServerSocket();
//监听绑定端口
server.bind(new InetSocketAddress(port));
System.out.println("start server");
try {
while (true) {
// 1.监听客户端的TCP连接,接到TCP连接后将其封装成task,由线程池执行,并且同时将socket送入(server.accept()=socket)
executor.execute(new ServiceTask(server.accept()));
}
} finally {
server.close();
}
}
//服务的注册:socket通讯+反射
public void register(Class serviceInterface, Class impl) {

serviceRegistry.put(serviceInterface.getName(), impl);
}

//服务的获取运行
private static class ServiceTask implements Runnable {
//客户端socket
Socket clent = null;

public ServiceTask(Socket client) {
this.clent = client;
}
//远程请求达到服务端,我们需要执行请求结果,并且把请求结果反馈至客户端,使用Socket通讯
public void run() {
//反射
//同样适用object流
ObjectInputStream inputStream = null;
ObjectOutputStream outputStream = null;
try {
//1.客户端发送的object对象拿到,2.在采用反射的机制进行调用,3.最后给返回结果
inputStream = new ObjectInputStream(clent.getInputStream());
//顺序发送数据:类名、方法名、参数类型、参数值
//拿到接口名
String serviceName = inputStream.readUTF();
//拿到方法名
String methodName = inputStream.readUTF();
//拿到参数类型
Class<?>[] paramTypes = ( Class<?>[])inputStream.readObject();
//拿到参数值
Object[] arguments = (Object[])inputStream.readObject();
//要到注册中心根据 接口名,获取实现类
Class serviceClass =serviceRegistry.get(serviceName);
//使用反射的机制进行调用
Method method = serviceClass.getMethod(methodName,paramTypes);
//反射调用方法,把结果拿到
Object result = method.invoke(serviceClass.newInstance(),arguments);
//通过执行socket返回给客户端
outputStream = new ObjectOutputStream(clent.getOutputStream());
// /把结果返回给客户端
outputStream.writeObject(result);
//记得关闭
outputStream.close();
inputStream.close();
clent.close();

}catch (Exception e){
e.printStackTrace();
}

}

}
}

接口

public interface TechInterface {
//洗脚服务
String XJ(String name);
}

接口实现类:

public class TechImpl implements TechInterface {
@Override
public String XJ(String name) {

return "您好,13号技师为你服务:"+name;
}
}

调用类:

public class Server {
public static void main(String[] args) throws Exception{
new Thread(new Runnable() {
public void run() {
try {
//起一个服务中心
RegisterCenter serviceServer = new RegisterCenter(8888);
//注册技师对象至注册中心
serviceServer.register(TechInterface.class, TechImpl.class);
//运行我们的服务
serviceServer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}

客户端

动态代理部分

public class RpcClientFrame {

/*远程服务的代理对象,参数为客户端要调用的的服务*/
public static <T> T getRemoteProxyObj(final Class<?> serviceInterface)
throws Exception {
// 默认端口8888
InetSocketAddress serviceAddr = new InetSocketAddress("127.0.0.1",8888);
// 1.将本地的接口调用转换成JDK的动态代理,在动态代理中实现接口的远程调用
//进行实际的服务调用(动态代理)
return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface},new DynProxy(serviceInterface,serviceAddr));
}

/*动态代理类,实现了对远程服务的访问*/
private static class DynProxy implements InvocationHandler {
//接口
private final Class<?> serviceInterface;
//远程调用地址
private final InetSocketAddress addr;

//构造函数
public DynProxy(Class<?> serviceInterface, InetSocketAddress addr) {
this.serviceInterface = serviceInterface;
this.addr = addr;
}

/*动态代理类,增强:实现了对远程服务的访问*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
/* 网络增强部分*/
Socket socket = null;
//因为传递的大部分是 方法、参数,所以我们使用Object流对象
ObjectInputStream objectInputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
//新建一个Socket
socket = new Socket();
//连接到远程的地址和端口
socket.connect(addr);
//往远端 发送数据,按照顺序发送数据:类名、方法名、参数类型、参数值
//拿到输出的流
objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
//发送 调用方法的 类名,使用UTF-8避免乱码
objectOutputStream.writeUTF(serviceInterface.getName());
//发送 方法名
objectOutputStream.writeUTF(method.getName());
//发送 参数类型,使用Object
objectOutputStream.writeObject(method.getParameterTypes());
//发送 参数的值,使用UTF-8避免乱码
objectOutputStream.writeObject(args);
//刷新缓冲区,使得数据立马发送
objectOutputStream.flush();
//立马拿到远程执行的结果
objectInputStream = new ObjectInputStream(socket.getInputStream());
//我们要把调用的细节打印出来
System.out.println("远程调用成功!" + serviceInterface.getName());
//最后要网络的请求返回给返回
return objectInputStream.readObject();
} finally {

//最后记得关闭
socket.close();
objectOutputStream.close();
objectInputStream.close();

}
}
}
}

接口

public interface TechInterface {
//洗脚服务
String XJ(String name);
}

调用:

public class Client {
public static void main(String[] args) throws Exception {
//动态代理获取我们的对象
TechInterface techInterface = RpcClientFrame.getRemoteProxyObj(TechInterface.class);
//进远程调用我们的对象
System.out.println(techInterface.XJ("king"));

}
}