Java实现的一个原生RPC例子
这是一个简单的原生RPC例子,用了JDK动态代理,反射,JDK自带的序列化和反序列化以及JAVA原生Socket通信
本项目Github地址: https://github.com/JasonkayZK/Java_Samples/tree/java-rpc
一个Java实现的原生RPC例子
1. 项目说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| . └── main ├── java │ └── rpc │ ├── api │ │ ├── bean │ │ │ ├── NetModel.java │ │ │ └── Person.java │ │ └── util │ │ └── SerializeUtils.java │ ├── client │ │ ├── proxy │ │ │ └── ProxyFactory.java │ │ └── RpcClient.java │ └── server │ ├── RpcServer.java │ └── service │ ├── HelloService.java │ └── impl │ └── HelloServiceImpl.java └── resources └── config.properties
|
本项目为一个RPC Maven项目, 共有三个模块:
- API模块: 公共类
- Server模块: 服务端
- Client模块: 客户端
2. API模块
1) JavaBean: Person
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| package rpc.api.bean;
import java.io.Serializable;
public class Person implements Serializable {
private static final long serialVersionUID = 5542635716484888244L;
private String name;
private Integer age;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
@Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
|
作为RPC传输的Java Bean.
2): Model: NetModel RPC网络传输模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| package rpc.api.bean;
import java.io.Serializable; import java.util.Arrays;
public class NetModel implements Serializable {
private static final long serialVersionUID = 3152168260407524091L;
private String className;
private String method;
private Object[] args;
private String[] types;
public String getClassName() { return className; }
public void setClassName(String className) { this.className = className; }
public String getMethod() { return method; }
public void setMethod(String method) { this.method = method; }
public Object[] getArgs() { return args; }
public void setArgs(Object[] args) { this.args = args; }
public String[] getTypes() { return types; }
public void setTypes(String[] types) { this.types = types; }
@Override public String toString() { return "NetModel{" + "className='" + className + '\'' + ", method='" + method + '\'' + ", args=" + Arrays.toString(args) + ", types=" + Arrays.toString(types) + '}'; } }
|
公共网络通信模型类:
- 通过序列化该类, 将客户端调用的接口, 方法, 参数类型封装;
- 然后服务端反序列化, 再通过反射, 调取相应实现类的方法!
此NetModel也是以JavaBean的形式给出的! 用于封装RPC的调用参数!
3): Util: SerializeUtils 序列化工具类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
| package rpc.api.util;
import java.io.*;
public class SerializeUtils {
public static byte[] serialize(Object object) { ByteArrayOutputStream os = null; ObjectOutputStream outputStream = null; try { os = new ByteArrayOutputStream(); outputStream = new ObjectOutputStream(os);
outputStream.writeObject(object); outputStream.flush();
byte[] bytes = os.toByteArray();
return bytes; } catch (IOException e) { e.printStackTrace(); } finally { try { if (outputStream != null) { outputStream.close(); } if (os != null) { os.close(); } } catch (IOException e) { e.printStackTrace(); } } throw new RuntimeException("Fail to serialize!"); }
public static Object deserialize(byte[] buf) { ByteArrayInputStream is = null; ObjectInputStream inputStream = null; try { is = new ByteArrayInputStream(buf); inputStream = new ObjectInputStream(is); Object object = inputStream.readObject();
return object; } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { try { if (inputStream != null) { inputStream.close(); } if (is != null) { is.close(); } } catch (IOException e) { e.printStackTrace(); } }
return new RuntimeException("Fail to deserialize!"); }
}
|
通过调用JDK提供的原生序列化和反序列化方法完成对RPC对象的序列化方法!
3. Server模块
1): Service: HelloService & HelloServiceImpl 业务方法接口与实现类
业务接口:
1 2 3 4 5 6 7 8 9 10 11
| package rpc.server.service;
import rpc.api.bean.Person;
public interface HelloService { String sayHello(String name);
Person getPerson(String name); }
|
业务接口实现类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| package rpc.server.service.impl;
import rpc.api.bean.Person; import rpc.server.service.HelloService;
public class HelloServiceImpl implements HelloService {
@Override public String sayHello(String name) { return "Say hello to " + name; }
@Override public Person getPerson(String name) { Person person = new Person(); person.setName(name); person.setAge(22);
return person; } }
|
业务接口的对应实现类!
2): Server: RpcServer 服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
| package rpc.server;
import rpc.api.bean.NetModel; import rpc.api.util.SerializeUtils;
import java.io.*; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.ServerSocket; import java.net.Socket; import java.util.Properties;
public class RpcServer {
private static Properties properties;
static { properties = new Properties(); InputStream in = null; try { in = RpcServer.class.getClassLoader().getResourceAsStream("config.properties"); properties.load(in); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } } } }
public static void main(String[] args) { openServer(); }
public static void openServer() { ServerSocket serverSocket = null; Socket socket = null; try { serverSocket = new ServerSocket(9999); System.out.println("Service on!");
while (true) { socket = serverSocket.accept(); System.out.println(socket.getInetAddress() + "-connected!");
InputStream in = socket.getInputStream(); byte[] buf = new byte[1024]; in.read(buf);
byte[] formatData = formatData(buf);
OutputStream out = socket.getOutputStream(); out.write(formatData); } } catch (IOException e) { e.printStackTrace(); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
private static byte[] formatData(byte[] bs) { try { NetModel netModel = (NetModel) SerializeUtils.deserialize(bs); String className = netModel.getClassName(); String[] types = netModel.getTypes(); Object[] args = netModel.getArgs();
Class<?> clazz = Class.forName(getPropertyValue(className)); Class<?>[] typeClazzs = null;
if (types != null) { typeClazzs = new Class[types.length]; for (int i = 0; i < types.length; i++) { typeClazzs[i] = Class.forName(types[i]); } }
Method method = clazz.getMethod(netModel.getMethod(), typeClazzs); Object object = method.invoke(clazz.newInstance(), args);
byte[] bytes = SerializeUtils.serialize(object); return bytes; } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } throw new RuntimeException("Fail to format data"); }
private static String getPropertyValue(String key) { return properties.getProperty(key); }
}
|
使用JDK自带的ServerSocket进行. 服务端收到数据后, 对对象进行处理, 处理完成后再将结果返回给客户端!
对应的配置文件: config.properties
1
| rpc.server.service.HelloService=rpc.server.service.impl.HelloServiceImpl
|
4. Client 模块
1): Client: RpcClient
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| package rpc.client;
import rpc.client.proxy.ProxyFactory; import rpc.api.util.SerializeUtils; import rpc.server.service.HelloService;
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException;
public class RpcClient {
public static Object send(byte[] bs) { Socket socket = null; OutputStream outputStream = null; InputStream in = null;
try { socket = new Socket("127.0.0.1", 9999);
outputStream = socket.getOutputStream();
outputStream.write(bs);
in = socket.getInputStream(); byte[] buf = new byte[1024]; in.read(buf);
Object formatData = SerializeUtils.deserialize(buf);
return formatData; } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } }
throw new RuntimeException("Fail to send data!"); }
public static void main(String[] args) { HelloService helloService = ProxyFactory.getInstance(HelloService.class); System.out.println("say: " + helloService.sayHello("zhangsan")); System.out.println("Person: " + helloService.getPerson("zhangsan")); }
}
|
客户端的send方法通过Socket与服务端通信: 发送序列化过的请求信息, 并接收服务端处理过得数据并进行反序列化后返回!
2): Proxy: ProxyFactory 动态代理类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| package rpc.client.proxy;
import rpc.api.bean.NetModel; import rpc.api.util.SerializeUtils; import rpc.client.RpcClient;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy;
public class ProxyFactory {
private static InvocationHandler handler = (proxy, method, args) -> { NetModel netModel = new NetModel();
Class<?>[] classes = proxy.getClass().getInterfaces(); String className = classes[0].getName();
netModel.setClassName(className); netModel.setArgs(args); netModel.setMethod(method.getName()); String[] types = null;
if (args != null) { types = new String[args.length]; for (int i = 0; i < types.length; i++) { types[i] = args[i].getClass().getName(); } } netModel.setTypes(types);
byte[] bytes = SerializeUtils.serialize(netModel);
return RpcClient.send(bytes); };
@SuppressWarnings("unchecked") public static <T> T getInstance(Class<T> serviceClass) { return (T) Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class[] {serviceClass}, handler); } }
|
代理工厂类ProxyFactory.
客户端通过调用方法getInstance(),动态代理生成代理接口类;
代理接口调用方法的时候,该代理类会去调用InvocationHandler 中的invoke方法;
在invoke方法中能得到代理接口的全名,调用的方法,参数和参数类型,然后将这些参数封装在NetModel中, 然后序列化NetModel,将序列化后的byte数组调用RPCClient中的send方法,将消息发送给服务端;
服务端然后将消息反序列化,得到接口名,方法名,参数,参数类型,最后通过JDK反射,来调取实现类的方法,并将调取结果,序列化为byte数组,然后返回;
send再将byte数组反序列化对象,返回给调用者
最后invoke再将send方法的返回值返回,这个返回值就是客户端调用接口的返回值了!
这个过程也就是客户端与服务端通信的过程
5. 调用测试
先开启Server端, 输出:
再开启客户端, 向服务端发送请求, 客户端收到回复:
1 2
| say: Say hello to zhangsan Person: Person{name='zhangsan', age=22}
|
服务端收到Socket请求:
1 2 3
| Service on! /127.0.0.1-connected! /127.0.0.1-connected!
|
附录
本项目Github地址: https://github.com/JasonkayZK/Java_Samples/tree/java-rpc