既然是循序渐进,就一步一步来,先搭建一个简易的框架,再慢慢进行完善。从大局上来说,服务端应该是一个24小时运行的服务器,不断监听客户端发送来的请求,然后调用本地服务,然后返回结果给客户端。客户端应该知晓服务端的地址,并且知道服务端提供什么样的服务,向服务端发送一个带有足够信息的请求。在通信方式上,使用原生的socket。
先定义一个注册在服务端的方法接口:
public interface HelloService {
String hello(HelloObject object);
}
@Data
@AllArgsConstructor
public class HelloObject implements Serializable {
private Integer id;
private String message;
}
要调用hello方法,需要传入一个HelloObject,此对象要实现Serialiazable接口,因为此对象将来要通过socket传输。
这个方法的具体实现很简单,返回一个字符串:
public class HelloServiceImpl implements HelloService {
private static final Logger logger = LoggerFactory.getLogger(HelloServiceImpl.class);
@Override
public String hello(HelloObject object) {
logger.info("接收到:{}", object.getMessage());
return "这是调用的返回值,id=" + object.getId();
}
}
定义客户端请求格式:
客户端通过接口的名字,和方法的名字,但是由于方法重载的缘故,还需要这个方法的所有参数和参数类型可以确认服务端提供的唯一的服务。定义如下Rpcrequest对象:
@Data @Builder public class RpcRequest implements Serializable { /** * 待调用接口名称 */ private String interfaceName; /** * 待调用方法名称 */ private String methodName; /** * 调用方法的参数 */ private Object[] parameters; /** * 调用方法的参数类型 */ private Class<?>[] paramTypes; }
定义响应对象:
@Data public class RpcResponse<T> implements Serializable { /** * 响应状态码 */ private Integer statusCode; /** * 响应状态补充信息 */ private String message; /** * 响应数据 */ private T data; public static <T> RpcResponse<T> success(T data) { RpcResponse<T> response = new RpcResponse<>(); response.setStatusCode(ResponseCode.SUCCESS.getCode()); response.setData(data); return response; } public static <T> RpcResponse<T> fail(ResponseCode code) { RpcResponse<T> response = new RpcResponse<>(); response.setStatusCode(code.getCode()); response.setMessage(code.getMessage()); return response; } }
使用jdk动态代理生成实例,在调用方法时构造rpcrequest对象并且发送给服务器端:
public class RpcClientProxy implements InvocationHandler { private String host; private int port; public RpcClientProxy(String host, int port) { this.host = host; this.port = port; } @SuppressWarnings("unchecked") public <T> T getProxy(Class<T> clazz) { return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz}, this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { RpcRequest rpcRequest = RpcRequest.builder() .interfaceName(method.getDeclaringClass().getName()) .methodName(method.getName()) .parameters(args) .paramTypes(method.getParameterTypes()) .build(); RpcClient rpcClient = new RpcClient(); return ((RpcResponse) rpcClient.sendRequest(rpcRequest, host, port)).getData(); } }
客户端实现
客户端有sendRequest方法,把请求发送给服务器端,最简单的实现就是使用socket通信。同时从socket中读取服务端的调用结果。
public class RpcClient { private static final Logger logger = LoggerFactory.getLogger(RpcClient.class); public Object sendRequest(RpcRequest rpcRequest, String host, int port) { try (Socket socket = new Socket(host, port)) { ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); objectOutputStream.writeObject(rpcRequest); objectOutputStream.flush(); return objectInputStream.readObject(); } catch (IOException | ClassNotFoundException e) { logger.error("调用时有错误发生:", e); return null; } } }
服务端的实现
服务端循环监听特定的端口,从端口中读取字节流,通过反射方法,解析字节流中的数据,使用线程池循环处理请求。
public RpcServer() { int corePoolSize = 5; int maximumPoolSize = 50; long keepAliveTime = 60; BlockingQueue<Runnable> workingQueue = new ArrayBlockingQueue<>(100); ThreadFactory threadFactory = Executors.defaultThreadFactory(); threadPool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workingQueue, threadFactory); }
循环监听socket中是否有请求到来,如果有,加入线程池任务。
public void register(Object service, int port) { try (ServerSocket serverSocket = new ServerSocket(port)) { logger.info("服务器正在启动..."); Socket socket; while((socket = serverSocket.accept()) != null) { logger.info("客户端连接!Ip为:" + socket.getInetAddress()); threadPool.execute(new WorkerThread(socket, service)); } } catch (IOException e) { logger.error("连接时有错误发生:", e); } }
工作线程负责解析请求,调用方法,然后返回数据
public class WorkerThread implements Runnable { private static final Logger logger = LoggerFactory.getLogger(WorkerThread.class); private Socket socket; private Object service; public WorkerThread(Socket socket, Object service) { this.socket = socket; this.service = service; } @Override public void run() { try (ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream())) { RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject(); Method method = service.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getParamTypes()); Object returnObject = method.invoke(service, rpcRequest.getParameters()); objectOutputStream.writeObject(RpcResponse.success(returnObject)); objectOutputStream.flush(); } catch (IOException | ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { logger.error("调用或发送时有错误发生:", e); } } }