短信接口被狂刷怎么处理

短信接口被狂刷怎么处理

"紧急止损→多层防护→业务优化→监控复盘"

第一阶段:紧急止损🔥(黄金 30 分钟)

先把问题压下来,避免损失扩大,这是优先级最高的操作:

  1. 立即降级:通过配置中心将短信接口切换为 "测试模式",返回成功但实际不发送短信;或直接熔断接口
  2. IP 黑名单:在 Nginx 层快速封禁异常请求 IP 段,拦截 90% 以上的恶意流量
  3. 限流兜底:在网关层开启全局限流(如每秒 100 次),防止服务被打垮
  4. 临时关停:如果以上措施无效,直接下线短信发送功能,优先保障核心业务可用

第二阶段:多层防护体系🛡️(全链路拦截)

建立从前端到后端、从网关到业务的立体防护网,层层过滤恶意请求:

各层具体实现要点

防护层级 核心技术 拦截效果 实现难度
前端层 图形验证码、短信倒计时、按钮禁用 拦截 80% 普通刷量 ★☆☆☆☆
Nginx 层 IP 限流、Referer 校验、User-Agent 过滤 拦截 15% 爬虫流量 ★★☆☆☆
网关层 令牌桶限流、API 签名、IP 黑白名单 拦截 4% 恶意攻击 ★★★☆☆
业务层 手机号频次限制、设备指纹、行为风控 拦截 0.9% 高级攻击 ★★★★☆
第三方层 平台自带风控、日发送量上限 最后一道防线 ★☆☆☆☆

第三阶段:业务侧优化🎯(从根源解决)

技术防护只能治标,业务逻辑优化才能治本:

  1. 频次限制:同一手机号 60 秒内只能发 1 次,24 小时内最多 5 次,1 个月内最多 20 次
  2. 场景隔离:注册、登录、找回密码等不同场景分别限流,避免一个场景被刷影响全局
  3. 验证升级:对异常 IP / 手机号强制要求滑块验证码或语音验证码
  4. 成本分摊:对高风险操作(如批量发送)引入付费机制,提高攻击者成本
  5. 号码校验:对接运营商号码库,过滤虚拟号、物联网卡和风险号码

第四阶段:监控与事后复盘📊

  1. 实时监控:搭建短信发送量、成功率、失败率、异常 IP 请求数等指标的大盘,设置阈值告警
  2. 日志审计:记录所有短信发送请求的 IP、手机号、设备信息、时间戳,便于事后溯源
  3. 攻击分析:定期分析攻击特征,更新风控规则,形成闭环
  4. 压力测试:模拟恶意刷量场景,验证防护体系的有效性

核心技术细节(加分项✨)

  • 限流算法 :推荐使用令牌桶算法(支持突发流量),而非漏桶算法;分布式环境下用 Redis+Lua 脚本实现原子性限流
  • 缓存设计 :手机号频次限制用 Redis 的INCR+EXPIRE命令,过期时间自动重置
  • 防刷技巧:使用 Redis 的HyperLogLog统计每日不同手机号数量,节省内存;用布隆过滤器快速过滤无效手机号
  • 灰度发布:新的风控规则先在小流量范围验证,再全量上线

面试官可能追问的问题及回答思路

Q:分布式环境下如何保证限流的准确性?

A:使用 Redis+Lua 脚本实现原子性操作,避免并发问题;同时采用 "最终一致性" 思想,允许极小误差

Q:攻击者使用代理 IP 池怎么办?

A:结合设备指纹、手机号、行为特征进行多维度风控,而不仅仅依赖 IP

Q:Redis 挂了怎么办?

A:采用 Redis 集群保证高可用;同时在本地内存中保留一份降级限流规则,作为兜底

核心代码实现(带技术亮点标注✨)

1. Redis+Lua 分布式令牌桶限流(最核心!面试必问)

技术亮点:Lua 脚本保证原子性,避免并发计数偏差;支持突发流量,性能是分布式锁的 10 倍以上

lua 复制代码
-- 令牌桶限流Lua脚本(KEYS[1]=限流key, ARGV[1]=桶容量, ARGV[2]=令牌生成速率/秒, ARGV[3]=当前时间戳)
local key = KEYS[1]
local capacity = tonumber(ARGV[1])
local rate = tonumber(ARGV[2])
local now = tonumber(ARGV[3])

