深入浅出Jetty:功能、特性及核心实现

深入浅出系列

深入浅出Jetty:功能、特性及核心实现

在Java Web服务器领域,Jetty始终以“轻量、灵活、高性能”的标签占据一席之地,无论是嵌入式部署场景,还是高并发生产环境,都能看到它的身影。不同于Tomcat的“重量级全能”,Jetty以模块化设计为核心,凭借优秀的架构设计和高效的算法支撑,成为微服务、嵌入式应用的首选服务器之一。本文将从Jetty的核心功能、显著特点入手,层层拆解其底层架构和核心算法,揭秘这些特性背后的技术支撑。

一、Jetty 核心功能:不止是Web服务器

Jetty本质上是一个开源的Java Web服务器和Servlet容器,由Eclipse Foundation维护,核心定位是“轻量且可扩展”,其功能覆盖了Web服务的全流程,同时兼顾灵活性和兼容性,具体可分为以下5类核心功能:

1. 基础Web服务功能

作为Web服务器,Jetty支持HTTP/1.1、HTTP/2、HTTPS等主流网络协议,能够监听端口、接收客户端请求、处理请求并返回响应,完美兼容Java EE规范,完整支持Servlet 3.1/4.0/5.0、JSP、WebSocket,可通过集成Jasper等引擎支持JavaServer Pages(JSP),能直接部署和运行Java Web应用程序、部署WAR包,满足常规Web应用的部署需求。

同时,它提供了完整的SSL/TLS配置支持,可通过代码或配置文件快速启用HTTPS,保障数据传输安全,还支持JAAS(Java认证和授权服务)和JNDI(Java命名和目录接口),进一步完善企业级应用的安全与命名服务需求。

此外,Jetty原生支持HTTP/2和WebSocket协议,能满足现代Web应用对低延迟和实时通信的需求,既可以高效提供静态文件服务,也能通过Servlet处理动态请求,实现静态与动态内容的高效处理,其中WebSocket服务器支持全双工通信,特别适用于实时应用场景;同时完美支持Server-Sent Events (SSE),满足长连接通信需求。

2. 嵌入式部署功能

这是Jetty最具特色的功能之一,其轻量级和模块化的设计使其可以被直接嵌入到Java应用程序中,无需单独部署独立的Web服务器,为应用提供HTTP服务。仅需几行Java代码,就能快速启动一个完整的Web服务器,让应用程序自带Web服务能力,极大简化了部署流程,尤其适合桌面应用、微服务、自动化测试等场景。例如,Spring Boot早期版本默认使用Jetty作为嵌入式服务器,正是看中了它的轻量和便捷性。

3. 灵活的配置与扩展功能

Jetty支持多种配置方式,包括XML配置、Java代码配置、Maven/Gradle依赖配置等,默认配置即可满足大多数场景需求,同时允许开发者根据业务需求自定义配置,如线程池大小、连接器参数、日志级别等。此外,它的模块化设计让扩展变得简单,开发者可以按需加载功能模块,无需加载无关组件,比如不需要JSP支持时,可直接关闭JSP模块,进一步精简体积。同时,Jetty支持热部署与热重载功能,能够实现应用的热更新,极大便利了开发和调试工作,提升开发效率。

4. 监控与运维功能

Jetty内置JMX支持,可实时监控服务器的运行状态,包括线程池状态、连接数、请求处理耗时等关键指标,方便开发者进行性能排查和运维管理。同时,它支持自定义日志配置,可集成Logback、Log4j等主流日志框架,通过日志精准定位请求处理过程中的问题,此外还支持通过Admin Context进行可视化运维。

二、核心优势:轻量、高效、灵活

Jetty的功能之所以能灵活适配多种场景,核心在于其独特的设计特点,这些特点也决定了它与其他Web服务器(如Tomcat、Undertow)的差异,具体可总结为以下5点:

1. 轻量级,启动速度快

Jetty的核心JAR包仅约1MB大小,远小于Tomcat的核心体积,内存占用极低,核心库体积小、资源消耗低,非常适合微服务和云原生环境。同时,它的启动流程简洁,无需加载过多无关组件,启动时间可控制在几秒内,这对于开发测试、微服务部署等对启动速度有要求的场景至关重要——开发者在调试时可快速重启服务器,微服务集群可实现快速扩容和部署。此外,Jetty支持Docker、Kubernetes等容器化部署,采用无状态设计,具备极强的云原生友好性,适配云原生架构的部署需求。

2. 模块化设计,可按需扩展

Jetty的所有功能都以模块形式存在,基于OSGi的模块化设计,模块之间相互独立,支持按需加载,开发者可以根据需要选择和组合功能模块,避免资源浪费。例如,HTTP模块、WebSocket模块、JSP模块、SSL模块等均可独立启用或关闭,这种设计不仅让Jetty保持了轻量,还能灵活适配不同业务场景:嵌入式应用可只加载核心Web模块,而复杂Web应用可按需添加Servlet、Session等模块。Jetty的模块还支持依赖管理,比如HTTP模块依赖于服务器模块,服务器模块又依赖于线程池和日志模块,确保模块间的协同工作。

3. 高性能,支持高并发

Jetty基于非阻塞I/O模型和事件驱动机制,原生支持NIO/HTTP2/WebSocket异步处理,能够用更少的线程处理更多的客户端连接,相比传统BIO模型(一个连接对应一个线程),其并发处理能力大幅提升,可轻松应对数千甚至数万个并发连接,这也是其高并发与高性能的核心体现,能实现高性能与低延迟,适合高并发场景。同时,它支持Servlet 3.1+ 的异步处理模型,可有效提高请求吞吐量,再通过内存缓冲区复用、线程池优化等机制,进一步降低资源消耗,提升请求处理效率,适配高并发Web应用场景。

4. 嵌入式友好,集成性强

Jetty的设计初衷就是支持嵌入式部署,其API简洁易用,开发者可通过少量代码快速集成到Java应用中,无需修改应用本身的逻辑,易于嵌入与扩展。同时,Jetty通过Handler机制方便地进行功能扩展,满足不同业务的定制化需求。除了Spring Boot,Hadoop、Eclipse IDE等知名项目也集成了Jetty:Hadoop的NameNode和JobTracker通过Jetty呈现管理页面,Eclipse IDE则利用Jetty提供内置Web服务支持。

5. 兼容性强,适配广泛

Jetty全面兼容Java EE规范,支持最新的Servlet版本,同时兼容HTTP/1.1、HTTP/2、WebSocket等主流协议,可无缝部署各类Java Web应用。此外,它支持多种操作系统(Windows、Linux、Mac)和JDK版本,适配不同的部署环境,无论是开发测试环境还是生产环境,都能稳定运行。

三、核心架构

Jetty的所有功能和特点,都依赖于其简洁而强大的核心架构。不同于Tomcat的“Service-Connector-Container”三层架构,Jetty的架构更加轻量化,核心由“Server-Connector-Handler”三大组件构成,再配合线程池、缓冲区池等辅助组件,形成一个高效、可扩展的整体架构,其核心架构体系包含整体分层设计和核心组件架构,可概括为“一个核心、两大组件、三大辅助”。

1. 核心组件:Server(服务器实例)

Server是Jetty的核心调度中心,作为顶层容器和生命周期管理器,负责管理整个服务器的生命周期(启动、停止、重启),协调Connector、Handler、线程池等所有其他组件的启动、运行和停止,统筹管理所有组件的工作。它就像一个“总指挥”,接收Connector传递的请求,将请求分发到Handler链进行处理,同时管理线程资源和组件依赖。

Server的核心职责包括:初始化所有组件、启动Connector和Handler、管理全局线程池、处理组件间的协同逻辑。开发者通过Server实例可配置端口、线程池、SSL等核心参数,也可添加多个Connector和Handler,实现多端口监听和多请求处理逻辑。

2. 核心组件:Connector(连接器)

Connector是Jetty与客户端交互的“门户”,作为网络接口,负责处理网络连接、监听端口、接受客户端连接,并将请求分发给处理线程。它基于Java NIO实现,核心抽象接口包括`Connector`、`EndPoint`、`Connection`,通过SelectorManager管理网络事件,并将连接抽象为Connection对象进行协议解析,将客户端请求封装后传递给Handler链,同时将Handler处理后的响应返回给客户端。Jetty支持多种Connector类型,适配不同的协议和I/O模型,包括NIO、HTTP/2、SSL等连接器,具体实现类如下:

