关于作者:技术博主,专注底层原理和实战。更多干货请关注 CSDN:CodeStats-CSDN博客


📑 文章目录

  • 一、什么是C10K问题?为什么NIO能解决?

  • 二、BIO vs NIO:从代码看本质区别

  • 三、Tomcat核心架构:一张图看懂容器层级

  • 四、启动入口:Catalina如何组装所有组件?

  • 五、NIO连接器:手写Selector管理万级连接

  • 六、HTTP协议解析:如何把Socket字节流变成Request对象?

  • 七、Pipeline-Valve:如何用责任链解耦处理逻辑?

  • 八、Servlet映射:如何从URL找到对应的Servlet?

  • 九、类加载隔离:如何让不同应用用不同版本的JAR?

  • 十、DispatcherServlet:Spring MVC风格的入口

  • 十一、完整请求链路串联

  • 十二、运行验证

  • 十三、核心代码文件清单


一、什么是C10K问题?为什么NIO能解决?

1.1 C10K问题的由来

C10K = Concurrent 10K connections,即单机同时处理1万个网络连接

这个问题的提出者 Dan Kegel 在 2003 年指出:当时的 Web 服务器(Apache)使用 BIO(阻塞I/O)模型每个连接占用一个线程,当并发达到 1 万时:

  • 需要 1 万个线程

  • 每个线程栈默认 1MB → 10GB 内存

  • CPU 大量时间花在线程上下文切换

  • 服务器直接崩溃或响应极慢

1.2 BIO 的瓶颈本质

java

// BIO 模型伪代码
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
    Socket socket = serverSocket.accept();  // ① 阻塞:等待连接
    new Thread(() -> {
        while (true) {
            socket.getInputStream().read();  // ② 阻塞:等待数据
            // 处理业务...
        }
    }).start();
}

阻塞点

  1. accept() 阻塞 → 线程闲等新连接

  2. read() 阻塞 → 线程闲等数据到来

结论:BIO 的线程利用率极低,大部分时间在空等。

1.3 NIO 如何解决问题?

NIO 的核心是 Selector(多路复用器) + 非阻塞 Channel

java

// NIO 模型核心思想
Selector selector = Selector.open();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);  // 非阻塞
serverChannel.register(selector, SelectionKey.OP_ACCEPT);

while (true) {
    selector.select();  // 只阻塞到有事件发生
    for (SelectionKey key : selector.selectedKeys()) {
        if (key.isAcceptable()) {
            // 有新连接 → 注册到 Selector
            SocketChannel client = serverChannel.accept();
            client.configureBlocking(false);
            client.register(selector, SelectionKey.OP_READ);
        } else if (key.isReadable()) {
            // 有数据可读 → 交给线程池处理
            threadPool.submit(() -> handle(key));
        }
    }
}

核心突破

对比项 BIO NIO
线程模型 1连接 → 1线程 1线程 → 万级连接
阻塞点 accept()、read() 只有 select()
1万连接内存 ~10GB ~16MB
CPU消耗 大量线程切换 少量事件处理

二、BIO vs NIO:从代码看本质区别

2.1 BIO 版服务器(CodeStats 对比示例)

java

