SpringBoot整合ActiveMQ实战:从依赖配置到死信队列的避坑指南
1. 项目概述与核心痛点最近在重构一个老项目的消息中间件从传统的JMS API直接调用迁移到SpringBoot的怀抱里。选型上团队还是决定沿用已经比较稳定的ActiveMQ 5.x版本。本以为SpringBoot的“约定大于配置”能让我省点心结果从引入依赖到消息收发一路踩坑不断。这篇文章我就把这次整合ActiveMQ 5.16.5与SpringBoot 2.7.18过程中遇到的那些“坑”以及填坑的详细过程记录下来。如果你也在做类似的技术选型或者正被一些奇怪的连接、序列化、事务问题困扰希望我的这些实战经验能帮你少走弯路。ActiveMQ作为一个老牌的消息队列稳定性和功能丰富度是没得说但正是因为它“老”在和SpringBoot这种现代框架整合时一些默认的配置、版本兼容性、甚至是思维习惯上的差异就会成为隐藏的陷阱。这次整合核心要解决的就是如何让SpringBoot应用优雅、可靠地作为生产者和消费者与ActiveMQ进行交互并处理好消息的持久化、事务以及异常情况。2. 环境搭建与基础配置的“暗礁”万事开头难整合的第一步——引入依赖和基础配置就给了我一个下马威。2.1 依赖引入的版本“玄学”最开始我理所当然地在pom.xml里加入了SpringBoot官方提供的Starter。dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-activemq/artifactId /dependency启动项目控制台一片祥和。然而当我尝试发送一条消息时直接抛出了ClassNotFoundException: org.apache.activemq.ActiveMQConnectionFactory。这个错误很诡异因为Starter理应传递了所有必要的依赖。排查后发现spring-boot-starter-activemq默认引入的是activemq-client和activemq-pool等客户端库但它不包含ActiveMQ服务端本身用于内嵌模式In-Memory Broker的依赖。坑点一Starter的“不完整”性。这个Starter的设计初衷是连接一个已存在的外部ActiveMQ Broker。如果你想像测试环境那样在SpringBoot应用内启动一个内嵌的Broker需要额外引入activemq-broker。dependency groupIdorg.apache.activemq/groupId artifactIdactivemq-broker/artifactId /dependency坑点二版本冲突的“幽灵”。引入了activemq-broker后又出现了新的问题NoClassDefFoundError或MethodNotFoundException指向一些内部类。这是因为SpringBoot Parent Pom中管理的ActiveMQ版本可能与你本地安装或期望使用的Broker版本不一致。比如SpringBoot 2.7.18 默认管理的是ActiveMQ 5.16.5而你的服务器上跑的是5.15.x。最稳妥的做法是在properties中显式声明所有ActiveMQ相关组件的统一版本。properties activemq.version5.16.5/activemq.version /properties然后在所有ActiveMQ相关的依赖中包括spring-boot-starter-activemq间接引入的通过exclusions排除旧版本或者确保所有相关依赖如activemq-client,activemq-pool,activemq-broker,activemq-kahadb-store的版本号都被这个属性覆盖。我的经验是对于这类中间件所有相关jar包的版本必须严格一致一个都不能错。2.2 连接配置的“多义性”与池化陷阱配置连接工厂application.yml里看似简单几行水深得很。spring: activemq: broker-url: tcp://localhost:61616 user: admin password: admin packages: trust-all: false # 重要默认是false但需要关注 # in-memory: true # 如果你想用内嵌Broker打开这个并配置broker-url为 vm://localhost?broker.persistentfalse坑点三broker-url的协议歧义。如果你配置broker-url: tcp://localhost:61616SpringBoot会自动为你创建一个SingleConnectionFactory。这个工厂有个特点它会对所有调用createConnection()的请求返回同一个连接对象。这在某些需要连接隔离如不同事务上下文的场景下会有问题。更推荐的做法是使用池化连接工厂它能更好地管理连接资源防止连接泄漏并提升性能。但这里又有一个大坑SpringBoot默认不提供池化。你需要手动引入并配置PooledConnectionFactory。通常我们用org.messaginghub:pooled-jms这个依赖。dependency groupIdorg.messaginghub/groupId artifactIdpooled-jms/artifactId /dependency然后通过配置类来定义它Configuration public class ActiveMQConfig { Value(${spring.activemq.broker-url}) private String brokerUrl; Bean public ConnectionFactory jmsConnectionFactory() { ActiveMQConnectionFactory factory new ActiveMQConnectionFactory(brokerUrl); // 可以在这里设置一些连接属性比如信任的包 // factory.setTrustAllPackages(false); // 强烈建议关闭 // factory.setTrustedPackages(new ArrayList(Arrays.asList(com.yourdomain.dto))); PooledConnectionFactory pooledFactory new PooledConnectionFactory(); pooledFactory.setConnectionFactory(factory); pooledFactory.setMaxConnections(10); // 最大连接数 pooledFactory.setMaximumActiveSessionPerConnection(50); // 每个连接最大活动会话数 pooledFactory.setIdleTimeout(30000); // 空闲超时(毫秒) return pooledFactory; } Bean // 将JmsTemplate也注入进来使用我们定义的连接工厂 public JmsTemplate jmsTemplate(ConnectionFactory jmsConnectionFactory) { return new JmsTemplate(jmsConnectionFactory); } }坑点四连接池参数的“想当然”。MaxConnections不是越大越好。设置过大会耗尽ActiveMQ Broker的资源如线程和文件句柄。一般根据应用实例数和并发消费者数量来估算。MaximumActiveSessionPerConnection也要小心一个连接上的所有会话共享同一个TCP连接如果会话太多且消息吞吐量大可能会成为瓶颈。我的经验是从一个保守值开始比如5-10个连接每个连接20-50个会话通过监控Broker的连接数和应用性能逐步调整。2.3 信任包与消息序列化的“安全门”当你尝试发送一个自定义的Java对象实现了Serializable接口作为ObjectMessage时可能会遇到SecurityException: Package is not trusted错误。坑点五默认不信任任何包。出于安全考虑ActiveMQ默认不反序列化来自任何包的类。你需要明确告诉它哪些包是可信的。在ActiveMQConnectionFactory上设置信任包列表是必须的步骤。我强烈建议不要使用setTrustAllPackages(true)这会带来极大的反序列化安全漏洞风险想想Log4Shell这类漏洞。正确的做法是在生产环境中严格限定信任的包范围。ActiveMQConnectionFactory factory new ActiveMQConnectionFactory(brokerUrl); ListString trustedPackages new ArrayList(); trustedPackages.add(com.yourcompany.project.dto); // 只信任你的DTO所在包 trustedPackages.add(java.util); // 如果需要可以添加JDK标准包 factory.setTrustedPackages(trustedPackages); factory.setTrustAllPackages(false); // 显式设置为false更好的实践是避免直接发送ObjectMessage。因为Java原生序列化有版本兼容性问题而且效率不高。可以考虑使用TextMessage传递JSON字符串配合Jackson或者使用BytesMessage传递Protocol Buffers、Avro等二进制格式。这样不仅安全而且跨语言兼容性更好。如果一定要用ObjectMessage务必确保生产者和消费者使用的类路径类名、serialVersionUID完全一致。3. 消息生产与消费的实战“雷区”基础配置通了接下来就是编写生产者和消费者。这里面的坑更多而且更隐蔽。3.1 JmsTemplate的“非直观”行为JmsTemplate是Spring提供的“神器”它简化了JMS操作但也隐藏了一些细节容易让人误解。坑点六send方法的默认目的地。你可以通过jmsTemplate.convertAndSend(destinationName, payload)来发送消息。但是如果你在JmsTemplate上设置了默认目的地setDefaultDestinationName那么调用convertAndSend(payload)单参数方法时消息就会发往那个默认目的地。这个设计在代码简洁的同时也容易导致消息发错地方。我的建议是除非业务逻辑极其简单且单一否则尽量避免使用默认目的地始终显式指定目的地名称或对象。坑点七连接、会话、生产者的资源管理。JmsTemplate在每次操作时默认会从连接工厂获取连接、创建会话和生产者操作完成后关闭它们。对于低频操作没问题但在高频发送场景下这会带来巨大的开销。JmsTemplate提供了缓存选项来优化Bean public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory) { JmsTemplate template new JmsTemplate(connectionFactory); template.setSessionCacheSize(5); // 缓存JMS Session提升性能 // template.setCacheLevelName(CACHE_CONNECTION); // 已废弃使用连接池替代 // 更推荐使用前面提到的PooledConnectionFactory它是在更底层做连接和会话的池化。 return template; }实际上在现代SpringBoot连接池的方案下JmsTemplate的缓存级别设置已经不那么关键了因为PooledConnectionFactory已经高效地管理了连接和会话。你需要关注的是连接池本身的参数。3.2 消费者端的“并发”与“事务”迷思使用JmsListener注解来声明消息监听器非常方便但关于并发和事务的配置一不小心就会掉坑里。坑点八默认的单线程消费者。一个JmsListener方法默认是单线程顺序消费的。如果你的队列消息堆积严重或者处理逻辑比较耗时这将成为性能瓶颈。你需要通过配置来增加并发消费者数量。Configuration EnableJms public class JmsConfig { Bean public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory) { DefaultJmsListenerContainerFactory factory new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setConcurrency(3-10); // 最小3个最大10个并发消费者 // factory.setConcurrency(10); // 固定10个并发消费者 return factory; } }然后在JmsListener注解中指定这个容器工厂Component public class OrderMessageListener { JmsListener(destination order.queue, containerFactory jmsListenerContainerFactory) public void processOrder(OrderDTO order) { // 处理订单 } }坑点九concurrency字符串的格式。它支持“1-5”这样的范围也支持固定值“5”。使用范围时容器会根据负载动态调整消费者数量。但要注意增加消费者数量会增加ActiveMQ Broker上的连接和会话数需要确保Broker和连接池的资源配置足够。坑点十事务与确认模式的纠缠。这是最复杂、最容易出问题的地方。DefaultJmsListenerContainerFactory有两个关键属性sessionTransacted和sessionAcknowledgeMode。sessionTransacted true启用本地JMS事务。消息消费会在监听方法成功执行后提交如果方法抛出异常消息会回滚根据重试策略可能会重新投递。这通常与数据库事务协调使用但要注意JMS事务和数据库事务是两回事需要借助ChainedTransactionManager或分布式事务如JTA来保证一致性这非常重。对于大多数应用更轻量的方式是使用“客户端确认”模式。sessionAcknowledgeMode确认模式。默认是AUTO_ACKNOWLEDGE意味着监听方法成功返回后会话会自动确认消息。如果方法内抛出异常消息不会被确认并且会根据Broker的重发策略重新投递。一个常见的需求是消息处理与数据库操作保持一致如果数据库操作失败消息不能丢失要能重新处理。一个相对简单的模式是关闭JMS事务使用CLIENT_ACKNOWLEDGE模式并在业务逻辑成功后手动确认。Bean public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory) { DefaultJmsListenerContainerFactory factory new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setConcurrency(3-5); factory.setSessionTransacted(false); // 关闭JMS事务 factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE); // 客户端手动确认 return factory; }在监听方法中通过注入javax.jms.Session来手动确认JmsListener(destination order.queue, containerFactory jmsListenerContainerFactory) public void processOrder(OrderDTO order, Session session, Message message) throws JMSException { try { // 1. 执行核心业务逻辑例如操作数据库 orderService.process(order); // 2. 业务成功手动确认本条消息 message.acknowledge(); } catch (BusinessException e) { // 3. 业务失败记录日志不调用acknowledge()。 // 根据Broker配置如redeliveryPolicy消息会被重新投递。 log.error(订单处理失败消息将重试。订单ID: {}, order.getId(), e); // 注意不要在这里调用session.recover()这取决于你的重试策略设计。 // 通常让Broker的重发机制处理即可。 } catch (Exception e) { // 4. 系统异常同样不确认等待重试或进入死信队列 log.error(处理订单时发生系统异常, e); throw e; // 抛出异常容器会知道处理失败 } }这种模式将消息确认的时机牢牢掌握在自己手里可以更灵活地与本地数据库事务用Transactional结合。但务必注意手动确认后消息就从Broker中删除了如果后续业务逻辑在acknowledge()调用之后失败消息就无法恢复了。因此确保acknowledge()是业务成功的最后一步。4. 持久化、重试与死信队列的“生存法则”在生产环境中消息的可靠性是生命线。ActiveMQ与SpringBoot整合时关于消息持久化、消费失败重试和死信队列的配置需要仔细考量。4.1 消息持久化的级别选择ActiveMQ支持多种持久化方式如KahaDB、JDBC、LevelDB等。对于SpringBoot应用我们主要关注消息的发送模式。坑点十一默认是持久化消息。JmsTemplate发送的消息默认是DeliveryMode.PERSISTENT。这意味着消息会被存储到Broker的持久化存储中即使Broker重启消息也不会丢失。这是生产环境的推荐设置。如果你发送的是非关键性的日志或实时状态消息可以设置为非持久化以提升性能。// 通过JmsTemplate发送非持久化消息 jmsTemplate.setDeliveryMode(DeliveryMode.NON_PERSISTENT); // 或者针对某次发送设置 jmsTemplate.convertAndSend(destination, message, postProcessor - { postProcessor.setDeliveryMode(DeliveryMode.NON_PERSISTENT); return postProcessor; });重要提示非持久化消息在Broker内存不足或重启时会丢失。务必根据业务重要性做出选择。4.2 消费失败的重试策略当消费者抛出异常消息未被确认时Broker会重新投递。ActiveMQ Broker端可以配置全局的重发策略Redelivery Policy包括最大重试次数、初始重试延迟、延迟倍数等。但更常见的做法是在消费者端即Spring的DefaultMessageListenerContainer层面进行控制。坑点十二无限重试的噩梦。如果代码有Bug导致某条消息永远处理失败默认情况下Broker会无限次重发塞满你的错误日志并占用消费者线程。我们必须配置最大重试次数。这通常通过配置一个RedeliveryPolicy并关联到ActiveMQConnectionFactory来实现但更Spring Boot的方式是利用其自带的Retry机制。不过对于JMS监听器更直接的是使用DefaultJmsListenerContainerFactory的相关属性并结合ErrorHandler。一个实用的模式是配置一个DefaultJmsListenerContainerFactory并设置ErrorHandler来捕获异常决定消息的最终去向比如重试N次后转发到死信队列。首先你需要定义一个RedeliveryPolicy这实际上是ActiveMQ客户端的策略对于Broker端的重发需要在Broker配置中设置但客户端策略可以影响连接行为。Bean public ConnectionFactory jmsConnectionFactory() { ActiveMQConnectionFactory factory new ActiveMQConnectionFactory(brokerUrl); // ... 其他配置信任包等 RedeliveryPolicy policy new RedeliveryPolicy(); policy.setMaximumRedeliveries(3); // 最大重试3次不含第一次 policy.setInitialRedeliveryDelay(5000); // 首次重试延迟5秒 policy.setUseExponentialBackOff(true); // 启用指数退避 policy.setBackOffMultiplier(2); // 退避倍数 factory.setRedeliveryPolicy(policy); PooledConnectionFactory pooledFactory new PooledConnectionFactory(); pooledFactory.setConnectionFactory(factory); // ... 池化配置 return pooledFactory; }这个策略会在客户端层面控制重试。但对于JmsListener更精细的控制需要实现org.springframework.util.ErrorHandler接口。Component public class JmsErrorHandler implements ErrorHandler { private static final Logger log LoggerFactory.getLogger(JmsErrorHandler.class); Override public void handleError(Throwable t) { // 这里可以获取到导致错误的原消息但需要一些技巧通常需要自定义MessageListenerAdapter log.error(JMS监听器发生未捕获的异常消息处理失败。, t); // 在此处你可以将错误信息和如果可能消息内容记录到数据库或特定日志文件 // 但无法直接在此处将消息转发到死信队列除非你有访问原JMS Session和Message的上下文。 } }然后在容器工厂中设置这个错误处理器Bean public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory, JmsErrorHandler errorHandler) { DefaultJmsListenerContainerFactory factory new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(connectionFactory); factory.setErrorHandler(errorHandler); // 设置自定义错误处理器 // ... 其他配置 return factory; }4.3 死信队列DLQ的配置与处理当一条消息重试达到最大次数后仍然失败ActiveMQ Broker会将其移入死信队列Dead Letter Queue默认名称通常是ActiveMQ.DLQ。这是一个非常重要的保障机制防止“毒药消息”阻塞正常队列。坑点十三默认的DLQ可能成为“垃圾场”。所有队列的失败消息默认都进入同一个ActiveMQ.DLQ不便于区分和处理。我们需要为重要的业务队列配置独立的死信队列。这需要在ActiveMQ Broker的配置文件中通常是activemq.xml进行设置定义个性化的死信队列策略。broker ... destinationPolicy policyMap policyEntries !-- 为所有队列设置默认策略 -- policyEntry queue deadLetterStrategy !-- individualDeadLetterStrategy 为每个队列创建独立的DLQ queuePrefixDLQ队列名前缀 useQueueForQueueMessages对队列消息使用队列DLQ默认true -- individualDeadLetterStrategy queuePrefixDLQ. useQueueForQueueMessagestrue processExpiredfalse / !-- 是否处理过期消息按需 -- /deadLetterStrategy !-- 可以在这里配置Redelivery Policy (Broker端) -- redeliveryPolicy redeliveryPolicy maximumRedeliveries3 initialRedeliveryDelay5000 useExponentialBackOfftrue backOffMultiplier2/ /redeliveryPolicy /policyEntry /policyEntries /policyMap /destinationPolicy /broker这样配置后如果队列order.queue有消息处理失败超过3次就会被移动到名为DLQ.order.queue的死信队列中。在你的SpringBoot消费者应用中可以专门监听这些死信队列进行告警、人工干预或数据修复。Component public class DlqMessageListener { JmsListener(destination DLQ.order.queue) public void handleDlqMessage(Message message) throws JMSException { // 1. 记录详细的错误信息包括消息ID、原始目的地、重试次数等 String messageId message.getJMSMessageID(); String originalQueue message.getStringProperty(originalQueue); // 可能需要你在生产消息时设置这个属性 log.error(收到死信消息。消息ID: {}, 原始队列: {}, messageId, originalQueue); // 2. 可以解析消息体尝试诊断失败原因 if (message instanceof TextMessage) { String text ((TextMessage) message).getText(); log.error(死信消息内容: {}, text); } // 3. 触发告警发送邮件、短信、钉钉等 alertService.sendAlert(订单处理死信告警, 消息ID: messageId); // 注意处理完死信消息后通常需要手动确认或将其转移到其他存储如数据库进行归档。 // 这里简单确认从DLQ中移除。 message.acknowledge(); } }5. 监控、排查与性能调优要点系统上线后监控和排查问题同样重要。整合ActiveMQ和SpringBoot有几个关键点需要关注。5.1 连接与会话泄漏排查这是最常见的问题之一。症状可能是ActiveMQ Broker的连接数不断增长直至达到上限或者应用出现JMSException: Could not connect to broker。排查工具ActiveMQ Web Console通过http://broker-host:8161/admin/默认访问查看Connections、Queues、Topics的状态。重点关注连接数、会话数、消费者数量是否与你的应用配置相符。应用日志确保连接池如PooledConnectionFactory的日志级别设置为DEBUG或TRACE可以查看连接的创建和关闭情况。线程Dump如果怀疑有线程持有JMS会话未释放可以获取应用的线程Dump搜索JMSMessageListenerContainer或Session相关的线程。预防措施始终使用连接池并合理设置maxConnections和idleTimeout。确保你的JmsListener方法不会因为无限循环或长时间阻塞而阻止会话关闭。在消费方法中避免在catch块中捕获所有异常然后“吞掉”这可能导致消息既没确认也没回滚会话状态异常。5.2 消息堆积与消费延迟分析如果发现队列中消息堆积消费速度跟不上生产速度。可能原因及对策可能原因排查方向解决方案消费者并发度不足查看ActiveMQ控制台该队列的Number of Consumers数量。增加JmsListener的concurrency如从“1”调整为“5-10”。消费逻辑耗时过长检查应用日志计算单个消息处理时间。优化消费端业务逻辑。考虑异步处理、批量处理或将耗时操作剥离。网络或Broker性能瓶颈监控Broker所在服务器的CPU、内存、磁盘IO。检查网络延迟。升级Broker硬件/配置。考虑集群化部署ActiveMQ。对于非关键消息使用非持久化模式。生产者流量激增对比生产速度和消费速度的历史数据。引入流量控制Producer Flow Control或在生产者端进行限流。增加消费者应用实例数。使用Spring Boot Actuator监控如果引入了spring-boot-starter-activemqActuator会提供/actuator/metrics/jms等相关端点可以监控消息发送和接收的速率有助于定位问题。5.3 序列化与兼容性问题的预防在分布式环境下生产者和消费者可能独立部署和升级。最佳实践定义契约使用JSON等文本格式或Protobuf等二进制格式作为消息体并明确定义消息模式Schema。向后兼容在更新DTO时尽量只添加字段不删除或修改现有字段。使用JsonIgnoreProperties(ignoreUnknown true)Jackson注解来反序列化以容忍未知字段。版本号在消息头Header或消息体中加入版本号字段消费者根据版本号决定如何解析。集成测试建立包含真实ActiveMQ Broker的集成测试在部署前验证生产者和消费者的兼容性。5.4 一个完整的配置类示例最后贴一个我项目中经过打磨的相对完整的配置类它集成了连接池、手动确认、错误处理等特性Configuration EnableJms public class ActiveMQConfig { Value(${spring.activemq.broker-url}) private String brokerUrl; Bean public ActiveMQConnectionFactory activeMQConnectionFactory() { ActiveMQConnectionFactory factory new ActiveMQConnectionFactory(brokerUrl); // 安全设置信任的包严禁信任所有包 ListString trustedPackages new ArrayList(); trustedPackages.add(com.yourcompany.project.messaging.dto); factory.setTrustedPackages(trustedPackages); factory.setTrustAllPackages(false); // 客户端重试策略 RedeliveryPolicy policy new RedeliveryPolicy(); policy.setMaximumRedeliveries(3); policy.setInitialRedeliveryDelay(5000L); // 5秒 policy.setUseExponentialBackOff(true); policy.setBackOffMultiplier(2.0); factory.setRedeliveryPolicy(policy); return factory; } Bean public ConnectionFactory jmsConnectionFactory(ActiveMQConnectionFactory activeMQConnectionFactory) { PooledConnectionFactory pooledFactory new PooledConnectionFactory(); pooledFactory.setConnectionFactory(activeMQConnectionFactory); pooledFactory.setMaxConnections(10); pooledFactory.setMaximumActiveSessionPerConnection(50); pooledFactory.setIdleTimeout(30 * 1000L); // 30秒 // 启用连接池内部统计便于监控 pooledFactory.setStatisticsEnabled(true); return pooledFactory; } Bean public JmsTemplate jmsTemplate(ConnectionFactory jmsConnectionFactory) { JmsTemplate jmsTemplate new JmsTemplate(jmsConnectionFactory); // 默认持久化消息 jmsTemplate.setDeliveryPersistent(true); // 可以设置默认目的地但建议显式指定 // jmsTemplate.setDefaultDestinationName(default.queue); return jmsTemplate; } Bean public DefaultJmsListenerContainerFactory jmsListenerContainerFactory( ConnectionFactory jmsConnectionFactory, JmsErrorHandler jmsErrorHandler) { DefaultJmsListenerContainerFactory factory new DefaultJmsListenerContainerFactory(); factory.setConnectionFactory(jmsConnectionFactory); factory.setConcurrency(2-5); // 根据实际负载调整 factory.setSessionTransacted(false); // 不使用JMS事务 factory.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE); // 手动确认 factory.setErrorHandler(jmsErrorHandler); // 自定义错误处理 // 设置消息转换器如果发送的是JSON文本 // factory.setMessageConverter(jacksonJmsMessageConverter()); return factory; } // 如果需要JSON消息转换 // Bean // public MessageConverter jacksonJmsMessageConverter() { // MappingJackson2MessageConverter converter new MappingJackson2MessageConverter(); // converter.setTargetType(MessageType.TEXT); // converter.setTypeIdPropertyName(_type); // 用于反序列化时识别类型 // return converter; // } }整合ActiveMQ与SpringBoot是一个细致活每一个配置项背后都可能对应着一个生产环境中的“坑”。从依赖版本、连接池、序列化安全到消费者并发、事务确认、死信处理每一步都需要结合具体的业务场景仔细斟酌。我的经验是在开发测试阶段就尽可能模拟生产环境的压力和不稳定情况充分测试消息的发送、消费、重试、死信等完整链路才能让这套组合在实际运行中真正地可靠、高效。

