手写一个简易版rpc框架

 

 

创建一个maven工程,这个是服务端,命名为 rpc-order,maven创建的时候选择快速启动就好。创建服务端项目,名字为rpc-order,里面有两个module,分别命名为 order -api 这是接口层;还有实现层 order-provider,实现提供方法给客户端调用。

接口层创建接口方法:

package com.lp;

/**
 * @auther lp
 * @date 2020/6/20 0020 13:35
 */
public interface IOrderService {
    String queryOrderList();
    String queryById(String id);
}

 

然后定义一个rpc连接请求的参数类:

package com.lp;

import java.io.Serializable;

/**
 * @auther lp
 * @date 2020/6/20 0020 14:29
 */
public class RpcRequest implements Serializable {
    private String className;
    private String methodName;
    private Object[] args;
    private Class[] types;

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Object[] getArgs() {
        return args;
    }

    public void setArgs(Object[] args) {
        this.args = args;
    }

    public Class[] getTypes() {
        return types;
    }

    public void setTypes(Class[] types) {
        this.types = types;
    }
}

这样api端就已经完成了,install一下。然后开始写实现的module和socket连接,实现方法,上面的注解可以先不看。

package com.lp;

/**
 * @auther lp
 * @date 2020/6/20 0020 13:38
 */

@LpRemoteService
public class OrderServiceImpl implements IOrderService {
    @Override
    public String queryOrderList() {
        return "查询订单列表";
    }

    @Override
    public String queryById(String id) {
        return "通过Id查询订单";
    }
}

定义一个bean里面是这个累的实例和方法

package com.lp;

import java.lang.reflect.Method;

/**
 * @auther lp
 * @date 2020/6/20 0020 16:01
 */
public class BeanMethod {
    private Object bean;

    private Method method;

    public Object getBean() {
        return bean;
    }

    public void setBean(Object bean) {
        this.bean = bean;
    }

    public Method getMethod() {
        return method;
    }

    public void setMethod(Method method) {
        this.method = method;
    }
}

再定义一个类来存储发布过的实例

package com.lp;

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

/**
 * @auther lp
 * @date 2020/6/20 0020 16:00
 */
