高性能企业级消息中心架构实现与分享(二)

企业级消息中心核心实现篇:深入代码层面的技术实现

系列文章第二篇:详细解析消息中心的整体架构设计和核心功能实现

📖 系列文章导读

本系列文章将全面解析企业级消息中心的设计与实现,共分为5篇:

  1. 架构设计篇:设计哲学、架构演进、技术选型
  2. 核心实现篇(本篇):整体架构设计、核心功能实现
  3. 存储与可靠性篇:数据存储设计、高可用保障
  4. 运维与扩展篇:监控运维、扩展性设计
  5. 实战总结篇:业务价值、经验总结

🏗️ 整体架构设计

系统架构图

markdown 复制代码
┌─────────────────────────────────────────────────────────────┐
│                        业务系统层                            │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐          │
│  │   订单系统   │ │   用户系统   │ │   营销系统   │          │
│  └─────────────┘ └─────────────┘ └─────────────┘          │
└─────────────────────┬───────────────────────────────────────┘
                      │ HTTP/RPC调用
┌─────────────────────▼───────────────────────────────────────┐
│                    消息中心接入层                            │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐          │
│  │  API网关    │ │  参数校验    │ │   限流控制   │          │
│  └─────────────┘ └─────────────┘ └─────────────┘          │
└─────────────────────┬───────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────┐
│                    消息中心业务层                            │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐          │
│  │  模板引擎    │ │  渠道路由    │ │  用户偏好    │          │
│  └─────────────┘ └─────────────┘ └─────────────┘          │
└─────────────────────┬───────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────┐
│                    消息队列层                                │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐          │
│  │  RocketMQ   │ │  事务消息    │ │  延时消息    │          │
│  └─────────────┘ └─────────────┘ └─────────────┘          │
└─────────────────────┬───────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────┐
│                    消息通道层                                │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐          │
│  │   短信通道   │ │   邮件通道   │ │   推送通道   │          │
│  └─────────────┘ └─────────────┘ └─────────────┘          │
└─────────────────────┬───────────────────────────────────────┘
                      │
┌─────────────────────▼───────────────────────────────────────┐
│                    数据存储层                                │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────┐          │
│  │    MySQL    │ │    Redis    │ │   MongoDB   │          │
│  └─────────────┘ └─────────────┘ └─────────────┘          │
└─────────────────────────────────────────────────────────────┘

模块划分与职责

1. 接入层(Gateway Layer)
java 复制代码
@RestController
@RequestMapping("/api/message")
@Validated
public class MessageController {
    
    @Autowired
    private MessageService messageService;
    
    @Autowired
    private RateLimiter rateLimiter;
    
    /**
     * 发送消息接口
     * 职责:参数校验、限流控制、统一响应
     */
    @PostMapping("/send")
    public R<String> sendMessage(@Valid @RequestBody MessageRequest request) {
        // 1. 限流检查
        if (!rateLimiter.tryAcquire()) {
            return R.fail("请求过于频繁,请稍后重试");
        }
        
        // 2. 参数校验(通过@Valid自动校验)
        
        // 3. 调用业务层
        String taskId = messageService.sendMessage(request);
        
        // 4. 统一响应
        return R.success(taskId, "消息提交成功");
    }
    
    /**
     * 查询消息状态
     */
    @GetMapping("/status/{taskId}")
    public R<MessageStatusVO> getMessageStatus(@PathVariable String taskId) {
        MessageStatusVO status = messageService.getMessageStatus(taskId);
        return R.success(status);
    }
}
2. 业务层(Service Layer)
java 复制代码
@Service
@Slf4j
public class MessageServiceImpl implements MessageService {
    
    @Autowired
    private MessageTemplateService templateService;
    
    @Autowired
    private MessageRouterService routerService;
    
    @Autowired
    private MessageProducer messageProducer;
    
    @Autowired
    private MessageTaskMapper messageTaskMapper;
    
    /**
     * 发送消息的核心业务逻辑
     */
    @Override
    @Transactional
    public String sendMessage(MessageRequest request) {
        // 1. 生成任务ID
        String taskId = IdUtil.fastSimpleUUID();
        
        // 2. 构建消息任务
        MessageTask task = buildMessageTask(taskId, request);
        
        // 3. 保存任务记录(先存储,保证消息不丢失)
        messageTaskMapper.insert(task);
        
        // 4. 发送到MQ(事务消息保证一致性)
        messageProducer.sendTransactionMessage(task);
        
        log.info("消息任务创建成功: taskId={}, type={}, recipient={}", 
            taskId, request.getMessageType(), request.getRecipient());
        
        return taskId;
    }
    
