使用 Socket和动态代理以及反射 实现一个简易的 RPC 调用

使用 Socket、动态代理、反射 实现一个简易的 RPC 调用


我们前面有一篇 socket 的文章,再之前,还有一篇 java动态代理的文章,本文用到了那两篇文章中的知识点,需要的话可以回顾一下。


下面正文开始:

我们的背景是一个名为cuihua-snack(翠花小吃店)的客户端,要调用cuihua-core(翠花核心厨房)的服务端的接口,要求采用RPC的方式调用。

首先,我们创建两个项目cuihua-snack、cuihua-core

其中,cuihua-core 包含两个模块(关于 maven的模块化开发 可以再去了解一下),core-api中定义了客户端与服务端共同需要的接口和参数;core-service 则是服务端的主要逻辑。

我们有这样一张系统调用关系示意图。

① 服务端启动socket服务;

② 客户端开始调用,通过代理的方式调用;

③ 代理中包含 socket 客户端,与服务端建立连接后,将请求接口的对象信息封装后进行序列化。

④ 服务端接收到 socket 请求后,反序列化拿到请求的对象信息。

⑤ 通过反射,调用接口实现类的方法并返回结果。

⑥ 服务端对执行结果序列化并通过socket 返回给客户端。

⑦ 客户端对服务端返回的数据进行反序列化后,输出。

以上七个步骤,就是整个简易RPC的调用过程。


下面我们来看代码:

首先看 core-api 中的代码:

java 复制代码
/**
 * 上菜服务
 */
public interface DishService {
    String servePickedChineseCabbage(RequestDTO dto);
}
java 复制代码
/**
 * 上菜requestDTO
 */
@Data
public class RequestDTO implements Serializable {
    /**
     * 菜量
     */
    private String quantityType;//big;medium;small;
    /**
     * 是否加辣
     */
    private boolean spicy;
    /**
     * 冷热
     */
    private String coldOrHot;//cold/hot
}
java 复制代码
/**
 * RPC Request
 */

@Data
public class RpcRequest implements Serializable {
    private String className;
    private String methodName;
    private Object[] args;
    private Class[] types;

}

再来看 core-service的代码:

java 复制代码
public class DishServiceImpl implements DishService {
    public String servePickedChineseCabbage(RequestDTO dto) {
        System.out.println(dto);
        return "Please wait a moment! The dish you want will come soon!";
    }
}
java 复制代码
public class RpcService {
    private DishService service;
    public RpcService(DishService service){
        this.service = service;
    }
    public void serverRun() throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        ServerSocket serverSocket = new ServerSocket(8000);
        while(true) {
            Socket socket = serverSocket.accept();
            handler(socket);
        }

    }

    private void handler(Socket socket) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        //读取 socket 传输的数据
        ObjectInputStream objectinputstrem = new ObjectInputStream(socket.getInputStream());
        RpcRequest rpcRequest = (RpcRequest) objectinputstrem.readObject();
        String result = (String) this.invoke(rpcRequest);
        //将结果写回 socket
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
        objectOutputStream.writeObject(result);
        objectOutputStream.flush();
    }

    private Object invoke(RpcRequest request) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        //通过反射进行服务的调用
        Class clazz=Class.forName(request.getClassName());
        //找到目标方法
        Method method=clazz.getMethod(request.getMethodName(),request.getTypes());
        return method.invoke(service,request.getArgs());
    }
}
java 复制代码
public class App 
{
    public static void main( String[] args ) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {
        DishService dishService = new DishServiceImpl();
        RpcService rpcService = new RpcService(dishService);
        rpcService.serverRun();
    }
}

最后,我们来看 cuihua-snack的代码:

java 复制代码
public class CuihuaInvocationHandler implements InvocationHandler {

    private String host;
    private int port;
    public CuihuaInvocationHandler(String host,int port){
        this.host = host;
        this.port = port;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //开启客户端
        SocketClient socketClient = new SocketClient(host,port);
        //请求参数
        RpcRequest rpcRequest = new RpcRequest();
        rpcRequest.setArgs(args);
        rpcRequest.setTypes(method.getParameterTypes());
        rpcRequest.setClassName(method.getDeclaringClass().getName());
        rpcRequest.setMethodName(method.getName());
        //发送
        Object obj = socketClient.transMessage(rpcRequest);

        //响应结果
        return obj;
    }
}
java 复制代码
/**
 * 代理服务
 */
public class ProxyDishService {

    public  <T> T  getInstance(Class<T> clazz){
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(),new Class<?>[]{clazz},new CuihuaInvocationHandler("localhost",8000));
    }
}
java 复制代码
public class SocketClient {
    private String host;
    private int port;

    public SocketClient(String host,int port){
        this.host = host;
        this.port = port;
    }

    public Socket getSocket() throws IOException {
        Socket socket = new Socket(host,port);
        return socket;
    }

    public Object transMessage(RpcRequest request){
        ObjectOutputStream objectOutputStream = null;
        ObjectInputStream objectInputStream = null;

        try {
            Socket socket = getSocket();
            //将RpcRequest 写入到输出流中,通过socket传递给服务端
            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(request);
            objectOutputStream.flush();

            //通过输入流,读取服务端通过socket返回的结果
            objectInputStream = new ObjectInputStream(socket.getInputStream());
            Object obj = objectInputStream.readObject();
            return obj;
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            try {
                objectInputStream.close();
                objectOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
}
java 复制代码
public class App 
{
    public static void main( String[] args )
    {
        //调用代理
        ProxyDishService proxy = new ProxyDishService();
        RequestDTO dto = new RequestDTO();
        dto.setColdOrHot("hot");
        dto.setSpicy(true);
        dto.setQuantityType("big");
        String responseStr = proxy.getInstance(DishService.class).servePickedChineseCabbage(dto);
        System.out.println("响应结果:"+responseStr);

    }
}

执行 cuihua-core 中 core-service 下的 App 的main方法,启动 ServerSocket;

然后,执行cuihua-snack 中 App 的 main方法,socket客户端发起调用。

服务端打印日志如下:

shell 复制代码
RequestDTO(quantityType=big, spicy=true, coldOrHot=hot)

客户端打印日志如下:

shell 复制代码
响应结果:Please wait a moment! The dish you want will come soon!

以上就是 《使用 Socket和动态代理以及反射 实现一个简易的 RPC 调用》的全部内容,感谢阅读!

相关推荐
EasyDSS5 小时前
视频监控从安装到优化的技术指南,视频汇聚系统EasyCVR智能安防系统构建之道
大数据·网络·网络协议·音视频
rufeike5 小时前
UDP协议理解
网络·网络协议·udp
GKoSon7 小时前
加入RPC shell指令 温箱长时间监控
网络·网络协议·rpc
hgdlip8 小时前
关闭IP属地显示会影响账号的正常使用吗
网络·网络协议·tcp/ip·ip属地
Zz_waiting.8 小时前
网络原理 - 7(TCP - 4)
网络·网络协议·tcp/ip
zheshiyangyang9 小时前
HTTP相关
网络·网络协议·http
爱编程的鱼10 小时前
Windows 各版本查找计算机 IP 地址指南
人工智能·windows·网络协议·tcp/ip·tensorflow
xxy!11 小时前
OSI七层模型和TCP/IP四层模型
网络·网络协议·tcp/ip
宝耶13 小时前
HTTP协议-请求协议
网络协议·http·servlet
神的孩子都在歌唱16 小时前
网络IP冲突的成因与解决方案
网络·网络协议·tcp/ip