使用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功能,后续会继续优化代码功能,本次先把基本的流程搭建起来