A. ServerConnector:标准NIO连接器,默认的HTTP/1.1连接器,基于Java NIO实现,支持非阻塞I/O,是最常用的连接器;

B. HTTP2ServerConnector:支持HTTP/2协议,适用于高并发、低延迟的场景,对应HTTP2ServerConnection实现;

C. SslConnector:提供HTTPS支持,封装了SSL/TLS加密逻辑,对应SslConnection实现,保障数据传输安全;

D. HttpConnection:专门负责HTTP/1.1协议的解析和处理,是HTTP/1.1请求的核心处理组件;

E. HTTP2ServerConnector:支持HTTP/2协议,适用于高并发、低延迟的场景;

F. SslConnector:提供HTTPS支持,封装了SSL/TLS加密逻辑,保障数据传输安全。

Connector的内部结构进一步拆分,通过Acceptor、SelectorManager、Connection三个子组件协同工作:Acceptor负责阻塞接受客户端连接,将连接设置为非阻塞模式后交给SelectorManager;SelectorManager管理多个Selector,通过多路复用监听I/O事件;Connection则封装应用层协议差异,处理请求和响应的数据读写。

3. 核心组件:Handler(处理器)

Handler是Jetty处理请求的核心逻辑载体,负责对客户端请求进行具体处理(如安全验证、会话管理、Servlet调用等),其链式架构是Jetty架构的核心,采用职责链模式(Chain of Responsibility)设计,核心接口为`Handler.handle(Request, Response)`,通过一系列Handler处理请求(如ServletHandler、ResourceHandler),这种架构的优势在于灵活可配置,易于定制处理逻辑,实现组件可插拔,具备极高的扩展性。与Tomcat的Container不同,Jetty的Handler采用“链式结构”(Handler Chain),本质是责任链模式的实现,多个Handler可以嵌套组合,请求会依次经过链中的每个Handler,每个Handler专注于单一职责,实现解耦。

Handler的关键实现类丰富,可根据业务需求灵活组合,常见类型包括:

A. ServletHandler:管理Servlet映射和调用,是Servlet容器功能的核心实现;
B. HandlerCollection:顺序执行多个Handler,实现多逻辑组合处理;
C. HandlerList:顺序执行多个Handler,直到某个Handler返回true即停止执行;
D. ContextHandlerCollection:基于请求路径的上下文路由,实现多Web应用的路径隔离;
E. WebAppContext:负责完整Web应用的生命周期管理,适配标准Web应用部署;
F. SessionHandler:处理用户会话,管理会话的创建、销毁和存储;
G. SecurityHandler:负责安全验证,如用户认证、权限控制等;
H. ContextHandler:处理请求的上下文路径,管理Web应用的上下文配置;
I. ResourceHandler:处理静态资源(如HTML、CSS、JS文件)的请求。

开发者可以自定义Handler,添加到Handler链中,实现自定义的请求处理逻辑,这种设计让Jetty的扩展变得异常灵活。

4. 辅助组件:线程池、缓冲区池、选择器(完善线程模型架构)

除了三大核心组件,Jetty的架构还包含三个关键辅助组件,它们是保障高性能和轻量性的重要支撑,其中ThreadPool是核心辅助组件之一,Jetty的线程模型架构分工明确,具体分为三类线程,配合线程池实现高效调度:

A. 线程池(QueuedThreadPool):即Worker线程池,与Server和Connector集成,负责提供工作线程来执行具体的业务逻辑,将I/O事件处理与业务处理分离,避免阻塞,其核心作用是管理处理请求的线程,优化线程创建和销毁带来的性能开销。Jetty采用全局共享的线程池,所有Connector和Handler共享线程资源,相比Tomcat每个Connector独立线程池的设计,更能提高线程利用率,减少资源浪费。线程池通过任务队列管理请求任务,支持工作窃取算法和优先级队列,实现线程池动态伸缩,优化线程调度效率;

B. Acceptor线程:专门负责监听端口、接受客户端连接,默认配置1-2个线程,避免过多线程阻塞在连接接受环节;

C. Selector线程:负责管理NIO Channel的I/O事件,默认配置数量与CPU核数一致,通过多路复用机制高效监听多个连接的I/O状态,确保I/O事件的快速响应;

D. 缓冲区池(ByteBufferPool):负责复用ByteBuffer,减少内存分配和垃圾回收(GC)压力,通过桶式内存分配算法,根据缓冲区大小进行分类管理,实现高效复用;

E. 选择器(Selector):基于Java NIO的Selector机制,实现I/O多路复用,让单个线程可以监听多个客户端连接的I/O事件,大幅提升并发处理能力,这也是NIO Selector机制的核心作用——单线程处理大量连接,减少线程上下文切换开销;

F. 缓冲区池(ByteBufferPool):负责复用ByteBuffer,减少内存分配和垃圾回收(GC)压力,通过桶式内存分配算法,根据缓冲区大小进行分类管理,实现高效复用;

G. 选择器(Selector):基于Java NIO的Selector机制,实现I/O多路复用,让单个线程可以监听多个客户端连接的I/O事件,大幅提升并发处理能力,这也是NIO Selector机制的核心作用——单线程处理大量连接,减少线程上下文切换开销;

5. 架构交互流程

Jetty的核心组件交互流程简洁清晰,可概括为以下步骤:

A. 客户端发送请求,Connector的Acceptor接受连接,将连接设置为非阻塞模式后交给SelectorManager;

B. SelectorManager通过Selector监听连接的I/O事件,当有数据可读时,由Connection组件读取请求数据并封装为HttpChannel;

C. HttpChannel将请求传递给Server,Server将请求分发到Handler链;

D. 请求依次经过Handler链中的各个Handler(如SecurityHandler、SessionHandler、ServletHandler),最终由ServletHandler调用具体的Servlet处理请求;

E. 处理完成后,响应数据通过HttpChannel写回Connector,由Connector返回给客户端。

四、核心算法:支撑高性能与灵活性的底层动力

如果说架构是Jetty的“骨架”,那么核心算法就是Jetty的“肌肉”,它支撑着Jetty的高性能、高并发和轻量性。Jetty的核心算法主要集中在I/O处理、线程调度、内存管理和请求解析四个方面,每一种算法都针对性地解决了Web服务器的核心痛点。

1. I/O多路复用算法(Reactor模式)

Jetty基于Java NIO的Selector机制,实现了Reactor模式,这是其高并发能力的核心支撑。Reactor模式在Connector中实现,通过一个或多个线程(Reactor)利用Selector多路复用器来监听和分发大量连接的网络事件(如可读、可写),是实现高并发的基础。Reactor模式通过一个“反应器”(SelectorManager)监听多个I/O事件,当事件触发时(如客户端连接、数据可读),反应器将事件分发给对应的处理器(Connection)处理,实现“单线程监听、多线程处理”的高效模式。

具体来说,SelectorManager管理多个ManagedSelector(实际的Selector实例),每个ManagedSelector负责监听一部分客户端连接的I/O事件。当Acceptor接受一个新连接后,会选择一个ManagedSelector,将连接注册到该Selector上,并绑定对应的EndPoint和Connection。Selector通过select()方法阻塞监听事件,当有事件发生时,遍历触发的SelectionKey,交由Connection处理数据读写。这种算法让单个线程可以管理数千个客户端连接,大幅降低线程资源消耗,提升并发处理能力。

同时,非阻塞I/O(NIO)贯穿于网络通信的始终,无论是Connector接收请求还是Handler处理响应,都使用非阻塞的方式,确保线程不会被I/O操作长时间占用,这正是NIO Selector机制的核心应用,通过单线程处理大量连接,减少线程上下文切换开销。

此外,Jetty还采用Eat What You Kill线程消费模式,让接受连接的线程直接处理请求,进一步减少线程上下文切换;同时通过Produce Consume生产者-消费者模式,分离I/O读取和业务处理,提升处理效率;在SSL处理上,采用异步TLS握手,避免阻塞Selector线程,优化SSL连接性能。

2. 线程调度算法(工作窃取算法)

