0
点赞
收藏
分享

微信扫一扫

Spring Web vs Spring Webflux

凛冬已至夏日未远 2023-06-18 阅读 86

尝试自己手写一份tomcat服务,类似tomcat的功能。
tomcat是servlet容器。所以我们自己写的tomcat也要满足这个功能,能接受处理servlet请求。
这一段的代码还是很经典的,让我们可以真正的明白“tomcat是Servlet容器”这样一句话。
Service 容器包括了 Engine容器
Engine 容器包括了Host容器
Host 容器包括了Context容器

可以想象,Service容器是在外层的容器,他还有一个对外的Connector 接口,提供对外的连接服务。

大致思路是这样的:
1. 先创建一个 Tomcat 对象
2. Tomcat 对象创建一个 Server对象
3. Server对象创建一个Service容器
4. Service容器内创建一个Engine容器
5. Engine容器内创建一个Host容器
6. Host容器内创建一个Context容器

这样相当于我们手写了一个tomcat服务,实现对servlet请求的接收,处理工作。

创建一个tomcat容器

package com.simulate.protocol.http;

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 {

    public void start(String hostname, Integer port) {

        Tomcat tomcat = new Tomcat();

        Server server = tomcat.getServer();
        Service service = server.findService("Tomcat");

        Connector connector = new Connector();
        connector.setPort(port);

        Engine engine = new StandardEngine();
        engine.setDefaultHost(hostname);

        Host host = new StandardHost();
        host.setName(hostname);

        String contextPath = "";
        Context context = new StandardContext();
        context.setPath(contextPath);
        context.addLifecycleListener(new Tomcat.FixContextListener());

        host.addChild(context);
        engine.addChild(host);

        service.setContainer(engine);
        service.addConnector(connector);

        // 接收到的所有请求最终都会转发到 DispatcherServlet 上面去处理
        tomcat.addServlet(contextPath, "dispatcher", new DispatcherServlet());
        context.addServletMappingDecoded("/*", "dispatcher");


        try {
            tomcat.start();
            tomcat.getServer().await();
        } catch (LifecycleException e) {
            e.printStackTrace();
        }


    }
}

Servlet转发

package com.simulate.protocol.http;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class DispatcherServlet extends HttpServlet {

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

Servlet的处理工作

package com.simulate.protocol.http;

import com.alibaba.fastjson.JSONObject;
import com.simulate.framework.Invocation;
import com.simulate.provider.api.HelloService;
import com.simulate.provider.impl.HelloServiceImpl;
import org.apache.commons.io.IOUtils;
import com.simulate.provider.LocalRegister;

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

public class HttpServerHandler {

    // FIXME 这里应该是 服务启动的时候,自动注入进去,而不是在这个类中才注入,如果其他地方有用到这些对象的话,已经晚了
    public HttpServerHandler (){
        LocalRegister.regist(HelloService.class.getName(), HelloServiceImpl.class);
    }


    public void handler(HttpServletRequest req, HttpServletResponse resp) {

        // 获取到Request 请求中的url参数信息,然后可以转发给具体的servlet对象,进行处理。这里只做转发的工作。
        // 这里可以做很多工作,比如访问数据库,数据写入文件等,而我这里做的是通过反射,创建对象,调用对象的方法。
        try {
            Invocation invocation = JSONObject.parseObject(req.getInputStream(), Invocation.class);
            var interfaceName = invocation.getInterfaceName();
            var implClass = LocalRegister.get(interfaceName);
            var method = implClass.getMethod(invocation.getMethodName(), invocation.getParamType());
            var result = (String) method.invoke(implClass.newInstance(), invocation.getParams());

            System.out.println("tomcat:" + result);
            IOUtils.write(result, resp.getOutputStream());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }


    }
}

怎么启动这个tomcat server端呢?可以这样做:

package com.simulate.protocol.http;

import com.simulate.framework.URL;

public class HttpServerTest {

    public static void main(String[] args) {
        URL url = new URL("localhost", 9090);
        HttpServer httpServer = new HttpServer();
        httpServer.start(url.getHostname(), url.getPort());
        System.out.println("==========[httpserver start!]==============");
    }
}

上面的 tomcat启动完成后,就处于等待监听的状态,本地端口是9090的等待状态。等待什么?等待客户端的请求,如果有客户端请求 这个地址 http://localhost:9090/xxx 就会被这个tomcat容器捕捉到,进行后续的处理。

那我们继续看看客户端怎么发起请求呢?

我这里用的是jdk11, 在jdk11中,把HttpClient这个对象纳入进去了,而不需要像以前一样,还要依赖第三方的jar包。如果你用的不是jdk11,可以使用下面注释的代码,就是原生的HttpURLConnection 对象进行 http 连接,请求操作。

package com.simulate.protocol.http;

import com.alibaba.fastjson.JSONObject;
import com.simulate.framework.Invocation;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class HttpClient {

    public String send(String hostname, Integer port, Invocation invocation) {

        try {
            var request = HttpRequest.newBuilder()
                    .uri(new URI("http", null, hostname, port, "/", null, null))
                    .POST(HttpRequest.BodyPublishers.ofString(JSONObject.toJSONString(invocation)))
                    .build();
            var client = java.net.http.HttpClient.newHttpClient();

            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

            String result = response.body();

//            URL url = new URL("http", hostname, port, "/");
//            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
//
//            httpURLConnection.setRequestMethod("POST");
//            httpURLConnection.setDoOutput(true);
//
//            OutputStream outputStream = httpURLConnection.getOutputStream();
//            ObjectOutputStream oos = new ObjectOutputStream(outputStream);
//
//            oos.writeObject(invocation);
//            oos.flush();
//            oos.close();
//
//            InputStream inputStream = httpURLConnection.getInputStream();
//            String result = IOUtils.toString(inputStream);
            return result;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

        return null;

    }
}

那这个客户端怎么使用呢?

package com.simulate.protocol.http;

import com.simulate.framework.Invocation;
import com.simulate.framework.URL;
import com.simulate.provider.api.HelloService;

public class HttpClientTest {

    public static void main(String[] args) {
        URL url = new URL("localhost", 9090);
        HttpClient httpClient = new HttpClient();
        String data = httpClient.send("localhost", 9090, new Invocation(HelloService.class.getName(), "sayHello",
                new Object[]{"World"}, new Class[]{String.class}));

        // LocalRegister.regist(HelloService.class.getName(), HelloServiceImpl.class);
        System.out.println("===========");
        System.out.println(data);
        System.out.println("===========");
    }
}

其他代码:

package com.simulate.framework;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.io.Serializable;

@Data
@AllArgsConstructor
public class Invocation implements Serializable {

    private String interfaceName;
    private String methodName;
    private Object[] params;
    private Class[] paramType;
}



package com.simulate.provider.api;

public interface HelloService {

    String sayHello(String userName);
}



package com.simulate.provider.impl;

import com.simulate.provider.api.HelloService;

public class HelloServiceImpl implements HelloService {

    @Override
    public String sayHello(String userName) {
        return "Hello: " + userName;
    }
}

先启动HttpServerTest中的main方法

在启动HttpClientTest 中的main方法, 就能看到结果了。

这样相当于我们手写了一个tomcat服务,实现对servlet请求的接收,处理工作。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion> 

    <groupId>com.simulate</groupId>
    <artifactId>simulate</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.16.Final</version>
        </dependency>

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

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3.2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.51</version>
        </dependency>

        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-framework</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-client</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.13</version>
        </dependency>
    </dependencies>
</project>
举报

相关推荐

0 条评论