相关新闻

崩坏星穹铁道三月七小助手使用指南:四个场景看懂日常与周常的一键全自动托管

崩坏星穹铁道三月七小助手使用指南:四个场景看懂日常与周常的一键全自动托管

崩坏星穹铁道三月七小助手使用指南:四个场景看懂日常与周常的一键全自动托管 【免费下载链接】March7thAssistant 崩坏:星穹铁道全自动 三月七小助手 项目地址: https://gitcode.com/gh_mirrors/ma/March7thAssistant 三月七小助手(Ma…

2026/10/17 20:29:03 阅读更多 →
NAS共享协议全解析:SMB、NFS、FTP、WebDAV选型与配置实战

NAS共享协议全解析:SMB、NFS、FTP、WebDAV选型与配置实战

1. 从“文件柜”到“网络枢纽”:NAS共享协议的角色演进如果你刚接触NAS,可能会觉得它就是个插着网线的大号移动硬盘。但当你真正开始用它来备份照片、共享电影给家人,或者作为团队的项目文件中心时,很快就会发现一个核心问题&…

2026/10/17 20:29:56 阅读更多 →
Leaflet + eegeo.js:低成本实现Web二维地图三维化升级实践

Leaflet + eegeo.js:低成本实现Web二维地图三维化升级实践