Jetty的QueuedThreadPool采用工作窃取(Work-Stealing)算法,优化线程调度效率,避免线程空闲和任务堆积。线程池内部维护一个任务队列(BlockingQueue),当工作线程完成自身任务后,会主动从其他线程的任务队列中“窃取”任务执行,而不是一直空闲等待。

这种算法的优势在于,能够平衡各个线程的任务负载,避免某些线程任务堆积而其他线程空闲的情况,尤其适合高并发场景下的任务调度。同时,QueuedThreadPool支持配置最小线程数、最大线程数和空闲超时时间,可根据请求量动态调整线程数量,进一步优化资源利用率——请求量小时,减少线程数量降低内存消耗;请求量高时,增加线程数量提升处理能力。

3. 内存管理算法(桶式内存分配+缓冲区复用)

为了减少内存分配和GC压力,Jetty采用ByteBufferPool管理内存缓冲区,核心算法是桶式内存分配和缓冲区复用,同时结合零拷贝优化技术——通过使用ByteBufferPool池化技术复用直接内存(Direct Buffer),数据可以直接在用户空间和内核空间之间传输,减少了内存拷贝和垃圾回收(GC)压力,这也是ByteBufferPool池化技术的核心价值。ByteBufferPool是核心接口,主要实现类包括ArrayByteBufferPool(基于数组的池化实现,轻量高效),同时支持MappedByteBuffer(大文件内存映射,实现零拷贝传输);此外,DefaultServlet通过sendfile系统调用传输静态文件,进一步实现零拷贝优化,提升静态资源传输性能。ByteBufferPool将缓冲区按照大小分为不同的“桶”(如1KB、2KB、4KB等),每个桶对应一个缓冲区队列,当需要使用缓冲区时,从对应大小的桶中获取空闲缓冲区;使用完成后,将缓冲区归还给对应的桶,实现复用。

这种算法避免了频繁创建和销毁ByteBuffer带来的内存开销和GC压力,同时通过ConcurrentBucketMap数据结构管理不同大小的桶,确保缓冲区的高效获取和归还。此外,缓冲区复用机制还能减少内存碎片,提升内存使用效率,这也是Jetty内存占用低的重要原因之一。

4. 请求解析算法(确定有限状态机DFA)

Jetty的HttpParser组件负责解析HTTP请求报文,核心采用确定有限状态机(DFA)算法,实现高效的增量解析——HTTP报文解析器(HttpParser)采用增量解析的方式,能够高效地处理不完整或流式的HTTP数据,提升了协议解析的性能。HTTP请求报文的结构具有固定的格式(如请求行、请求头、请求体),DFA算法通过定义不同的状态(如解析请求行、解析请求头、解析请求体),根据输入的字符流切换状态,逐步完成请求解析。

相比传统的字符串匹配算法,DFA算法的解析效率更高,能够快速识别请求报文的各个部分,同时支持增量解析——无需等待整个请求报文接收完成,即可逐步解析已接收的部分,减少请求处理延迟。这种算法确保了Jetty在高并发场景下,能够快速处理大量HTTP请求,提升整体响应速度。

五、总结:Jetty的核心竞争力与适用场景

Jetty之所以能在众多Web服务器中脱颖而出,核心在于其“轻量、灵活、高性能”的平衡——模块化架构让它能够按需扩展,适配不同场景;非阻塞I/O和高效算法让它在高并发场景下表现优异;嵌入式设计让它能够轻松集成到各类Java应用中。Jetty通过其高效的NIO架构、灵活的Handler链和优化的资源管理(线程、缓冲池),在保持轻量级的同时提供了企业级的Web服务能力,特别适合微服务架构和云原生环境。

从底层逻辑来看,Jetty的功能和特点是相互支撑的:轻量级源于模块化设计和内存优化算法;高性能源于Reactor模式、工作窃取算法和DFA解析算法;灵活性源于Handler链式结构和可插拔模块。这些架构和算法的有机结合,让Jetty成为嵌入式应用、微服务、高并发Web应用的理想选择。

如果你的项目需要快速启动、低内存占用,或者需要嵌入式部署、高并发处理能力,那么Jetty无疑是一个值得深入学习和使用的Web服务器。深入理解其核心架构和算法,不仅能帮助我们更好地使用Jetty,还能为我们设计高性能的Web应用提供思路和借鉴。

如果觉得这篇文章对你有帮助,欢迎点赞、收藏,也可以在评论区留言,聊聊你在使用Jetty时遇到的问题~

深入浅出Nginx:功能、特性及核心实现

深入浅出系列

深入浅出Nginx:功能、特性及核心实现

Nginx 是一款高性能的 HTTP 和反向代理服务器,以其高并发、低内存消耗和高稳定性著称,广泛应用于互联网架构的流量入口、负载分发等场景,同时支持多种现代协议与云原生集成,是企业级架构的核心组件。本文介绍了Nginx的功能、特点及其核心架构与算法。

一、核心功能

Nginx 的核心功能围绕“流量处理、分发与优化”展开,覆盖从客户端请求接收到底层服务响应的全链路,兼顾性能、安全性与扩展性:

1. Web服务器

A. 静态资源服务:直接托管 HTML、CSS、JS、图片、视频等静态文件,支持目录索引、文件权限控制、路径别名配置。

B. 索引和自动索引:支持手动配置索引页面,也可开启自动索引功能,方便查看目录下的文件列表。

C. 缓存加速:包含静态文件缓存、FastCGI缓存、代理缓存三大类,可灵活配置缓存策略,减轻后端压力。

D. 大文件传输优化:借助 sendfile 零拷贝机制、TCP_NOPUSH 和 TCP_NODELAY 选项,提升大文件传输效率,减少延迟。

E. 补充特性:支持 Range 分片传输(断点续传)、Gzip/Brotli 压缩、静态资源缓存策略(如 expires 头设置),大幅提升静态资源加载速度,降低带宽消耗。

2. 反向代理 (Reverse Proxy)

A. HTTP/HTTPS反向代理:作为客户端与后端应用服务器(如 Tomcat、Node.js、PHP-FPM)的中间层,接收客户端所有请求,转发至对应后端服务,再将后端响应回传给客户端。

B. 负载均衡:集成多种负载均衡算法,实现流量的合理分发(详情见“负载均衡”模块)。

C. SSL/TLS终端(SSL termination):集中处理 HTTPS 协议的 SSL/TLS 加密与解密操作,后端服务器仅需处理明文 HTTP 请求,无需承担加密解密的 CPU 开销。

D. WebSocket代理:支持 WebSocket 长连接代理,实现客户端与后端服务的双向实时通信(如聊天、实时通知等场景);同时支持 gRPC 代理,适配微服务架构下的远程调用场景。

E. 补充特性:隐藏后端服务器真实 IP 和部署结构,提升系统安全性;支持请求/响应头改写、URL 重写,适配后端服务路径调整;支持多层代理嵌套,灵活适配复杂架构。

3. 负载均衡 (Load Balancing)

A. 协议支持:支持 HTTP、TCP、UDP 三种协议的负载均衡,可适配 Web 服务、数据库、Redis、RPC 等多种后端服务。

B. 健康检查:包含主动健康检查(定期探测后端服务器状态)和被动健康检查(根据请求响应状态判断),自动剔除故障节点、恢复正常节点。

C. 会话保持(Session Persistence):通过 IP 哈希等算法,确保同一客户端的请求固定分配到同一后端服务器,解决 Session 共享问题。

D. 动态配置:借助 upstream zone 共享内存,实现负载均衡后端节点的动态配置,无需重启服务即可更新节点信息。

E. 补充特性:支持会话保持(配合 IP 哈希等算法),保障用户连续访问体验;可配置备份服务器,当所有主节点故障时,自动切换至备份节点。

4. 缓存系统

A. 代理缓存(Proxy Cache):缓存后端服务的响应结果(如接口返回数据、动态页面渲染结果),后续相同请求可直接从 Nginx 缓存返回,无需请求后端。

B. FastCGI缓存:专门针对 FastCGI 协议(如 PHP 服务)的缓存机制,优化动态页面的访问速度。

C. 缓存失效策略:支持基于时间的过期失效、主动清理等策略,同时支持缓存切片(Cache Slicing),提升大文件缓存的效率。

