Java应用层DDoS防御实战:IP限流、滑动窗口挑战与异步化策略
1. 项目概述当Java服务遭遇DDoS攻击最近在维护一个对外的Java Web服务时经历了一次不大不小的DDoS攻击。攻击流量不算特别巨大但足以让服务器CPU和带宽瞬间飙升导致正常用户访问缓慢甚至超时。这让我意识到对于任何暴露在公网的服务DDoS防御不是“要不要做”的问题而是“必须做”且“要提前做”的基础工作。很多开发者包括之前的我总觉得DDoS离自己很远或者认为这是运维和网络层的事情。但实际上从应用层Java代码层面开始我们就有很多简单有效的手段可以快速部署为服务构建第一道防线为后续更专业的网络层防御争取宝贵的时间。这次攻击事件让我系统地梳理和验证了几种在Java项目中快速防范DDoS攻击的常用方法。这些方法的核心思路不是去硬扛巨大的流量那是高防IP、云厂商清洗中心的工作而是通过应用逻辑快速识别并“劝退”那些非正常的、恶意的请求将宝贵的服务器资源留给真正的用户。整个过程可以概括为“发现-分析-解决”的闭环。今天我就先分享这个闭环的第一部分重点聊聊几种在代码层面就能快速实现的、成本低廉的防护策略以及如何发现攻击的蛛丝马迹。2. 核心思路应用层DDoS防御的“快准稳”在深入具体方法前我们必须明确应用层防御的定位和目标。DDoS分布式拒绝服务攻击的本质是利用海量傀儡机肉鸡发送大量请求耗尽目标服务器的资源如连接数、带宽、CPU、内存。网络层的防御如流量清洗、黑洞路由固然强大但通常有延迟且成本较高。我们Java应用层能做的是在恶意流量到达业务核心逻辑如数据库查询、复杂计算之前就将其拦截。我们的目标不是“防住所有”而是“快速识别异常保护核心业务为运维响应争取时间”。基于这个目标我总结了几个核心原则快速识别识别逻辑必须轻量级不能为了防御而引入新的性能瓶颈。通常基于请求频率、IP、行为模式等简单维度。精准拦截尽量避免误伤正常用户。这需要结合业务特点设计规则例如对登录接口和公开API的防护策略应有所不同。稳定可靠防御组件本身要健壮不能成为单点故障。例如用于存储计数器的组件必须高性能、高可用。可观测必须提供清晰的日志和指标让我们能实时看到攻击态势和防御效果这是“分析”环节的基础。基于这些原则下面几种方法是我在实践中验证过能够快速集成到现有Spring Boot等Java Web项目中的。2.1 方法一基于IP地址的频率限制限流这是最直观、最常用的一招。思路很简单在单位时间内同一个IP地址对某个接口的请求次数不能超过一个阈值。为什么有效大多数DDoS攻击流量来源于海量不同的IP但每个IP在短时间内会发起远超正常频率的请求。通过限制单个IP的频率可以大幅削弱每个攻击源的影响。虽然攻击者可以更换IP或使用更大的IP池但这增加了其成本和复杂度对于中小规模的攻击或“脉冲式”攻击非常有效。如何快速实现我们不需要重复造轮子利用现有的限流库是最佳选择。这里我强烈推荐Resilience4j或Bucket4j。它们功能强大但今天我们只聚焦其最核心的限流功能与Spring Boot的快速集成。以Spring Boot项目为例假设我们要对/api/public/data这个接口进行IP限流。第一步添加依赖dependency groupIdio.github.resilience4j/groupId artifactIdresilience4j-spring-boot2/artifactId version2.1.0/version !-- 请使用最新稳定版 -- /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-aop/artifactId /dependency第二步配置限流规则在application.yml中配置resilience4j.ratelimiter: instances: ipRateLimiter: limit-for-period: 50 # 时间窗口内允许的请求数 limit-refresh-period: 1s # 时间窗口长度 timeout-duration: 0 # 获取许可的等待时间0表示立即失败 allow-health-indicator-to-fail: true register-health-indicator: true这个配置定义了一个名为ipRateLimiter的限流器规则是每秒内最多允许50次请求。第三步创建自定义注解和切面这是实现IP维度的关键。我们需要自定义一个注解并在切面中根据请求IP来应用限流器。// 1. 自定义注解 Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface IpRateLimit { String value() default ipRateLimiter; // 对应配置中的实例名 }// 2. 切面实现 Aspect Component Slf4j public class IpRateLimitAspect { Autowired private HttpServletRequest request; // 为每个IP创建独立的限流器缓存 private final MapString, RateLimiter rateLimiterMap new ConcurrentHashMap(); Around(annotation(ipRateLimit)) public Object around(ProceedingJoinPoint joinPoint, IpRateLimit ipRateLimit) throws Throwable { String ip getClientIp(request); // 获取客户端IP的方法 String rateLimiterName ipRateLimit.value() _ ip; RateLimiter rateLimiter rateLimiterMap.computeIfAbsent(rateLimiterName, key - { // 从基础配置创建新的限流器实例 RateLimiterConfig config RateLimiterConfig.custom() .limitForPeriod(50) .limitRefreshPeriod(Duration.ofSeconds(1)) .timeoutDuration(Duration.ofMillis(0)) .build(); return RateLimiter.of(rateLimiterName, config); }); // 尝试获取许可 boolean acquired rateLimiter.acquirePermission(); if (!acquired) { log.warn(IP限流触发: ip{}, uri{}, ip, request.getRequestURI()); throw new RuntimeException(请求过于频繁请稍后再试); // 或返回自定义错误响应 } return joinPoint.proceed(); } private String getClientIp(HttpServletRequest request) { // 处理经过代理的IP获取真实IP String ip request.getHeader(X-Forwarded-For); if (ip null || ip.isEmpty() || unknown.equalsIgnoreCase(ip)) { ip request.getHeader(Proxy-Client-IP); } if (ip null || ip.isEmpty() || unknown.equalsIgnoreCase(ip)) { ip request.getHeader(WL-Proxy-Client-IP); } if (ip null || ip.isEmpty() || unknown.equalsIgnoreCase(ip)) { ip request.getRemoteAddr(); } // 多个IP时取第一个 if (ip ! null ip.contains(,)) { ip ip.split(,)[0].trim(); } return ip; } }第四步在Controller方法上使用注解RestController RequestMapping(/api/public) public class DataController { GetMapping(/data) IpRateLimit // 只需添加这个注解 public ResponseEntityData getPublicData() { // 你的业务逻辑 return ResponseEntity.ok(new Data()); } }注意获取真实IP是关键如果服务前方有Nginx、CDN等反向代理必须正确配置代理服务器传递X-Forwarded-For头并在代码中像上面那样优先读取。否则所有请求的IP都会是代理服务器的IP导致限流完全失效或误伤所有用户。实操心得与避坑指南阈值设定limit-for-period和limit-refresh-period需要根据具体接口的业务特性调整。对于登录、验证码发送等敏感接口可以设置得更严格如每分钟5次。对于普通的查询接口可以宽松一些。最佳实践是结合监控数据观察正常用户的访问频率分布来设定。内存管理上面的示例使用ConcurrentHashMap缓存限流器对于IP数量不多的情况没问题。但如果面对海量IP攻击可能导致内存溢出。生产环境建议将计数器存储在外部缓存中如 Redis。可以利用Redis的INCR和EXPIRE命令轻松实现分布式IP限流这样既解决了内存问题也使得防御策略在集群环境下生效。误伤与豁免需要考虑是否存在合法的代理IP池如企业出口IP或合作伙伴IP他们的请求频率也可能较高。可以为这些可信IP设置白名单在切面中优先判断绕过限流逻辑。响应方式直接抛出异常对用户不友好。更好的做法是返回一个格式良好的错误JSON并包含建议的重试时间Retry-After头。2.2 方法二基于滑动时间窗口的请求验证单纯的IP频率限制有时会被绕过比如攻击者使用庞大的IP池每个IP只发送少量请求。这时我们可以引入更复杂一点的行为验证核心是在滑动时间窗口内对异常高频率的请求进行挑战。思路解析我们不再简单地拒绝超频请求而是对其发起一个“挑战”。例如在10秒的时间窗口内来自同一IP或同一会话Session对特定路径的请求超过N次后后续的请求需要先完成一个简单的验证比如回答一个算术题“35?”或者点击一个前端生成的图形验证码。只有验证通过请求才会被放行至业务逻辑。为什么有效这利用了DDoS攻击流量通常是自动化脚本产生的特点。脚本可以很容易地发送大量HTTP请求但很难像人一样解析页面中的验证问题并提交答案。这能有效过滤掉大部分非人工的恶意流量。这种方法对“慢速攻击”Slowloris或旨在消耗应用线程资源的攻击尤其有效。快速实现方案我们可以利用Servlet Filter或Spring Interceptor在请求链的早期实现这个逻辑。这里以Interceptor为例因为它更易于集成到Spring生态中。第一步定义验证处理器Component public class ChallengeService { // 使用Redis存储挑战信息和计数 Autowired private StringRedisTemplate redisTemplate; private static final String PREFIX_COUNT challenge:count:; private static final String PREFIX_ANSWER challenge:answer:; private static final Duration WINDOW Duration.ofSeconds(10); private static final int THRESHOLD 30; // 10秒内阈值 /** * 检查是否需要挑战并返回挑战令牌如果需要的话 */ public ChallengeResult checkAndChallenge(String clientKey) { String countKey PREFIX_COUNT clientKey; Long count redisTemplate.opsForValue().increment(countKey); if (count 1) { redisTemplate.expire(countKey, WINDOW); // 设置过期时间 } if (count THRESHOLD) { // 超过阈值需要挑战 String token UUID.randomUUID().toString(); int a ThreadLocalRandom.current().nextInt(10, 99); int b ThreadLocalRandom.current().nextInt(1, 9); int answer a b; String question a b ?; // 将答案和token关联有效期短 redisTemplate.opsForValue().set(PREFIX_ANSWER token, String.valueOf(answer), Duration.ofMinutes(2)); return ChallengeResult.required(token, question); } return ChallengeResult.passed(); } /** * 验证挑战答案 */ public boolean verifyChallenge(String token, String userAnswer) { String key PREFIX_ANSWER token; String correctAnswer redisTemplate.opsForValue().getAndDelete(key); // 验证后删除 return correctAnswer ! null correctAnswer.equals(userAnswer.trim()); } Data AllArgsConstructor public static class ChallengeResult { private boolean required; private String challengeToken; private String question; public static ChallengeResult required(String token, String question) { return new ChallengeResult(true, token, question); } public static ChallengeResult passed() { return new ChallengeResult(false, null, null); } } }第二步实现拦截器Component public class DDoSChallengeInterceptor implements HandlerInterceptor { Autowired private ChallengeService challengeService; Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 1. 排除静态资源、健康检查等不需要防护的路径 String uri request.getRequestURI(); if (uri.startsWith(/css/) || uri.startsWith(/js/) || uri.equals(/health)) { return true; } // 2. 获取客户端标识优先用IP可结合User-Agent或SessionId String clientKey getClientIp(request) : request.getRequestURI(); // 3. 检查是否需要挑战 ChallengeService.ChallengeResult result challengeService.checkAndChallenge(clientKey); if (!result.isRequired()) { // 无需挑战放行 return true; } // 4. 需要挑战检查本次请求是否已携带答案 String challengeToken request.getHeader(X-Challenge-Token); String userAnswer request.getHeader(X-Challenge-Answer); if (challengeToken ! null userAnswer ! null) { // 携带了答案进行验证 if (challengeService.verifyChallenge(challengeToken, userAnswer)) { // 验证通过放行 return true; } } // 5. 未通过验证或首次触发挑战返回挑战信息 response.setStatus(429); // Too Many Requests response.setContentType(application/json;charsetUTF-8); MapString, Object resp new HashMap(); resp.put(code, 429); resp.put(message, 请求频率过高请完成验证); resp.put(data, Map.of( challengeToken, result.getChallengeToken(), question, result.getQuestion(), instruction, 请在后续请求头中携带 X-Challenge-Token 和 X-Challenge-Answer )); response.getWriter().write(new ObjectMapper().writeValueAsString(resp)); return false; } private String getClientIp(HttpServletRequest request) { // 复用上文获取IP的方法 // ... } }第三步注册拦截器并配置前端在Web配置中注册这个拦截器使其对需要防护的路径生效。Configuration public class WebConfig implements WebMvcConfigurer { Autowired private DDoSChallengeInterceptor ddosChallengeInterceptor; Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(ddosChallengeInterceptor) .addPathPatterns(/api/**) // 保护所有API接口 .excludePathPatterns(/api/public/health); // 排除健康检查 } }前端在收到429状态码和挑战信息后需要将问题展示给用户如果是浏览器或者由客户端自动计算答案仅适用于简单的算术挑战对于更复杂的验证码则需要用户交互并在重试原请求时在请求头中附加X-Challenge-Token和X-Challenge-Answer。注意事项与扩展挑战复杂度示例中的算术题对于简单脚本可能仍能被破解。生产环境建议使用更复杂的验证码如简单的图形验证码需要集成Kaptcha等库或使用无感验证方案如行为分析。核心是增加攻击脚本的模拟成本。客户端标识clientKey示例中使用了IP URI。你也可以结合User-Agent、Session ID或一个前端生成的设备指纹来更精确地标识客户端减少因NAT网关后多个用户共享IP导致的误伤。性能考量所有计数和验证操作都依赖Redis确保了分布式环境下的一致性和可扩展性。务必保证Redis的高可用和低延迟。用户体验对于API接口返回标准错误码和提示对于网页可以优雅地弹出验证框。要避免对正常用户造成过多干扰阈值应设置合理。2.3 方法三关键资源操作的延迟响应与异步化有些DDoS攻击并非追求流量洪峰而是针对消耗服务器特定资源的操作例如用户注册、短信发送、密码重置等。这些操作往往涉及数据库写入、调用第三方服务如短信网关成本较高。攻击者通过频繁调用这些接口不仅能耗尽你的数据库连接、线程池还可能产生实实在在的经济损失如短信费用。防御策略对于这类关键、高成本的写操作实施“令牌桶”或“队列”机制。令牌桶为每个用户或IP分配一个令牌桶。执行操作需要消耗令牌。令牌以固定速率生成。例如每个手机号每天只能获取3次短信验证码。这可以通过Redis轻松实现。延迟与异步化对于非即时性的操作引入延迟。例如用户提交注册信息后立即返回“提交成功请等待审核”的页面而将实际的数据库写入、邮件发送等操作放入消息队列如RabbitMQ, Kafka异步处理。这样即使有大量恶意注册请求它们也只是在队列中堆积不会瞬间压垮数据库。以“短信发送防刷”为例实现令牌桶Service public class SmsAntiBrushService { Autowired private StringRedisTemplate redisTemplate; private static final String PREFIX sms_bucket:; private static final int CAPACITY 3; // 桶容量 private static final Duration REFILL_TIME Duration.ofDays(1); // 补充周期 /** * 尝试获取发送短信的许可 * param phoneNumber 手机号 * return true 可以发送false 触发限流 */ public boolean tryAcquire(String phoneNumber) { String key PREFIX phoneNumber; // 使用Redis的Lua脚本保证原子性 String luaScript local key KEYS[1] local capacity tonumber(ARGV[1]) local refill_time tonumber(ARGV[2]) local now tonumber(ARGV[3]) local current redis.call(get, key) if current false then -- 第一次访问设置初始值并过期时间 redis.call(setex, key, refill_time, capacity - 1) return 1 end local tokens tonumber(current) if tokens 0 then redis.call(decr, key) return 1 else return 0 end ; DefaultRedisScriptLong script new DefaultRedisScript(); script.setScriptText(luaScript); script.setResultType(Long.class); ListString keys Collections.singletonList(key); // 这里简化处理将过期时间秒作为参数传入 Long result redisTemplate.execute(script, keys, String.valueOf(CAPACITY), String.valueOf(REFILL_TIME.getSeconds()), String.valueOf(System.currentTimeMillis()/1000)); return result ! null result 1L; } }在发送短信的Controller中先调用tryAcquire(phone)进行检查如果返回false则直接返回“发送过于频繁”的错误。异步化处理注册请求示例RestController RequestMapping(/api/user) public class UserController { Autowired private RabbitTemplate rabbitTemplate; PostMapping(/register) public ResponseEntity? register(RequestBody UserRegisterDTO dto) { // 1. 极速的初步验证如格式校验 // ... // 2. 将注册任务放入消息队列立即返回成功响应 rabbitTemplate.convertAndSend(user.register.queue, dto); // 3. 返回异步处理响应 return ResponseEntity.accepted().body(Map.of( message, 注册请求已接收正在处理中, trackingId, UUID.randomUUID().toString() )); } } // 消费者服务异步处理注册 Component Slf4j public class UserRegistrationConsumer { Autowired private UserService userService; RabbitListener(queues user.register.queue) public void processRegistration(UserRegisterDTO dto) { try { // 这里执行耗时的操作写入数据库、发送欢迎邮件等 userService.createUser(dto); log.info(用户注册成功: {}, dto.getUsername()); } catch (Exception e) { log.error(用户注册处理失败: {}, dto.getUsername(), e); // 可以进入死信队列进行进一步处理或告警 } } }这种方法的优势保护核心资源数据库和第三方服务得到了缓冲不会被突发流量直接击垮。提升用户体验用户请求得到快速响应202 Accepted感知不到延迟。增强系统弹性消息队列起到了削峰填谷的作用系统处理能力变得平滑。重要提示异步化后需要考虑如何向用户反馈最终结果。对于注册可能需要在处理成功后发送一封激活邮件或短信。同时需要监控消息队列的积压情况积压过多意味着处理能力不足或遭受攻击。3. 攻击发现与分析建立你的监控雷达“防范”的前提是“发现”。再好的防御代码如果不知道攻击何时发生、规模多大也是盲人摸象。因此建立一套简单的应用层监控体系至关重要。这不需要复杂的APM工具利用Spring Boot Actuator、Micrometer和日志就能搭建。3.1 监控关键指标在application.yml中启用并配置Actuator暴露监控端点注意做好安全限制management: endpoints: web: exposure: include: health,info,metrics,prometheus # 暴露给监控系统 metrics: export: prometheus: enabled: true tags: application: ${spring.application.name}重点关注以下指标http.server.requests请求计数和耗时按URI、方法、状态码分类。突然出现某个特定URI的请求量暴增是DDoS的典型信号。system.cpu.usage系统CPU使用率。jvm.memory.usedJVM内存使用量。tomcat.threads.busyTomcat繁忙线程数。线程数长时间处于高位可能意味着有慢请求或资源耗尽型攻击。3.2 日志分析与告警在拦截器或过滤器中详细记录被拒绝的请求log.warn(DDoS防御拦截 - IP: {}, URI: {}, 原因: {}, User-Agent: {}, clientIp, requestUri, IP限流触发, request.getHeader(User-Agent));将这些WARN/ERROR级别的日志收集到ELKElasticsearch, Logstash, Kibana或类似系统中。可以设置告警规则例如频率告警单位时间内“DDoS防御拦截”的日志条数超过阈值。来源集中告警拦截日志中来自某个IP段或ASN自治系统号的请求占比突然飙升。错误率告警全局或特定接口的4xx/5xx错误率突然升高。3.3 简易实时看板利用Grafana连接Prometheus收集Actuator指标可以快速搭建一个监控看板实时展示总QPS趋势图Top 10 请求路径的QPS按状态码分布的请求数系统CPU、内存使用率Tomcat线程池活跃数当发现图表出现以下异常模式时应立刻警惕QPS曲线呈“垂直增长”很可能是流量型攻击。某个不起眼的API端点请求量异常突出可能是针对性的应用层攻击。CPU/内存使用率与QPS增长不匹配但线程数很高可能是慢速攻击或资源消耗型攻击。4. 方案选型与组合策略上面介绍了三种方法在实际项目中我们很少只使用一种而是根据业务场景进行组合。场景一公开API服务策略方法一IP限流方法二滑动窗口挑战。部署对所有/api/**路径先应用IP限流第一层过滤对于高频IP再触发挑战机制第二层过滤。限流阈值可以设置得相对宽松挑战阈值设置得严格一些。场景二用户中心含登录、注册、短信策略方法一IP限流方法三令牌桶/异步化。部署对/login,/register,/sms/send等关键路径实施严格的IP限流和用户维度令牌桶。注册流程采用异步化避免数据库被刷爆。场景三管理后台或内部接口策略除了上述方法最重要的是网络层隔离如通过VPN、白名单IP访问。在应用层可以增加二次认证如动态令牌或基于地理位置的拦截非本国IP直接拒绝。一个组合配置的示例思路你可以创建一个全局的DDoSDefenseFilter它内部按顺序执行检查链IP黑名单检查查询内存或Redis中的实时黑名单由监控系统分析后动态添加命中则直接拒绝。全局IP频率检查基于Redis的计数器限制单个IP对全站的总体请求频率如每秒100次防止攻击者“打一枪换一个地方”地扫描不同接口。路径特异性检查根据请求的URI匹配不同的规则。如果是/api/sms/**应用严格的令牌桶方法三。如果是/api/public/**应用滑动窗口挑战方法二。如果是其他API应用标准IP限流方法一。通过所有检查请求才被放行至真正的业务逻辑。5. 常见问题与排查技巧实录在实际部署和运维这些防护措施时我踩过不少坑也总结了一些排查技巧。问题1限流后正常用户也访问缓慢甚至被误拦。排查首先检查获取客户端IP的逻辑是否正确。这是最高频的错误来源在测试环境用不同方式发起请求直接访问、通过Nginx代理打印并核对日志中的IP。检查限流阈值是否设置过低。通过分析历史访问日志计算正常用户特别是爬虫、集成客户端在高峰期的请求频率。检查是否存在共享IP的场景如公司、学校、大型公共场所的出口IP。这些IP下的所有用户会被视为同一个“客户端”。解决修正IP获取逻辑。针对API接口和网页接口设置不同的阈值。网页请求可能包含大量静态资源请求频率自然高。建立IP白名单机制将已知的、可信的IP段如公司办公网IP、合作伙伴IP加入白名单绕过限流。问题2Redis成为性能瓶颈或单点故障。排查监控Redis的CPU、内存、连接数指标。在防御策略生效期间观察Redis的响应延迟是否显著增加。解决使用Redis集群确保高可用和水平扩展能力。使用本地缓存作为第一级对于IP限流可以先使用Guava Cache或Caffeine在应用本地维护一个短时间如1秒的计数器超限后再去Redis进行全局校验。这能减少大量对Redis的访问。但要注意在集群环境下本地缓存会导致限流精度下降适用于对精度要求不极高的场景。优化Redis命令使用INCR和EXPIRE的Pipeline或者使用上文提到的Lua脚本减少网络往返。问题3攻击者变换User-Agent或使用海量IP池使基于IP和简单特征的规则失效。排查观察攻击流量的模式。如果拦截日志中的IP分布非常分散且没有明显规律可能是高水平的攻击。解决升级挑战难度将算术题升级为需要图像识别或行为验证的验证码如Geetest、腾讯云验证码。这能极大提高自动化攻击的成本。引入更复杂的指纹结合IP、User-Agent、TCP/IP协议栈特征如TTL、窗口大小、TLS指纹等生成一个更稳定的客户端指纹作为限流和挑战的标识。但这需要更专业的库或中间件支持。联动网络层当应用层检测到攻击模式如大量不同IP请求同一资源通过API调用云服务商或安全设备的接口将这些IP动态添加到网络层的黑名单或限速策略中。这是从应用层防御到网络层防御的升级。问题4防御代码本身引入Bug导致服务不可用。排查任何防御代码上线前必须经过严格测试包括单元测试、集成测试和压力测试。特别要测试“防御功能被触发时”和“Redis不可用时”系统的行为。解决设置熔断和降级在防御组件如调用Redis上设置熔断器Resilience4j也提供。当Redis连续失败多次熔断器打开暂时绕过防御逻辑保证核心业务可用同时发出严重告警。提供快速开关为每个防御功能如IP限流、挑战配置一个动态开关如存储在数据库或配置中心。在出现问题时可以快速关闭某个防御功能。详尽的日志防御组件的日志级别设为DEBUG或INFO记录每一个决策如“IP xxx 通过/拒绝当前计数yyy”便于事后复盘和问题排查。防御DDoS是一个持续对抗的过程。今天分享的这些Java应用层方法是成本最低、生效最快的“贴身防护”。它们无法抵御超大流量的网络层攻击但足以应对日常大多数中小规模的、针对应用漏洞的恶意流量。更重要的是它们为你识别攻击、分析模式、争取响应时间提供了至关重要的“战场情报”。在后续的分享中我会再深入探讨如何基于这些“情报”与云防火墙、高防IP等网络层防护进行联动构建更深度的防御体系。