-- 获取桶内剩余令牌和上次刷新时间
local bucket = redis.call('HMGET', key, 'tokens', 'last_refresh')
local tokens = tonumber(bucket[1]) or capacity
local last_refresh = tonumber(bucket[2]) or now

-- 计算生成的新令牌数
local delta = math.max(0, now - last_refresh) * rate
tokens = math.min(capacity, tokens + delta)

-- 尝试获取令牌
if tokens >= 1 then
    redis.call('HMSET', key, 'tokens', tokens - 1, 'last_refresh', now)
    redis.call('EXPIRE', key, 3600) -- 1小时过期
    return 1 -- 获取成功
else
    return 0 -- 获取失败
end
java 复制代码
// Java调用端
@Service
public class RateLimitService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    private static final String LUA_SCRIPT = "上面的Lua脚本内容";
    
    /**
     * 令牌桶限流
     * @param key 限流维度(ip:xxx / phone:xxx / device:xxx)
     * @param capacity 桶容量
     * @param rate 每秒生成令牌数
     * @return 是否通过限流
     */
    public boolean tryAcquire(String key, int capacity, int rate) {
        long now = System.currentTimeMillis() / 1000;
        Long result = redisTemplate.execute(
            new DefaultRedisScript<>(LUA_SCRIPT, Long.class),
            Collections.singletonList(key),
            String.valueOf(capacity),
            String.valueOf(rate),
            String.valueOf(now)
        );
        return result != null && result == 1;
    }
}

2. 手机号频次限制(Redis 原子操作)

技术亮点:INCR+EXPIRE 组合实现滑动窗口,避免多线程并发问题;不同场景隔离限流

java 复制代码
@Service
public class SmsFrequencyService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    
    // 不同场景限流配置(可配置化)
    private static final Map<String, int[]> SCENE_LIMIT = Map.of(
        "register", new int[]{1, 60, 5, 86400}, // 注册:60s1次,24h5次
        "login", new int[]{1, 60, 10, 86400},   // 登录:60s1次,24h10次
        "reset", new int[]{1, 60, 3, 86400}     // 找回密码:60s1次,24h3次
    );
    
    public boolean checkFrequency(String phone, String scene) {
        int[] limit = SCENE_LIMIT.get(scene);
        if (limit == null) return false;
        
        String minuteKey = "sms:freq:" + scene + ":minute:" + phone;
        String dayKey = "sms:freq:" + scene + ":day:" + phone;
        
        // 1分钟内次数检查
        Long minuteCount = redisTemplate.opsForValue().increment(minuteKey);
        if (minuteCount == 1) redisTemplate.expire(minuteKey, limit[1], TimeUnit.SECONDS);
        if (minuteCount > limit[0]) return false;
        
        // 24小时内次数检查
        Long dayCount = redisTemplate.opsForValue().increment(dayKey);
        if (dayCount == 1) redisTemplate.expire(dayKey, limit[3], TimeUnit.SECONDS);
        return dayCount <= limit[2];
    }
}

3. 布隆过滤器快速过滤无效手机号

技术亮点:空间效率极高,1000 万条数据仅需 12MB 内存;O (1) 时间复杂度查询

java 复制代码
// 基于Guava布隆过滤器
@Component
public class PhoneBlacklistFilter {
    private BloomFilter<String> bloomFilter;
    
    @PostConstruct
    public void init() {
        // 预期插入1000万条数据,误判率0.01%
        bloomFilter = BloomFilter.create(Funnels.stringFunnel(StandardCharsets.UTF_8), 10_000_000, 0.0001);
        
        // 启动时加载历史黑名单手机号
        List<String> blacklist = phoneBlacklistMapper.selectAll();
        blacklist.forEach(bloomFilter::put);
    }
    
    public boolean mightContain(String phone) {
        return bloomFilter.mightContain(phone);
    }
    
    // 新增黑名单时同步更新布隆过滤器
    public void addBlacklist(String phone) {
        bloomFilter.put(phone);
        phoneBlacklistMapper.insert(phone);
    }
}

4. API 签名验证(防止接口被非法调用)

技术亮点:时间戳 + 随机数防重放,签名算法防篡改,客户端密钥动态更新

java 复制代码
@Service
public class ApiSignService {
    // 客户端密钥(存储在配置中心,支持动态更新)
    @Value("${sms.api.secret}")
    private String apiSecret;
    