    /**
     * 构建消息任务
     */
    private MessageTask buildMessageTask(String taskId, MessageRequest request) {
        // 1. 获取消息模板
        MessageTemplate template = templateService.getTemplate(
            request.getTemplateId(), request.getLocale());
        
        // 2. 渲染消息内容
        String content = templateService.renderTemplate(template, request.getParams());
        
        // 3. 路由选择推送渠道
        List<String> channels = routerService.routeChannels(
            request.getMessageType(), request.getRecipient());
        
        // 4. 构建任务对象
        return MessageTask.builder()
            .taskId(taskId)
            .messageType(request.getMessageType())
            .recipient(request.getRecipient())
            .content(content)
            .channels(channels)
            .priority(request.getPriority())
            .status(MessageStatus.PENDING)
            .createTime(LocalDateTime.now())
            .build();
    }
}
3. 队列层(Queue Layer)
java 复制代码
@Component
@Slf4j
public class MessageProducer {
    
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    
    /**
     * 发送事务消息
     * 保证消息发送和本地事务的一致性
     */
    public void sendTransactionMessage(MessageTask task) {
        // 构建消息
        Message<MessageTask> message = MessageBuilder
            .withPayload(task)
            .setHeader("taskId", task.getTaskId())
            .setHeader("messageType", task.getMessageType())
            .build();
        
        // 发送事务消息
        rocketMQTemplate.sendMessageInTransaction(
            "message-topic", 
            message, 
            task
        );
        
        log.info("事务消息发送成功: taskId={}", task.getTaskId());
    }
    
    /**
     * 发送延时消息
     * 用于重试机制
     */
    public void sendDelayMessage(MessageTask task, int delayLevel) {
        Message<MessageTask> message = MessageBuilder
            .withPayload(task)
            .setHeader("taskId", task.getTaskId())
            .setHeader("retryCount", task.getRetryCount())
            .build();
        
        // 发送延时消息
        rocketMQTemplate.syncSend(
            "message-retry-topic", 
            message, 
            3000, 
            delayLevel
        );
        
        log.info("延时消息发送成功: taskId={}, delayLevel={}", 
            task.getTaskId(), delayLevel);
    }
}
4. 通道层(Channel Layer)
java 复制代码
/**
 * 消息通道接口
 * 定义统一的推送接口,支持插件化扩展
 */
public interface MessageChannel {
    
    /**
     * 获取通道类型
     */
    String getChannelType();
    
    /**
     * 发送消息
     */
    SendResult sendMessage(MessageTask task);
    
    /**
     * 批量发送消息
     */
    List<SendResult> batchSendMessage(List<MessageTask> tasks);
    
    /**
     * 检查通道是否可用
     */
    boolean isAvailable();
}

/**
 * 短信通道实现
 */
@Component
@Slf4j
public class SmsChannel implements MessageChannel {
    
    @Autowired
    private SmsClient smsClient;
    
    @Override
    public String getChannelType() {
        return "SMS";
    }
    
    @Override
    public SendResult sendMessage(MessageTask task) {
        try {
            // 1. 参数校验
            validateSmsParams(task);
            
            // 2. 调用短信服务
            SmsResponse response = smsClient.sendSms(
                task.getRecipient(), 
                task.getContent()
            );
            
            // 3. 处理响应
            if (response.isSuccess()) {
                log.info("短信发送成功: taskId={}, phone={}", 
                    task.getTaskId(), task.getRecipient());
                return SendResult.success(response.getMessageId());
            } else {
                log.warn("短信发送失败: taskId={}, error={}", 
                    task.getTaskId(), response.getErrorMessage());
                return SendResult.failure(response.getErrorMessage());
            }
            
        } catch (Exception e) {
            log.error("短信发送异常: taskId={}", task.getTaskId(), e);
            return SendResult.failure(e.getMessage());
        }
    }
    
