高阶项目中自定义拒绝策略需显性化意图、可追溯行为、分层逻辑:命名携带场景/动作/兜底等级三重语义;构造函数显式注入上下文;拒绝动作分记录/响应/补偿三层;默认接入多维指标与链路追踪;采用责任链支持动态组合。

高阶项目里,自定义拒绝策略不是写个rejectedExecution方法就完事——它得让新同学一眼看懂“任务被拒时系统在做什么”,让监控能精准归因,让故障复盘有据可查。核心不在于多炫技,而在于把意图显性化、行为可追溯、逻辑可分层。
命名即契约:用类名和参数直说业务语义
别叫MyRejectHandler或CustomPolicy。名称要携带三重信息:场景(如订单/日志)、动作(如降级/落库/告警)、兜底等级(如一级/二级)。例如:
-
OrderSubmitFallbackToDbPolicy—— 订单提交被拒时持久化到DB再异步重试 -
AnalyticsDiscardWithMetricsPolicy—— 埋点类任务被拒时静默丢弃并上报丢弃量与原因标签 -
NotifyServiceRetryOnRejectPolicy—— 通知服务被拒时走独立重试通道,不挤占主链路资源
构造函数中显式接收关键上下文,比如String bizScene、MetricRegistry metrics、TaskPersistenceService persistence,避免从Spring容器硬取或静态单例调用,提升单元测试友好度与职责清晰度。
拒绝动作分层:分离“记录”“响应”“补偿”三件事
一个拒绝策略内部应明确切分为三个阶段,每阶段职责单一、日志可定位、异常可捕获:
-
记录层:统一打结构化日志,含
taskType、executorName、queueSize、poolActiveCount、rejectTimestamp。不拼字符串,用StructuredArgument或Map.of()输出JSON片段 - 响应层:决定当前调用线程是否阻塞、是否抛异常、是否返回fallback值。例如对RPC接口,这里可触发降级返回默认值;对内部定时任务,可直接跳过不报错
-
补偿层:仅当业务允许且有配套能力时启用,如写MQ、存DB、发告警。必须带重试幂等键(如
rejectId = bizId + timestamp + hash(task)),并配置最大重试次数与TTL
可观测先行:拒绝即指标,拒绝即链路
每个拒绝策略实例启动时,自动注册以下基础指标(接入Micrometer或Cat):
-
threadpool.reject.count{scene, policy, reason}—— 按场景、策略类型、拒绝原因(如QUEUE_FULL、SHUTDOWN、REJECT_OVERFLOW)多维打点 -
threadpool.reject.latency{scene}—— 记录从触发拒绝到完成记录/补偿的耗时分布 -
threadpool.reject.task.size{scene}—— 若任务实现Serializable或含sizeHint(),采样记录体积,识别大任务冲击
同时,在拒绝日志中注入当前TraceID(若使用SkyWalking或Sleuth),确保能在APM中反查:“这个拒绝是哪个用户请求引发的?上游调用链是否已超时?”
策略组合可插拔:用责任链替代if-else嵌套
面对多级兜底需求(如先尝试内存队列缓冲,失败再落库,再失败才告警),不要写层层if判断。采用轻量级责任链:
- 定义
RejectedExecutionHandlerChain,持有一个List<rejectedexecutionhandler></rejectedexecutionhandler> - 每个handler只专注一件事:成功处理则return,失败则抛出
TryNextHandlerException或返回CONTINUE - 链式执行中自动统计各环节成功率、耗时,并暴露为指标
reject.handler.attempt{index, name}
这样既保持单策略高内聚,又支持运行时动态增删环节(如灰度开启DB补偿),代码像配置一样清晰。
不复杂但容易忽略:可读性不是靠注释堆出来的,而是靠命名约束、分层边界和指标埋点共同塑造的。拒绝策略写得清楚,线上抖动时你就能少花70%时间在“这到底拒了啥”。











