手写一个RPC,更深刻的理解RPC原理。

写一个简单的RPC其实很简单,服务的提供者,服务的消费者,注册中心以及协议。

  1. 编写服务的提供者
    在这里插入图片描述
    在这里插入图片描述
    2.编写注册中心:此次注册中心我们将服务注册到MAP集合中,Map<String,Map<URL,Class>>,外面map的key存服务接口的全类名,URL封装了调用服务的ip和port,里面的value指定具体的实现类,注册中心类提供注册服务并暴露服务和发现服务功能:
    首先编写一个url类:
package com.chao.pojo;

import java.util.Objects;

public class URL {
    private String hostname;
    private Integer port;

    public URL() {
    }

    public URL(String hostname, Integer port) {
        this.hostname = hostname;
        this.port = port;
    }

    public String getHostname() {
        return hostname;
    }

    public void setHostname(String hostname) {
        this.hostname = hostname;
    }

    public Integer getPort() {
        return port;
    }

    public void setPort(Integer port) {
        this.port = port;
    }
//重写equals和hashcode是为了比较内容,如果不进行重写那么就是比较地址
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        URL url = (URL) o;
        return Objects.equals(hostname, url.hostname) && Objects.equals(port, url.port);
    }

    @Override
    public int hashCode() {
        return Objects.hash(hostname, port);
    }
}

package com.chao.registry;

import com.chao.pojo.URL;

import java.util.HashMap;
import java.util.Map;

public class NativeRegistry {
    //构建注册中心
    private static Map<String, Map<URL,Class>> registryCenter = new HashMap<String,Map<URL,Class>>();
    /*
     * 注册服务
     * */
    public static void regist(String interfaceName,URL url,Class implClass) {
        Map map = new HashMap();
        map.put(url,implClass);
        registryCenter.put(interfaceName,map);
    }
    /*
    * 获取服务信息
    *
    * */
    public static Class get(String interfaceName,URL url) {
        Map<URL, Class> urlClassMap = registryCenter.get(interfaceName);
        Class aClass = urlClassMap.get(url);
        return aClass;
    }
}

服务的注册:

package com.chao.provider;

import com.chao.pojo.URL;
import com.chao.registry.NativeRegistry;
import com.chao.service.HelloService;
import com.chao.service.impl.HelloServiceImpl;

public class ServiceProvider {
    public static void main(String[] args) {
        //真正的注册服务
        NativeRegistry.regist(HelloService.class.getName(),new URL("localhost",8080), HelloServiceImpl.class);
        //System.out.println(HelloService.class.getName());
    }
}

  1. 服务的暴露
    服务之间调用的通信协议采用的是http协议,所以在服务provider中启动tomcat暴露服务

添加内嵌tomcat的依赖

 <dependency>
        <groupId>org.apache.tomcat.embed</groupId>
        <artifactId>tomcat-embed-core</artifactId>
        <version>9.0.13</version>
    </dependency>

配置tomcat:

package com.chao.tomcat;

import org.apache.catalina.*;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.startup.Tomcat;

public class HttpServer {
    /*
    * tomcat服务启动
    * 参考tomcat配置
    * <Server port="8005" shutdown="SHUTDOWN">
      <Service name="Catalina">
         <Connector port="8080" protocol="HTTP/1.1"
                connectionTimeout="20000"
                redirectPort="8443"
          URLEncoding= "UTF-8"/>
         <Engine name="Catalina" defaultHost="localhost">
         <Host name="localhost" appBase="webapps"
              unpackWARs="true" autoDeploy="true">
              <Context path="" doBase="WORKDIR" reloadable="true"/>
              </Host>
           </Engine>
        </Service>
     </Server>
    *
    *
    * */