    @Override
    public List<SendResult> batchSendMessage(List<MessageTask> tasks) {
        // 批量发送实现
        List<SendResult> results = new ArrayList<>();
        
        // 分批处理,避免单次请求过大
        List<List<MessageTask>> batches = Lists.partition(tasks, 100);
        
        for (List<MessageTask> batch : batches) {
            try {
                List<SendResult> batchResults = smsClient.batchSendSms(batch);
                results.addAll(batchResults);
            } catch (Exception e) {
                // 批量失败时,逐个重试
                for (MessageTask task : batch) {
                    results.add(sendMessage(task));
                }
            }
        }
        
        return results;
    }
    
    @Override
    public boolean isAvailable() {
        try {
            return smsClient.healthCheck();
        } catch (Exception e) {
            return false;
        }
    }
    
    private void validateSmsParams(MessageTask task) {
        if (!PhoneUtil.isValidPhone(task.getRecipient())) {
            throw new IllegalArgumentException("无效的手机号码: " + task.getRecipient());
        }
        
        if (StringUtils.isBlank(task.getContent())) {
            throw new IllegalArgumentException("短信内容不能为空");
        }
        
        if (task.getContent().length() > 500) {
            throw new IllegalArgumentException("短信内容超长,最大500字符");
        }
    }
}

⚡ 核心功能实现深度解析

消息生产流程的设计思考

1. 消息接入层的设计哲学

为什么需要统一的接入层?

markdown 复制代码
问题:各业务系统直接调用消息服务
后果:
1. 参数校验逻辑重复
2. 限流策略难以统一
3. 接口变更影响面大
4. 监控指标分散

解决:统一接入层
好处:
1. 统一参数校验和错误处理
2. 统一限流和熔断策略
3. 统一监控和日志记录
4. 接口版本管理

接入层的核心实现:

java 复制代码
@Component
public class MessageGateway {
    
    @Autowired
    private List<MessageValidator> validators;
    
    @Autowired
    private RateLimiter rateLimiter;
    
    @Autowired
    private CircuitBreaker circuitBreaker;
    
    /**
     * 统一消息处理入口
     */
    public R<String> processMessage(MessageRequest request) {
        // 1. 限流检查
        if (!rateLimiter.tryAcquire(request.getTenantId())) {
            return R.fail("请求频率超限");
        }
        
        // 2. 参数校验
        ValidationResult validation = validateRequest(request);
        if (!validation.isValid()) {
            return R.fail(validation.getErrorMessage());
        }
        
        // 3. 熔断保护
        return circuitBreaker.executeSupplier(() -> {
            return doProcessMessage(request);
        }).recover(throwable -> {
            log.error("消息处理失败,触发熔断", throwable);
            return R.fail("系统繁忙,请稍后重试");
        });
    }
    
    /**
     * 链式参数校验
     */
    private ValidationResult validateRequest(MessageRequest request) {
        for (MessageValidator validator : validators) {
            ValidationResult result = validator.validate(request);
            if (!result.isValid()) {
                return result;
            }
        }
        return ValidationResult.success();
    }
}
2. 模板引擎的智能化设计

模板引擎要解决什么问题?

markdown 复制代码
问题:消息内容硬编码
后果:
1. 内容修改需要发版
2. 多语言支持困难
3. 个性化定制复杂
4. A/B测试无法进行

解决:可配置的模板引擎
好处:
1. 内容与代码分离
2. 支持多语言和个性化
3. 支持动态变量替换
4. 支持模板版本管理

模板引擎的核心实现:

java 复制代码
@Service
public class MessageTemplateEngine {
    
    @Autowired
    private MessageTemplateCache templateCache;
    
    @Autowired
    private TemplateRenderer templateRenderer;
    
    /**
     * 渲染消息模板
     */
    public String renderTemplate(Long templateId, Map<String, Object> params, String locale) {
        // 1. 获取模板(三级缓存)
        MessageTemplate template = getTemplateWithCache(templateId, locale);
        
        // 2. 参数预处理
        Map<String, Object> processedParams = preprocessParams(params, template);
        
        // 3. 模板渲染
        String content = templateRenderer.render(template.getContent(), processedParams);
        
        // 4. 后处理(表情符号、敏感词过滤等)
        return postProcessContent(content, template);
    }
    
