实现一个自己的RPC框架3.0

前言


上一种方法是采用最简单的线程的方式,当客户端发起一个请求的时候,服务端就要相应的生成一个线程来处理这个请求,并且在处理一个请求的时候,其他请求都会被阻塞。试想一下,当在同一时间有大量的请求访问服务端时,服务端能够创建的线程数量是有一定的限制的,并不能无限地创建,这样一来就会产生服务端内存溢出或者线程不够用的错误,在该版本中我引入了线程池来管理客户端发送过来的请求,这样就不会产生这类问题。

由于只要在服务端修改即可,所以客户端的代码和上个版本一致,下面直接来看服务端的代码。

1
2
3
4
5
6
7
8
9
10
11
public static void exportHelloService_v2(final Object service, int port) throws IOException {
System.out.println("建立Socket请求, port = " + port);
ServerSocket serverSocket = new ServerSocket(port);
//创建线程池
ExecutorService executor = Executors.newCachedThreadPool();
// 监听请求
while (true) {
final Socket socket = serverSocket.accept();
executor.execute(new SocketThread(service, port, socket));
}
}

这段是暴露服务端的主要函数实现,其中引入了线程池的概念,将监听到的每个请求都放入线程池中进行管理。每个线程池中的线程是这样定义的,其实和上个版本的基本一致,在这里我只是对其进行了一个封装。

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
public class SocketThread implements Runnable {
private Object service;
private int port;
private Socket socket;

public SocketThread(Object service, int port, Socket socket) {
this.service = service;
this.port = port;
this.socket = socket;
}

public Socket getSocket() {
return socket;
}

public void setSocket(Socket socket) {
this.socket = socket;
}

public Object getService() {
return service;
}

public void setService(Object service) {
this.service = service;
}

public int getPort() {
return port;
}

public void setPort(int port) {
this.port = port;
}

@Override
public void run() {
try {
/**解析请求*/
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
// String methodName = input.readUTF();
String methodName = (String) input.readObject();
System.out.println("methodName:" + methodName);
Class<?>[] parameterTypes = (Class<?>[])input.readObject();
System.out.println("ParameterTypes:" + Arrays.toString(parameterTypes));
Object[] args = (Object [])input.readObject();
System.out.println(Arrays.toString(args));

/**处理请求,输出结果*/
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
// 反射调用,处理请求
Method method = service.getClass().getMethod(methodName, parameterTypes);
Object result = method.invoke(service, args);
System.out.println("服务器端处理完并返回响应:" + result);
output.writeObject(result);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}

总结一下:这种伪异步的方式虽然解决了线程上的管理,但是仍然是一种阻塞的方法,在效率上可能并不能提高。后面的优化考虑到用NIO的方式。