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

分布式:

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方法来远程调用接口中的方法

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实现核心——动态代理+网络通讯增强

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

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

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

序列化与反序列化

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

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

具体实现方式:

服务端

注册中心

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"));

    }
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值