    public boolean verifySign(Map<String, String> params) {
        // 1. 检查时间戳是否在5分钟内
        long timestamp = Long.parseLong(params.get("timestamp"));
        if (System.currentTimeMillis() - timestamp > 5 * 60 * 1000) return false;
        
        // 2. 检查随机数是否已使用(防重放)
        String nonce = params.get("nonce");
        if (redisTemplate.hasKey("sms:nonce:" + nonce)) return false;
        redisTemplate.opsForValue().set("sms:nonce:" + nonce, "1", 5, TimeUnit.MINUTES);
        
        // 3. 生成签名并比对
        String clientSign = params.remove("sign");
        String serverSign = generateSign(params, apiSecret);
        return clientSign.equals(serverSign);
    }
    
    private String generateSign(Map<String, String> params, String secret) {
        // 参数按字典序排序
        List<String> keys = new ArrayList<>(params.keySet());
        Collections.sort(keys);
        
        // 拼接参数和密钥
        StringBuilder sb = new StringBuilder();
        for (String key : keys) {
            sb.append(key).append("=").append(params.get(key)).append("&");
        }
        sb.append("secret=").append(secret);
        
        // MD5加密并转大写
        return DigestUtils.md5DigestAsHex(sb.toString().getBytes()).toUpperCase();
    }
}

技术难点与解决方案(面试加分项🔥)

技术难点 核心问题 解决方案 实现要点
分布式限流计数不准确 多节点同时请求导致计数超发 Redis+Lua 原子操作 所有判断和更新在一个 Lua 脚本中执行,避免竞态条件
代理 IP 池绕过 IP 限流 攻击者使用百万级代理 IP 轮换 多维度联合风控 IP + 手机号 + 设备指纹 + 行为特征(如点击间隔、滑动轨迹)
Redis 宕机导致防刷失效 单点故障引发整个防护体系崩溃 集群 + 本地限流兜底 1. Redis 主从 + 哨兵集群2. 本地内存保留降级限流规则(如单机每秒 10 次)
验证码被打码平台破解 图形验证码识别成本降至 0.01 元 / 条 动态验证码升级 1. 异常用户强制滑块验证码2. 高风险场景使用语音验证码3. 验证码图片添加干扰线和噪点
高并发下 Redis 性能瓶颈 每秒 10 万 + 请求导致 Redis CPU 打满 多级缓存 + 分片 1. 本地缓存热点数据(如 5 分钟内的限流结果)2. Redis 按业务分片部署3. 使用 Redis Pipeline 批量操作
误杀正常用户 风控规则过严影响用户体验 灰度发布 + 白名单 1. 新规则先 10% 流量灰度验证2. 白名单机制放行内部测试和老用户3. 误判申诉通道快速恢复
虚拟号 / 物联网卡批量注册 攻击者使用低成本虚拟号码 运营商号码校验 1. 对接三大运营商号码库2. 过滤 170/171/162 等虚拟号段3. 高风险号码要求实名认证
短信接口被 CC 攻击 每秒数十万请求导致服务雪崩 全链路限流 + 熔断 1. Nginx 层 IP 限流(每秒 10 次)2. 网关层全局限流(每秒 1000 次)3. Sentinel 熔断降级
短信发送链路超时 第三方平台响应慢导致线程阻塞 异步发送 + 超时控制 1. 使用线程池异步发送短信2. 设置第三方接口超时时间(5 秒)3. 失败重试机制(最多 2 次)
攻击溯源困难 无法定位攻击者真实身份 全链路日志审计 1. 记录所有请求的 IP、UA、设备指纹、时间戳2. 日志存储在 ELK 集群,支持快速检索3. 异常请求自动生成攻击报告

面试终极加分回答💯

面试官如果问:你觉得这个方案还有什么可以优化的地方?

我觉得可以从三个方向进一步优化:

  1. 引入 AI 风控:通过机器学习模型分析用户行为特征,自动识别异常请求,比规则风控更精准
  2. 短信通道降级:当主通道被攻击时,自动切换到备用通道,保证业务连续性
  3. 成本优化:对不同风险等级的用户采用不同的验证方式,高风险用户用短信,低风险用户用验证码,降低短信成本

真实面试模拟

真实面试模拟

面试官 😊:

来,咱们聊个很接地气的线上问题------短信接口被狂刷,你会怎么处理? 你可以先整体说说思路。

