使用 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 中的代码:
在这里插入图片描述

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

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

}

再来看 core-service的代码:
在这里插入图片描述

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!";
    }
}
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());
    }
}
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的代码:
在这里插入图片描述

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;
    }
}
/**
 * 代理服务
 */
public class ProxyDishService {

    public  <T> T  getInstance(Class<T> clazz){
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(),new Class<?>[]{clazz},new CuihuaInvocationHandler("localhost",8000));
    }
}
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;
    }
}
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客户端发起调用。
服务端打印日志如下:

RequestDTO(quantityType=big, spicy=true, coldOrHot=hot)

客户端打印日志如下:

响应结果:Please wait a moment! The dish you want will come soon!

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

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于Java Socket TCP和UDP实现一个简易的网络文件服务程序,包含服务器端fileserve,可以按照以下步骤进行: 1. 首先,创建一个服务器端程序fileserve,使用TCP Socket连接。创建一个ServerSocket对象,并绑定指定的端口号,然后通过accept方法监听客户端的连接请求。当有客户端连接时,获取与客户端通信的Socket对象,并在此Socket上开启一个线程,用于处理该客户端的请求。 2. 在该线程中,首先解析客户端发送的请求消息,根据请求的内容,采取相应的操作。例如,如果是上传文件的请求,将客户端发送的文件数据保存到服务器上指定的目录中;如果是下载文件的请求,从服务器上读取请求的文件内容,然后将文件数据发送给客户端。 3. 对于上传和下载文件的操作,可以使用IO流来实现。当客户端发送请求时,根据请求中的文件信息,将文件数据读取到输入流,并写入到服务器上指定的文件中。当客户端请求下载文件时,从服务器上读取文件内容,并将文件数据写入到输出流发送给客户端。 4. 接下来,实现UDP Socket连接。在服务器端的fileserve中,创建一个DatagramSocket对象,指定一个端口用于监听客户端的连接。然后,在一个循环中,通过receive方法接收客户端发送的请求消息,然后根据请求的内容进行相应的操作,如上传文件或下载文件。 5. 对于UDP连接中的文件上传和下载操作,仍然可以使用IO流来实现。根据客户端请求中的文件信息,在服务器端的指定目录下创建或打开指定的文件,然后将文件数据读取到字节数组中,并构造一个DatagramPacket对象,将字节数组作为数据发送给客户端。 6. 最后,可以在fileserve中添加一些额外功能,如文件删除、重命名等。根据客户端发送的请求,服务器端可以根据请求的内容进行相应的操作,如删除指定的文件或重命名指定的文件。 通过以上步骤,可以实现一个基于Java Socket TCP和UDP的简易网络文件服务程序fileserve,可以在客户端和服务器端之间进行文件的上传和下载操作。当然,这只是一个简单的示例,实际应用中还需要考虑诸多细节和安全性,如文件的校验、并发访问等问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值