D. 补充特性:支持内存缓存与磁盘缓存结合,可配置缓存过期时间、缓存清理策略;支持按 URL、请求头、Cookie 等维度精准缓存,同时支持缓存命中统计,便于优化缓存策略。

5. SSL/TLS功能

A. SNI(Server Name Indication)支持:可在同一 IP 和端口下部署多个 HTTPS 域名,实现多域名共享证书或独立证书部署。

B. OCSP Stapling(在线证书状态协议装订):减少 HTTPS 握手延迟,避免客户端查询证书状态时的额外网络请求。

C. SSL会话复用(Session Reuse):复用已建立的 SSL 会话,减少握手开销,提升 HTTPS 访问速度。

D. 动态证书加载:NGINX Plus(商业版本)支持无需重启服务,动态加载新的 SSL 证书,提升运维效率。

E. 补充特性:支持 SSL/TLS 协议版本控制、加密套件配置;支持证书自动续期、多证书管理,适配多域名 HTTPS 部署。

6. 其他关键功能

A. 协议支持:支持 HTTP/2、HTTP/3(QUIC)协议,提升网络传输效率,适配现代浏览器与应用场景。

B. 压缩功能:支持 gzip、brotli 两种主流压缩算法,压缩响应内容,降低带宽消耗,提升加载速度。

C. 访问控制:支持 IP 黑白名单、Basic Auth 基础认证,限制非法访问,提升服务安全性。

D. 速率限制(Rate Limiting):通过漏桶、令牌桶等算法,限制单位时间内的请求数,防止突发流量冲垮后端服务。

E. 重写引擎(Rewrite Module):支持 URL 重写、路径跳转,适配业务路由调整、SEO 优化等场景。

F. 日志系统:包含 Access Log(访问日志)和 Error Log(错误日志),可配置日志格式,便于问题排查与流量分析。

二、核心架构

Nginx 的高性能和高稳定性,源于其“简洁、高效、可扩展”的底层架构设计,核心围绕进程管理、事件处理和模块化设计展开,同时适配云原生场景的扩展需求:

1. Master-Worker 多进程架构

A. Master Process(管理进程):负责读取并解析 Nginx 配置文件(nginx.conf),验证配置合法性;管理端口绑定、Worker 进程生命周期(启动、停止、重启、平滑升级);接收外部信号(如 reload、stop),并同步给所有 Worker 进程;不处理任何网络请求,仅负责管理协调。

B. Worker Processes(工作进程):实际处理客户端的网络事件(连接建立、请求接收、响应返回)和业务逻辑(静态资源读取、反向代理、缓存查询等);多个 Worker 进程平等竞争客户端连接,进程间相互独立,无共享资源,避免锁竞争。

C. Cache Manager(缓存管理进程):负责管理缓存文件的元数据,执行缓存过期清理策略,确保缓存资源合理利用。

D. Cache Loader(缓存加载进程):Nginx 启动时,将磁盘上的缓存数据加载到内存索引中,提升缓存查询效率。

其中,Master 进程为单进程,占用资源极少,是 Nginx 服务的“大脑”;Worker 进程数量通常配置为等于或略大于 CPU 核心数,充分利用多核 CPU 资源。

2. 事件驱动架构 (Event-Driven)

A. 单线程事件循环:每个 Worker 进程运行一个单线程事件循环,避免多线程上下文切换开销,提升资源利用率。

B. 非阻塞 I/O:所有网络操作均为非阻塞模式,当 Worker 进程处理 I/O 操作(如读取磁盘文件、转发请求到后端)时,若操作未就绪,不会阻塞进程,而是立即返回,继续处理其他就绪事件。

C. Reactor模式:使用 I/O 多路复用技术集中管理连接事件,基于“事件通知-回调处理”的逻辑,实现一个线程处理多个连接。

D. 底层实现:Linux 系统下使用 epoll 机制,FreeBSD/Mac 系统下使用 kqueue 机制,Solaris 系统下使用 /dev/poll 机制,Windows 系统下使用 IOCP 完成端口机制,均为高效的 I/O 多路复用机制。

3. 进程模型细节

A. CPU亲和性:Worker 进程可绑定到特定 CPU 核心,减少 CPU 缓存失效,提升处理效率。

B. 惊群效应避免:通过 `SO_REUSEPORT` 选项或互斥锁机制,确保只有一个 Worker 进程处理新连接,避免多个进程同时竞争连接导致的资源浪费。

C. 优雅重启:支持零停机配置重载(执行 nginx -s reload)和二进制升级,Master 进程加载新配置或新二进制文件后,逐步替换旧 Worker 进程,确保业务零中断。

三、核心算法与机制

Nginx 的各项功能和特性,均依赖底层高效算法的支撑,核心算法围绕事件处理、负载分发、内存管理和连接处理展开,兼顾效率与公平性:

1. I/O多路复用算法

不同操作系统的实现机制
A. Linux:epoll 机制,支持边缘触发(ET)和水平触发(LT),时间复杂度 O(1),可高效处理大量连接。
B. FreeBSD/macOS:kqueue 机制,高效事件通知机制,适配 BSD 系列系统的特性。
C. Windows:IOCP(完成端口)机制,适合 Windows 系统下的高并发场景。

关键机制
A. epoll事件循环:通过 `epoll_wait()` 系统调用监控文件描述符状态,当事件就绪时,触发回调函数处理,无需轮询所有连接。
B. 连接状态机:每个连接在 `ngx_connection_t` 结构中维护自身状态(如连接建立、数据读取、数据发送、连接关闭),确保连接处理的有序性。

2. 负载均衡算法

常用算法说明及适用场景

A. Round Robin(轮询):默认算法,按时间顺序依次分配请求,支持权重配置;适用于服务器性能均衡、请求处理时间相近的场景。

B. Least Connections(最少连接):实时统计每台后端服务器的当前活跃连接数,将新请求分配给连接数最少的服务器;适用于长连接应用、请求处理时间差异大的场景。

C. IP Hash(IP哈希):基于客户端 IP 地址进行 CRC32 哈希计算,根据哈希结果分配固定后端服务器;适用于需要会话保持、无共享 Session 的场景。

D. Generic Hash(自定义Key哈希):基于自定义 Key(如 URI、请求头)进行哈希分配;适用于缓存服务器、特定业务路由场景。

E. Least Time (Plus)(最低响应时间):结合最低平均响应时间和最少连接数分配请求;仅 NGINX Plus 支持,适用于对延迟敏感的应用。

F. Random (Plus)(随机选择):随机选择后端服务器,可结合 Two Choices 策略优化;仅 NGINX Plus 支持,适用于大规模分布式环境。

一致性哈希

A. 支持 Ketama 一致性哈希算法(通过 `hash … consistent` 配置),当后端服务器集群扩容或缩容时,可最小化缓存失效范围,减少业务影响。

3. 内存管理算法

A. 内存池(Pool):Nginx 启动时,预先分配一大块内存(内存池),请求处理过程中,从内存池中申请所需内存,请求处理完成后,统一释放整个内存池(或部分内存块),避免频繁调用 malloc/free 系统调用,减少内存碎片和系统开销。

B. Slab分配器:用于共享内存(如 upstream zone)的管理,高效管理固定大小的内存对象,提升内存利用率。

C. 数据结构:使用链表与红黑树,分别用于定时器管理、缓存索引等场景,确保高效的增删改查操作。

D. 补充说明:内存池分为全局内存池和请求级内存池,请求级内存池随请求结束而释放,资源管理更高效;共享内存由 Master 进程创建,所有 Worker 进程可读写,通过信号量实现进程间同步。

4. 哈希算法
A. CRC32:主要用于 IP Hash 和 Generic Hash 的计算,确保哈希结果的均匀性。
B. MurmurHash:用于 Nginx 内部部分哈希表的计算,具有高效、低碰撞的特点。

5. 连接处理算法
A. 监听套接字共享:所有 Worker 进程共享监听端口,通过内核负载均衡(SO_REUSEPORT)或互斥锁分配新连接,确保连接分配的均匀性。
B. accept队列管理:处理 SYN 队列和 Accept 队列的连接,避免队列溢出,确保新连接能够及时被处理。
C. HTTP流水线解析:采用增量式 HTTP 请求解析方式,边接收数据边解析,降低请求处理延迟。

