Java Web流量分析软件实战:从Filter采集到批量入库的完整实现

发布时间:2026/10/7 1:36:14
Java Web流量分析软件实战:从Filter采集到批量入库的完整实现 简介这是一份面向高校计算机与网络相关专业学生的Java Web课程设计资源围绕跨平台网络流量实时监控与分析软件展开。项目采用Java完成后台开发前端以Web客户端形式呈现便于无图形界面系统或远程目标机通过浏览器查看分析结果同时兼顾数据传输安全与系统运行稳定性。压缩包共77个文件约11.31MB以27个java源码、15个js与9个jsx前端脚本为核心辅以html、css、json、gradle构建脚本、jar包、keystore与crt证书等覆盖后端逻辑、前端界面、构建配置与安全通信模块。资源内附课程设计报告书、任务说明与README文档便于理解整体架构与实现思路。目前已有323人学习下载适合作为网络流量分析、Java Web开发与课程设计参考帮助读者快速掌握项目结构、核心代码组织方式及跨平台监控方案的设计要点。1. 抓包抓不到业务流量先搞清楚 Java Web 流量分析软件到底在分析什么线上一个 Spring Boot 接口突然变慢运维说带宽没打满日志里也没报错你打开浏览器 F12 只能看到自己这一次请求的耗时看不到整条链路上到底谁在拖后腿。这时候你需要的不是再加一行日志而是一个能挂在 Web 服务入口、把每一次 HTTP 请求的进出流量、响应时间、状态码、来源 IP、UA、请求体大小全部记下来的分析软件。基于 Java 实现的 Web 网络流量分析软件核心就是干这件事它不替代 Wireshark 那种网卡级抓包而是在应用层把 HTTP 流量结构化让你能按接口、按时间段、按来源去查。这类软件适合谁一是做企业级 Web 开发的 Java 工程师需要给现有 Spring Boot 或 Servlet 项目加一层流量观测二是运维和 SRE想在不改业务代码的前提下拿到接口级流量画像三是安全方向的同学做 Web 安全审计时需要一个能落库、能检索的请求记录底座。它解决的不是网络通不通而是流量长什么样、异常在哪一段。下面从选型、实现到踩坑把一条能复现的路径讲清楚。2. 技术选型为什么用 Java 做 Web 流量分析而不是直接上 tcpdump2.1 应用层流量分析 vs 网卡层抓包边界在哪很多人第一反应是 tcpdump 加 Wireshark抓下来慢慢看。这条路在排查单次连接问题时确实好用但放到 Web 流量分析场景就会翻车。原因有三点第一网卡层抓到的是 TCP 包你要自己重组 HTTP 报文遇到分块传输、gzip 压缩、HTTPS 就基本歇菜第二抓包文件是二进制 pcap没法直接按接口路径或状态码做 SQL 查询第三生产环境你不可能长期开着全量抓包磁盘和 CPU 都扛不住。Java 做这件事的优势在于它天然就在应用层。Servlet 规范里的 Filter、Spring 的 Interceptor、Netty 的 ChannelHandler都能在请求进入业务逻辑之前拿到已经解析好的 HttpServletRequest 对象。你拿到的是 method、URI、header、body、响应状态码这些结构化字段直接就能入库。代价是它只能看到经过这个 JVM 的流量跨服务、跨机器的流量需要每个节点都部署。所以选型结论很明确要做接口级、可检索、可长期运行的 Web 流量分析Java 应用层方案比网卡抓包更合适要做协议级、底层异常排查还是得回到 tcpdump。2.2 三种采集方式的取舍Filter、Interceptor、Netty Handler在 Java Web 里采集流量常见做法有三种我一般按项目现状选。第一种是 Servlet Filter。它工作在 Servlet 容器层Tomcat、Jetty、Undertow 都支持不依赖 Spring。优点是通用缺点是拿不到 Spring MVC 的 Handler 信息比如你没法直接知道这个请求最终落到哪个 Controller 方法。第二种是 Spring Interceptor。它工作在 DispatcherServlet 之后、Controller 之前能拿到 HandlerMethod可以精确记录哪个类的哪个方法处理了这个请求。缺点是对静态资源、非 Spring 管理的 Servlet 不生效。第三种是 Netty ChannelHandler。如果你的服务本身就是 Netty 写的或者你要做网关级流量分析那就在 pipeline 里加一个 handler在 channelRead 里解析 FullHttpRequest。这种方式性能最好但需要自己处理 HTTP 解码、连接复用、背压。选型建议普通 Spring Boot 项目用 Interceptor 加 Filter 组合Interceptor 记业务维度Filter 记全局兜底网关或 Netty 服务用 ChannelHandler。下面实现部分我以 Spring Boot 的 Filter 方案为主线因为它最容易复现也最容易迁移到其他框架。2.3 存储选型为什么流量数据不建议直接塞 MySQL流量数据的特点是写入量大、字段多、查询模式以时间范围加条件过滤为主。一个中等流量的接口一天几十万条记录很正常。直接写 MySQL 单表几天就上千万行查询开始变慢索引也撑不住。常见做法是分两层热数据写 Elasticsearch 或 ClickHouse按天建索引或分区支持按 URI、状态码、时间范围快速检索冷数据定期归档到对象存储或压缩后的文件。如果项目规模小用 MySQL 按天分表也能撑一阵但要在采集端做限流和采样别把数据库写挂。我一般会在采集层加一个内存队列加批量写入比如每 500 条或每 2 秒 flush 一次避免每条请求都单独开一次数据库连接。3. 动手实现一个能落库的 Java Web 流量采集最小闭环3.1 用 Filter 包装请求拿到可重复读的 bodyServlet 的 request body 默认只能读一次你在 Filter 里读完了Controller 里就拿不到参数了。这是第一个必须解决的坑。做法是用 HttpServletRequestWrapper 把 body 缓存到字节数组重写 getInputStream 和 getReader。public class BodyCachingRequestWrapper extends HttpServletRequestWrapper { private final byte[] body; public BodyCachingRequestWrapper(HttpServletRequest request) throws IOException { super(request); // 一次性把请求体读进内存后续 getInputStream 都从这里返回 this.body StreamUtils.copyToByteArray(request.getInputStream()); } Override public ServletInputStream getInputStream() { ByteArrayInputStream bais new ByteArrayInputStream(body); return new ServletInputStream() { Override public int read() { return bais.read(); } Override public boolean isFinished() { return bais.available() 0; } Override public boolean isReady() { return true; } Override public void setReadListener(ReadListener listener) { } }; } Override public BufferedReader getReader() { return new BufferedReader(new InputStreamReader( new ByteArrayInputStream(body), StandardCharsets.UTF_8)); } public byte[] getBody() { return body; } }逻辑说明构造时把原始 InputStream 全部读出存成 byte[]之后无论业务代码调用多少次 getInputStream 或 getReader都从这份缓存里重新构造流。参数说明这里没有限制 body 大小生产环境必须加一个上限比如超过 1MB 就不缓存只记录长度否则一个大文件上传就能把 JVM 内存打爆。3.2 采集字段设计与入库代码字段设计决定了你后面能查什么。最小可用字段集我一般这么定字段名类型说明trace_idvarchar(32)请求唯一标识用 UUID 生成urivarchar(512)请求路径不含 querymethodvarchar(8)GET/POST 等statusint响应状态码cost_msbigint从进入 Filter 到响应完成的毫秒数client_ipvarchar(64)取 X-Forwarded-For 第一个非 unknown 值user_agentvarchar(512)截断到 512避免超长req_sizeint请求体字节数resp_sizeint响应体字节数created_atdatetime请求开始时间采集逻辑放在 Filter 的 doFilter 里用 try-finally 保证异常也能记录。Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; long start System.currentTimeMillis(); String traceId UUID.randomUUID().toString().replace(-, ); BodyCachingRequestWrapper wrapper new BodyCachingRequestWrapper(request); try { chain.doFilter(wrapper, response); } finally { long cost System.currentTimeMillis() - start; TrafficRecord record new TrafficRecord(); record.setTraceId(traceId); record.setUri(request.getRequestURI()); record.setMethod(request.getMethod()); record.setStatus(response.getStatus()); record.setCostMs(cost); record.setClientIp(resolveIp(request)); record.setUserAgent(truncate(request.getHeader(User-Agent), 512)); record.setReqSize(wrapper.getBody().length); record.setCreatedAt(new Date(start)); // 投递到内存队列由后台线程批量入库 TrafficCollector.submit(record); } }逻辑说明start 时间在 chain.doFilter 之前取cost 在 finally 里算这样即使业务抛异常也能记录耗时和状态码。参数说明resolveIp 要按 X-Forwarded-For、X-Real-IP、RemoteAddr 的顺序取因为经过 Nginx 后 RemoteAddr 是代理 IP。truncate 防止 UA 超长导致入库失败。3.3 批量写入与背压保护每条请求都同步写库QPS 一上来数据库就顶不住。用 BlockingQueue 加后台消费线程做批量写入。public class TrafficCollector { private static final BlockingQueueTrafficRecord QUEUE new LinkedBlockingQueue(10000); private static final int BATCH_SIZE 500; public static void submit(TrafficRecord record) { // offer 不阻塞队列满直接丢弃并计数保护业务线程 if (!QUEUE.offer(record)) { Metrics.droppedCounter.increment(); } } static { Thread worker new Thread(() - { ListTrafficRecord batch new ArrayList(BATCH_SIZE); while (true) { try { TrafficRecord first QUEUE.take(); batch.add(first); QUEUE.drainTo(batch, BATCH_SIZE - 1); trafficMapper.batchInsert(batch); batch.clear(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } }, traffic-writer); worker.setDaemon(true); worker.start(); } }逻辑说明业务线程只做 offer不阻塞后台线程用 take 阻塞等待再用 drainTo 一次性取走剩余凑成一批写入。参数说明队列容量 10000 和批量 500 是两个关键参数队列太小会频繁丢弃太大则内存占用高批量太小写入效率低太大则单次事务时间长。我一般按 QPS 乘以 2 秒来估算队列容量。丢弃计数必须暴露出来否则你根本不知道数据丢了多少。4. 避坑与排查流量分析软件上线后最容易翻车的 5 个点4.1 现象Controller 里拿不到请求参数报 Required request body is missing原因Filter 里读了 body 但没有包装成可重复读的 Wrapper原始流被消费掉了。解决确认 Filter 里用的是 BodyCachingRequestWrapper并且 chain.doFilter 传的是 wrapper 而不是原始 request。如果用了 Spring 的 ContentCachingRequestWrapper注意它是在读取时才缓存你在 Filter 里读之前拿不到内容得在 doFilter 之后读。4.2 现象内存持续上涨Full GC 频繁最后 OOM原因body 缓存没有大小限制一个大文件上传请求就能占几十 MB或者队列积压TrafficRecord 对象堆积。解决给 body 缓存加阈值超过 1MB 只记录长度不缓存内容队列设上限并用 offer 而非 put监控队列 size 和丢弃计数超过阈值告警。4.3 现象client_ip 全是 Nginx 的 IP拿不到真实客户端地址原因请求经过反向代理RemoteAddr 是代理地址。解决按 X-Forwarded-For 第一个非 unknown 值取取不到再退到 X-Real-IP最后才是 RemoteAddr。注意 X-Forwarded-For 可以被伪造如果做安全审计要结合可信代理列表来判断。4.4 现象HTTPS 流量抓不到明文记录里 body 是乱码原因在应用层拿到的已经是解密后的明文但如果你的服务前面有 TLS 终止Filter 拿到的就是明文不该乱码。乱码通常是字符集没指定或者 body 是 gzip 压缩的。解决读 body 时显式指定 UTF-8检查 Content-Encoding 是否为 gzip如果是需要先解压再记录或者只记录长度不记录内容。4.5 现象开启采集后接口 P99 延迟明显上升原因Filter 里的同步操作太多比如每条都同步写库、每条都做复杂字符串处理。解决把入库改成异步批量UA 截断、IP 解析这些操作尽量轻量如果还是慢加采样率比如只记录 10% 的请求或者只记录耗时超过阈值的慢请求。采样率要可配置别写死。5. 进阶让流量数据真正能用来定位问题采集只是第一步数据躺在库里不会自己说话。我一般会在这几个方向上加一层分析。第一个是慢接口排行。按 uri 分组算 cost_ms 的 P95 和 P99每天跑一次找出那些平均值不高但长尾很长的接口。平均值会骗人P99 才是用户真实感受到的卡顿。第二个是异常状态码趋势。按小时统计 4xx 和 5xx 的数量画成曲线。如果某个接口 5xx 突然抬头结合 trace_id 去日志里捞对应请求比盲目翻日志快得多。第三个是来源分析。按 client_ip 和 user_agent 聚合能看出哪些客户端在刷接口、哪些 UA 的失败率异常高。做 Web 安全审计时这个维度能帮你快速定位可疑来源。验证采集是否准确有个简单办法用 curl 发一个已知大小和耗时的请求然后去库里查这条记录对比 req_size、cost_ms、status 是否一致。如果对不上先查 Filter 的执行顺序再看是不是有多个 Filter 重复记录。# 发一个带 body 的请求body 为 11 字节 curl -X POST http://localhost:8080/api/test \ -H Content-Type: application/json \ -d {a:12345} # 去库里查最近一条记录核对 req_size 是否为 11参数说明curl 的 -d 内容长度就是 req_size 的预期值如果库里记录的是 0 或者别的数说明 body 缓存或读取有问题。最后说个我自己的习惯流量分析软件本身也要被监控。我会给它加一个自检接口返回当前队列长度、丢弃计数、最近一分钟写入条数。上线第一周每天看一眼这三个数比出事之后再查强得多。这套东西不复杂但字段设计和背压保护这两块偷懒后面一定要还债。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询