    /**
     * 三级缓存获取模板
     */
    private MessageTemplate getTemplateWithCache(Long templateId, String locale) {
        // L1: 本地缓存
        String cacheKey = templateId + ":" + locale;
        MessageTemplate template = templateCache.getFromLocal(cacheKey);
        if (template != null) {
            return template;
        }
        
        // L2: Redis缓存
        template = templateCache.getFromRedis(cacheKey);
        if (template != null) {
            templateCache.putToLocal(cacheKey, template);
            return template;
        }
        
        // L3: 数据库
        template = templateMapper.selectByIdAndLocale(templateId, locale);
        if (template == null) {
            // 回退到默认语言
            template = templateMapper.selectByIdAndLocale(templateId, "zh_CN");
        }
        
        if (template != null) {
            templateCache.putToRedis(cacheKey, template);
            templateCache.putToLocal(cacheKey, template);
        }
        
        return template;
    }
    
    /**
     * 参数预处理
     */
    private Map<String, Object> preprocessParams(Map<String, Object> params, MessageTemplate template) {
        Map<String, Object> processed = new HashMap<>(params);
        
        // 1. 添加系统变量
        processed.put("currentTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        processed.put("currentDate", LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        
        // 2. 类型转换
        for (Map.Entry<String, Object> entry : processed.entrySet()) {
            Object value = entry.getValue();
            if (value instanceof Date) {
                entry.setValue(DateUtil.formatDateTime((Date) value));
            } else if (value instanceof BigDecimal) {
                entry.setValue(NumberUtil.decimalFormat("#,##0.00", (BigDecimal) value));
            }
        }
        
        // 3. 默认值处理
        for (String requiredParam : template.getRequiredParams()) {
            if (!processed.containsKey(requiredParam)) {
                processed.put(requiredParam, template.getDefaultValue(requiredParam));
            }
        }
        
        return processed;
    }
}
3. 消息路由的智能决策

路由策略的设计思考:

markdown 复制代码
问题:如何选择最优的推送渠道?
考虑因素:
1. 用户偏好:用户设置的接收偏好
2. 消息类型:不同类型消息的适合渠道
3. 渠道状态:渠道的可用性和性能
4. 成本控制:不同渠道的成本差异
5. 到达率:不同渠道的历史到达率

解决:智能路由引擎
策略:
1. 规则引擎:基于配置的路由规则
2. 机器学习:基于历史数据的智能推荐
3. 实时调整:基于实时监控的动态调整

路由引擎的核心实现:

java 复制代码
@Service
public class MessageRouterService {
    
    @Autowired
    private UserPreferenceService userPreferenceService;
    
    @Autowired
    private ChannelStatusService channelStatusService;
    
    @Autowired
    private RouteRuleEngine routeRuleEngine;
    
    /**
     * 智能路由选择
     */
    public List<String> routeChannels(String messageType, String recipient, MessagePriority priority) {
        // 1. 获取用户偏好
        UserPreference preference = userPreferenceService.getUserPreference(recipient);
        
        // 2. 获取可用渠道
        List<String> availableChannels = channelStatusService.getAvailableChannels();
        
        // 3. 应用路由规则
        RouteContext context = RouteContext.builder()
            .messageType(messageType)
            .recipient(recipient)
            .priority(priority)
            .userPreference(preference)
            .availableChannels(availableChannels)
            .build();
        
        List<String> selectedChannels = routeRuleEngine.selectChannels(context);
        
        // 4. 渠道排序(按优先级、成本、到达率等)
        return sortChannelsByPriority(selectedChannels, context);
    }
    
    /**
     * 渠道优先级排序
     */
    private List<String> sortChannelsByPriority(List<String> channels, RouteContext context) {
        return channels.stream()
            .map(channel -> {
                ChannelScore score = calculateChannelScore(channel, context);
                return new ChannelWithScore(channel, score);
            })
            .sorted((a, b) -> b.getScore().compareTo(a.getScore()))
            .map(ChannelWithScore::getChannel)
            .collect(Collectors.toList());
    }
    