四、关键设计特点

Nginx 的设计始终围绕“高性能、高可用、高灵活”三大目标,核心设计特点贴合企业级生产场景需求:

1. 高性能设计

A. 零拷贝:通过 `sendfile()` 系统调用,直接在内核态完成“磁盘 → 内核缓冲区 → 网卡”的数据传输,跳过用户态拷贝,减少 CPU 拷贝次数,提升传输效率。

B. 单线程Worker:每个 Worker 进程为单线程,消除多线程上下文切换开销,单个 Worker 可处理数万并发连接。

C. 内存效率:每个连接仅占用 100KB-1MB 内存,高并发场景下内存占用依然可控,远低于传统 Web 服务器。

2. 模块化架构

A. 核心模块:包含事件模块、HTTP 模块、Mail 模块、Stream 模块,负责 Nginx 的基础功能支撑。

B. 动态模块:支持将功能模块编译为动态 so 文件,运行时加载或卸载,无需重启服务,提升运维灵活性。

C. 第三方模块生态:拥有丰富的第三方模块(如 Lua 模块 OpenResty、Headers More 模块、WAF 模块 ngx_waf),可灵活扩展网关、限流、监控等功能,适配不同业务场景。

3. 配置系统

A. 声明式配置:采用层次化配置结构(main、events、http、server、location),结构清晰,易于理解和配置。

B. 变量系统:内置丰富的变量(如 `$uri`、`$args`、`$remote_addr` 等),同时支持自定义变量,可灵活适配业务配置需求。

C. 配置热加载:通过 `nginx -s reload` 命令,实现零停机更新配置,避免服务中断,提升运维效率。

4. 高可用机制

A. 健康检查:主动检测后端服务器状态(如 TCP 端口连通性、HTTP 响应状态),被动监控请求响应结果,及时发现故障节点。

B. 被动故障转移:根据 `max_fails`(最大失败次数)和 `fail_timeout`(失败超时时间)配置,自动剔除故障节点,故障节点恢复后自动重新加入集群。

C. 备份服务器:通过 `backup` 标记配置后备服务器,当所有主节点故障时,自动切换至备份服务器,保障服务连续性。

五、性能数据

Nginx 的高性能已在大量生产场景中得到验证,核心性能指标如下:

A. 单Worker吞吐量:可达 100,000 RPS(请求/秒),处理静态资源时性能更优。

B. 并发连接数:单实例可处理数百万并发连接(理论值),实际生产环境中可稳定支撑 10 万+ 并发连接。

C. 内存占用:每连接仅占用 100KB-1MB 内存,空闲状态下仅占用几 MB 内存。

D. 进程模型:通常配置 1 个 Worker 进程 per CPU 核心,充分利用多核资源。

六、架构对比

Nginx 与传统 Web 服务器(如 Apache Prefork 模式)在架构设计上存在显著差异,具体对比如下:

对比特性 Nginx 传统服务器(如Apache Prefork模式)
并发模型 事件驱动、非阻塞 I/O 模型 进程/线程每连接模型
内存占用 低(共享内存、小栈空间) 高(每个进程独立内存空间)
上下文切换 极少(单线程 Worker) 频繁(多线程调度)
可扩展性 水平/垂直扩展均优秀,适配大规模集群 垂直扩展受限,难以应对高并发场景
适用场景 高并发、静态服务、反向代理、负载均衡场景 动态内容、需要 .htaccess 灵活配置的场景

七、演进与扩展

Nginx 不断迭代演进,适配现代互联网架构的需求,核心扩展方向如下:

A. NGINX Plus:Nginx 的商业版本,在开源版本基础上,提供高级负载均衡、监控 API、动态配置、动态证书加载等增值功能,适合企业级生产环境。

B. 与云原生集成:支持作为 Kubernetes Ingress Controller,实现云原生环境下的流量入口管理;同时可作为 Service Mesh Sidecar,适配微服务架构的流量治理需求。

C. 现代协议支持:持续优化 HTTP/3(QUIC)、TLS 1.3、gRPC-Web 等现代协议的支持,提升网络传输效率和安全性,适配新一代应用场景。

如果觉得这篇文章对你有帮助,欢迎点赞、收藏,也可以在评论区留言,聊聊你在使用Nginx时遇到的问题~

Apache2配置SSL证书

一、申请证书
各大云厂商均有销售,如果没有特殊需求买最便宜的或免费的,需要做域名验证,按指引做好验证即可
申请后会得到三个文件,保管好,不要给别人
ca.crt、server.crt、server.key
将这三个文件拷贝到/etc/apache2/ssl/

二、启用https
1、apache2启用ssl模块

sudo apt-get install openssl
sudo a2enmod ssl

2、配置ssl虚拟站点

# 建立软连接
sudo ln -s /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-enabled/001-default-ssl.conf

# 编辑https配置文件
sudo vi /etc/apache2/sites-enabled/001-default-ssl.conf
# 修改下面三行
SSLCertificateFile       /etc/apache2/ssl/server.crt
SSLCertificateKeyFile    /etc/apache2/ssl/server.key
SSLCertificateChainFile  /etc/apache2/ssl/ca.crt

3、重启apache服务

# 重启服务
sudo systemctl apache2 restart
# 查看状态
sudo systemctl status apache2.service

4、登录wordpress后台,修改网站地址为https地址

5、此时应该就可以用https进行访问了

三、设置http重定向到https
1、启动重定向模块

sudo a2enmod rewrite

2、设置http重定向

# 编辑http配置文件
sudo vi /etc/apache2/sites-available/000-default.conf 
# 在需要重定向的VirtualHost中,增加下面三行
<VirtualHost *:80>
    RewriteEngine on
    RewriteCond %{HTTPS} !=on
    RewriteRule ^(.*) https://%{SERVER_NAME}$1 [L,R]
<\VirtualHost> 

3、重启apache服务

# 重启服务
sudo systemctl apache2 restart
# 查看状态
sudo systemctl status apache2.service

4、现在访问http地址,就会自动跳转到https地址了

JMX获取Tomcat管理信息

1、首先配置JVM
JVM要放到NTFS卷中,在JVM路径下找到jmxremote.access及jmxremote.password.template两个文件。
将jmxremote.password.template复制一份为jmxremote.password,对运行用户可读,对普通用户不可读。

2、配置Tomcat启动参数
修改catalina.bat,增加下面一行

SET "JAVA_OPTS=%JAVA_OPTS% -Dcom.sun.management.jmxremote.port=8686 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=true"

3、启动Tomcat

4、客户端程序JMXInfo.java

package com.neohope.jmx.test;