候选人 🧑‍💻:

好的面试官。这个问题本质是 "用最小成本把正常用户和脚本/黑产区分开,保护短信资费和接口可用性"。

我不会只靠一个点去防,而是按请求的流转路径,从外到内搭一套立体防御。我用张图先给您看下架构:

总共五层:客户端人机识别 → 网关 IP 限流 → 业务多维风控 → 通道熔断兜底 → 事后日志反哺黑名单

面试官 👍:

图很清晰,那咱们一层层拆。先说第一层,前端怎么做?

候选人 🧑‍💻:

第一层必须上行为式验证码,比如滑块、点选,不能用传统的字符验证码,那个早就被打穿了。

前端拿一个 ticket,请求发短信时带上,后端再调第三方服务(极验、网易易盾等)做二次校验。

这一层 ROI 最高,能把绝大部分脚本直接挡掉,后端压力小很多。

面试官 🤔:

嗯,那假如黑产绕过了验证码,比如用打码平台,接下来怎么防?

候选人 🧑‍💻:

那就到了网关层 。这里我针对 IP 维度做高频限流。

Redis + Lua 实现原子化的滑动窗口或令牌桶,比如单个 IP 每分钟最多请求 5 次短信接口。伪代码逻辑是这样的:

lua 复制代码
local key = "sms_limit:ip:" .. ip
local current = redis.call("INCR", key)
if current == 1 then
    redis.call("EXPIRE", key, 60)  -- 60秒窗口
end
if current > 5 then
    return 0  -- 拒绝
end
return 1

如果有 API 网关(Kong/APISIX),直接配插件就行,更低成本。

面试官 💡:

这个思路没问题,但如果对方用大量代理 IP 池来刷,IP 限制不就失效了?

候选人 🧑‍💻:

没错,这时候 IP 限制效果会打折,所以必须上升到业务层多维风控,不只盯 IP。

我一般会拉上这几个维度一起打组合拳:

维度 规则举例 目的
手机号 同手机号 60s 内只能发 1 次,24h 最多 5 次 防单号爆破
用户ID 单用户每日获取上限 10 次 防登录态被盗用刷量
设备指纹 同设备短时间内换不同手机号请求 → 触发风控 识别脚本换号
IP+手机号关联 1 个 IP 在窗口期内请求超过 3 个不同手机号 → 拉黑 IP 猫池设备特征

设备指纹很难批量伪造,加上手机号频控,攻击成本就上去了。风控可以用规则引擎动态调整,不用每次改代码。

面试官 👏:

不错,这就能对付 IP 池了。那假如前端、网关、业务层全被打穿(虽然概率极低),你最底线的保护是什么?

候选人 🧑‍💻:

最后一道防线是短信通道自身的熔断和费用兜底

  • 我会对单分钟发送量设阈值,超了就熔断,直接降级返回"发送失败,请稍后重试",不再调供应商接口。
  • 同时做日费用监控,比如预算 500 元,用掉 80% 钉钉预警,100% 强制关通道。
  • 另外通道隔离很重要,登录、支付等核心业务和营销短信用不同通道,营销被打爆也不影响核心功能。💰

面试官 🧐:

除了实时防御,事后还有什么手段?

候选人 🧑‍💻:

我会把所有短信请求的完整上下文------IP、设备指纹、用户ID、手机号、验证码结果等,全量落日志到 ELK 或 ClickHouse。

离线分析出攻击特征后,反向输出黑名单(IP 段、设备指纹),回灌到线上限流和风控系统里,让整个防御体系"越打越聪明"。🔄

面试官 😄:

可以,整个链路都考虑到了,而且层与层之间有互补。那你觉得在整个方案里,性价比最高的一层是哪里

候选人 🧑‍💻:

绝对是行为式验证码。成本低,接入快,能直接杀掉 80% 以上的自动化脚本,给后面几层降压。

但我不会只靠它,因为一旦被黑产针对性突破,后面必须有纵深防御兜底。

面试官 👍✨:

很好,这个问题就聊到这儿,思路很扎实,场景感也不错。通过!

候选人内心 OS:😌🛡️ 果然,立体防御 yyds。

面试官 🤓:

思路很清晰,那咱们深入一点。你刚才提到业务层的多维风控和通道熔断,能不能挑一两个技术亮点,用代码表达一下你的设计? 不用全写,关键部分就行。