    /**
     * 计算渠道评分
     */
    private ChannelScore calculateChannelScore(String channel, RouteContext context) {
        ChannelMetrics metrics = channelStatusService.getChannelMetrics(channel);
        
        double score = 0.0;
        
        // 用户偏好权重 40%
        if (context.getUserPreference().getPreferredChannels().contains(channel)) {
            score += 0.4;
        }
        
        // 到达率权重 30%
        score += metrics.getDeliveryRate() * 0.3;
        
        // 响应时间权重 20%
        score += (1.0 - metrics.getAvgResponseTime() / 10000.0) * 0.2;
        
        // 成本权重 10%(成本越低分数越高)
        score += (1.0 - metrics.getCostPerMessage() / 1.0) * 0.1;
        
        return new ChannelScore(score);
    }
}

消息消费流程的可靠性保证

1. 消费者的设计原则
markdown 复制代码
设计原则:
1. 幂等性:重复消费不会产生副作用
2. 可重试:失败后能够自动重试
3. 可监控:消费状态可观测
4. 可扩展:支持水平扩展
5. 故障隔离:单个消费者故障不影响整体

消费者的核心实现:

java 复制代码
@Component
@RocketMQMessageListener(
    topic = "message-topic",
    consumerGroup = "message-consumer-group",
    consumeMode = ConsumeMode.CONCURRENTLY
)
public class MessageConsumer implements RocketMQListener<MessageTask> {
    
    @Autowired
    private MessageChannelFactory channelFactory;
    
    @Autowired
    private MessageTaskService messageTaskService;
    
    @Autowired
    private MessageRetryService retryService;
    
    @Override
    public void onMessage(MessageTask task) {
        String taskId = task.getTaskId();
        
        try {
            // 1. 幂等性检查
            if (messageTaskService.isProcessed(taskId)) {
                log.info("消息已处理,跳过: taskId={}", taskId);
                return;
            }
            
            // 2. 更新任务状态
            messageTaskService.updateStatus(taskId, MessageStatus.PROCESSING);
            
            // 3. 执行消息推送
            List<SendResult> results = processMessage(task);
            
            // 4. 处理推送结果
            handleSendResults(task, results);
            
        } catch (Exception e) {
            log.error("消息处理失败: taskId={}", taskId, e);
            
            // 5. 失败重试
            retryService.scheduleRetry(task, e);
        }
    }
    
    /**
     * 执行消息推送
     */
    private List<SendResult> processMessage(MessageTask task) {
        List<SendResult> results = new ArrayList<>();
        
        for (String channelType : task.getChannels()) {
            try {
                // 获取通道实例
                MessageChannel channel = channelFactory.getChannel(channelType);
                
                // 检查通道可用性
                if (!channel.isAvailable()) {
                    log.warn("通道不可用,跳过: channel={}, taskId={}", channelType, task.getTaskId());
                    results.add(SendResult.failure("通道不可用: " + channelType));
                    continue;
                }
                
                // 发送消息
                SendResult result = channel.sendMessage(task);
                results.add(result);
                
                // 记录发送日志
                logSendResult(task, channelType, result);
                
            } catch (Exception e) {
                log.error("通道发送失败: channel={}, taskId={}", channelType, task.getTaskId(), e);
                results.add(SendResult.failure(e.getMessage()));
            }
        }
        
        return results;
    }
    
    /**
     * 处理发送结果
     */
    private void handleSendResults(MessageTask task, List<SendResult> results) {
        boolean hasSuccess = results.stream().anyMatch(SendResult::isSuccess);
        boolean allFailed = results.stream().allMatch(result -> !result.isSuccess());
        
        if (hasSuccess) {
            // 至少一个通道成功
            messageTaskService.updateStatus(task.getTaskId(), MessageStatus.SUCCESS);
            log.info("消息发送成功: taskId={}", task.getTaskId());
        } else if (allFailed) {
            // 所有通道都失败
            if (task.getRetryCount() >= MAX_RETRY_COUNT) {
                messageTaskService.updateStatus(task.getTaskId(), MessageStatus.FAILED);
                log.error("消息发送最终失败: taskId={}", task.getTaskId());
            } else {
                // 安排重试
                retryService.scheduleRetry(task, new RuntimeException("所有通道发送失败"));
            }
        }
        
        // 保存发送记录
        saveSendRecords(task, results);
    }
}
2. 重试机制的设计

重试策略的考虑因素:

markdown 复制代码
重试策略设计:
1. 重试次数:避免无限重试
2. 重试间隔:指数退避,避免雪崩
3. 重试条件:区分可重试和不可重试错误
4. 重试队列:使用延时消息实现
5. 死信队列:最终失败的消息处理

重试服务的实现:

java 复制代码
@Service
public class MessageRetryService {
    
