文章524
标签277
分类67

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;

/**
* 普通公共Bean
*/
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.*;

/**
* @author zk
*/
public class SerializeUtils {

/**
* 序列化
*
* @param object
* @return
*/
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!");
}


/**
* 反序列化
*
* @param buf
* @return
*/
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;

/**
* RPC服务实现类
*
*/
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;

/**
* Server服务端
*
* 使用JDK自带的ServerSocket进行.
*
* 服务端收到数据之后, 对数据进行处理, 处理完成之后, 把结果返回给客户端!
*
*/
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();
}
}
}
}

/**
* 处理接收到的数据, 通过反序列化得到传递的NetModel!
*
* 然后得到接口名, 方法名, 参数, 参数类型;
*
* 最后通过JDK反射, 调用实现类的方法, 并将结果序列化后, 返回byte数组
*
* @param bs
* @return
*/
private static byte[] formatData(byte[] bs) {
try {
// 收到的NetModel二进制反序列化为NetModel模型, 然后通过反射调用服务实现类的方法
NetModel netModel = (NetModel) SerializeUtils.deserialize(bs);
String className = netModel.getClassName();
String[] types = netModel.getTypes();
Object[] args = netModel.getArgs();

/*
1. 通过Map来做接口映射到实现类, 从map取出实现类方法

Map<String, String> map = new HashMap<>();
map.put("rpc.server.service.HelloService", "rpc.server.service.impl.HelloServiceImpl");
Class<?> clazz = Class.forName(map.className);
*/

/*
2. 放在配置文件下, 读取配置文件读取
*/
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;

/**
* RPC客户端, 使用Socket与服务端通信
*
*/
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!");
}

/**
* 运行main, 开启客户端
*
* @param args
*/
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
Service on!

再开启客户端, 向服务端发送请求, 客户端收到回复:

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


本文作者:Jasonkay
本文链接:https://jasonkayzk.github.io/cn/2019/09/13/Java%E5%AE%9E%E7%8E%B0%E7%9A%84%E4%B8%80%E4%B8%AA%E5%8E%9F%E7%94%9FRPC%E4%BE%8B%E5%AD%90/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可