使用socket实现一个简单的RPC服务端和客户端

本文通过实例代码详细介绍了如何使用Java的socket实现一个简单的RPC服务端和客户端。首先定义了请求协议类,接着创建了服务消费端的请求代理类,然后实现了服务提供端的逻辑,最后展示了消费端的测试代码和运行结果,以此来理解RPC的基本工作流程。
摘要由CSDN通过智能技术生成

使用socket实现一个简单的RPC服务端和客户端



前言

在学习一些大的RPC框架时(SOFA-RPC、Dubbo),往往因为源码架构庞大,不知道如何下手,或者在阅读源码的过程中被弄的晕头转向。所以这篇文章采用最简单的方式,实现一个简单RPC服务端和客户端,重在理解RPC框架的工作流程


一、jar包引用

只是用到了fastjson作为字符串工具使用

		<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>

二、服务消费端代码编写(Consumer)

1.定义一个请求协议封装类

import java.util.Arrays;
import java.util.List;

public class RequestProtocol {
    private String interfaceName;
    private String methodName;
    private List<Object> args;

    public String getInterfaceName() {
        return interfaceName;
    }

    public void setInterfaceName(String interfaceName) {
        this.interfaceName = interfaceName;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public List<Object> getArgs() {
        return args;
    }

    public void setArgs(List<Object> args) {
        this.args = args;
    }

    public void setArgs(Object[] args) {
        this.args = Arrays.asList(args);
    }

    @Override
    public String toString() {
        return "RequestProtocol{" +
                "interfaceName='" + interfaceName + '\'' +
                ", methodName='" + methodName + '\'' +
                ", args=" + args +
                '}';
    }
}

2.定义请求代理类(用于调用远程接口)

代码如下(示例):

import com.alibaba.fastjson.JSONObject;
import com.sample.rpc.sockt.protocol.RequestProtocol;

import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.Socket;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class RequestProxyHandler implements InvocationHandler {
    /**
     * 发送数据使用的socket
     */
    private final Socket socket;
    /**
     * 被代理的对象信息
     */
    private final Object proxyObject;

    public RequestProxyHandler(Socket socket, Object proxyObject) {
        this.socket = socket;
        this.proxyObject = proxyObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        /**
         * 1.构建出请求协议对象
         */
        RequestProtocol requestProtocol = new RequestProtocol();
        requestProtocol.setInterfaceName(proxyObject.getClass().getTypeName());
        requestProtocol.setMethodName(method.getName());
        requestProtocol.setArgs(args);
        /**
         * 将对象转换成json字符串进行传输
         */
        String requestJson = JSONObject.toJSONString(requestProtocol);
        /**
         * 获取socket的输出流
         */
        OutputStream outputStream = socket.getOutputStream();
        /**
         * 将字符串转换成byte[]为网络传输做准备
         */
        byte[] requestMessageByte = requestJson.getBytes(StandardCharsets.UTF_8);
        /**
         * 将转换好的byte[]写入到输出流中
         */
        outputStream.write(requestMessageByte);
        /**
         * 调用flush方法将数据全部发送出去
         */
        outputStream.flush();
        /**
         * 创建数组用于接受服务返回的数据
         */
        byte[] responseMessageByte = new byte[512];
        /**
         * 阻塞获取服务端返回的数据流
         */
        InputStream inputStream = socket.getInputStream();
        /**
         * 将流数据全部读取到byte[]数组中
         */
        inputStream.read(responseMessageByte);
        /**
         * 将数据转换成String进行返回
         */
        return new String(responseMessageByte, Charset.defaultCharset());
    }
}

3.定义测试使用的接口和实现类

public interface HelloRpc {
    /**
     * 测试方法
     *
     * @param name name
     * @return 返回固定字符串+name
     */
    String hello(String name);
}
import com.sample.rpc.sockt.service.HelloRpc;

public class HelloRpcImpl implements HelloRpc {
    @Override
    public String hello(String name) {
        return "hello " + name;
    }
}

三、服务提供端代码编写(Provider)

import com.alibaba.fastjson.JSONObject;
import com.sample.rpc.sockt.protocol.RequestProtocol;
import com.sample.rpc.sockt.service.HelloRpc;
import com.sample.rpc.sockt.service.impl.HelloRpcImpl;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

public class Provider {
    public static void main(String[] args) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        /**
         * 用map存储接口和实现类的对于信息
         */
        Map<String, Object> providerCache = new HashMap<>();
        /**
         * 将测试接口和实现类信息,注册到map中
         */
        providerCache.put(HelloRpc.class.getName(), new HelloRpcImpl());
        /**
         * 创建ServerSocket,绑定8888端口
         */
        ServerSocket serverSocket = new ServerSocket(8888);
        while (true) {
            /**
             * 等待客户端链接
             */
            Socket clientSocket = serverSocket.accept();
            /**
             * 获取数据流
             */
            InputStream inputStream = clientSocket.getInputStream();
            /**
             * 创建byte[],用于获取流中的数据
             */
            byte[] requestMessageByte = new byte[512];
            /**
             * 将流中的数据读取到byte[]数组中
             */
            inputStream.read(requestMessageByte);
            /**
             * 将byte[]数组转换成字符串,并将其转换成RequestProtocol对象
             */
            RequestProtocol requestProtocol = JSONObject.parseObject(new String(requestMessageByte, Charset.defaultCharset()), RequestProtocol.class);
            /**
             * 获取调用接口实现对象
             */
            Object object = providerCache.get(requestProtocol.getInterfaceName());
            /**
             * 调用方法
             */
            Method declaredMethod = object.getClass().getDeclaredMethod(requestProtocol.getMethodName(), String.class);
            String param = (String) requestProtocol.getArgs().get(0);
            String result = (String) declaredMethod.invoke(object, param);
            /**
             * 获取输出流将结果返回给消费端
             */
            OutputStream outputStream = clientSocket.getOutputStream();
            byte[] bytes = result.getBytes(StandardCharsets.UTF_8);
            outputStream.write(bytes);
            outputStream.flush();
            outputStream.close();
        }
    }
}

四、消费端测试代码和结果演示

import com.sample.rpc.sockt.proxy.RequestProxyHandler;
import com.sample.rpc.sockt.service.HelloRpc;

import java.io.IOException;
import java.lang.reflect.Proxy;
import java.net.Socket;

public class Consumer {
    public static void main(String[] args) throws IOException {
        /**
         * 创建socket链接
         */
        Socket socket = new Socket("127.0.0.1", 8888);
        /**
         * 创建动态代理对象
         */
        HelloRpc helloRpc = (HelloRpc) Proxy.newProxyInstance(HelloRpc.class.getClassLoader()
                , new Class[]{HelloRpc.class}, new RequestProxyHandler(socket, HelloRpc.class));
        /**
         * 获取到返回结果并打印
         */
        String result = helloRpc.hello("张三");
        System.out.println(result);
    }
}

测试返回结果

hello 张三

总结

代码简单实现了基本的RPC功能,后续会继续优化代码功能,本次先把基本的流程搭建起来

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值