Tomcat Jersey处理OPTIONS预检请求时阻塞服务器问题求助
针对你遇到的跨源OPTIONS请求随机延迟5-10秒、阻塞整个服务器,且仅在浏览器等跨源工具触发时出现的问题,结合你的代码和排查信息,我整理了几个核心排查方向和解决方案:
一、最可能的根源:OPTIONS请求未被及时终止,触发不必要的资源匹配
你的EnterpriseAPIRequestFilter在处理OPTIONS请求时只是简单return,这会让请求继续向下流转,尝试匹配Jersey的资源方法。如果你的API没有定义对应的OPTIONS资源方法,Jersey会进入错误处理流程,这就是延迟的主要来源,甚至会占用线程导致服务器阻塞。
修正方案:直接终止OPTIONS请求并返回CORS响应
修改你的EnterpriseAPIRequestFilter,在检测到OPTIONS请求时,直接通过ctx.abortWith()返回带CORS头的响应,终止请求流程:
@PreMatching @Priority(value = 1) @Provider public class EnterpriseAPIRequestFilter implements ContainerRequestFilter { @Context private HttpServletRequest sr; @Context private HttpServletResponse response; // 新增注入HttpServletResponse private Logger logger = Logger.getLogger(EnterpriseAPIRequestFilter.class); public void filter(ContainerRequestContext ctx) throws IOException { logger.info("Request Received for :" + sr.getPathInfo()); logger.info("Remote IP :" + sr.getRemoteAddr()); // 注意:这里你写的是sr.getLocalAddr()(服务器IP),如果是要打印客户端主机名,应该是sr.getRemoteHost(),但后者会触发DNS解析,后面会提到 logger.info("Remote Host :" + sr.getLocalAddr()); if (sr.getMethod().equalsIgnoreCase("OPTIONS")) { // 直接构建CORS响应,终止请求 response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "OPTIONS, GET, POST, DELETE, PUT"); response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type, X-Codingpedia, channelRole, channelauthkey,channeltype,channelname,channelidentifier,orgid,id,channelOrgId,key,employeeId,subRole"); ctx.abortWith(Response.ok().build()); // 立即返回200响应,终止后续流程 return; } // 原有的认证逻辑放在这里 Authenticator authenticator = new Authenticator(); MultivaluedMap<String, String> headers = ctx.getHeaders(); String role = headers.getFirst("channelRole"); String locale = headers.getFirst("locale"); // MORE CODE EXISTS HERE. } }
同时可以考虑移除单独的CORSResponseFilter,避免重复添加响应头,减少不必要的处理。
二、潜在坑点:DNS反向解析导致的阻塞
看你的日志代码里有一行logger.info("Remote Host :" + sr.getLocalAddr());——这明显是笔误(getLocalAddr()是服务器自身的IP),如果你实际代码中是调用sr.getRemoteHost()来获取客户端主机名,那就要注意了:
Tomcat默认enableLookups="true",调用getRemoteHost()时会尝试反向解析客户端IP到主机名。如果客户端IP无法被DNS服务器解析(比如内网IP、动态IP),这个过程会阻塞线程,导致请求延迟5-10秒(DNS超时时间),甚至耗尽Tomcat线程池,阻塞所有请求。
解决方法:
- 修正日志代码,如果你不需要客户端主机名,直接用
sr.getRemoteAddr()(客户端IP)即可,避免调用getRemoteHost()。 - 强制关闭Tomcat的DNS解析:修改
conf/server.xml中的Connector配置,添加enableLookups="false":
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" enableLookups="false"/>
三、优化Tomcat连接器,避免阻塞式IO导致的线程耗尽
Tomcat 7默认使用BIO连接器(阻塞式IO),每个请求占用一个线程。如果OPTIONS请求因为上述原因阻塞,会快速耗尽线程池,导致其他请求无法处理。建议切换到NIO连接器(非阻塞式),并调整线程池参数:
修改conf/server.xml:
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" connectionTimeout="20000" maxThreads="200" acceptCount="100" enableLookups="false" URIEncoding="UTF-8"/>
NIO连接器能更高效地处理并发请求,减少阻塞带来的影响。
四、其他排查方向
- 检查Jersey与Tomcat的兼容性:确保你使用的Jersey版本和Tomcat 7兼容,推荐使用Jersey 2.25.1这类稳定版本。
- 排查全局CORS配置冲突:如果你的Tomcat
web.xml中配置了全局CORS过滤器,可能和Jersey的过滤器冲突,建议统一使用一种方式处理CORS。 - 验证浏览器预检缓存:浏览器会缓存OPTIONS预检响应,随机延迟可能是缓存失效时触发的新请求,可以通过浏览器开发者工具查看
Access-Control-Max-Age头是否正确设置,延长缓存时间减少预检次数。
内容的提问来源于stack exchange,提问作者krish advani