// BIO 版本 - 每个请求一个线程
public class BioServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        ExecutorService threadPool = Executors.newCachedThreadPool();
        
        while (true) {
            Socket socket = serverSocket.accept();  // 阻塞点1
            threadPool.submit(() -> {
                try {
                    BufferedReader reader = new BufferedReader(
                        new InputStreamReader(socket.getInputStream()));
                    String line;
                    while ((line = reader.readLine()) != null && !line.isEmpty()) {
                        // 阻塞点2:read() 等待数据
                    }
                    // 处理请求...
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }
}

2.2 NIO 版服务器(CodeStats 自研实现)

java

// CodeStats NIO 连接器核心
public class Connector {
    private Selector selector;
    private ServerSocketChannel serverChannel;
    private ExecutorService threadPool;
    
    public void start() throws IOException {
        serverChannel = ServerSocketChannel.open();
        serverChannel.configureBlocking(false);  // 关键:非阻塞
        serverChannel.bind(new InetSocketAddress(8080));
        
        selector = Selector.open();
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);
        
        while (true) {
            selector.select();  // 阻塞,但有事件就返回
            Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
            while (iter.hasNext()) {
                SelectionKey key = iter.next();
                iter.remove();
                
                if (key.isAcceptable()) {
                    handleAccept();
                } else if (key.isReadable()) {
                    threadPool.submit(() -> handleRead(key));
                }
            }
        }
    }
}

关键差异:BIO 的每个线程都在 read() 上阻塞,NIO 只有极少数线程在 select() 上阻塞。


三、Tomcat核心架构:一张图看懂容器层级

text

┌─────────────────────────────────────────────────────────────────┐
│                         Catalina                                 │
│                      (启动入口 + 组装器)                          │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                          Server                                  │
│                     (顶层容器,管理Service)                       │
└─────────────────────────────┬───────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                          Service                                 │
│                 (包含 Connector + Engine)                        │
└──────────────┬──────────────────────────────┬───────────────────┘
               │                              │
               ▼                              ▼
      ┌────────────────┐              ┌─────────────────┐
      │   Connector    │              │     Engine      │
      │  (NIO连接器)    │◄────────────►│   (引擎容器)     │
      │ - 接收请求      │              │ - 管理Host       │
      │ - 解析HTTP     │              └────────┬────────┘
      └────────────────┘                       │
                                               ▼
                                      ┌─────────────────┐
                                      │      Host       │
                                      │   (虚拟主机)     │
                                      │ - 管理Context    │
                                      └────────┬────────┘
                                               │
                                               ▼
                                      ┌─────────────────┐
                                      │    Context      │
                                      │   (Web应用)      │
                                      │ - 类加载隔离      │
                                      │ - Servlet映射    │
                                      └────────┬────────┘
                                               │
                                               ▼
                                      ┌─────────────────┐
                                      │    Wrapper      │
                                      │   (Servlet)     │
                                      │ - service()     │
                                      └─────────────────┘

一句话总结:每个组件持有子组件,形成树形结构,请求从 Connector 进入,逐层向下传递。


四、启动入口:Catalina如何组装所有组件?

java

// 源码:Catalina.java
public class Catalina {
    private Server server;
    
    public void load() {
        // 1. 创建 Server
        StandardServer server = new StandardServer();
        
        // 2. 创建 Service,添加到 Server
        StandardService service = new StandardService();
        server.addService(service);
        
        // 3. 创建 Connector(NIO 连接器)
        Connector connector = new Connector(28080);
        service.setConnector(connector);
        
        // 4. 创建容器层级:Engine → Host → Context → Wrapper
        StandardEngine engine = new StandardEngine();
        service.setEngine(engine);
        
        StandardHost host = new StandardHost();
        host.setAppBase("webapps");
        engine.addChild(host);
        
        StandardContext context = new StandardContext();
        context.setDocBase("webapps/demo");
        host.addChild(context);
        
        // 5. 注册 DispatcherServlet
        StandardWrapper wrapper = new StandardWrapper();
        wrapper.setServletClass("com.omni.framework.spring.mvc.DispatcherServlet");
        context.addChild(wrapper);
        context.addServletMapping("/*", wrapper.getName());
        
        this.server = server;
    }
    
    public void start() {
        server.init();
        server.start();
    }
}

五、NIO连接器:手写Selector管理万级连接

java

// 源码:Connector.java - 完整NIO实现
public class Connector implements Lifecycle {
    private ServerSocketChannel serverChannel;
    private Selector selector;
    private ExecutorService threadPool;
    private volatile boolean running = true;
    
    @Override
    public void init() {
        threadPool = Executors.newCachedThreadPool();
    }
    
    @Override
    public void start() {
        new Thread(() -> {
            try {
                // 1. 初始化 NIO 组件
                serverChannel = ServerSocketChannel.open();
                serverChannel.configureBlocking(false);
                serverChannel.socket().bind(new InetSocketAddress(port));
                
                selector = Selector.open();
                serverChannel.register(selector, SelectionKey.OP_ACCEPT);
                
                log.info("Connector listening on {}", port);
                
                // 2. NIO 事件循环
                while (running) {
                    selector.select();  // 阻塞到有事件发生
                    Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                    
                    while (iterator.hasNext()) {
                        SelectionKey key = iterator.next();
                        iterator.remove();
                        
                        if (key.isAcceptable()) {
                            // 接受新连接
                            SocketChannel client = serverChannel.accept();
                            client.configureBlocking(false);
                            client.register(selector, SelectionKey.OP_READ);
                            log.debug("New connection from {}", client.getRemoteAddress());
                        } else if (key.isReadable()) {
                            // 有数据可读,交给线程池处理
                            SocketChannel client = (SocketChannel) key.channel();
                            threadPool.submit(() -> processRequest(client));
                        }
                    }
                }
            } catch (IOException e) {
                log.error("Connector error", e);
            }
        }).start();
    }
    
    private void processRequest(SocketChannel client) {
        try (InputStream in = Channels.newInputStream(client);
             OutputStream out = Channels.newOutputStream(client)) {
            
            // 解析 HTTP 请求
            Request request = new Request(in);
            request.parse();
            
            // 处理响应
            Response response = new Response(out);
            
            // 调用 Pipeline 处理
            service.getEngine().getPipeline().invoke(request, response);
            
            response.send();
        } catch (Exception e) {
            log.error("Request processing failed", e);
        }
    }
}

六、HTTP协议解析:如何把Socket字节流变成Request对象?

java

// 源码:Request.java
public class Request implements HttpServletRequest {
    private String method;
    private String uri;
    private String queryString;
    private Map<String, String> headers = new HashMap<>();
    private Map<String, String> parameters = new HashMap<>();
    private String body;
    
    public void parse() throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(input));
        
        // 1. 解析请求行:GET /demo/user/name?name=Tom HTTP/1.1
        String requestLine = reader.readLine();
        if (requestLine == null) return;
        
        String[] parts = requestLine.split(" ");
        method = parts[0];
        
        // 2. 解析 URI 和 QueryString
        String fullUri = parts[1];
        int qIdx = fullUri.indexOf('?');
        if (qIdx >= 0) {
            queryString = fullUri.substring(qIdx + 1);
            fullUri = fullUri.substring(0, qIdx);
            parseQueryString(queryString, parameters);
        }
        uri = fullUri;
        
        // 3. 解析 Headers
        String line;
        while ((line = reader.readLine()) != null && !line.isEmpty()) {
            int colon = line.indexOf(':');
            if (colon > 0) {
                String name = line.substring(0, colon).trim().toLowerCase();
                String value = line.substring(colon + 1).trim();
                headers.put(name, value);
                
                // 特殊处理 Content-Type
                if ("content-type".equals(name) && value.startsWith("multipart/form-data")) {
                    isMultipart = true;
                }
            }
        }
        
        // 4. 解析 Body(POST 请求)
        if ("POST".equalsIgnoreCase(method) && !isMultipart) {
            StringBuilder bodyBuilder = new StringBuilder();
            while (reader.ready()) {
                bodyBuilder.append((char) reader.read());
            }
            body = bodyBuilder.toString();
            
            // 表单格式也解析到 parameters
            String contentType = headers.get("content-type");
            if (contentType != null && contentType.startsWith("application/x-www-form-urlencoded")) {
                parseQueryString(body, parameters);
            }
        }
    }
    
    private void parseQueryString(String query, Map<String, String> target) {
        if (query == null) return;
        for (String pair : query.split("&")) {
            int eq = pair.indexOf("=");
            if (eq > 0) {
                try {
                    String key = URLDecoder.decode(pair.substring(0, eq), "UTF-8");
                    String val = URLDecoder.decode(pair.substring(eq + 1), "UTF-8");
                    target.put(key, val);
                } catch (Exception ignored) {}
            }
        }
    }
}