候选人 🧑‍💻:

没问题。我写两个最核心的:一是基于注解的分布式多维限流 ,二是滑动窗口熔断器。这两块代码是直接能扛流量的。

💎 亮点一:声明式多维限流(Redis + Lua + 自定义注解)

我一般会封装一个 @RateLimit 注解,可以灵活指定限流的维度(手机号、IP、用户ID)和阈值,底层用 Redis + Lua 保证原子性。

1. 自定义注解

java 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    String key();              // 支持 SpEL,如 "#mobile" "#ip"
    int limit();               // 窗口内最大请求数
    int window();              // 窗口大小,秒
    String message() default "请求过于频繁,请稍后再试";
}

2. AOP 切面 + Lua 原子限流

java 复制代码
@Aspect
@Component
public class RateLimitAspect {

    @Autowired
    private StringRedisTemplate redisTemplate;

    // Lua 脚本:原子性检查并递增
    private static final String LUA_SCRIPT = 
        "local key = KEYS[1] " +
        "local limit = tonumber(ARGV[1]) " +
        "local window = tonumber(ARGV[2]) " +
        "local current = redis.call('INCR', key) " +
        "if current == 1 then " +
        "    redis.call('EXPIRE', key, window) " +
        "end " +
        "if current > limit then " +
        "    return 0 " +  // 超频
        "else " +
        "    return 1 " +  // 放行
        "end";

    @Around("@annotation(rateLimit)")
    public Object around(ProceedingJoinPoint pjp, RateLimit rateLimit) throws Throwable {
        // 解析 SpEL,拿到动态 key
        String dynamicKey = parseSpel(rateLimit.key(), pjp);
        String redisKey = "rate_limit:" + rateLimit.key() + ":" + dynamicKey;

        Long result = redisTemplate.execute(
            new DefaultRedisScript<>(LUA_SCRIPT, Long.class),
            Collections.singletonList(redisKey),
            String.valueOf(rateLimit.limit()),
            String.valueOf(rateLimit.window())
        );

        if (result == 0) {
            throw new BusinessException(rateLimit.message()); // 统一异常处理
        }
        return pjp.proceed();
    }

    // SpEL 解析略,Spring 自带工具可做
}

3. 在短信接口上使用

java 复制代码
@RestController
public class SmsController {

    // 手机号维度:60秒内1次
    @RateLimit(key = "#mobile", limit = 1, window = 60, message = "验证码已发送,请稍后再试")
    // IP 维度:60秒内5次(可以和上面组合,或另外配置网关限流)
    @RateLimit(key = "#request.remoteAddr", limit = 5, window = 60, message = "请求频繁")
    @PostMapping("/send-sms")
    public Result sendSms(@RequestParam String mobile, HttpServletRequest request) {
        // ... 发送逻辑
    }
}

亮点:原子性由 Lua 保证,不会出现并发超发;声明式注解让业务代码零侵入,不同维度可以随意叠加;Redis 瓶颈极低,单机轻松扛万级 QPS。💪

💎 亮点二:短信通道滑动窗口熔断器

当调用短信供应商之前,需要实时判断当前通道是否过载。我用一个线程安全的滑动窗口计数器实现轻量熔断,不依赖外部组件。

java 复制代码
public class SlidingWindowCircuitBreaker {
    // 窗口大小(秒)
    private final int windowSize;
    // 窗口内最大允许请求数
    private final int maxRequests;
    // 每个时间槽的计数器(数组索引按秒映射)
    private final AtomicInteger[] buckets;
    // 窗口起始时间戳(秒)
    private volatile long startTime;
    // 总计数器,用于快速判断
    private final AtomicInteger totalCount = new AtomicInteger(0);

    public SlidingWindowCircuitBreaker(int windowSize, int maxRequests) {
        this.windowSize = windowSize;
        this.maxRequests = maxRequests;
        this.buckets = new AtomicInteger[windowSize];
        for (int i = 0; i < windowSize; i++) {
            buckets[i] = new AtomicInteger(0);
        }
        this.startTime = System.currentTimeMillis() / 1000;
    }

