这个问题本身有个误解:把三个东西都叫「web server」,会让人以为它们是同一种东西的三种实现。其实不是。nginx 和 apache 是 http 服务器,tomcat 是 servlet 容器,它们干的活不在一个层次上。
nginx 和 apache(一般说的 apache 指的是 apache http server,也就是 httpd)是 http 服务器:收 http 请求、按配置干活、回 http 响应。它们擅长扛静态文件、做反向代理、做负载均衡,但它们不执行 java 代码。你打一个 .war 包丢给 nginx,nginx 不知道怎么处理——它只会返回 404 或者把请求转给别人。
tomcat 是 servlet 容器,不是完整的 java ee 应用服务器(那是 wildfly、weblogic、websphere 干的事,它们支持 ejb、jms、jta 等完整规范)。tomcat 只实现 servlet 和 jsp 规范,核心能力是:把 http 请求交给你的 java 代码去处理,再把结果变成 http 响应发回去。
所以「java 后台程序能不能用 apache 和 nginx」——能,而且生产环境里经常是「nginx/apache 在前,tomcat 在后」:前面负责扛流量、静态资源、https 终结、负载均衡,后面专门跑 java。
nginx 和 apache:都是 http 服务器,架构不一样
两者都能做静态文件服务、反向代理、负载均衡,但内部设计完全不同。
apache 有三种工作模式(mpm):prefork 是一个连接一个进程,worker 是多进程+多线程,event 是在 worker 基础上优化了 keep-alive 连接的处理。现代 apache(2.4+)默认用 event mpm,处理 keep-alive 的方式已经接近事件驱动了,不完全是老式的「一个连接占一个线程」。但不管哪种模式,apache 的并发上限都受限于进程/线程数——每个线程有自己的栈空间(linux 默认 8mb),1000 个线程光栈就要 8gb 内存,还没算堆上的数据。所以 apache 的并发连接数一般在几百到几千这个量级。
nginx 是另一种思路:少量 worker 进程 + 事件循环。每个 worker 用 epoll(linux)/ kqueue(macos)做 i/o 多路复用,一个 worker 可以同时挂着几万条连接。大部分连接在等 i/o,不需要单独的线程,也就不需要那 8mb 的栈空间。所以同样一台机器,nginx 能撑的并发连接数比 apache 高一个数量级。
这也是很多人说「nginx 比 apache 性能好」的原因——不是 nginx 处理单个请求更快,而是它用更少的资源就能维持大量连接。 如果你的场景是几十个并发、主要跑 php,apache + mod_php 用着挺好,没必要换。但如果要扛几万并发、做反向代理或者负载均衡,nginx 的模型更合适。
tomcat:能直接对外,但不擅长
这里要纠正一个常见的说法:「tomcat 必须放在 nginx 后面」。
tomcat 自带 http 连接器(coyote),可以直接监听 80 或 443 端口对外服务。开发的时候大家天天直接访问 localhost:8080,没什么问题。spring boot 更进一步——内嵌 tomcat 打成一个 jar 包,java -jar 直接跑,连单独部署 tomcat 都省了。
很多微服务架构里,每个服务就是一个内嵌 tomcat 的 spring boot 应用,前面挂一个 api 网关(spring cloud gateway、kong 之类的)做路由和鉴权,根本没有单独部署 nginx 的环节。
但 tomcat 直接对外有几个短板:
静态文件性能。 nginx 处理静态文件用的是 sendfile 系统调用(之前零拷贝那篇讲过),数据不经过用户空间,直接从磁盘到网卡。tomcat 处理静态文件要经过 java 的 io 层,多了一次拷贝和 jvm 的开销。量小的时候感知不到,量大了差距就出来了。
ssl 终结。 nginx 的 ssl 实现基于 openssl,经过大量优化,支持 session 复用、ocsp stapling 这些。tomcat 也能做 ssl,但性能和配置灵活性都不如 nginx。把 ssl 卸载到 nginx,tomcat 和 nginx 之间走 http 明文,tomcat 的负担更轻。
限流、缓存、负载均衡。 这些 nginx 用几行配置就能搞定,tomcat 要么不支持,要么需要写 java 代码或者引入额外组件。
所以典型的生产部署是这样的:
upstream tomcat_backend {
server 127.0.0.1:8080;
server 127.0.0.1:8081; # 多实例负载均衡
}
server {
listen 443 ssl;
ssl_certificate /etc/nginx/cert.pem;
ssl_certificate_key /etc/nginx/key.pem;
location /static/ {
alias /var/www/static/;
}
location / {
proxy_pass http://tomcat_backend;
proxy_set_header host $host;
proxy_set_header x-real-ip $remote_addr;
proxy_set_header x-forwarded-proto $scheme;
}
}
nginx 扛 ssl、吐静态文件、做负载均衡,动态请求转给后面的 tomcat。但这不是唯一的架构——小项目、内部系统、微服务里 tomcat 直接对外也很常见,取决于你的流量规模和运维需求。
为啥早年都是 apache + tomcat
早期 nginx 还没普及的时候,apache 是 linux 上默认的 http 服务器。java 项目的标准搭配是 apache + mod_jk(或 mod_proxy)+ tomcat:apache 在前面接请求,通过 ajp 协议或 http 代理转给 tomcat。
mod_jk 用的是 ajp 协议(apache jserv protocol),比 http 更紧凑,省了 http 头的解析开销。但 ajp 协议在 2020 年爆出过 ghostcat 漏洞(cve-2020-1938),之后很多团队开始关闭 ajp 端口,改用 http 代理。
现在新项目基本都用 nginx 替代 apache 当入口了。apache 在需要 .htaccess(目录级配置覆盖)或者跑 mod_php 的场景还有优势,但纯做反向代理和负载均衡,nginx 的资源占用和并发能力都更好。
怎么判断你的项目该用哪种组合
spring boot 微服务、内部系统、流量不大: 内嵌 tomcat 直接对外,前面挂个网关或者云厂商的负载均衡器就行,不需要单独部署 nginx。
对外的 web 应用、有静态资源、需要 https: nginx 在前做 ssl 终结和静态资源,动态请求 proxy_pass 到 tomcat。
php + java 混合部署(老项目): apache 跑 mod_php 处理 php,同时 mod_proxy 把 java 请求转给 tomcat。不过这种架构越来越少了。
纯静态站点、cdn 回源、api 网关: 只需要 nginx,不需要 tomcat。
nginx 和 tomcat 不是竞争关系,apache 和 nginx 才是。而即便是 apache 和 nginx,在大部分场景下也不是「谁好谁差」的问题——nginx 在高并发反向代理上更强,apache 在需要 .htaccess 和动态模块加载的场景更方便。
总结
到此这篇关于tomcat与nginx、apache区别是什么的文章就介绍到这了,更多相关tomcat与nginx、apache的区别内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论