    @Autowired
    private MessageProducer messageProducer;
    
    @Autowired
    private MessageTaskService messageTaskService;
    
    // 重试延时级别(秒)
    private static final int[] RETRY_DELAYS = {10, 30, 60, 300, 600, 1800};
    
    /**
     * 安排重试
     */
    public void scheduleRetry(MessageTask task, Exception exception) {
        // 1. 判断是否可重试
        if (!isRetryable(exception)) {
            log.warn("异常不可重试,标记为失败: taskId={}, error={}", 
                task.getTaskId(), exception.getMessage());
            messageTaskService.updateStatus(task.getTaskId(), MessageStatus.FAILED);
            return;
        }
        
        // 2. 检查重试次数
        int retryCount = task.getRetryCount();
        if (retryCount >= RETRY_DELAYS.length) {
            log.error("重试次数超限,标记为失败: taskId={}, retryCount={}", 
                task.getTaskId(), retryCount);
            messageTaskService.updateStatus(task.getTaskId(), MessageStatus.FAILED);
            return;
        }
        
        // 3. 计算延时级别
        int delaySeconds = RETRY_DELAYS[retryCount];
        int delayLevel = calculateDelayLevel(delaySeconds);
        
        // 4. 更新重试信息
        task.setRetryCount(retryCount + 1);
        task.setLastRetryTime(LocalDateTime.now());
        task.setNextRetryTime(LocalDateTime.now().plusSeconds(delaySeconds));
        messageTaskService.updateRetryInfo(task);
        
        // 5. 发送延时消息
        messageProducer.sendDelayMessage(task, delayLevel);
        
        log.info("安排重试: taskId={}, retryCount={}, delaySeconds={}", 
            task.getTaskId(), task.getRetryCount(), delaySeconds);
    }
    
    /**
     * 判断异常是否可重试
     */
    private boolean isRetryable(Exception exception) {
        // 网络异常可重试
        if (exception instanceof ConnectException || 
            exception instanceof SocketTimeoutException) {
            return true;
        }
        
        // 服务暂时不可用可重试
        if (exception instanceof ServiceUnavailableException) {
            return true;
        }
        
        // 参数错误不可重试
        if (exception instanceof IllegalArgumentException) {
            return false;
        }
        
        // 认证失败不可重试
        if (exception instanceof AuthenticationException) {
            return false;
        }
        
        // 其他异常默认可重试
        return true;
    }
    
    /**
     * 计算RocketMQ延时级别
     */
    private int calculateDelayLevel(int delaySeconds) {
        // RocketMQ延时级别:1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
        if (delaySeconds <= 1) return 1;
        if (delaySeconds <= 5) return 2;
        if (delaySeconds <= 10) return 3;
        if (delaySeconds <= 30) return 4;
        if (delaySeconds <= 60) return 5;
        if (delaySeconds <= 120) return 6;
        if (delaySeconds <= 180) return 7;
        if (delaySeconds <= 240) return 8;
        if (delaySeconds <= 300) return 9;
        if (delaySeconds <= 360) return 10;
        if (delaySeconds <= 420) return 11;
        if (delaySeconds <= 480) return 12;
        if (delaySeconds <= 540) return 13;
        if (delaySeconds <= 600) return 14;
        if (delaySeconds <= 1200) return 15;
        if (delaySeconds <= 1800) return 16;
        if (delaySeconds <= 3600) return 17;
        return 18; // 2小时
    }
}

📊 性能优化实践

1. 批量处理优化

批量处理的核心思想:

markdown 复制代码
问题:单条消息处理效率低
原因:
1. 网络开销:每次调用都有网络往返
2. 连接开销:频繁建立和释放连接
3. 事务开销:每次操作都要提交事务

解决:批量处理
好处:
1. 减少网络往返次数
2. 复用连接和事务
3. 提高吞吐量

批量处理的实现:

java 复制代码
@Component
public class BatchMessageProcessor {
    
    @Autowired
    private MessageChannelFactory channelFactory;
    
    // 批量大小
    private static final int BATCH_SIZE = 100;
    
    // 批量等待时间(毫秒)
    private static final long BATCH_TIMEOUT = 1000;
    
    // 批量队列
    private final BlockingQueue<MessageTask> batchQueue = new LinkedBlockingQueue<>();
    
    @PostConstruct
    public void startBatchProcessor() {
        // 启动批量处理线程
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);
        