    public void start(String hostname,int port) {
        //实例一个tomcat
        Tomcat tomcat = new Tomcat();

        //构建server
        Server server = tomcat.getServer();

        //获取service
        Service service = server.findService("Tomcat");

        //构建Connector
        Connector connector = new Connector();
        connector.setPort(port);
        connector.setURIEncoding("UTF-8");

        //构建Engine
        Engine engine = new StandardEngine();
        engine.setDefaultHost(hostname);

        //构建Host
        Host host = new StandardHost();
        host.setName(hostname);

        //构建Context
        String contextPath = "";
        Context context = new StandardContext();
        context.setPath(contextPath);
        context.addLifecycleListener(new Tomcat.FixContextListener());//生命周期监听器

        //然后按照server.xml,一层层把子节点添加到父节点
        host.addChild(context);
        engine.addChild(host);
        service.setContainer(engine);
        service.addConnector(connector);
        //service在getServer时被添加到server节点了
        //tomcat是一个servlet,设置路径与映射
        tomcat.addServlet(contextPath,"dispatcher",new DispatcherServlet());
        context.addServletMappingDecoded("/*","dispatcher");
        try {
            tomcat.start();//启动tomcat
            tomcat.getServer().await();//接受请求
        }catch (LifecycleException e) {
            e.printStackTrace();
        }

    }
}

  1. 创建一个HttpServletHandler:
package com.chao.tomcat;

import com.chao.pojo.Invocation;
import com.chao.pojo.URL;
import com.chao.registry.NativeRegistry;
import sun.nio.ch.IOUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class HttpServletHandler {

    public void handle(HttpServletRequest req, HttpServletResponse resp) {
        try {
            //服务请求的处理逻辑

            //1. 通过请求流获取请求服务调用的参数
            InputStream inputStream = req.getInputStream();
            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
            Invocation invocation = (Invocation) objectInputStream.readObject();

            //2. 从注册中心获取服务的列表
            Class implClass = NativeRegistry.get(invocation.getInterfaceName(),new URL("localhost",8080));

            //3. 调用服务 反射
            Method method = implClass.getMethod(invocation.getMethodName(),invocation.getParaTypes());
            String result = (String) method.invoke(implClass.newInstance(),invocation.getParams());

            //4. 结果返回
            IOUtils.write(result,resp.getOutputStream());

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

创建Invocation这个类:

package com.chao.pojo;

public class Invocation {
    private String interfaceName;
    private String methodName;
    private Object[] params;
    private Class[] paraTypes;

    public Invocation() {
    }

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

    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 Object[] getParams() {
        return params;
    }

    public void setParams(Object[] params) {
        this.params = params;
    }

    public Class[] getParaTypes() {
        return paraTypes;
    }

    public void setParaTypes(Class[] paraTypes) {
        this.paraTypes = paraTypes;
    }
}

注意:要实现IOUtils需要导入一个依赖

<dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.6</version>
    </dependency>
  1. 启动tomcat ,暴露服务
    在这里插入图片描述

运行tomcat:
在这里插入图片描述

至此服务的提供者和注册中心就完成了

  1. 编写服务端
    创建一个HelloClient
package com.chao.consumer;

import com.chao.pojo.Invocation;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;

public class HelloClient {
    public String post(String hostName, int port, Invocation invocation) throws IOException {
        //1.进行连接
        URL url = new URL("http", hostName, port, "/client");
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setRequestMethod("POST");
        urlConnection.setDoOutput(true);
        //2.发送调用的信息
        OutputStream outputStream = urlConnection.getOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
        objectOutputStream.writeObject(invocation);
        objectOutputStream.flush();
        objectOutputStream.close();

        //3.将输入流转为字符串,获取远程调用的结果
        InputStream inputStream = urlConnection.getInputStream();
        return IOUtils.toString(inputStream);
    }
}

创建一个ConsumerMain

package com.chao.consumer;

import com.chao.pojo.Invocation;
import com.chao.service.HelloService;

import java.io.IOException;

public class ConsumerMain {
    public static void main(String[] args) throws IOException {
        Invocation invocation = new Invocation(HelloService.class.getName(), "sayHello", new Object[]{"myRPC客户端"}, new Class[]{String.class});

        HttpClient httpClient = new HttpClient();
        String result = httpClient.post("localhost", 8080, invocation);
        System.out.println(result);
    }
}

结果:先启动tomcat同时服务的提供发已经注册到自定义的map(注册中心),在启动服务的消费者
在这里插入图片描述
在这里插入图片描述
成功实现手写RPC

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值