基于 SpringBoot 手写 RPC 框架

GitHub地址

https://github.com/Asperger12345/shenrpc-spring-boot-starter

文件目录

在这里插入图片描述

Message

传递的消息载体类

package com.shen.api;

import lombok.Builder;
import lombok.Data;

import java.io.Serializable;

@Data
@Builder
public class Message implements Serializable {
	//服务接口名
    private String className;
    //方法名
    private String methodName;
    //参数
    private Object[] args;
    //接口类型
    private Class[] types;
}

consumer

服务消费方

@EnableRpcConsumer

打在SpringBoot启动类上

package com.shen.api.consumer;

import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({ReferenceInvokeProxy.class,RpcHandler.class})
public @interface EnableRpcConsumer {
}

@Reference

打在注入的接口上

package com.shen.api.consumer;

import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Reference {
}

ReferenceInvokeProxy

对于每个有 @Reference 注解的接口,生成代理

package com.shen.api.consumer;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.lang.reflect.Field;
import java.lang.reflect.Proxy;

public class ReferenceInvokeProxy implements BeanPostProcessor {

    @Autowired
    RpcHandler invocationHandler;


    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Field[] fields=bean.getClass().getDeclaredFields();
        for(Field field:fields){
            if(field.isAnnotationPresent(Reference.class)){
                field.setAccessible(true);
                Object proxy= Proxy.newProxyInstance(field.getType().getClassLoader(),new Class<?>[]{field.getType()},invocationHandler);
                try {
                    field.set(bean,proxy);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }
}

RpcHandler

让接口生成代理

调用服务的方法,实际上是在进行 BIO 通信

通过在 application.properties 配置 provider.host,provider.port,确定提供方主机号、端口号

package com.shen.api.consumer;

import com.shen.api.Message;
import org.springframework.beans.factory.annotation.Value;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.Socket;

public class RpcHandler implements InvocationHandler {

    @Value("${provider.host}")
    private String host;
    @Value("${provider.port}")
    private int port;



    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Socket socket = new Socket(host, port);
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
        Message message = Message.builder()
                .className(method.getDeclaringClass().getName())
                .methodName(method.getName())
                .args(args)
                .types(method.getParameterTypes()).build();
        objectOutputStream.writeObject(message);
        objectOutputStream.flush();
        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
        return objectInputStream.readObject();

    }
}

provider

服务提供方

@EnableRpcProvider

打在SpringBoot启动类上

package com.shen.api.provider;

import org.springframework.context.annotation.Import;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({SocketServerInitial.class,InitialMediator.class,})
public @interface EnableRpcProvider {

}

@Service

打在提供的服务实现类上

package com.shen.api.provider;

import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface Service {

}

BeanMethod

封装所有提供的类和方法

package com.shen.api.provider;

import lombok.Data;

import java.lang.reflect.Method;

@Data
public class BeanMethod {
    private Object bean;

    private Method method;

}

InitialMediator

对于每个打了自定义的 @Service 的实例化后的 Bean,遍历所有方法,以"接口名.方法名"为key,存储实现类类名和方法名的封装到 map 中

package com.shen.api.provider;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.lang.reflect.Method;

public class InitialMediator implements BeanPostProcessor {


    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

        if(bean.getClass().isAnnotationPresent(Service.class)){
            Method[] methods=bean.getClass().getDeclaredMethods();
            for(Method method:methods){
                String key=bean.getClass().getInterfaces()[0].getName()+"."+method.getName();
                BeanMethod beanMethod=new BeanMethod();
                beanMethod.setBean(bean);
                beanMethod.setMethod(method);
                Mediator.map.put(key,beanMethod);
            }
        }
        return bean;
    }
}

Mediator

"接口名.方法名"为key,存储实现类类名和方法名的封装

根据实现类类名和方法名,反射调用本地方法,给出返回值

package com.shen.api.provider;

import com.shen.api.Message;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class Mediator {


    public static Map<String ,BeanMethod> map=new ConcurrentHashMap();

    private volatile static Mediator instance;

    private Mediator(){}

    public static Mediator getInstance(){
        if(instance==null){
            synchronized (Mediator.class){
                if(instance==null){
                    instance=new Mediator();
                }
            }
        }
        return instance;
    }

    public  Object process(Message message){
        String key=message.getClassName()+"."+message.getMethodName();
        BeanMethod beanMethod=map.get(key);
        if(beanMethod==null){
            return null;
        }
        Object bean=beanMethod.getBean();
        Method method=beanMethod.getMethod();
        try {
            return method.invoke(bean,message.getArgs());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

}

ServiceHandler

伪异步处理 Message

package com.shen.api.provider;

import com.shen.api.Message;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

public class ServiceHandler implements Runnable {

    private Socket socket;

    public ServiceHandler(Socket socket) {
        this.socket = socket;
    }

    public void run() {
        ObjectInputStream objectInputStream = null;
        ObjectOutputStream outputStream = null;
        try {
            objectInputStream = new ObjectInputStream(socket.getInputStream());
            Message message = (Message) objectInputStream.readObject();

            Mediator mediator=Mediator.getInstance();
            Object rs=mediator.process(message);

            outputStream = new ObjectOutputStream(socket.getOutputStream());
            outputStream.writeObject(rs);
            outputStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(objectInputStream != null) {
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outputStream != null) {
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}

SocketServerInitial

容器 Refresh 后,调用此方法监听请求。

通过在 application.properties 配置 provider.port,确定提供方端口号。

package com.shen.api.provider;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class SocketServerInitial implements ApplicationListener<ContextRefreshedEvent> {

    private final ExecutorService executorService= Executors.newCachedThreadPool();

    @Value("${provider.port}")
    private int port;


    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {

        ServerSocket serverSocket=null;
        try {
            serverSocket=new ServerSocket(port);
            while(true){
                Socket socket=serverSocket.accept(); 
                executorService.execute(new ServiceHandler(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

示例

consumer

在这里插入图片描述

启动类

package com.shen.consumer;

import com.shen.api.consumer.EnableRpcConsumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("com.shen.consumer")
@EnableRpcConsumer
public class BootStrap {
    public static void main(String[] args) {
        SpringApplication.run(BootStrap.class,args);
    }

}

配置文件

provider.host = localhost
provider.port = 8888

server.port=8080

TestController

package com.shen.consumer;

import com.shen.api.ExampleService;
import com.shen.api.consumer.Reference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Reference
    ExampleService exampleService;

    @GetMapping("/test")
    public String test(){
        return exampleService.info();
    }
    
}

provider

在这里插入图片描述

启动类

package com.shen.provider;


import com.shen.api.provider.EnableRpcProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan("com.shen.provider")
@EnableRpcProvider
public class BootStrap {
    public static void main(String[] args) {
        SpringApplication.run(BootStrap.class,args);
    }

}

配置文件

provider.port = 8888

server.port=8081

ExampleServiceImpl

package com.shen.provider.service;

import com.shen.api.ExampleService;
import com.shen.api.provider.Service;


@Service
public class ExampleServiceImpl implements ExampleService {
    public String info() {
        return "example";
    }
}

结果

在这里插入图片描述

©️2020 CSDN 皮肤主题: 岁月 设计师: pinMode 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值