import java.io.IOException;
import java.lang.management.MemoryUsage;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import javax.management.MBeanServerConnection;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class JMXInfo {
	public static void main(String[] args) throws Exception {
		JMXConnector connector = null;
		try {
			// 获取JMX连接
			String ip = "127.0.0.1";
			String port = "8686";
			JMXServiceURL serviceURL = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://" + ip + ":" + port + "/jmxrmi");
			Map<String, String[]> map = new HashMap<String, String[]>();
			String[] credentials = new String[] { "controlRole", "R&D" };
			map.put("jmx.remote.credentials", credentials);
			connector = JMXConnectorFactory.connect(serviceURL, map);
			MBeanServerConnection mbsc = connector.getMBeanServerConnection();

			// 获取JVM信息
			ObjectName runtimeObjName = new ObjectName("java.lang:type=Runtime");
			System.out.println("厂商:" + (String) mbsc.getAttribute(runtimeObjName, "VmVendor"));
			System.out.println("程序:" + (String) mbsc.getAttribute(runtimeObjName, "VmName"));
			System.out.println("版本:" + (String) mbsc.getAttribute(runtimeObjName, "VmVersion"));
			
			// 获取JVM运行时间
			Date starttime = new Date((Long) mbsc.getAttribute(runtimeObjName, "StartTime"));
			SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			System.out.println("启动时间:" + df.format(starttime));
			Long timespan = (Long) mbsc.getAttribute(runtimeObjName, "Uptime");
			System.out.println("持续工作时间:" + JMXInfo.formatTimeSpan(timespan));

			// 获取JVM内存信息
			ObjectName heapObjName = new ObjectName("java.lang:type=Memory");
			MemoryUsage heapMemoryUsage = MemoryUsage.from((CompositeDataSupport) mbsc.getAttribute(heapObjName, "HeapMemoryUsage"));
			long heapMaxMemory = heapMemoryUsage.getMax();
			long heapCommitMemory = heapMemoryUsage.getCommitted();
			long heapUsedMemory = heapMemoryUsage.getUsed();
			System.out.println("heap:" + (double) heapUsedMemory * 100 / heapCommitMemory + "%");

			MemoryUsage nonheapMemoryUsage = MemoryUsage.from((CompositeDataSupport) mbsc.getAttribute(heapObjName, "NonHeapMemoryUsage"));
			long nonHeapCommitMemory = nonheapMemoryUsage.getCommitted();
			long nonHeapUsedMemory = heapMemoryUsage.getUsed();
			System.out.println("nonheap:" + (double) nonHeapUsedMemory * 100 / nonHeapCommitMemory + "%");

			ObjectName permObjName = new ObjectName("java.lang:type=MemoryPool,name=Perm Gen");
			MemoryUsage permGenUsage = MemoryUsage.from((CompositeDataSupport) mbsc.getAttribute(permObjName, "Usage"));
			long permCommitted = permGenUsage.getCommitted();
			long permUsed = heapMemoryUsage.getUsed();
			System.out.println("perm gen:" + (double) permUsed * 100 / permCommitted + "%");
			
			// All Domains
			for (int j = 0; j < mbsc.getDomains().length; j++) {
				System.out.println(mbsc.getDomains()[j]);
			}
			
			// All MBeans
			Set<ObjectInstance> MBeanset = mbsc.queryMBeans(null, null);
			System.out.println("MBeanset.size() : " + MBeanset.size());
			Iterator<ObjectInstance> MBeansetIterator = MBeanset.iterator();
			while (MBeansetIterator.hasNext()) {
				ObjectInstance objectInstance = (ObjectInstance) MBeansetIterator.next();
				ObjectName objectName = objectInstance.getObjectName();
				String canonicalName = objectName.getCanonicalName();
				System.out.println("canonicalName : " + canonicalName);
			}
			
			// 全部线程池
			ObjectName threadpoolObjName = new ObjectName("Catalina:type=ThreadPool,*");
			Set<ObjectName> s2 = mbsc.queryNames(threadpoolObjName, null);
			for (ObjectName obj : s2) {
				System.out.println("端口名:" + obj.getKeyProperty("name"));
				ObjectName objname = new ObjectName(obj.getCanonicalName());
				System.out.println("最大线程数:" + mbsc.getAttribute(objname, "maxThreads"));
				System.out.println("当前线程数:" + mbsc.getAttribute(objname, "currentThreadCount"));
				System.out.println("繁忙线程数:" + mbsc.getAttribute(objname, "currentThreadsBusy"));
			}
			
			//HTTP Thread Pool
			ObjectName threadPoolObjName = new ObjectName("Catalina:type=ThreadPool,*");
			Set<ObjectName> threadPoolObjNames = mbsc.queryNames(threadPoolObjName, null);
			for (ObjectName obj : threadPoolObjNames) {
				if (obj.getKeyProperty("name").contains("http")) {
					ObjectName objname = new ObjectName(obj.getCanonicalName());
					System.out.println("HTTP最大线程数:"+ mbsc.getAttribute(objname, "maxThreads").toString());// 最大线程数
					System.out.println("HTTP当前线程数:"+ mbsc.getAttribute(objname, "currentThreadCount").toString());// 当前线程数
					System.out.println("HTTP繁忙线程数:" + mbsc.getAttribute(objname, "currentThreadsBusy").toString());// 繁忙线程数
				}
			}

			// 全部应用
			ObjectName managerObjName = new ObjectName("Catalina:type=Manager,*");
			Set<ObjectName> s = mbsc.queryNames(managerObjName, null);
			for (ObjectName obj : s) {
				System.out.println("应用名:" + obj.getKeyProperty("path"));
				ObjectName objname = new ObjectName(obj.getCanonicalName());
				System.out.println("最大会话数:" + mbsc.getAttribute(objname, "maxActiveSessions"));
				System.out.println("会话数:" + mbsc.getAttribute(objname, "activeSessions"));
				System.out.println("活动会话数:" + mbsc.getAttribute(objname, "sessionCounter"));
			}
			
			
			//RequestProcessor
			ObjectName requestObjName = new ObjectName("Catalina:type=RequestProcessor,*");
			Set<ObjectName> requestObjNameSet = mbsc.queryNames(requestObjName, null);
			Integer aliveSocketsCount = 0;
			Long maxProcessingTime = 0L;
			Long processingTime = 0L;
			Long requstCount = 0L;
			Long errorCount = 0L;
			BigDecimal bytesReceived = BigDecimal.ZERO;
			BigDecimal bytesSend = BigDecimal.ZERO;
			for (ObjectName obj : requestObjNameSet) {
				if (mbsc.getAttribute(obj, "stage").toString().trim().equals("1"))
					aliveSocketsCount++;
				long nowMaxProcessingTime = Long.parseLong(mbsc.getAttribute(obj, "maxTime").toString());
				if (maxProcessingTime < nowMaxProcessingTime)
					maxProcessingTime = nowMaxProcessingTime;
				processingTime += Long.parseLong(mbsc.getAttribute(obj, "processingTime").toString());
				requstCount += Long.parseLong(mbsc.getAttribute(obj, "requestCount").toString());
				errorCount += Long.parseLong(mbsc.getAttribute(obj, "errorCount").toString());
				bytesReceived = bytesReceived.add(new BigDecimal(mbsc.getAttribute(obj, "bytesReceived").toString()));
				bytesSend = bytesSend.add(new BigDecimal(mbsc.getAttribute(obj, "bytesSent").toString()));
			}
			System.out.println("活动sockets计数:"+ aliveSocketsCount.toString());
			System.out.println("最大处理时间:"+ maxProcessingTime.toString());
			processingTime = processingTime / 1000;
			System.out.println("总处理时间:"+ processingTime.toString());
			System.out.println("请求总数:"+ requstCount.toString());
			System.out.println("错误总数:"+ errorCount.toString());
			System.out.println("接收字节数:"+ bytesReceived.divide(new BigDecimal(1024L * 1024))
					.setScale(2, RoundingMode.HALF_UP).toPlainString());
			System.out.println("发送字节数:"+
					bytesSend.divide(new BigDecimal(1024L * 1024)).setScale(2, RoundingMode.HALF_UP).toPlainString());

		} catch (Exception e) {
			e.printStackTrace();
		}
		finally
		{
			if(connector!=null)
			{
				try {
					connector.close();
				} catch (IOException e) {
				}
			}
		}
		
	}

	public static String formatTimeSpan(long span) {
		long minseconds = span % 1000;

		span = span / 1000;
		long seconds = span % 60;

		span = span / 60;
		long mins = span % 60;

		span = span / 60;
		long hours = span % 24;

		span = span / 24;
		long days = span;
		return (new Formatter()).format("%1$d天 %2$02d小时%3$02d分%4$02d秒%5$03d毫秒", days, hours, mins, seconds, minseconds)
				.toString();
	}
}

5.运行结果