1. 项目缘起:当二维地图遇上三维世界 作为一名常年和地图打交道的开发者,我经历过从静态图片到动态瓦片,再到如今追求沉浸式体验的完整周期。Leaflet,这个轻量级的开源地图库,几乎成了WebGIS领域的“瑞士军刀”&#x…

2026/10/7 7:41:10 阅读更多 →

最新新闻

2026年AI编程工具全解析:33款主流助手与IDE选型指南

2026年AI编程工具全解析:33款主流助手与IDE选型指南

2024年大家还在争论“AI能不能写代码”,到2026年这个问题基本不需要讨论了。我今年在好几个项目里把AI编程工具从“偶尔试用”变成了“日常主力”,从代码补全到自动化测试再到完整功能模块的实现,工具链已经成熟得超出预期。但问题也变成了另…

2026/10/17 21:02:08 阅读更多 →
Vibe Coding第二课:零基础搭建个人主页和数字分身

Vibe Coding第二课:零基础搭建个人主页和数字分身

Vibe Coding这个词,最近在开发者圈子里被反复讨论,但对真正零基础的人来说,它最诱人的点其实特别简单:你不需要先把编程学个半吊子,就能做出能给别人看的东西。第二课我们直接来干一件特别实在的事——从0到1搭一个你自…