        // 定时批量处理
        executor.scheduleWithFixedDelay(this::processBatch, 0, BATCH_TIMEOUT, TimeUnit.MILLISECONDS);
        
        // 队列满时立即处理
        executor.submit(this::processWhenFull);
    }
    
    /**
     * 添加消息到批量队列
     */
    public void addToBatch(MessageTask task) {
        try {
            batchQueue.put(task);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            log.error("添加批量队列被中断", e);
        }
    }
    
    /**
     * 定时批量处理
     */
    private void processBatch() {
        List<MessageTask> batch = new ArrayList<>();
        batchQueue.drainTo(batch, BATCH_SIZE);
        
        if (!batch.isEmpty()) {
            processBatchMessages(batch);
        }
    }
    
    /**
     * 队列满时立即处理
     */
    private void processWhenFull() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                if (batchQueue.size() >= BATCH_SIZE) {
                    processBatch();
                }
                Thread.sleep(100); // 避免CPU空转
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
    }
    
    /**
     * 批量处理消息
     */
    private void processBatchMessages(List<MessageTask> batch) {
        // 按通道分组
        Map<String, List<MessageTask>> channelGroups = batch.stream()
            .collect(Collectors.groupingBy(
                task -> task.getChannels().get(0) // 简化处理,取第一个通道
            ));
        
        // 并行处理各通道
        channelGroups.entrySet().parallelStream().forEach(entry -> {
            String channelType = entry.getKey();
            List<MessageTask> tasks = entry.getValue();
            
            try {
                MessageChannel channel = channelFactory.getChannel(channelType);
                List<SendResult> results = channel.batchSendMessage(tasks);
                
                // 处理批量结果
                handleBatchResults(tasks, results);
                
                log.info("批量处理完成: channel={}, count={}", channelType, tasks.size());
                
            } catch (Exception e) {
                log.error("批量处理失败: channel={}, count={}", channelType, tasks.size(), e);
                
                // 批量失败时,逐个重试
                tasks.forEach(task -> {
                    try {
                        MessageChannel channel = channelFactory.getChannel(channelType);
                        SendResult result = channel.sendMessage(task);
                        handleSingleResult(task, result);
                    } catch (Exception ex) {
                        log.error("单个重试也失败: taskId={}", task.getTaskId(), ex);
                    }
                });
            }
        });
    }
}

2. 连接池优化

连接池的重要性:

markdown 复制代码
问题:频繁创建和销毁连接
后果:
1. 性能开销大
2. 资源浪费
3. 连接数限制
4. 响应时间长

解决:连接池管理
好处:
1. 复用连接,减少开销
2. 控制连接数,避免资源耗尽
3. 提高响应速度
4. 连接健康检查

连接池的配置:

java 复制代码
@Configuration
public class ConnectionPoolConfig {
    
    /**
     * HTTP连接池配置
     */
    @Bean
    public CloseableHttpClient httpClient() {
        // 连接池管理器
        PoolingHttpClientConnectionManager connectionManager = 
            new PoolingHttpClientConnectionManager();
        
        // 最大连接数
        connectionManager.setMaxTotal(200);
        
        // 每个路由的最大连接数
        connectionManager.setDefaultMaxPerRoute(50);
        
        // 连接超时时间
        RequestConfig requestConfig = RequestConfig.custom()
            .setConnectTimeout(5000)
            .setSocketTimeout(10000)
            .setConnectionRequestTimeout(3000)
            .build();
        
        return HttpClients.custom()
            .setConnectionManager(connectionManager)
            .setDefaultRequestConfig(requestConfig)
            .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true))
            .build();
    }
    
    /**
     * 数据库连接池配置
     */
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource.hikari")
    public HikariConfig hikariConfig() {
        HikariConfig config = new HikariConfig();
        
        // 连接池大小
        config.setMaximumPoolSize(20);
        config.setMinimumIdle(5);
        
        // 连接超时
        config.setConnectionTimeout(30000);
        config.setIdleTimeout(600000);
        config.setMaxLifetime(1800000);
        
        // 连接测试
        config.setConnectionTestQuery("SELECT 1");
        config.setValidationTimeout(5000);
        
        return config;
    }
    
    /**
     * Redis连接池配置
     */
    @Bean
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig config = new JedisPoolConfig();
        
        // 连接池大小
        config.setMaxTotal(50);
        config.setMaxIdle(20);
        config.setMinIdle(5);
        
        // 连接超时
        config.setMaxWaitMillis(3000);
        
        // 连接测试
        config.setTestOnBorrow(true);
        config.setTestOnReturn(true);
        config.setTestWhileIdle(true);
        
        return config;
    }
}