厂商:Oracle Corporation
程序:Java HotSpot(TM) Client VM
版本:24.65-b04
启动时间:2016-06-21 20:57:37
持续工作时间:0天 00小时29分31秒018毫秒
heap:60.11998789417739%
nonheap:54.523986450299915%
perm gen:165.7018025716146%
Users
JMImplementation
com.sun.management
Catalina
java.nio
java.lang
java.util.logging
MBeanset.size() : 150
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Servlet,name=HTMLManager
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Servlet,name=Manager
canonicalName : java.lang:type=Memory
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=stock
canonicalName : JMImplementation:type=MBeanServerDelegate
canonicalName : Catalina:context=/host-manager,host=localhost,name=StandardContextValve,type=Valve
canonicalName : Catalina:port=8080,type=Connector
canonicalName : Catalina:context=/examples,host=localhost,name=foo/name1,resourcetype=Context,type=Environment
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Filter,name=Tomcat WebSocket (JSR356) Filter
canonicalName : Catalina:context=/host-manager,host=localhost,type=WebappClassLoader
canonicalName : java.nio:name=mapped,type=BufferPool
canonicalName : java.lang:name=MarkSweepCompact,type=GarbageCollector
canonicalName : Catalina:context=/manager,host=localhost,name=StandardContextValve,type=Valve
canonicalName : Catalina:context=/examples,host=localhost,name=name3,resourcetype=Context,type=Environment
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/,j2eeType=Filter,name=Tomcat WebSocket (JSR356) Filter
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=default
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/,j2eeType=Servlet,name=jsp
canonicalName : Catalina:host=localhost,name=ErrorReportValve,type=Valve
canonicalName : Catalina:context=/host-manager,host=localhost,type=Cache
canonicalName : java.lang:name=Tenured Gen,type=MemoryPool
canonicalName : Catalina:context=/manager,host=localhost,name=BasicAuthenticator,type=Valve
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Filter,name=Tomcat WebSocket (JSR356) Filter
canonicalName : Catalina:type=Engine
canonicalName : Catalina:context=/,host=localhost,type=Manager
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Servlet,name=JMXProxy
canonicalName : Catalina:context=/docs,host=localhost,type=WebappClassLoader
canonicalName : java.lang:name=Code Cache,type=MemoryPool
canonicalName : java.util.logging:type=Logging
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,name=jsp,type=JspMonitor
canonicalName : Catalina:realmPath=/realm0/realm0,type=Realm
canonicalName : Catalina:name=StandardEngineValve,type=Valve
canonicalName : Catalina:port=8009,type=ProtocolHandler
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=RequestParamExample
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,j2eeType=WebModule,name=//localhost/
canonicalName : Catalina:context=/manager,host=localhost,type=Manager
canonicalName : Catalina:context=/host-manager,host=localhost,name=BasicAuthenticator,type=Valve
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Filter,name=Set Character Encoding
canonicalName : Catalina:context=/,host=localhost,type=NamingResources
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Filter,name=Request Dumper Filter
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=ServletToJsp
canonicalName : Catalina:name=HttpRequest1,type=RequestProcessor,worker="http-apr-8080"
canonicalName : java.lang:type=Compilation
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/host-manager,j2eeType=Filter,name=SetCharacterEncoding
canonicalName : java.lang:name=Survivor Space,type=MemoryPool
canonicalName : Catalina:port=8080,type=ProtocolHandler
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=ChatServlet
canonicalName : Catalina:context=/examples,host=localhost,type=WebappClassLoader
canonicalName : Catalina:context=/examples,host=localhost,name=foo/bar/name2,resourcetype=Context,type=Environment
canonicalName : Catalina:type=StringCache
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/docs,j2eeType=Filter,name=Tomcat WebSocket (JSR356) Filter
canonicalName : java.lang:type=Threading
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Servlet,name=Status
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,j2eeType=WebModule,name=//localhost/manager
canonicalName : Catalina:host=localhost,name=StandardHostValve,type=Valve
canonicalName : Users:database=UserDatabase,rolename=manager-gui,type=Role
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Servlet,name=default
canonicalName : java.lang:name=Perm Gen,type=MemoryPool
canonicalName : Catalina:port=8009,type=Mapper
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=wsEchoStream
canonicalName : Catalina:context=/docs,host=localhost,type=Loader
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Servlet,name=jsp
canonicalName : Catalina:context=/examples,host=localhost,name=FormAuthenticator,type=Valve
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=wsSnake
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/host-manager,j2eeType=Servlet,name=jsp
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/host-manager,j2eeType=Servlet,name=HostManager
canonicalName : Catalina:name="http-apr-8080",type=GlobalRequestProcessor
canonicalName : Catalina:context=/docs,host=localhost,type=NamingResources
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=jsp
canonicalName : java.lang:name=Perm Gen [shared-rw],type=MemoryPool
canonicalName : Catalina:context=/,host=localhost,type=Cache
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=wsChat
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=CompressionFilterTestServlet
canonicalName : java.lang:name=CodeCacheManager,type=MemoryManager
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/host-manager,j2eeType=Servlet,name=HTMLHostManager
canonicalName : Catalina:host=localhost,type=Host
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/docs,j2eeType=Servlet,name=jsp
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=HelloWorldExample
canonicalName : Catalina:name=HttpRequest2,type=RequestProcessor,worker="http-apr-8080"
canonicalName : Catalina:name="ajp-apr-8009",type=GlobalRequestProcessor
canonicalName : java.lang:type=Runtime
canonicalName : java.nio:name=direct,type=BufferPool
canonicalName : java.lang:name=Copy,type=GarbageCollector
canonicalName : Catalina:name=HttpRequest3,type=RequestProcessor,worker="http-apr-8080"
canonicalName : Catalina:context=/host-manager,host=localhost,type=Manager
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,name=jsp,type=JspMonitor
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/,name=jsp,type=JspMonitor
canonicalName : Catalina:host=localhost,name=AccessLogValve,type=Valve
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,j2eeType=WebModule,name=//localhost/examples
canonicalName : Catalina:name="ajp-apr-8009",type=ThreadPool
canonicalName : Catalina:type=Service
canonicalName : com.sun.management:type=HotSpotDiagnostic
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/,j2eeType=Servlet,name=default
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/host-manager,j2eeType=Filter,name=Tomcat WebSocket (JSR356) Filter
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/docs,j2eeType=Servlet,name=default
canonicalName : Catalina:context=/,host=localhost,type=WebappClassLoader
canonicalName : java.lang:type=OperatingSystem
canonicalName : Catalina:context=/examples,host=localhost,name=StandardContextValve,type=Valve
canonicalName : Catalina:type=Server
canonicalName : Catalina:port=8009,type=Connector
canonicalName : Catalina:name="http-apr-8080",type=ThreadPool
canonicalName : Catalina:context=/,host=localhost,name=StandardContextValve,type=Valve
canonicalName : Catalina:class=org.apache.catalina.UserDatabase,name="UserDatabase",resourcetype=Global,type=Resource
canonicalName : Catalina:context=/manager,host=localhost,type=WebappClassLoader
canonicalName : Catalina:context=/examples,host=localhost,type=Manager
canonicalName : Catalina:context=/examples,host=localhost,type=Cache
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,j2eeType=WebModule,name=//localhost/host-manager
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,j2eeType=WebModule,name=//localhost/docs
canonicalName : Users:database=UserDatabase,type=UserDatabase
canonicalName : Catalina:context=/examples,host=localhost,name=foo/name4,resourcetype=Context,type=Environment
canonicalName : Catalina:context=/docs,host=localhost,name=StandardContextValve,type=Valve
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Filter,name=Compression Filter
canonicalName : Catalina:type=NamingResources
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=SessionExample
canonicalName : Catalina:context=/,host=localhost,type=Loader
canonicalName : Catalina:name=common,type=ServerClassLoader
canonicalName : Catalina:context=/examples,host=localhost,type=Loader
canonicalName : Catalina:context=/manager,host=localhost,type=Cache
canonicalName : Catalina:context=/docs,host=localhost,type=Cache
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/host-manager,j2eeType=Filter,name=CSRF
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=CookieExample
canonicalName : Users:database=UserDatabase,type=User,username="tomcat"
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Filter,name=SetCharacterEncoding
canonicalName : Catalina:host=localhost,type=Deployer
canonicalName : Catalina:type=MBeanFactory
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=RequestInfoExample
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Filter,name=Timing filter
canonicalName : Catalina:context=/examples,host=localhost,type=NamingResources
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/host-manager,j2eeType=Servlet,name=default
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/manager,j2eeType=Filter,name=CSRF
canonicalName : Catalina:context=/manager,host=localhost,type=Loader
canonicalName : Users:database=UserDatabase,type=User,username="admin"
canonicalName : Catalina:context=/docs,host=localhost,type=Manager
canonicalName : java.lang:name=Eden Space,type=MemoryPool
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=RequestHeaderExample
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/docs,name=jsp,type=JspMonitor
canonicalName : java.lang:name=Perm Gen [shared-ro],type=MemoryPool
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/host-manager,name=jsp,type=JspMonitor
canonicalName : Catalina:context=/host-manager,host=localhost,type=NamingResources
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=async2
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=async3
canonicalName : Catalina:context=/examples,host=localhost,name=minExemptions,resourcetype=Context,type=Environment
canonicalName : Catalina:context=/manager,host=localhost,type=NamingResources
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=async0
canonicalName : java.lang:type=ClassLoading
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=async1
canonicalName : Catalina:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/examples,j2eeType=Servlet,name=wsEchoMessage
canonicalName : Catalina:realmPath=/realm0,type=Realm
canonicalName : Catalina:context=/host-manager,host=localhost,type=Loader
canonicalName : Users:database=UserDatabase,rolename=tomcat,type=Role
canonicalName : Catalina:port=8080,type=Mapper
端口名:"ajp-apr-8009"
最大线程数:200
当前线程数:0
繁忙线程数:0
端口名:"http-apr-8080"
最大线程数:200
当前线程数:10
繁忙线程数:0
HTTP最大线程数:200
HTTP当前线程数:10
HTTP繁忙线程数:0
应用名:null
最大会话数:-1
会话数:0
活动会话数:0
应用名:null
最大会话数:-1
会话数:0
活动会话数:0
应用名:null
最大会话数:-1
会话数:0
活动会话数:0
应用名:null
最大会话数:-1
会话数:0
活动会话数:0
应用名:null
最大会话数:-1
会话数:0
活动会话数:0
活动sockets计数:0
最大处理时间:176
总处理时间:0
请求总数:9
错误总数:0
接收字节数:0.00
发送字节数:0.07