七、Pipeline-Valve:如何用责任链解耦处理逻辑?

java

// 源码:SimplePipeline.java
public class SimplePipeline implements Pipeline {
    private List<Valve> valves = new ArrayList<>();
    private Valve basic;
    private Container container;
    
    public SimplePipeline(Container container) {
        this.container = container;
    }
    
    @Override
    public void invoke(Request request, Response response) throws Exception {
        new ValveChainImpl().invokeNext(request, response);
    }
    
    // 责任链核心:递归调用
    private class ValveChainImpl implements Valve.ValveChain {
        private int index = 0;
        
        @Override
        public void invokeNext(Request request, Response response) throws Exception {
            if (index < valves.size()) {
                // 执行当前 Valve,并传入链对象
                valves.get(index++).invoke(request, response, this);
            } else if (basic != null) {
                basic.invoke(request, response, this);
            } else {
                // 没有更多 Valve,交给容器处理
                dispatchToContainer(request, response);
            }
        }
    }
    
    // 容器处理逻辑:Engine → Host → Context → Wrapper
    private void dispatchToContainer(Request request, Response response) throws Exception {
        if (container instanceof Engine) {
            // Engine → 找到默认 Host
            String hostName = ((Engine) container).getDefaultHost();
            Container host = container.findChild(hostName);
            host.getPipeline().invoke(request, response);
            
        } else if (container instanceof Host) {
            // Host → 根据 Context Path 找到 Context
            String contextPath = request.getContextPath();
            Container context = container.findChild(contextPath);
            if (context != null) {
                context.getPipeline().invoke(request, response);
            } else {
                response.setStatus(404, "Context not found");
            }
            
        } else if (container instanceof Context) {
            // Context → 根据 URI 找到 Servlet
            String servletName = ((Context) container).findServletMapping(request.getUri());
            Container wrapper = container.findChild(servletName);
            if (wrapper != null) {
                wrapper.getPipeline().invoke(request, response);
            } else {
                response.setStatus(404, "Servlet not found");
            }
            
        } else if (container instanceof Wrapper) {
            // Wrapper → 调用 Servlet
            ((Wrapper) container).invokeServlet(request, response);
        }
    }
}

