我们将新写一个服务接口:通过某个id查询blog信息
我们本次改进希望重构服务端server的代码,使得server可以容纳多个service接口的调用。我们将为此通过HashMap构建一个service的映射关系。
另外原来的server处理请求是用BIO的监听,捕获消息之后直接new一个线程来处理,我们这里将使用一个线程池来代替直接new 一个线程,实现线程的有效管理。
实现 项目创建我们创建一个名为 simpleRPC-03 的module:
依赖配置跟simpleRPC-02一样:
pom.xml
commonSimpleRPC org.example 1.0-SNAPSHOT 4.0.0 simpleRPC-038 8 org.projectlombok lombok1.18.12 provided
RPCRequest.java和simpleRPC-02一样:
package com.rpc.common;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
@Data
@Builder
public class RPCRequest implements Serializable {
// 服务类名,客户端只知道接口名,在服务端中用接口名指向实现类
private String interfaceName;
// 方法名
private String methodName;
// 参数列表
private Object[] params;
// 参数类型
private Class>[] paramsTypes;
}
RPCResponse.java和simpleRPC-02一样:
package com.rpc.common;
import lombok.Builder;
import lombok.Data;
import java.io.Serializable;
@Data
@Builder
public class RPCResponse implements Serializable {
// 状态信息
private int code;
private String message;
// 具体数据
private Object data;
public static RPCResponse success(Object data) {
return RPCResponse.builder().code(200).data(data).build();
}
public static RPCResponse fail() {
return RPCResponse.builder().code(500).message("服务器发生错误").build();
}
}
User.java和simpleRPC-02一样:
package com.rpc.common;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
// 客户端和服务端共有的
private Integer id;
private String userName;
private Boolean sex;
}
我们添加一个新的实例:Blog.java
package com.rpc.common;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Blog implements Serializable {
private Integer id;
private Integer useId;
private String title;
}
service
UserService.java和simpleRPC-02一样:
package com.rpc.service;
import com.rpc.common.User;
public interface UserService {
// 客户端通过这个接口调用服务端的实现类
User getUserByUserId(Integer id);
// 给这个服务增加一个功能
Integer insertUserId(User user);
}
UserServiceImpl.java和simpleRPC-02:
package com.rpc.service;
import com.rpc.common.User;
public class UserServiceImpl implements UserService {
@Override
public User getUserByUserId(Integer id) {
// 模拟从数据库中取用户的行为
User user = User.builder()
.id(id)
.userName("he2121")
.sex(true).build();
System.out.println("客户端查询了" + id + "的用户");
return user;
}
@Override
public Integer insertUserId(User user) {
System.out.println("插入数据成功: " + user);
return 1;
}
}
添加新的服务:
BlogService.java
package com.rpc.service;
import com.rpc.common.Blog;
public interface BlogService {
Blog getBlogById(Integer id);
}
BlogServiceImpl.java
package com.rpc.service;
import com.rpc.common.Blog;
public class BlogServiceImpl implements BlogService {
@Override
public Blog getBlogById(Integer id) {
Blog blog = Blog.builder()
.id(id)
.title("我的博客")
.useId(22).build();
System.out.println("客户端查询了" + id + "博客");
return blog;
}
}
ServiceProvider.java
package com.rpc.service;
import java.util.HashMap;
import java.util.Map;
public class ServiceProvider {
private Map interfaceProvider;
// 构造函数,初始化一个空的 hashmap 赋给 Map interfaceProvider
public ServiceProvider() {
this.interfaceProvider = new HashMap<>();
}
public void provideServiceInterface(Object service) {
// 反射,.getClass().getInterfaces()得到class的interface,按照interfaces name(key)和object(value)存入map
Class>[] interfaces = service.getClass().getInterfaces();
for (Class clazz : interfaces) {
interfaceProvider.put(clazz.getName(), service);
}
}
public Object getService(String interfaceName) {
return interfaceProvider.get(interfaceName); // 通过interface name得到object
}
}
client
client在simpleRPC-03中基本没改动,只是在RPCClient.java中增加了调用的方法。
IOClient.java
package com.rpc.client;
import com.rpc.common.RPCRequest;
import com.rpc.common.RPCResponse;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class IOClient {
public static RPCResponse sendRequest(String host, int port, RPCRequest request) throws IOException, ClassNotFoundException {
// 老样子,创建Socket对象,定义host和port
Socket socket = new Socket(host, port);
// 定义输入输出流对象
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
System.out.println("request: " + request);
// 输出流写入request对对象,刷新输出流
objectOutputStream.writeObject(request);
objectOutputStream.flush();
// 通过输入流的readObject方法,得到服务器端传来的RPCResponse,并返回RPCResponse对象
RPCResponse response = (RPCResponse) objectInputStream.readObject();
return response;
}
}
ClientProxy.java
package com.rpc.client;
import com.rpc.common.RPCRequest;
import com.rpc.common.RPCResponse;
import lombok.AllArgsConstructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
@AllArgsConstructor
public class ClientProxy implements InvocationHandler {
private String host;
private int port;
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 构建RPCRequest对象,初始化其中的四个重要参数,使用了lombok中的builder。
// 初始化interfaceName。初始化methodName,初始化params,,初始化paramsTypes
RPCRequest request = RPCRequest.builder()
.interfaceName(method.getDeclaringClass().getName())
.methodName(method.getName())
.params(args)
.paramsTypes(method.getParameterTypes())
.build();
// 调用IOClient,通过输入输出流进行request的数据传输,并返回服务器端传来的response
RPCResponse response = IOClient.sendRequest(host, port, request);
System.out.println("response: " + response);
return response.getData(); // 获取RPCResponse中的目标数据(因为RPCResponse中除了目标数据,还有状态码和状态信息这些非目标数据)
}
T getProxy(Class clazz) {
// 传入目标接口的类加载器,目标接口,和InvocationHandler(的实现类,也就是本类,this),生成动态代理类实例
Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this);
return (T)o;
}
}
RPCClient.java
package com.rpc.client;
import com.rpc.common.Blog;
import com.rpc.common.User;
import com.rpc.service.BlogService;
import com.rpc.service.UserService;
public class RPCClient {
public static void main(String[] args) {
ClientProxy rpcClientProxy = new ClientProxy ("127.0.0.1", 8899); // 初始化host和port
UserService proxy = rpcClientProxy .getProxy(UserService.class);
// 服务的方法1
User userByUserId = proxy.getUserByUserId(10);
System.out.println("从服务器端得到的user为:" + userByUserId);
// 服务的方法2
User user = User.builder().userName("张三").id(100).sex(true).build();
Integer integer = proxy.insertUserId(user);
System.out.println("向服务器端插入数据" + integer);
// 服务的方法3
BlogService blogService = rpcClientProxy.getProxy(BlogService.class);
Blog blogById = blogService.getBlogById(10000);
System.out.println("从服务端得到的blog为:" + blogById);
}
}
server
我们规范RPCServer的方法,定义接口RPCServer.java
package com.rpc.server;
public interface RPCServer {
void start(int port);
void stop();
}
定义线程池版本的服务器端实现:ThreadPoolRPCRPCServer.java
package com.rpc.server;
import com.rpc.service.ServiceProvider;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolRPCRPCServer implements RPCServer {
private final ThreadPoolExecutor threadPool;
private ServiceProvider serviceProvider;
// 默认构造函数:函数里默认初始化ThreadPoolExecutor线程池,初始化serviceProvider
public ThreadPoolRPCRPCServer(ServiceProvider serviceProvider) {
threadPool = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
1000, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100));
this.serviceProvider = serviceProvider;
}
// 自定义构造函数:函数里自己初始化ThreadPoolExecutor线程池,初始化serviceProvider
public ThreadPoolRPCRPCServer(ServiceProvider serviceProvider, int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) {
threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
this.serviceProvider = serviceProvider;
}
@Override
public void start(int port) {
System.out.println("线程池服务器端启动了");
try {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket socket = serverSocket.accept();
threadPool.execute(new WorkThread(socket, serviceProvider)); // threadPool提交任务
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void stop() {
}
}
我们实现单线程要做的任务,也是我们之前一直在写的RPC逻辑:WorkThread.java
package com.rpc.server;
import com.rpc.common.RPCRequest;
import com.rpc.common.RPCResponse;
import com.rpc.service.ServiceProvider;
import lombok.AllArgsConstructor;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
@AllArgsConstructor
public class WorkThread implements Runnable {
private Socket socket;
private ServiceProvider serviceProvider;
@Override
public void run() {
try {
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
// 读取客户端传过来的request
RPCRequest request = (RPCRequest) ois.readObject();
// 反射调用服务方法获得返回值
RPCResponse response = getResponse(request);
// 写入客户端
oos.writeObject(response);
oos.flush();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
System.out.println("从IO中读取错误数据");
}
}
private RPCResponse getResponse(RPCRequest request) {
// 得到服务名
String interfaceName = request.getInterfaceName();
// 通过serviceProvider得到服务器端相应的服务实现类
Object service = serviceProvider.getService(interfaceName);
// 反射调用方法
Method method = null;
try {
method = service.getClass().getMethod(request.getMethodName(), request.getParamsTypes());
Object invoke = method.invoke(service, request.getParams());
return RPCResponse.success(invoke);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
System.out.println("方法执行错误");
return RPCResponse.fail();
}
}
}
最后我们实现服务器TestServer.java
package com.rpc.server;
import com.rpc.service.*;
public class TestServer {
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
BlogService blogService = new BlogServiceImpl();
// Map serviceProvide = new HashMap<>();
// serviceProvide.put("com.ganghuan.myRPCVersion2.service.UserService",userService);
// serviceProvide.put("com.ganghuan.myRPCVersion2.service.BlogService",blogService);
ServiceProvider serviceProvider = new ServiceProvider();
serviceProvider.provideServiceInterface(userService); // 把userService存入 serviceProvider
serviceProvider.provideServiceInterface(blogService); // 把blogService存入 serviceProvider
RPCServer RPCServer = new ThreadPoolRPCRPCServer(serviceProvider);
RPCServer.start(8899);
}
}
文件结构
我们文件结构如下:
运行我们先运行TestServer.java
然后再运行RPCClient.java



