随着微服务、分布式的流行,基本复杂点的项目都会涉及到远程调用,最基础的可以使用http来实现调用,也可以通过一些RPC框架来实现,比如Ailiaba 的dubbo,Thrift等。那么作为一个rpc框架,需要具备哪些基本的元素呢?
当然,如果功能更加完善,一个优秀的rpc框架往往还会具备注册中心,负载均衡等功能,这里就先不做要求了
调用方式 : 在客户端引入服务端接口的jar包,直接调用接口就可以实现远程调用
分析:
因为客户端调用服务端api后,最后需要调用到服务端具体接口的具体方法,那么
1)需要通过动态代理来创建接口的代理类(接口无法实例化直接调用);
2)请求参数里就要求有类信息,方法名,参数集合,这样服务端才能通过反射来调用
3)InvocationHandler的实现类里的invoke具体实现应该是socket调用,这样才能在执行接口方法的时候进行socket通讯
我把这个rpc实现分为两部分,一部分是rpc-server服务端实现,另一部分是rpc-client实现
rpc-server又分为两部分 rpc-server-api(提供jar包给rpc-client调用)和rpc-server-provider
/**
* 2020/3/7
* created by chenpp
* 定义接口,将rpc-server-api打包提供给client端使用,
* client只要调用对应的接口方法就可以远程调用服务端的具体实现
*
*/
public interface IUserService {
public void saveUser(User user);
public User getUserById(Integer id);
}
/**
* 2020/3/7
* created by chenpp
* 请求rpc时需要的参数
*/
public class RpcRequest implements Serializable {
private String className;
private String methodName;
private Object[] args;
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
...
}
/**
* 2020/3/7
* created by chenpp
* 远程调用的服务端入口,使用socket监听
*/
public class RpcServer {
private static final ExecutorService executor = Executors.newCachedThreadPool();
/**
* 注册服务实例,服务注册后,其他客户端通过接口调用就可以调用服务端的实现
* */
public void register(Object service ,int port) {
ServerSocket serverSocket = null;
try {
//创建socket
serverSocket = new ServerSocket(port);
while(true){
//监听端口,是个阻塞的方法
Socket socket = serverSocket.accept();
//处理rpc请求,这里使用线程池来处理
executor.submit(new HandleThread(service,socket));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
package com.chenpp.server;
import com.chenpp.request.RpcRequest;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.Socket;
/**
* 2020/3/7
* created by chenpp
* 处理RPC请求的线程
*/
public class HandleThread implements Runnable {
private Socket socket;
private Object serviceInstance;
public HandleThread(Object serviceInstance, Socket socket) {
this.socket = socket;
this.serviceInstance = serviceInstance;
}
public void run() {
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
//从socket中获取RPC请求
ois = new ObjectInputStream(socket.getInputStream());
RpcRequest rpcRequest = (RpcRequest) ois.readObject();
Object result = invoke(rpcRequest);
oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(result);
oos.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
ois.close();
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private Object invoke(RpcRequest rpcRequest) {
String className = rpcRequest.getClassName();
Object result = null;
try {
Class> clazz = Class.forName(className);
//这里无法实例化,因为传入的是接口类型,接口无法实力哈
Object[] parameters = rpcRequest.getArgs();
if (parameters == null) {
Method method = clazz.getMethod(rpcRequest.getMethodName());
result = method.invoke(serviceInstance);
} else {
Class[] types = new Class[parameters.length];
for (int i = 0; i < parameters.length; i++) {
types[i] = parameters[i].getClass();
}
Method method = clazz.getMethod(rpcRequest.getMethodName(), types);
result = method.invoke(serviceInstance, parameters);
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
/**
* 2020/3/7
* created by chenpp
* 启动类,启动rpc服务端
*/
public class StartServer {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
RpcServer rpcServer = new RpcServer();
rpcServer.register(userService,8080);
}
}
public class StartClient {
public static void main(String[] args) {
//由于rpc-server-api里只有实体类和接口类,想要实例化只能通过代理来实现
IUserService userService = RpcProxy.getInstance(IUserService.class,"localhost",8080);
User user = new User();
user.setAge(12);
user.setName("chenpp");
userService.saveUser(user);
User user1 = userService.getUserById(1);
System.out.println(user1.getName()+","+user1.getAge());
}
}
/**
* 2020/3/7
* created by chenpp
* 创建代理对象
*/
public class RpcProxy {
public static T getInstance(Class classInterface, String host, int port) {
return (T) Proxy.newProxyInstance(classInterface.getClassLoader(), new Class[]{classInterface}, new RpcInvocationHandler(host, port));
}
}
/**
* 2020/3/7
* created by chenpp
*/
public class RpcInvocationHandler implements InvocationHandler {
private String host;
private int port;
public RpcInvocationHandler(String host,int port){
this.host = host;
this.port = port;
}
/**
* 增强的InvocationHandler,接口调用方法的时候实际是调用socket进行传输
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//将远程调用需要的接口类、方法名、参数信息封装成RPCRequest
RpcRequest rpcRequest = new RpcRequest();
rpcRequest.setArgs(args);
rpcRequest.setClassName(method.getDeclaringClass().getName());
rpcRequest.setMethodName(method.getName());
//通过socket发送RPCRequest给服务端并获取结果返回
Socket socket= new Socket(host,port);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(rpcRequest);
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object result = ois.readObject();
return result;
}
}
因为接口无法实例化,所以对于每个实现类我都自己手动创建了一个实例对象并发布,可以考虑引入spring来优化对bean的管理,并通过注解来实现服务的发布
/**
* 2020/3/7
* created by chenpp
* 引入Component注解,加了RegisterService注解的类都会被Spring容器管理
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface RegisterService {
String interfaceClass() ;
}
/**
* 实现了InitializingBean接口,那么会在对应的AutoRpcServer实例化之后调用afterPropertiesSet方法
* 而实现了ApplicationContextAware接口,当spring容器初始化的时候,会自动的将ApplicationContext注入进来,
* 使得当前bean可以获得ApplicationContext上下文
* */
@Component
public class AutoRpcServer implements ApplicationContextAware, InitializingBean {
private int port;
public AutoRpcServer(int port){
this.port = port;
}
private static final ExecutorService executor = Executors.newCachedThreadPool();
//key 为对应的接口类名,valeu 为具体的实例
private Map map = new HashMap();
public void afterPropertiesSet() throws Exception {
ServerSocket serverSocket = null;
try {
//创建socket
serverSocket = new ServerSocket(port);
while(true){
//监听端口,是个阻塞的方法
Socket socket = serverSocket.accept();
//处理rpc请求,这里使用线程池来处理
executor.submit(new HandleThread(map,socket));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void setApplicationContext(ApplicationContext context) throws BeansException {
//从spring上下文中获取添加了RegisterService的注解的bean
String[] beanNames = context.getBeanNamesForAnnotation(RegisterService.class);
for(String beanName : beanNames){
Object bean = context.getBean(beanName);
RegisterService annotation = bean.getClass().getAnnotation(RegisterService.class);
Class interfaceClass = annotation.interfaceClass();
//将接口的类名和对应的实例bean对应起来
map.put(interfaceClass.getName(),bean);
}
}
}
/**
* 2020/3/7
* created by chenpp
* 扫描com.chenpp.impl包下的类,注入spring容器
*/
@Component
@ComponentScan("com.chenpp.impl")
public class SpringConfig {
//把autoRpcServer bean注入spring容器,初始化完成后会触发InitializingBean接口的afterPropertiesSet调用
@Bean
public AutoRpcServer autoRpcServer(){
//这里直接写死启动的端口 : 8080
return new AutoRpcServer(8080);
}
}
/**
* 2020/3/7
* created by chenpp
* 启动类,通过注解方式启动rpc服务端
*/
public class StartServer {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class);
((AnnotationConfigApplicationContext) applicationContext).start();
}
}
GitHub源码地址:https://github.com/dearfulan/cp-rpc
参考:https://www.jianshu.com/p/775d49b30567