// 自定义 Valve 示例:日志 Valve
public class AccessLogValve implements Valve {
    @Override
    public void invoke(Request request, Response response, ValveChain chain) throws Exception {
        long start = System.currentTimeMillis();
        chain.invokeNext(request, response);
        long elapsed = System.currentTimeMillis() - start;
        System.out.printf("%s %s %dms%n", request.getMethod(), request.getUri(), elapsed);
    }
}

八、Servlet映射:如何从URL找到对应的Servlet?

java

// 源码:StandardContext.java
public class StandardContext extends ContainerBase implements Context {
    private Map<String, String> servletMappings = new HashMap<>();
    private String defaultServletName;
    
    @Override
    public void addServletMapping(String pattern, String servletName) {
        servletMappings.put(pattern, servletName);
        if ("/*".equals(pattern)) {
            this.defaultServletName = servletName;
        }
    }
    
    @Override
    public String findServletMapping(String uri) {
        // 1. 精确匹配:/user/info
        String servletName = servletMappings.get(uri);
        if (servletName != null) return servletName;
        
        // 2. 路径前缀匹配:/user/* → 匹配 /user/info、/user/list
        for (Map.Entry<String, String> entry : servletMappings.entrySet()) {
            String pattern = entry.getKey();
            if (pattern.endsWith("/*")) {
                String prefix = pattern.substring(0, pattern.length() - 2);
                if (uri.startsWith(prefix)) {
                    return entry.getValue();
                }
            }
        }
        
        // 3. 后缀匹配:*.do(扩展实现)
        for (Map.Entry<String, String> entry : servletMappings.entrySet()) {
            String pattern = entry.getKey();
            if (pattern.startsWith("*.")) {
                String suffix = pattern.substring(1);
                if (uri.endsWith(suffix)) {
                    return entry.getValue();
                }
            }
        }
        
        // 4. 默认 Servlet(/*)
        return defaultServletName;
    }
}

