手写dubbo 12-基于tomcat实现RPC(番外篇)

2021-01-21 18:08:02 浏览数 (1)

博客中代码地址:https://github.com/farliu/farpc.git

dubbo结构

直至上一章,手写dubbo全部介绍完了,这一章作为番外篇,了解如何使用内嵌tomcat。

项目结构介绍

本节涉及博客中代码的module,farpc-rpc(远程调用)。

内嵌tomcat使用

使用tomcat,自然就是http协议,我们先导入tomcat的依赖。

代码语言:javascript复制
 <dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-core</artifactId>
    <version>9.0.27</version>
</dependency>

继续实现之前留下的扩展接口IConsumerServer、IProviderServer。在实现IProviderServer之前,我们需要先得到一个Servlet用于处理请求。

代码语言:javascript复制
public class DispatcherServlet extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        new HttpServerHandler().handle(req, resp);
    }
}

为了方便扩展,我在servlet中调用定义的Handler,后续如果需要增加Handler,也只要修改这里就行了。

代码语言:javascript复制
public class HttpServerHandler {
    private static final Logger logger = LoggerFactory.getLogger(HttpServerHandler.class);

    public void handle(HttpServletRequest req, HttpServletResponse resp) {
        try {
            ServletInputStream inputStream = req.getInputStream();
            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
            RequestDTO requestDTO = (RequestDTO) objectInputStream.readObject();

            Object result = new Object();

            logger.info("receive request.. {}", requestDTO);
            if (Container.getProviders().containsKey(requestDTO.getClassName())) {
                Object provider = Container.getProviders().get(requestDTO.getClassName());

                Class<?> providerClazz = provider.getClass();
                Method method = providerClazz.getMethod(requestDTO.getMethodName(), requestDTO.getTypes());
                result = method.invoke(provider, requestDTO.getParams());
            }

            ObjectOutputStream objectOutputStream = new ObjectOutputStream(resp.getOutputStream());
            objectOutputStream.writeObject(result);
            objectOutputStream.flush();
            objectOutputStream.close();
//            IOUtils.write(result.toString(), resp.getOutputStream());
        } catch (Exception e) {
        }
    }
}

Handler的代码也很容易理解,就是从Request中获得请求参数,然后根据请求参数反射执行对应的方法,然后输出到输出流中。

对于IConsumerServer的实现,就是Http调用了,代码如下:

代码语言:javascript复制
public class HttpConsumerServer implements IConsumerServer {
    public Object execute(String address, RequestDTO requestDTO) {
        String[] addrs = address.split(":");
        String ip = addrs[0];
        Integer port = Integer.parseInt(addrs[1]);

        try {
            URL url = new URL("http", ip, port, "/");
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("POST");
            httpURLConnection.setDoOutput(true);
            httpURLConnection.setDoInput(true);

            OutputStream outputStream = httpURLConnection.getOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
            objectOutputStream.writeObject(requestDTO);
            objectOutputStream.flush();
            objectOutputStream.close();


            InputStream inputStream = httpURLConnection.getInputStream();
            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
            Object result =  objectInputStream.readObject();
            return result;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

还有别忘了增加SPI扩展点

代码语言:javascript复制
http=com.ofcoder.farpc.rpc.http.HttpConsumerServer

http=com.ofcoder.farpc.rpc.http.HttpProviderServer
测试

测试代码位于farpc-demo的模块下。

代码语言:javascript复制
@Test
public void providerTest() throws IOException {
    IProviderServer server = RpcFactory.getProviderServer();
    server.start("127.0.0.1:20880");
    System.in.read();
}

@Test
public void consumerTest(){
    IWelcome welcome = ConsumerProxy.create(IWelcome.class);
    String far = welcome.greet("far");
    System.out.println(far);
}

可以分别看到相应的日志。服务提供端日志如下:

代码语言:javascript复制
...
十月 28, 2019 9:55:57 下午 org.apache.coyote.AbstractProtocol init
信息: Initializing ProtocolHandler ["http-nio-20880"]
十月 28, 2019 9:55:57 下午 org.apache.catalina.core.StandardService startInternal
信息: Starting service [Tomcat]
十月 28, 2019 9:55:57 下午 org.apache.catalina.core.StandardEngine startInternal
信息: Starting Servlet engine: [Apache Tomcat/9.0.27]
十月 28, 2019 9:55:57 下午 org.apache.coyote.AbstractProtocol start
信息: Starting ProtocolHandler ["http-nio-20880"]

消费端:

代码语言:javascript复制
...
[28/10/19 21:56:56:191 CST] main-EventThread  INFO state.ConnectionStateManager: State change: CONNECTED
hello far, welcome to ofcoder.

0 人点赞