Tomcat指定native library路径

一般,指定JVM的native library路径,只需要用下面的参数就好了

-Djava.library.path=PATH_TO_NATIVE_LIBRARY

Tomcat指定native library路径时,还是不要使用这边参数的好。
建议直接修改PATH环境变量,将dll或so放到环境变量PATH的路径下就好了。
TOMCAT默认将PATH赋值给-Djava.library.path参数的。

IIS开启程序集绑定日志

最近,将一个AnyCPU+Win32的程序,调整到了x64架构,但一直各种绑定失败。
IIS7一直提示要开启程序就绑定日志:

警告: 程序集绑定日志记录被关闭。
要启用程序集绑定失败日志记录,请将注册表值 [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD)设置为 1。
注意: 会有一些与程序集绑定失败日志记录关联的性能损失。
要关闭此功能,请移除注册表值 [HKLM\Software\Microsoft\Fusion!EnableLog]。

好吧,命令行搞定

reg add HKLM\software\microsoft\fusion /v Enablelog /t REG_DWORD /d 1

然后,IIS7仍然坚持不懈的提示绑定失败。提示dll格式不正确。
弄了一个多小时,发现应用程序池的“Enable 32-Bit Applications”选项打开了,晕死。
关闭之,搞定!!!

PS:
按网上的说法,Fuslogvw.exe可以查看绑定失败的日志。但我打开后没有哦,以后再试试看。

IIS程序访问NAS

IIS程序访问NAS,一般有两种情况,加域和没加域
1、加域后,什么都好办,可以通过两种方式认证
A、采用域帐户认证
B、NAS资源可以直接授权给指定计算机访问

2、不加域,那一般有三种方式设置访问权限
A、通过指定程序的用户名和密码访问(这里指定的不是本地的用户名和密码,而是NAS的用户名和密码)
B、不指定用户名和密码,直接通过pass-through authentication方式访问,其规则为:
首先通过用户的真实id进行验证
如果用户没有授权,会尝试使用应用池的账户进行验证
如果应用池账号为NetworkService或LocalSystem,将使用计算机账号进行验证
采取这样的方式IIS会提出警告,因为IIS只有在运行之后,才知道能否访问NAS
C、通过代码实现

    //NAS访问封装类
    public class NetworkDrive
    {
        public enum ResourceScope
        {
            RESOURCE_CONNECTED = 1,
            RESOURCE_GLOBALNET,
            RESOURCE_REMEMBERED,
            RESOURCE_RECENT,
            RESOURCE_CONTEXT
        }

        public enum ResourceType
        {
            RESOURCETYPE_ANY,
            RESOURCETYPE_DISK,
            RESOURCETYPE_PRINT,
            RESOURCETYPE_RESERVED
        }

        public enum ResourceUsage
        {
            RESOURCEUSAGE_CONNECTABLE = 0x00000001,
            RESOURCEUSAGE_CONTAINER = 0x00000002,
            RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
            RESOURCEUSAGE_SIBLING = 0x00000008,
            RESOURCEUSAGE_ATTACHED = 0x00000010,
            RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
        }

        public enum ResourceDisplayType
        {
            RESOURCEDISPLAYTYPE_GENERIC,
            RESOURCEDISPLAYTYPE_DOMAIN,
            RESOURCEDISPLAYTYPE_SERVER,
            RESOURCEDISPLAYTYPE_SHARE,
            RESOURCEDISPLAYTYPE_FILE,
            RESOURCEDISPLAYTYPE_GROUP,
            RESOURCEDISPLAYTYPE_NETWORK,
            RESOURCEDISPLAYTYPE_ROOT,
            RESOURCEDISPLAYTYPE_SHAREADMIN,
            RESOURCEDISPLAYTYPE_DIRECTORY,
            RESOURCEDISPLAYTYPE_TREE,
            RESOURCEDISPLAYTYPE_NDSCONTAINER
        }

        [StructLayout(LayoutKind.Sequential)]
        private class NETRESOURCE
        {
            public ResourceScope dwScope = 0;
            public ResourceType dwType = 0;
            public ResourceDisplayType dwDisplayType = 0;
            public ResourceUsage dwUsage = 0;
            public string lpLocalName = null;
            public string lpRemoteName = null;
            public string lpComment = null;
            public string lpProvider = null;
        }

        [DllImport("mpr.dll")]
        private static extern int WNetAddConnection2(NETRESOURCE lpNetResource, string lpPassword, string lpUsername, int dwFlags);

        public static int MapNetworkDrive(string remotePath, string localDrive, string userName, string passWord)
        {
            NETRESOURCE myNetResource = new NETRESOURCE();
            myNetResource.lpLocalName = localDrive;
            myNetResource.lpRemoteName = remotePath;
            myNetResource.lpProvider = null;
            int result = WNetAddConnection2(myNetResource, passWord, userName, 0);
            return result;
        }
    }

    //调用示例
    NetworkDrive.MapNetworkDrive("NAS paht with ip", "Local Driver Letter", "user", "password");

参考资料:
What is “pass-through authentication” in IIS 7?

Build Jetty Lesson101

1. Download source code from Eclipse.
For example, I used this one:

From here: http://download.eclipse.org/jetty/
Get this one: jetty-8.1.15.v20140411

2. Read this:

http://docs.codehaus.org/display/JETTY/Building+from+Source

3. Prepare JDK and Maven:

SET JAVA_HOME=C:\Languages\Java\JDK\jdk_x86_1.6.0_34
SET MAVEN_HOME=C:\Languages\Java\JavaTools\apache-maven-3.0.4
SET PATH=%MAVEN_HOME%\bin;%JAVA_HOME%\bin;%PATH%
CMD

4. Run “mvn install”

5. Use eclipse to import maven project

Build Tomcat Lesson101

1. Download one source tag from Apache
For example, I used this tag:

http://svn.apache.org/repos/asf/tomcat/tc6.0.x/tags/TOMCAT_6_0_41

2. Read this:

http://tomcat.apache.org/tomcat-6.0-doc/building.html

3. Prepare JDK and Ant

SET JAVA_HOME=C:\Languages\Java\JDK\jdk_x86_1.6.0_34
SET ANT_HOME=C:\Languages\Java\JavaTools\apache-ant-1.9.0
SET PATH=%ANT_HOME%\bin;%JAVA_HOME%\bin;%PATH%
CMD

4. Copy build.properties.default to build.properties

5. Edit build.properties and set base.path

base.path=the path to store thirdpart libs

6. Run “Ant download”

7. Run “Ant”

8. Rename files

move eclipse.classpath .classpath
move eclipse.project .project

9. Use eclipse to import this project

10. Set break point and debug