详解-手写RPC框架(3)-处理RPC请求&服务的本地注册&多版本实现

引言

在前两篇博客中,我们介绍了RPC框架的概念和原理,并且使用java运行了tomcat,本章将会进一步了解rpc的核心功能。

本文文件目录

在这里插入图片描述

处理RPC请求

  1. 获取请求信息
  • 在HttpServer的Tomcat配置中添加下列语句,这让我们可以将请求接收到DispatcherServlet中
public class HttpServer {
    public void start(String hostname,Integer port){
		
		.....
		
        //接收到的请求都会经由DispatcherServlet处理
        tomcat.addServlet(contextPath,"dispatcher ",new DispatcherServlet());
        context.addServletMappingDecoded("/*","dispatcher");

        try{
            tomcat.start();
            tomcat.getServer().await();
        }catch (LifecycleException e){
            e.printStackTrace();
        }
    }
}
  • 在protocol下创建DispatcherServlet类,该类将作为中转,通过多个handler处理不同的请求
//DispatcherServlet只是中转站,可以通过多个handler处理不同请求
public class DispatcherServlet extends HttpServlet {
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            new HttpServerHandler().handler(req,resp);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) 		  {
            throw new RuntimeException(e);
        }
    }
}
  1. 解析请求信息并定位实现方法
  • 在common下创建Invocation类,并实现序列化接口,该类用于将当前调用的服务的信息封装,如服务接口、方法名、参数列表
public class Invocation implements Serializable {

    private String interfaceName;//当前调用的哪个接口

    private String methodName;//哪个方法
    private Class[] parameterTypes;//方法的参数类型
    private Object[] parameters;//调用时传入的参数值

    public Invocation(String interfaceName, String methodName, Class[] parameterTypes, Object[] parameters) {
        this.interfaceName = interfaceName;
        this.methodName = methodName;
        this.parameterTypes = parameterTypes;
        this.parameters = parameters;
    }

    public String getInterfaceName() {
        return interfaceName;
    }

    public void setInterfaceName(String interfaceName) {
        this.interfaceName = interfaceName;
    }

    public String getMethodName() {
        return methodName;
    }

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

    public Class[] getParameterTypes() {
        return parameterTypes;
    }

    public void setParameterTypes(Class[] parameterTypes) {
        this.parameterTypes = parameterTypes;
    }

    public Object[] getParameters() {
        return parameters;
    }

    public void setParameters(Object[] parameters) {
        this.parameters = parameters;
    }
}
  • 在protocol下创建HttpServerHandler类,用来接收请求,并根据Invocation对象解析请求后定位真正的实现类
public class HttpServerHandler {
    //接受请求
    public void handler(HttpServletRequest req, HttpServletResponse resp) throws IOException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        //处理请求-->调用哪个接口、方法、方法参数(核心)
        //进行反序列化,获取invacation对象
        Invocation invocation=(Invocation) new ObjectInputStream(req.getInputStream()).readObject();
        //拿到方法名、接口名等,再根据拿到的这些东西定位实现类
        String interfaceName=invocation.getInterfaceName();
        ...
    }
}

服务本地注册

我们已经解析到了invocation的封装信息,如何根据信息找到对应实现类呢?——本地注册

  1. 创建register包,并在包下创建LocalRegister类,用于在注册时传入接口名(可以是其他信息,这里用接口名)与对应实现类对象
public class LocalRegister {
    private static Map<String,Class> map=new HashMap<>();
    //注册时传入接口名与对应实现类对象

    public static void regist(String interfaceName,Class implClass){
        map.put(interfaceName,implClass);
    }
    public static Class get(String interfaceName){
        return map.get(interfaceName);
    }
}
  1. 在HttpServletHandler类中,通过本地注册获取接口名对应的实现类,进而获取其他信息(方法、参数类型等),生成方法对象,使用该对象进行方法的调用,写入结果
public class HttpServerHandler {
    //接受请求
    public void handler(HttpServletRequest req, HttpServletResponse resp) throws IOException, ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        //处理请求-->调用哪个接口、方法、方法参数(核心)
        //进行反序列化,获取invacation对象
        Invocation invocation=(Invocation) new ObjectInputStream(req.getInputStream()).readObject();
        //拿到方法名、接口名等,再根据拿到的这些东西定位实现类
        String interfaceName=invocation.getInterfaceName();
        //如何根据接口找到对应的实现类?-->本地注册


