предисловие
Многие вещи очень понятны на диаграмме последовательности, и нет необходимости представлять пошагово.Эта статья посвящена диаграмме, а затем кратко объясняет часть содержания.
- Инструменты, используемые для рисования графики: PlantUML + Visual Studio Code + PlantUML Extension.
Представление Tomcat в этой статье основано на Tomcat-9.0.0.M22.
Tomcat-9.0.0.M22 — это последняя версия Tomcat, но она еще не выпущена.Он реализует Servlet4.0 и JSP2.3 и предоставляет множество новых функций.Для поддержки требуется JDK 1.8 и выше и т. д. Подробнее , обратитесь к Tomcat-9.0-doc
- Overview
- Connector Init and Start
- Requtst Process
- Acceptor
- Poller
- Worker
- Container
- At last
Overview
- После запуска коннектора запускается набор потоков для разных этапов обработки запроса.
- Группа потока-акцептора. Используется для принятия новых подключений, инкапсуляции новых подключений и выбора опросчика для добавления новых подключений в очередь событий опросчика.
- Группа потоков опроса. Он используется для мониторинга событий Socket.Когда Socket доступен для чтения или записи и т. д., Socket инкапсулируется и добавляется в очередь задач пула рабочих потоков.
- Группа рабочих потоков. Он используется для обработки запроса, включая анализ сообщения запроса и создание объекта Request, а также вызов конвейера контейнера для обработки.
- ThreadPoolExecutor, в котором расположены Acceptor, Poller и worker, поддерживаются в NioEndpoint.
Connector Init and Start
- initServerSocket() откройте ServerSocket через ServerSocketChannel.open(), который по умолчанию привязан к порту 8080. Длина очереди ожидания соединения по умолчанию составляет 100. Если больше 100, в услуге будет отказано. Мы можем настроить его, настроив свойство acceptCount коннектора в conf/server.xml.
- createExecutor() используется для создания пула рабочих потоков. По умолчанию запущено 10 рабочих потоков, при обработке запросов Tomcat максимальное количество рабочих потоков равно 200. Мы можем настроить эти два свойства, настроив minSpareThreads и maxThreads для Connector в conf/server.xml.
- Поллор используется для обнаружения готовых сокетов. По умолчанию не более 2, Math.min(2,Runtime.getRuntime(). AvailableProcessors());. Мы можем настроить его, настроив pollerThreadCount.
- Acceptor используется для принятия новых подключений. По умолчанию 1. Мы можем настроить его, настроив acceptorThreadCount.
Requtst Process
Acceptor
- Acceptor будет блокироваться в методе ServerSocketChannel.accept(); после его запуска.Когда приходит новое соединение, метод возвращает SocketChannel.
- После настройки Socket инкапсулируйте Socket в NioChannel и зарегистрируйте его в Poller.Упомянутое значение заключается в том, что мы запускаем несколько потоков Poller в начале.При регистрации соединение справедливо распределяется между каждым Poller. NioEndpoint поддерживает массив Pollers, когда соединение назначается pollers[index], следующее соединение назначается pollers[(index+1)%pollers.length].
- Метод addEvent() добавит сокет в очередь PollerEvent этого опросчика. На этом задача Акцептора завершена.
Poller
- селектор.выбрать(1000). Когда Poller запускается, поскольку в селекторе нет зарегистрированного канала, он может блокироваться только при выполнении метода. Все опрашивающие устройства имеют общий селектор, класс реализации которого — sun.nio.ch.EPollSelectorImpl.
- Метод events() зарегистрирует Socket, добавленный в очередь событий методом addEvent(), в EPollSelectorImpl, и Poller обработает его, когда Socket станет доступным для чтения.
- Метод createSocketProcessor() инкапсулирует Socket в SocketProcessor, который реализует интерфейс Runnable. Рабочий поток обрабатывает Socket, вызывая его метод run().
- Метод execute(SocketProcessor) отправляет SocketProcessor в пул потоков и помещает его в workQueue пула потоков. workQueue — это экземпляр BlockingQueue. На этом задача Поллера завершена.
Worker
- После того, как рабочий поток создан, он выполняет метод runWorker() класса ThreadPoolExecutor, пытаясь получить отложенные задачи из workQueue, но сначала workQueue пуст, поэтому рабочий поток заблокируется в методе workQueue.take().
- Когда в workQueue добавляется новая задача, метод workQueue.take() возвращает Runnable, обычно SocketProcessor, а затем рабочий поток вызывает метод run() SocketProcessor для обработки Socket.
- createProcessor() создаст Http11Processor, который используется для анализа сокета и инкапсуляции содержимого сокета в запрос. Обратите внимание, что этот запрос является временным классом, полное имя которого — org.apache.coyote.Request.
- Метод postParseRequest() инкапсулирует запрос и обрабатывает отношение сопоставления (сопоставление URL-адреса с соответствующим хостом, контекстом, оболочкой).
- CoyoteAdapter инкапсулирует org.apache.coyote.Request в org.apache.catalina.connector.Request перед отправкой Rquest в Container для обработки, а запрос, переданный в Container для обработки, называется org.apache.catalina.connector.Request.
- Connector.getService().getMapper().map() используется для запроса отношения сопоставления URL-адресов в Mapper. Отношение сопоставления будет сохранено в org.apache.catalina.connector.Request. На этапе обработки контейнера request.getHost() использует узел сопоставления, запрошенный на этом этапе, и так далее request.getContext(), request.getWrapper( ) Оба есть.
- Connector.getService().getContainer().getPipeline().getFirst().invoke() передаст запрос в Контейнер для обработки.Конечно, обработка Контейнера также выполняется в потоке Worker, но это относительно независимый модуль.Так что это отдельный раздел.
Container
- Следует отметить, что в основном StandardPipeline каждого контейнера будет иметь несколько зарегистрированных клапанов, и мы сосредоточимся только на базовом клапане каждого контейнера. Другие клапаны выполняются перед основным клапаном.
- request.getHost().getPipeline().getFirst().invoke() сначала получает соответствующий StandardHost и выполняет его конвейер.
- request.getContext().getPipeline().getFirst().invoke() сначала получает соответствующий StandardContext и выполняет его конвейер.
- request.getWrapper().getPipeline().getFirst().invoke() сначала получает соответствующий StandardWrapper и выполняет его конвейер.
- Наиболее достойным упоминания является базовый клапан StandardWrapper, StandardWrapperValve.
- allocate() используется для загрузки и инициализации сервлетов. Упомянутое значение заключается в том, что сервлеты не все являются синглтонами. Когда сервлет реализует интерфейс SingleThreadModel, StandardWrapper будет поддерживать набор экземпляров сервлета, что является режимом легковеса. Конечно, SingleThreadModel устарела, начиная с Servlet 2.4.
- Метод createFilterChain() получит все фильтры из StandardContext, а затем выберет все фильтры, соответствующие URL-адресу запроса, и добавит их в filterChain.
- doFilter() выполняет цепочку фильтров и вызывает метод service() сервлета после выполнения всех фильтров.
Reference
- «Как работает Томкэт»
- «Анализ архитектуры Tomcat» — Лю Гуанжуй
- Tomcat-9.0-doc
- apache-tomcat-9.0.0.M22-src
- анализ архитектуры tomcat (реализация коннектора NIO)
Наконец
Прошу всех обратить внимание на мой паблик [Программист в погоне за ветром], в нем будут обновляться статьи, а также размещаться отсортированная информация.