2026/10/17 21:02:08 阅读更多 →
分步傅里叶法解NLS方程:MATLAB仿真孤子传输实战

分步傅里叶法解NLS方程:MATLAB仿真孤子传输实战

简介:非线性光纤光学与光通信领域的学习者、研究人员,常需掌握分步傅里叶法求解非线性薛定谔方程(NLS)的数值实现。该资源包提供了一份MATLAB源代码及详细解析文档,内容涵盖NLS方程建模、分步傅里叶法原理、代码结构梳…

2026/10/17 21:02:08 阅读更多 →
MATLAB调用外部软件:控件化封装与COM/system/MEX选型实战

MATLAB调用外部软件:控件化封装与COM/system/MEX选型实战

1. 项目缘起与整体设计思路1.1 为什么要在MATLAB里“调用”外部软件做过工程仿真或者数据处理的朋友大概率遇到过这种场景:手头有一套用MATLAB写的算法主流程,但某个关键环节依赖一个独立的第三方软件——可能是某个专业的求解器、一个老旧的C计算程序、…

2026/10/17 21:02:08 阅读更多 →
全网最全Python课程PDF如何高效利用:拆目录、验代码、避坑指南

全网最全Python课程PDF如何高效利用:拆目录、验代码、避坑指南

简介:一份以 PDF 文档形式整理的全网 Python 系统课程学习路线,适合零基础入门到希望进阶的开发者使用。文档提炼了 637 集课程的整体框架,按 Python 基础、进阶、终极、爬虫四篇展开,覆盖 Python 基础语法、坦克大战实战、网络并…

2026/10/17 21:02:08 阅读更多 →
约瑟夫环C语言循环链表实现:从建环到出圈的完整方案

约瑟夫环C语言循环链表实现:从建环到出圈的完整方案

简介:这是一份面向C语言初学者与数据结构学习者的约瑟夫死亡游戏完整实现资料,围绕循环链表这一核心结构展开,帮助读者理解环形链表的创建、遍历与结点删除等经典操作。资源包内仅含1个docx文档,大小约18KB,以文字形式…

2026/10/17 21:01:07 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/16 21:35:51 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/17 1:44:59 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 21:54:38 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/16 14:23:36 阅读更多 →