企业级消息中心核心实现篇:深入代码层面的技术实现
系列文章第二篇:详细解析消息中心的整体架构设计和核心功能实现
📖 系列文章导读
本系列文章将全面解析企业级消息中心的设计与实现,共分为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;
}
}
📝 本篇总结
在这篇文章中,我们深入探讨了消息中心的核心实现:
- 整体架构设计:分层架构、模块划分、接口设计
- 核心功能实现:消息生产、消费、路由、重试机制
- 关键技术细节:事务消息、模板引擎、智能路由
- 性能优化实践:批量处理、连接池、线程池调优
通过这些实现,我们构建了一个高性能、高可靠的消息中心系统。
🔮 下篇预告
在下一篇《存储与可靠性篇》中,我们将深入探讨:
- 🗄️ 数据存储设计:分库分表、冷热分离、数据归档
- 🛡️ 高可用保障:故障隔离、快速恢复、容灾备份
- 🔄 数据一致性:分布式事务、最终一致性、补偿机制
- 📊 性能监控:关键指标、实时监控、性能分析
敬请期待!