        //如果invocation没有给版本号,可以定义一个默认的版本号
        //通过本地注册后得到的映射关系获取接口名对应的实现类
        Class classImpl= LocalRegister.get(interfaceName,"1.0");
        //通过实现类获取方法、参数列表等,生成方法对象method
        Method method=classImpl.getMethod(invocation.getMethodName(),invocation.getParameterTypes());
        //通过反射的方式执行当前实现类对应的对象,可以取出参数值向方法传参
        String result=(String)method.invoke(classImpl.newInstance(),invocation.getParameters());

        //完成结果写入
        IOUtils.write(result,resp.getOutputStream());
    }
}
  1. 在Provider类中通过本地服务的方式获取接口名和实现类对象
public class Provider {
    public static void main(String[] args) {
        //希望启动时可以接收网络请求,可以用Netty、tomcat
        
        //启动器先进行注册,获取接口名和实现类对象
        //出现多个实现类时启动时可以指定版本号,根据不同版本执行不同的实现类方法
        LocalRegister.regist(HelloService.class.getName(),HelloServiceImpl.class);
        LocalRegister.regist(HelloService.class.getName(),HelloServiceImpl2.class);

        //rpc框架负责启动网络
        HttpServer httpServer=new HttpServer();
        httpServer.start("localhost",8080);
    }
}

多版本支持

在实际项目中,服务的版本迭代是非常常见的情况。为了兼容不同的版本,我们可以在RPC框架中加入多版本支持。

前提:在Provider应用下创建HelloServiceImpl2实现类,作为HelloServiceImpl的2.0版本,方法签与方法体可以同1.0版本,也可以自行改变

  1. 请求中携带版本号
  • 在注册类中加入版本字段实现注册时传入版本号
public class LocalRegister {
    private static Map<String,Class> map=new HashMap<>();
    //注册时传入接口名与对应实现类对象

    //支持多版本实现
    public static void regist(String interfaceName,String version,Class implClass){
        map.put(interfaceName+version,implClass);
    }
    public static Class get(String interfaceName,String version){
        return map.get(interfaceName);
    }
}
  1. 服务注册时指定版本号
    在服务注册过程中,我们可以为每个服务方法指定版本号。这样,当有多个版本的服务方法存在时,我们可以根据请求中指定的版本号找到合适的服务对象进行调用。
  • Provider中启动tomcat前进行注册时传入版本信息
public class Provider {
    public static void main(String[] args) {
        //希望启动时可以接收网络请求,可以用Netty、tomcat

        //启动器先进行注册,获取接口名和实现类对象
        //出现多个实现类时启动时可以指定版本号,根据不同版本执行不同的实现类方法
        LocalRegister.regist(HelloService.class.getName(),"1.0",HelloServiceImpl.class);
        LocalRegister.regist(HelloService.class.getName(),"2.0",HelloServiceImpl2.class);

        //rpc框架负责启动网络
        HttpServer httpServer=new HttpServer();
        httpServer.start("localhost",8080);
    }
}
  1. Provider中通过不同版本的字段调用不同版本的方法
public class Provider {
    public static void main(String[] args) {
        //希望启动时可以接收网络请求,可以用Netty、tomcat

        //启动器先进行注册,获取接口名和实现类对象
        //出现多个实现类时启动时可以指定版本号,根据不同版本执行不同的实现类方法
        LocalRegister.regist(HelloService.class.getName(),"1.0",HelloServiceImpl.class);
        LocalRegister.regist(HelloService.class.getName(),"2.0",HelloServiceImpl2.class);

        //rpc框架负责启动网络
        HttpServer httpServer=new HttpServer();
        httpServer.start("localhost",8080);
    }
}

结语

通过本篇博客,我们详细介绍了如何处理RPC请求、服务的本地注册和多版本实现。这些功能是一个完整RPC框架不可或缺的部分。在实际应用开发中,我们可以根据具体需求进行相应的扩展和优化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值