手写RPC框架V1
gitee地址:https://gitee.com/kylin1991_admin/rpc_handwriting
Api.jar
1、写一个IHelloService接口,类似dubbo
public interface IHelloService {
String sayHello(String context);
}
2、一个RpcRequest请求模型
@Data
public class RpcRequest implements Serializable {
private String className;
private String methodName;
private Object[] parameters;
}
Server
1、Server 服务注册
public class Server {
public static void main(String[] args) {
IHelloService helloService = new IHelloServiceImpl();
RpcProxyServer rpcProxyServer = new RpcProxyServer(helloService,8080);
rpcProxyServer.publisher();
}
}
2、RpcProxyServer 服务发布
public class RpcProxyServer {
private Object service;
private int port;
public RpcProxyServer(Object service, int port) {
this.service = service;
this.port = port;
}
public void publisher() {
try {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket socket = serverSocket.accept();
executor.execute(new ProcessorHandler(service,socket));
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static final Executor executor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors(),
60L,
TimeUnit.MINUTES,
new ArrayBlockingQueue<>(Runtime.getRuntime().availableProcessors()),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
}
3、请求处理ProcessorHandler
public class ProcessorHandler implements Runnable {
private Object service;
private Socket socket;
public ProcessorHandler(Object service, Socket socket) {
this.service = service;
this.socket = socket;
}
@Override
public void run() {
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream())) {
// 接受请求
RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject();
// 反射调用并且返回结果
objectOutputStream.writeObject(invoke(rpcRequest));
objectOutputStream.flush();
} catch (IOException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private Object invoke(RpcRequest rpcRequest, Object service) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class clazz = Class.forName(rpcRequest.getClassName());
Class<?>[] parametersTypes = new Class<?>[rpcRequest.getParameters().length];
for (int i = 0; i < parametersTypes.length; i++) {
parametersTypes[i] = rpcRequest.getParameters()[i].getClass();
}
Method method = clazz.getMethod(rpcRequest.getMethodName(), parametersTypes);
return method.invoke(service, rpcRequest.getParameters());
}
}
Client
1、Client 发起请求
public class Client {
public static void main(String[] args) {
RpcProxyClient rpcProxyClient = new RpcProxyClient();
IHelloService helloService = rpcProxyClient.proxyClient(IHelloService.class,"localhost",8080);
System.out.println(helloService.sayHello("lilei"));;
}
}
2、远程调用(采用jdk动态代理)
- RpcProxyClient
public class RpcProxyClient {
public <T> T proxyClient(Class<?> interfacesCls, String host, int port) {
return (T) Proxy.newProxyInstance(
interfacesCls.getClassLoader(),
new Class<?>[]{interfacesCls},
new RemoteInvocationHandler(host,port));
}
}
- RemoteInvocationHandler 用于远程方法调用
public class RemoteInvocationHandler implements InvocationHandler {
private String host;
private int port;
public RemoteInvocationHandler(String host, int port) {
this.host = host;
this.port = port;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//包装请求
RpcRequest rpcRequest = new RpcRequest();
rpcRequest.setClassName(method.getDeclaringClass().getName());
rpcRequest.setMethodName(method.getName());
rpcRequest.setParameters(args);
//发送请求,并且获得结果
RpcNetTransport rpcNetTransport = new RpcNetTransport(host, port);
return rpcNetTransport.send(rpcRequest);
}
}
3、RpcNetTransport 调用
public class RpcNetTransport {
private String host;
private int port;
public RpcNetTransport(String host, int port) {
this.host = host;
this.port = port;
}
public Object send(RpcRequest rpcRequest) {
Object result = null;
try (Socket socket = new Socket(host, port);
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream())) {
objectOutputStream.writeObject(rpcRequest);
objectOutputStream.flush();
result = objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return result;
}
}
测试
1、启动Server
2、启动Client
测试成功!
问题
- 是否可以用注解来实现?
手写RPC框架V2
- 通过注解来实现服务发布,和服务调用
重写Server
1、加入spring的容器
<!-- 为了方便用spring的注解 Component -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
2、创建配置类SpringConfig
@Configuration
public class SpringConfig {
@Bean(name = "rpcServer")
public RpcServer rpcServer() {
return new RpcServer(8080);
}
}
3、创建注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component // 被Spring进行扫描,并管理
public @interface RpcService {
Class<?> value();
}
4、RpcServer类
- 实现ApplicationContextAware
- 实现InitializingBean
- setApplicationContext 中获取被RpcService注解修饰的类。然后根据类引用路径作为name,对象作为值,保存到一个map中
- 把map作为传入参数传入线程processor
@Configuration
public class RpcProxyServer implements ApplicationContextAware, InitializingBean {
private static final Executor executor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors(),
60L,
TimeUnit.MINUTES,
new ArrayBlockingQueue<>(Runtime.getRuntime().availableProcessors()),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
private int port;
private Map<String, Object> handlerMap = new HashMap<>();
public RpcServer(int port) {
this.port = port;
}
@Override
public void afterPropertiesSet() throws Exception {
try {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket socket = serverSocket.accept();
executor.execute(new ProcessorHandler(handlerMap, socket));
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(RpcService.class);
if (!serviceBeanMap.isEmpty()) {
for (Object serviceBean : serviceBeanMap.values()) {
RpcService rpcService = serviceBean.getClass().getAnnotation(RpcService.class);
String serviceName = rpcService.value().getName();
handlerMap.put(serviceName, serviceBean);
}
}
}
}
5、修改ProcessorHandler
- 接受handlerMap集合
- 根据类名从集合中取出参数
public class ProcessorHandler implements Runnable {
private Socket socket;
private Map<String, Object> handlerMap;
public ProcessorHandler(Map<String, Object> handlerMap, Socket socket) {
this.handlerMap = handlerMap;
this.socket = socket;
}
@Override
public void run() {
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream())) {
// 接受请求
RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject();
// 反射调用并且返回结果
objectOutputStream.writeObject(invoke(rpcRequest));
objectOutputStream.flush();
} catch (IOException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private Object invoke(RpcRequest rpcRequest) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
String className = rpcRequest.getClassName();
Object service = handlerMap.get(className);
if (service == null) {
throw new RuntimeException("service not found :" + className);
}
Class clazz = Class.forName(className);
Class<?>[] parametersTypes = new Class<?>[rpcRequest.getParameters().length];
for (int i = 0; i < parametersTypes.length; i++) {
parametersTypes[i] = rpcRequest.getParameters()[i].getClass();
}
Method method = clazz.getMethod(rpcRequest.getMethodName(), parametersTypes);
return method.invoke(service, rpcRequest.getParameters());
}
}
6、测试是否可行
@ComponentScan("org.example")
public class Server {
public static void main( String[] args ) {
AnnotationConfigApplicationContext configApplicationContext =new AnnotationConfigApplicationContext(Server.class);
}
}
重写客户端
1、加入spring的容器
<!-- 为了方便用spring的注解 Component -->
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.4.RELEASE</version>
</dependency>
2、修改RpcProxyClient
public class RpcProxyClient {
private final String host;
private final int port;
public RpcProxyClient(String host, int port) {
this.host = host;
this.port = port;
}
public <T> T proxyClient(Class<?> interfacesCls) {
return (T) Proxy.newProxyInstance(interfacesCls.getClassLoader(), new Class<?>[]{interfacesCls}, new RemoteInvocationHandler(host, port));
}
}
3、创建SpringConfig
@Configuration
public class SpringConfig {
private static final RpcProxyClient PROXY_CLIENT = new RpcProxyClient("localhost", 8080);
@Bean
public IHelloService iHelloService() {
return PROXY_CLIENT.proxyClient(IHelloService.class);
}
}
4、测试是否可行
@ComponentScan("org.example")
public class Client {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Client.class);
IHelloService iHelloService = context.getBean(IHelloService.class);
String result = iHelloService.sayHello("lilei");
System.out.println(result);
}
}
备注,如果客户端向实现更简化的注解,那么可以利用BeanDefinition来实现,具体看链接
如何把接口加入到Spring容器(任何类/接口都可以用这个方式,例如动态代理)
支持多版本的实现
//todo 有时间在弄吧