zoukankan      html  css  js  c++  java
  • 自己动手实现RPC服务调用框架

     

    转自:http://www.cnblogs.com/rjzheng/p/8971629.html#3977269
    担心后面忘了,先转了,后面借鉴实现一下RPC
    --------------------------------------------------------------------------------------------------------------------------------------

    引言

    本文利用java自带的socket编程实现了一个简单的rpc调用框架,由两个工程组成分别名为battercake-provider(服务提供者)、battercake-consumer(服务调用者)。

    设计思路如下:
    1、在battercake-provider中,写一个服务叫BatterCakeService

    2、在battercake-provider中,启动RpcProvider,发布该服务

    3、在battercake-consumer中,启动测试类RpcTest

    4、在battercake-consumer中,利用jdk动态代理,获得BatterCakeService的动态代理类BatterCakeService$Proxy0

    5、在battercake-consumer中,动态代理类BatterCakeService$Proxy0,与battercake-provider建立socket连接,battercake-provider针对每一个连接,都会启动一个ServerThread处理请求,代理类则发送服务参数等相关信息

    6、在battercake-consumer中,接收battercake-provider的ServerThread请求返回的结果。

    上述过程时序图如下所示

    image

    接下来上代码!!

    服务提供者

    本部分的工程为battercake-provider,项目结构图如下图所示

    image
    先上使用的部分的代码
    先创建一个微服务,接口如下

    package com.rjzheng.service;
    
    public interface BatterCakeService {
        /**
         * 卖煎饼的服务
         * @param name
         * @return
         */
        public String sellBatterCake(String name);
    }

    实现类如下

    package com.rjzheng.service.impl;
    
    import com.rjzheng.service.BatterCakeService;
    
    public class BatterCakeServiceImpl implements BatterCakeService {
    
        @Override
        public String sellBatterCake(String name) {
            // TODO Auto-generated method stub
            return name+"煎饼,卖的特别好";
        }
    
    }

    接下来就是发布服务

    package com.rjzheng.start;
    
    import com.rjzheng.rpc.RpcProvider;
    import com.rjzheng.service.BatterCakeService;
    import com.rjzheng.service.impl.BatterCakeServiceImpl;
    
    public class RpcBootStrap {
        public static void main(String[] args) throws Exception {
            BatterCakeService batterCakeService =new BatterCakeServiceImpl();
            //发布卖煎饼的服务,注册在20006端口
            RpcProvider.export(20006,batterCakeService);
        }
    }

    接下来是rpc框架调用部分的代码,RpcProvider,该部分代码可以总结为两步

    1. 将需要发布的服务存储在一个内存变量serviceList中
    2. 启动socket,server.accept()方法阻塞在那,监听输入
    3. 针对每一个请求,单独启动一个线程处理
    package com.rjzheng.rpc;
    
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    /**
     * RPC服务提供器
     * @author zhengrongjun
     *
     */
    public class RpcProvider {
        
        //存储注册的服务列表
        private static List<Object> serviceList;
        
        /**
         * 发布rpc服务
         * @param object
         * @param port
         * @throws Exception
         */
        public static void export(int port,Object... services) throws Exception {
            serviceList=Arrays.asList(services);
            ServerSocket server = new ServerSocket(port);
            Socket client = null;
            while (true) {
                //阻塞等待输入
                client = server.accept();
                //每一个请求,启动一个线程处理
                new Thread(new ServerThread(client,serviceList)).start();
            }
        }
    }

    接下来ServerThread线程处理类的代码,ServerThread主要做以下几个步骤

    1. 读取客户端发送的服务名
    2. 判断服务是否发布
    3. 如果发布,则走反射逻辑,动态调用,返回结果
    4. 如果未发布,则返回提示通知
    package com.rjzheng.rpc;
    
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.Method;
    import java.net.Socket;
    import java.util.List;
    
    public class ServerThread implements Runnable {
    
        private Socket client = null;
    
        private List<Object> serviceList = null;
    
        public ServerThread(Socket client, List<Object> service) {
            this.client = client;
            this.serviceList = service;
        }
    
        @Override
        public void run() {
            ObjectInputStream input = null;
            ObjectOutputStream output = null;
            try {
                input = new ObjectInputStream(client.getInputStream());
                output = new ObjectOutputStream(client.getOutputStream());
                // 读取客户端要访问那个service
                Class serviceClass = (Class) input.readObject();
                // 找到该服务类
                Object obj = findService(serviceClass);
                if (obj == null) {
                    output.writeObject(serviceClass.getName() + "服务未发现");
                } else {
                    //利用反射调用该方法,返回结果
                    try {
                        String methodName = input.readUTF();
                        Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
                        Object[] arguments = (Object[]) input.readObject();
                        Method method = obj.getClass().getMethod(methodName, parameterTypes);  
                        Object result = method.invoke(obj, arguments);  
                        output.writeObject(result); 
                    } catch (Throwable t) {
                        output.writeObject(t);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    client.close();
                    input.close();
                    output.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
    
        }
    
        private Object findService(Class serviceClass) {
            // TODO Auto-generated method stub
            for (Object obj : serviceList) {
                boolean isFather = serviceClass.isAssignableFrom(obj.getClass());
                if (isFather) {
                    return obj;
                }
            }
            return null;
        }
    
    }

    服务消费者

    本部分的工程为battercake-consumer,项目结构图如下图所示
    image

    先上rpc框架调用部分的代码RpcConsumer,步骤分两步

    1. 封装一个代理类处理器
    2. 返回service的代理类对象
    package com.rjzheng.rpc;
    
    import java.lang.reflect.Proxy;
    
    public class RpcConsumer {
        
        public static <T> T getService(Class<T> clazz,String ip,int port) {
            ProxyHandler proxyHandler =new ProxyHandler(ip,port);
            return (T)Proxy.newProxyInstance(RpcConsumer.class.getClassLoader(), new Class<?>[] {clazz}, proxyHandler);
        }
    }

    接下来上代理类处理器的代码,代理类处理步骤分以下几步

    1. 建立socket连接
    2. 封装请求数据,发送给服务提供者
    3. 返回结果
    package com.rjzheng.rpc;
    
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.net.Socket;
    
    import com.rjzheng.service.BatterCakeService;
    
    public class ProxyHandler implements InvocationHandler {
    
        private String ip;
        private int port;
    
        public ProxyHandler(String ip, int port) {
            // TODO Auto-generated constructor stub
            this.ip = ip;
            this.port = port;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // TODO Auto-generated method stub
            Socket socket = new Socket(this.ip, this.port);
            ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
            ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
            try {
                output.writeObject(proxy.getClass().getInterfaces()[0]);
                output.writeUTF(method.getName());
                output.writeObject(method.getParameterTypes());
                output.writeObject(args);
                output.flush();
                Object result = input.readObject();
                if(result instanceof Throwable) {
                    throw (Throwable) result;
                }
                    return result;
            } finally {
                socket.shutdownOutput();
            }
        }
    
    }

    接下来建立一个测试类RpcTest如下(跑该测试类前,记得运行在battercake-provider端的RpcBootstrap类发布BatterCakeService服务)

    package com.rjzheng.start;
    
    import com.rjzheng.rpc.RpcConsumer;
    import com.rjzheng.service.BatterCakeService;
    public class RpcTest {
    
        public static void main(String[] args) {
            BatterCakeService batterCakeService=RpcConsumer.getService(BatterCakeService.class, "127.0.0.1", 20006);
            String result=batterCakeService.sellBatterCake("双蛋");
            System.out.println(result);
        }
    }

    输出结果如下

    双蛋煎饼,卖的特别好

    至此,我们就实现了一个简易的rpc服务调用框架

  • 相关阅读:
    vue 解析二维码
    自定义组件双向数据绑定,父v-model,子value用.$emit('input', newVal)
    懒加载组件
    float双飞布局
    [转]MySQL 8.0 Public Key Retrieval is not allowed 错误的解决方法
    【转】nginx产生【413 request entity too large】错误的原因与解决方法
    [其它]三维立体图简单入门含样例
    [其它] 美图看看 黑色皮肤下载不了
    vue element 表格内容文字太长加提示
    gitee webide怎么暂存文件(吐槽,gitee的产品真是吃屎的)
  • 原文地址:https://www.cnblogs.com/shoshana-kong/p/9056601.html
Copyright © 2011-2022 走看看