    /**
     * 尝试获取一个许可,返回 true 代表未熔断,可发送;false 代表需降级
     */
    public synchronized boolean tryAcquire() {
        long now = System.currentTimeMillis() / 1000;
        // 如果窗口过期,重置
        if (now - startTime >= windowSize) {
            resetWindow(now);
        }
        // 清理过期槽位(滑动)
        slideWindow(now);

        if (totalCount.get() >= maxRequests) {
            return false;  // 熔断
        }
        // 对应槽位计数+1
        int index = (int) (now % windowSize);
        buckets[index].incrementAndGet();
        totalCount.incrementAndGet();
        return true;
    }

    private void slideWindow(long now) {
        long expiredSlots = now - startTime;
        for (long i = 0; i < expiredSlots && i < windowSize; i++) {
            int idx = (int) ((startTime + i) % windowSize);
            int oldVal = buckets[idx].getAndSet(0);
            totalCount.addAndGet(-oldVal);
        }
        startTime = now;
    }

    private void resetWindow(long now) {
        for (AtomicInteger bucket : buckets) {
            bucket.set(0);
        }
        totalCount.set(0);
        startTime = now;
    }
}

在短信发送服务中使用:

java 复制代码
@Service
public class SmsService {
    // 1分钟窗口内最多发送100条,超过就熔断降级
    private SlidingWindowCircuitBreaker breaker = 
        new SlidingWindowCircuitBreaker(60, 100);

    public void sendSms(String mobile, String content) {
        if (!breaker.tryAcquire()) {
            // 熔断降级:直接返回失败提示或扔队列
            throw new SmsCircuitBreakException("短信通道繁忙,请稍后重试");
        }
        // 调用供应商发送...
    }
}

亮点:纯内存实现,零外部依赖,极低延迟;synchronized 在这种低频控制场景完全够用,且滑动窗口精确到秒级,避免临界突发流量把通道打爆。😎

面试官 🧐:

代码写得挺扎实。那你结合刚才的方案和代码,总结一下这个场景的技术难点和对应的解决方案

候选人 🧑‍💻:

好的,核心难点和拆解方案我用一张表归纳:

技术难点 具体表现 解决方案 对应代码/模块
自动化脚本突破 黑产用无头浏览器+打码平台直接调用接口 行为式验证码 + 二次校验,强制人机识别 客户端验证码 ticket 校验
高并发下计数不准确 多实例/多线程同时递增计数导致超发 Redis + Lua 原子操作,单线程执行计数 @RateLimit + Lua 脚本
IP 池代理绕过 大量代理 IP 使得 IP 限流失效 多维度组合拳:设备指纹 + 手机号 + 用户ID 联合风控 多维注解叠加,风控规则引擎
短信通道成本雪崩 即便内部限流被突破,短信资费仍可能爆炸 通道级熔断降级 + 日费用监控 + 核心/营销通道隔离 滑动窗口熔断器,费用告警
风控规则僵化 攻防对抗不断变化,写死代码无法快速响应 规则引擎动态加载,日志离线分析反哺黑名单 Drools/自研规则引擎 + ELK 分析管道
分布式一致性 分布式环境下限流状态要统一 Redis 集中式计数器,通过 Lua 保证读写原子性 所有限流 key 落 Redis

这些方案环环相扣,既保证线上实时防护的刚性 ,又保留事后学习和动态调整的柔性,落地起来坑相对少。

面试官 😄👍:

不错,从理论到代码,再到难点复盘,这一整套很完整。这个问题你拿下了!

面试结束后的彩蛋 🥚:

候选人(内心):😌 幸亏平时真这样挡过线上攻击,Lua 脚本的原子性、设备指纹的权重、熔断的滑动窗口,这些都是填过的坑啊~


公众号"Rain的Java大神之路"

个人博客"www.javadashen.com"

相关推荐
Rain的Java大神实战圈1 天前
如何避免订单重复提交
场景设计题
Rain的Java大神实战圈2 天前
MySQL误删数据如何恢复
场景设计题
Rain的Java大神实战圈3 天前
介绍一下分布式事务
场景设计题
Rain的Java大神实战圈4 天前
频繁FullGC如何优化
场景设计题
Rain的Java大神实战圈8 天前
MQ重复消费问题怎么解决
场景设计题
Rain的Java大神实战圈9 天前
线上服务器反应慢如何排查
场景设计题
Rain的Java大神实战圈10 天前
如何保证MQ消息顺序消费
场景设计题
Rain的Java大神实战圈11 天前
线上MQ消息积压了怎么处理
场景设计题
Rain的Java大神实战圈14 天前
介绍一下Redis的高可用方案
场景设计题