public class Mediator {
    //用来存储发布服务的实例,map相当于服务调用的路由
    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 processor(RpcRequest request){
        String key=request.getClassName()+"."+request.getMethodName(); //key
        BeanMethod beanMethod=map.get(key);
        if(beanMethod==null){
            return null;
        }
        Object bean=beanMethod.getBean();
        Method method=beanMethod.getMethod();
        try {
            return method.invoke(bean,request.getArgs());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }
}

代理类: 里面的ProcessHadlerZ类是我直接升级的为注解的版本,初始化版本源码里面也有

package com.lp;

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

/**
 * @auther lp
 * @date 2020/6/20 0020 14:02
 */
public class RpcProxyServer {
    private final ExecutorService executorService = Executors.newCachedThreadPool();
    public void publish(Object service,int port){
        ServerSocket serverSocket =null;
        try {
            serverSocket = new ServerSocket(port);
            while (true){
                Socket socket = serverSocket.accept();   //监听客户端请求
                //避免IO阻塞,但是受限制与线程的资源
                executorService.execute(new ProcessHadlerZ(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(serverSocket != null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
package com.lp;

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

/**
 * @auther lp
 * @date 2020/6/20 0020 16:30
 */
public class ProcessHadlerZ implements Runnable {
    private Socket socket;

    public ProcessHadlerZ(Socket socket) {

        this.socket = socket;
    }

    @Override
    public void run() {
        ObjectInputStream inputStream=null;
        ObjectOutputStream outputStream=null;
        try {
            inputStream=new ObjectInputStream(socket.getInputStream());
            RpcRequest request=(RpcRequest)inputStream.readObject(); //反序列化
            //路由
            Mediator mediator=Mediator.getInstance();
            Object rs=mediator.processor(request);
            System.out.println("服务端的执行结果:"+rs);
            outputStream=new ObjectOutputStream(socket.getOutputStream());
            outputStream.writeObject(rs);
            outputStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {

            if(inputStream!=null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (outputStream!=null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
package com.lp;

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

import java.lang.reflect.Method;

/**
 * @auther lp
 * @date 2020/6/20 0020 16:04
 */
@Component
public class IntialMediator implements BeanPostProcessor {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean.getClass().isAnnotationPresent(LpRemoteService.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;
    }
}

最后就是socketserver的相关操作

package com.lp;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

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

/**
 * @auther lp
 * @date 2020/6/20 0020 16:22
 * spring容器启用完成之后会发布一个ContextRefreshedEvent
 */
@Component
public class SocketServerIntial implements ApplicationListener<ContextRefreshedEvent> {
    private final ExecutorService executorService= Executors.newCachedThreadPool();
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        //启动服务
        ServerSocket serverSocket=null;
        try {
            serverSocket=new ServerSocket(8888);
            while(true){
                Socket socket=serverSocket.accept(); //监听客户端请求
                executorService.execute(new ProcessHadlerZ(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(serverSocket!=null){
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

自定义注解

package com.lp;

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;

/**
 * @auther lp
 * @date 2020/6/20 0020 15:51
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Component
public @interface LpRemoteService {
}

第一个版本的启动类:

package com.lp;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
     IOrderService iOrderService = new OrderServiceImpl();
     RpcProxyServer rpcProxyServer = new RpcProxyServer();
     rpcProxyServer.publish(iOrderService,8080);
    }
}

 

使用注解的启动:

package com.lp;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @auther lp
 * @date 2020/6/20 0020 16:43
 */
@Configuration
@ComponentScan("com.lp")
public class BootStrap {
        public static void main(String [] args){
            ApplicationContext applicationContext=
                    new AnnotationConfigApplicationContext(BootStrap.class);

            }

}

自此,服务端的接口实现方法只要打了自定义注解,就能被加载和识别,客户端只需要引入相应的自定义注解就可以调用,来看一下调用端,要引入这边的maven依赖

   <dependency>
      <groupId>com.lp</groupId>
      <artifactId>order-api</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>

然后因为使用了spring-boot,所以也需要引入相应的依赖,这里需要注意的是版本号我是在parent里面定义的。

 <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-web</artifactId>
      </dependency>

我们想跟使用spring的注解一样,将接口注入,这时候自定义注解

package com.lp;

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 LpReference {
}

 

对加了注解的bean判断处理

package com.lp;

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

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

/**
 * @auther lp
 * @date 2020/6/21 0021 12:52
 */
@Component
public class ReferenceProxy implements BeanPostProcessor {
    @Autowired
    RemoteHandler remoteHandler;
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Field [] fields = bean.getClass().getDeclaredFields();
        for (Field field:fields) {
            //针对加了这个注解的,设置为一个代理的值
            if(field.isAnnotationPresent(LpReference.class)){
                field.setAccessible(true);
                Object proxy = Proxy.newProxyInstance(field.getType().getClassLoader(),new Class<?>[]{field.getType()},remoteHandler);
                try {
                    //对加了注解的加了一个代理,代理实现了remoteHandler
                    field.set(bean,proxy);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }

            }

        }
        return bean;
    }
}

 

定义处理类,这里的端口号和ip是在配置文件里面通过spring,因为要使用springboot启动项目内置tomcat端口号8080,所以端口号有改动,这里为8888

package com.lp;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @auther lp
 * @date 2020/6/20 0020 14:14
 */
@Component
public class RemoteHandler implements InvocationHandler {
    @Value("${lp.host}")
    private String host;
    @Value("${lp.port}")
    private int port;

    public RemoteHandler() {

    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //建立远程连接
            RpcNetTransport rpcNetTransport = new RpcNetTransport(host,port);
            RpcRequest request = new RpcRequest();
            request.setArgs(args);
            request.setClassName(method.getDeclaringClass().getName());
            request.setTypes(method.getParameterTypes()); //参数的类型
            request.setMethodName(method.getName());
            return rpcNetTransport.send(request);
    }
}

配置文件:

lp.host=localhost
lp.port=8888

spring.application.name=rpc-service
server.port=8080

 

客户端代理

package com.lp;

import java.lang.reflect.Proxy;

/**
 * @auther lp
 * @date 2020/6/20 0020 14:08
 */
public class RpcProxyClient {
    public <T>T client(final Class<T> interfaceCls,final String host,int port){
        return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class<?>[]{interfaceCls}, new RemoteHandler());

    }

}

最后就是在controller,注入调用相对应的接口方法测试

package com.lp;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @auther lp
 * @date 2020/6/21 0021 12:48
 */
@RestController
public class ServiceController {
    @LpReference
    private IOrderService iOrderService;

    @LpReference
    private TestService testService;

    @GetMapping("/ser")
    public String ser(){
        return iOrderService.queryOrderList();
    }

    @GetMapping("/test")
    public String test(){
        return testService.say();
    }
}

 

全部完毕之后,先打开服务端,再打开客户端,注意这里使用注解端口号已经改为8888 ,如果要测试不带注解的版本,需要把端口号和handler里面的参数做相应的修改。打开浏览器输入 :localhost:8080/ser ,就可以调用其他服务的接口方法。这个源码里面是有没用注解和用了注解两个版本。

源码地址

     服务端:https://github.com/chonghuidt/rpc-order.git   

     客户端:https://github.com/chonghuidt/rpc-service.git

本文纯属个人学习研究,如果转载请标明出处,希望能你有所帮助。

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值