九、类加载隔离:如何让不同应用用不同版本的JAR?

java

// 源码:WebappClassLoader.java
public class WebappClassLoader extends URLClassLoader {
    
    public WebappClassLoader(ClassLoader parent, URL[] urls) {
        super(urls, parent);
    }
    
    @Override
    protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
        // 1. 已加载过的类直接返回
        Class<?> clazz = findLoadedClass(name);
        if (clazz != null) return clazz;
        
        // 2. 系统类(Java 核心库)委托给父加载器
        if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("sun.")) {
            return super.loadClass(name, resolve);
        }
        
        // 3. 优先从当前 Web 应用加载(打破双亲委派!)
        try {
            clazz = findClass(name);
            if (clazz != null) {
                if (resolve) resolveClass(clazz);
                return clazz;
            }
        } catch (ClassNotFoundException e) {
            // 当前应用没有,继续委托父加载器
        }
        
        // 4. 委托给父加载器
        return super.loadClass(name, resolve);
    }
}

为什么打破双亲委派

  • 双亲委派:子 → 父 → 祖父(向上委托)

  • Tomcat 需要:Web 应用优先加载自己的类(WEB-INF/classes、WEB-INF/lib)

  • 这样应用 A 可以用 Guava 23.0,应用 B 可以用 Guava 31.0,互不冲突


十、DispatcherServlet:Spring MVC风格的入口

java

// 源码:DispatcherServlet.java
public class DispatcherServlet extends HttpServlet {
    private List<HandlerMapping> handlerMappings = new ArrayList<>();
    private ObjectMapper objectMapper = new ObjectMapper();
    
    @Override
    public void init() throws Exception {
        // 从 IoC 容器获取所有 Controller
        AbstractApplicationContext ctx = SpringContextHolder.getApplicationContext();
        ConfigurableListableBeanFactory beanFactory = ctx.getBeanFactory();
        Map<String, Object> controllers = beanFactory.getBeansOfType(Object.class);
        
        for (Object controller : controllers.values()) {
            Class<?> clazz = controller.getClass();
            
            // 获取类上的 @RequestMapping 路径
            String basePath = "";
            if (clazz.isAnnotationPresent(RequestMapping.class)) {
                basePath = clazz.getAnnotation(RequestMapping.class).value();
                if (!basePath.startsWith("/")) basePath = "/" + basePath;
            }
            
            // 遍历方法,注册 @RequestMapping
            for (Method method : clazz.getDeclaredMethods()) {
                if (method.isAnnotationPresent(RequestMapping.class)) {
                    String methodPath = method.getAnnotation(RequestMapping.class).value();
                    String fullPath = (basePath + methodPath).replaceAll("//", "/");
                    boolean isResponseBody = method.isAnnotationPresent(ResponseBody.class);
                    
                    handlerMappings.add(new HandlerMapping(fullPath, controller, method, isResponseBody));
                    log.debug("Mapped {} -> {}", fullPath, method);
                }
            }
        }
    }
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse res) throws Exception {
        processRequest(req, res);
    }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse res) throws Exception {
        processRequest(req, res);
    }
    
    private void processRequest(HttpServletRequest req, HttpServletResponse res) throws Exception {
        String uri = req.getUri();
        
        for (HandlerMapping hm : handlerMappings) {
            if (hm.path.equals(uri)) {
                // 1. 解析方法参数(@RequestParam、@PathVariable、@RequestBody)
                Object[] args = ParamBinder.resolveParameters(hm.method, req, res, new HashMap<>());
                
                // 2. 反射调用 Controller 方法
                Object result = hm.method.invoke(hm.controller, args);
                
                // 3. 处理返回值
                if (hm.isResponseBody) {
                    res.setContentType("application/json; charset=utf-8");
                    String json = result instanceof String ? (String) result : toJson(result);
                    res.write(json);
                } else {
                    if (result != null) res.write(result.toString());
                }
                res.send();
                return;
            }
        }
        
        // 404
        res.setStatus(404, "Not Found");
        res.write("<h1>404 - Not Found</h1>");
        res.send();
    }
    
    private String toJson(Object obj) {
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (Exception e) {
            return JsonUtil.toJson(obj); // 降级方案
        }
    }
}