3. 线程池调优

线程池的设计原则:

markdown 复制代码
线程池设计考虑:
1. 核心线程数:CPU密集型 = CPU核数,IO密集型 = 2 * CPU核数
2. 最大线程数:根据系统负载和内存限制
3. 队列大小:平衡内存使用和响应时间
4. 拒绝策略:根据业务需求选择
5. 线程存活时间:平衡资源使用和响应速度

线程池的配置:

java 复制代码
@Configuration
@EnableAsync
public class ThreadPoolConfig {
    
    /**
     * 消息处理线程池
     */
    @Bean("messageExecutor")
    public ThreadPoolTaskExecutor messageExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        
        // 核心线程数
        executor.setCorePoolSize(10);
        
        // 最大线程数
        executor.setMaxPoolSize(50);
        
        // 队列大小
        executor.setQueueCapacity(1000);
        
        // 线程存活时间
        executor.setKeepAliveSeconds(60);
        
        // 线程名前缀
        executor.setThreadNamePrefix("message-");
        
        // 拒绝策略:调用者运行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        
        // 等待任务完成后关闭
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(30);
        
        executor.initialize();
        return executor;
    }
    
    /**
     * 短信发送线程池
     */
    @Bean("smsExecutor")
    public ThreadPoolTaskExecutor smsExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        
        // IO密集型,线程数可以多一些
        executor.setCorePoolSize(20);
        executor.setMaxPoolSize(100);
        executor.setQueueCapacity(2000);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("sms-");
        
        // 拒绝策略:丢弃最老的任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());
        
        executor.initialize();
        return executor;
    }
    
    /**
     * 邮件发送线程池
     */
    @Bean("emailExecutor")
    public ThreadPoolTaskExecutor emailExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        
        executor.setCorePoolSize(5);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(500);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("email-");
        
        // 拒绝策略:抛出异常
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        
        executor.initialize();
        return executor;
    }
}

📝 本篇总结

在这篇文章中,我们深入探讨了消息中心的核心实现:

  1. 整体架构设计:分层架构、模块划分、接口设计
  2. 核心功能实现:消息生产、消费、路由、重试机制
  3. 关键技术细节:事务消息、模板引擎、智能路由
  4. 性能优化实践:批量处理、连接池、线程池调优

通过这些实现,我们构建了一个高性能、高可靠的消息中心系统。

🔮 下篇预告

在下一篇《存储与可靠性篇》中,我们将深入探讨:

  • 🗄️ 数据存储设计:分库分表、冷热分离、数据归档
  • 🛡️ 高可用保障:故障隔离、快速恢复、容灾备份
  • 🔄 数据一致性:分布式事务、最终一致性、补偿机制
  • 📊 性能监控:关键指标、实时监控、性能分析

敬请期待!

相关推荐
一眼万年041 分钟前
Nginx Master-Worker 进程间的共享内存是怎么做到通用还高效的?
后端·nginx·面试
小华同学ai2 分钟前
惊喜! Github 10k+ star 的国产流程图框架,LogicFlow 能解你的图编辑痛点?
前端·后端·github
XuanXu19 分钟前
MCP简单研究以及介绍
后端·ai编程·cursor
该用户已不存在21 分钟前
我不管,我的 Claude Code 必须用上 Gemini 2.5 Pro
前端·人工智能·后端
FrigidCrow23 分钟前
最新实践LangGraph的记忆体
后端
iOS开发上架哦24 分钟前
iOS加固工具有哪些?项目场景下的组合策略与实战指南
后端
expect7g27 分钟前
Flink-反压-1.基本概念
后端·flink
itsoo28 分钟前
并发问题导致kafka consumer全部掉线
后端
苍狮技术团队28 分钟前
【苍狮技术团队】打造高效日志系统:Graylog + Docker 快速部署 + Spring Boot 日志集成全攻略
后端
起风了i29 分钟前
文件分片上传??拿捏
前端·javascript·后端