十一、完整请求链路串联

text

┌─────────────────────────────────────────────────────────────────────┐
│                        1. 浏览器发送请求                             │
│                    GET /omni/demo/user/name HTTP/1.1                 │
└─────────────────────────────┬───────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│              2. Connector(NIO)接收 Socket 请求                     │
│      - Selector 监听 OP_ACCEPT → 接受新连接                          │
│      - Selector 监听 OP_READ → 线程池处理读取                        │
└─────────────────────────────┬───────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│                 3. Request.parse() 解析 HTTP 协议                    │
│      - 解析请求行 → method=GET, uri=/omni/demo/user/name             │
│      - 解析 Headers → Content-Type, Host...                         │
│      - 解析 Body(POST时)                                           │
└─────────────────────────────┬───────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│                   4. Engine.Pipeline 处理                            │
│      - AccessLogValve:记录开始时间                                  │
│      - 根据 Host 头找到对应 Host                                     │
└─────────────────────────────┬───────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│                    5. Host.Pipeline 处理                             │
│      - 根据 Context Path(/omni)找到 Context                        │
└─────────────────────────────┬───────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│                  6. Context.Pipeline 处理                            │
│      - WebappClassLoader 加载 Web 应用类                             │
│      - 根据 URI 匹配 Servlet 映射 → /* → DispatcherServlet            │
└─────────────────────────────┬───────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│                  7. Wrapper.Pipeline 处理                            │
│      - 调用 servlet.service(request, response)                       │
└─────────────────────────────┬───────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│              8. DispatcherServlet 分发到 Controller                  │
│      - 匹配 @RequestMapping("/user/name")                            │
│      - 反射调用 userController.getName()                             │
│      - @ResponseBody → JSON 序列化                                   │
└─────────────────────────────┬───────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────────┐
│                  9. Response 构建 HTTP 响应                          │
│      - 设置状态码 200                                                │
│      - 设置 Content-Type: application/json                           │
│      - 写入响应体 "Jerry"                                            │
│      - Socket 返回                                                   │
└─────────────────────────────────────────────────────────────────────┘

十二、运行验证

12.1 下载与启动

bash

# 克隆项目
git clone https://gitee.com/zhouzuoli/code-stats.git

# 进入目录
cd code-stats

# Maven 编译
mvn clean package

# 启动服务
java -jar target/CodeStats.jar

12.2 验证 NIO 连接器

bash

# 测试 GET 请求
curl http://localhost:28080/omni/demo/user/name
# 返回: Jerry ✅

# 测试带参数的 GET
curl "http://localhost:28080/omni/demo/hello/text?name=Tom"
# 返回: Hello, Tom! ✅

# 测试 POST 请求
curl -X POST http://localhost:28080/omni/demo/user/manage/create \
  -H "Content-Type: application/json" \
  -d '{"name":"张三","age":25}'
# 返回: {"success":true} ✅

12.3 观察启动日志

text

[INFO] Connector listening on 28080
[INFO] DispatcherServlet 初始化完成,共映射 15 个处理器
[INFO] Tomcat started in 234 ms

十三、核心代码文件清单

文件路径 作用 核心代码行数
tomcat/Catalina.java 组装所有组件 ~80
tomcat/connector/Connector.java NIO 连接器 ~120
tomcat/connector/Request.java HTTP 协议解析 ~100
tomcat/container/SimplePipeline.java 责任链实现 ~80
tomcat/container/StandardContext.java Servlet 映射 ~100
tomcat/loader/WebappClassLoader.java 类加载隔离 ~50
spring/mvc/DispatcherServlet.java Spring MVC 入口 ~150

 开源项目地址

Giteehttps://gitee.com/zhouzuoli/code-stats.git

如果本文让你真正搞懂了 Tomcat 和 NIO 解决 C10K 的原理,请 Star 支持一下!

欢迎在评论区讨论:你遇到过的最大并发量是多少?用什么方案解决的?

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