JMS 消息传送模式、消息签收以及spring jmsTemplate配置

本文涉及的产品
RDS MySQL DuckDB 分析主实例,集群系列 4核8GB
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
简介: JMS 即 java message service 是为java提供了一种创建、发送、接收消息的通用方法。可以将复杂的系统进行业务分离,变成灵活的高度解耦合的布局。

JMS 即 java message service 是为java提供了一种创建、发送、接收消息的通用方法。可以将复杂的系统进行业务分离,变成灵活的高度解耦合的布局。同时对我们的日常业务需求开发,提供了非常灵活的业务解决方案。比如缴费还款送积分,送积分的业务逻辑不能影响到缴费还款的业务逻辑,所以最好的,就是缴费/还款逻辑执行完成之后,通过一种方式告诉积分系统,给用户发送积分,发送积分的结果不要影响到复杂的缴费还款的过程。这种情况下,采用jms进行异步处理,便是一个很好的选择。

要使用消息的方式来进行系统交互,我们需要一个消息中间平台,来进行消息的接受转发,同时处理复杂的消息持久化等问题。本文我们采用activemq来做实验。这样的架构下,我们的系统通常会变成如下架构:

 消息产生者 -> 消息中心 -> 消息消费者


1、消息的两种传播方式

JMS支持两种消息传播:PTP 和 PUB/SUB

PTP : 点对点发送。消息的发送方将消息放入管道中,消息的接收方从管道中取出消息并处理。

PUB/SUB : 发布/订阅方式。消息的发布者将自己的主题放入消息中心,同时进行消息投递,消息订阅者只获取自己订阅的消息。

jms为了支持上述两种模式,提供了两套针对同样接口的实现,对照关系如下:

ConnectionFacatory:被管理的对象,由客户端(发布者/接受者)使用,用来创建一个链接。

Connection:提供一个JMS消息的活动链接。

Destination:封装了消息目的地,或者主题类型。

Session:一个用来发送和接受消息的线上上下文。

MessageProducer:由session创建的,用来发送消息的对象。

MessageConsumer:由session创建的用来接受消息的对象。

2、jms消息模型

Jms的消息分为三部分:消息头、消息属性、消息体

消息头:包含了消息的客户端和提供者用来路由和识别消息的数据。

 消息头包含的字段:

 JMSDestination:包含了消息发往的目的地或者主题信息。

 JMSDeliveryMode:消息传送模式。spring提供额jms模板提供了2种模式(有默认模式):DEFAULT_DELEVERY_MODE 默认模式、DEFAULT_PRIORITY、DEFAULT_TIME_TO_LIVE

 JMSMessageID:消息标示,唯一性,每个消息都不同,即便是承载者相同消息体的消息。

 JMSTimestamp:发送时间

 JMSCorrelationID:与当前消息关联的其他消息的标示

 JMSReplyTo:回复消息的目的地。带有这样属性的消息通常是发送方希望有一个响应,这个响应是可选的。

 JMSRedelivered:带有该字段的消息通常过去发送过但是没有被确认,如果要再次发送,提供者必须设置该字段。如果true,则消息接受者必须进行消息重复处理的逻辑。

 JMSType:消息类型标示。官方文档的解释:

 

JMSType头字段包含了由客户端在发送消息时提供的消息类型标识。一些消息提供者使用消息库来存储由应用发送的消息定义。type头字段可以引用提供者库中的消息定义。JMS没有定义一个标准的消息定义库,也没有定义这个库中所包含的各种定义的命名策略。一些消息系统要求每个被创建的应用消息都必须有一个消息类型定义,并且每个消息都指定它的类型。为了能够使JMS工作于这些消息系统提供者,无论应用是否使用,JMS客户端最好赋值JMSType ,这样可以保证为需要该头字段的提供者提供了正确的设置。为了保证移植性,JMS客户端应使用安装时在提供者消息库中定义的语义值作为JMSType的值。

 JMSExpiration :过期时间。

 JMSPriority:优先级。

消息属性:包括了标准投资段之外的额外添加给消息的可选的字段。比如 应用指定的属性。

消息体:消息携带的内容。

3、消息传输编程步骤

 1)使用jndi获取一个ConnectionFacatory对象;

 2)使用jndi获取一个或者多个Destination对象;

 3)使用ConnectionFactory创建一个JMS连接;

 4)使用连接创建Jms session;

 5)使用session和destination创建MessageProducers和MessageConsumers

 6)使用Connection进行传输消息;

上述是jms的基础知识,简单了解可以便于下面的应用。jms本身提供了jar可以下载并使用相关配置,结合消息系统来完成消息的发送和接受等操作。但是一种便捷的方式,为加快开发,可以使用spring提供的jms模板,即JmsTemplate,这个类似于jdbcTemplate。

我们演示PTP和PUB/SUB两种模式的配置。

先看下基础公用的类:

我们定义:消息发送者、消息接受者、消息转换器

public class MessageSender {  
    // ~~~ jmsTemplate  
    public JmsTemplate jmsTemplate;  
      
    /** 
     * send message 
     */  
    public void sendMessage(){  
        jmsTemplate.convertAndSend("hello jms!");  
    }  
    public void setJmsTemplate(JmsTemplate jmsTemplate) {  
        this.jmsTemplate = jmsTemplate;  
    }  
}  

public class MessageReceiver implements MessageListener {  
    /* (non-Javadoc) 
     * @see javax.jms.MessageListener#onMessage(javax.jms.Message) 
     */  
    public void onMessage(Message m) {  
        System.out.println("[receive message]");  
          
        ObjectMessage om = (ObjectMessage) m;  
        try {  
            String key1 = om.getStringProperty("key1");  
              
            System.out.println(key1);  
              
            System.out.println("model:"+om.getJMSDeliveryMode());  
            System.out.println("destination:"+om.getJMSDestination());  
            System.out.println("type:"+om.getJMSType());  
            System.out.println("messageId:"+om.getJMSMessageID());  
            System.out.println("time:"+om.getJMSTimestamp());  
            System.out.println("expiredTime:"+om.getJMSExpiration());  
            System.out.println("priority:"+om.getJMSPriority());  
        } catch (JMSException e) {  
            e.printStackTrace();  
        }  
    }  
}  

public class MessageConvertForSys implements MessageConverter {  
    /* (non-Javadoc) 
     * @see org.springframework.jms.support.converter.MessageConverter#toMessage(java.lang.Object, javax.jms.Session) 
     */  
    public Message toMessage(Object object, Session session)  
            throws JMSException, MessageConversionException {  
        System.out.println("[toMessage]");  
        ObjectMessage objectMessage = session.createObjectMessage();  
          
        objectMessage.setJMSExpiration(1000);  
        objectMessage.setStringProperty("key1", object+"_add");  
          
        return objectMessage;  
    }  
    /* (non-Javadoc) 
     * @see org.springframework.jms.support.converter.MessageConverter#fromMessage(javax.jms.Message) 
     */  
    public Object fromMessage(Message message) throws JMSException,  
            MessageConversionException {  
        System.out.println("[fromMessage]");  
        ObjectMessage objectMessage = (ObjectMessage) message;  
          
        return objectMessage.getObjectProperty("key1");  
    }  
} 

第一种,PTP方式的配置:

<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  
    xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:aop="http://www.springframework.org/schema/aop"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans   
    http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
    http://www.springframework.org/schema/context   
    http://www.springframework.org/schema/context/spring-context-2.5.xsd "  
    default-autowire="byName">  
      
    <!-- JMS PTP MODEL -->  
    <!-- PTP链接工厂 -->  
    <bean id="queueConnectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory">  
        <property name="brokerURL" value="tcp://127.0.0.1:61616" />  
        <!-- <property name="brokerURL" value="vm://normandy.notify" /> -->  
        <property name="useAsyncSend" value="true" />  
    </bean>  
    <!-- 定义消息队列 -->  
    <bean id="dest" class="org.apache.activemq.command.ActiveMQQueue">  
        <constructor-arg value="queueDest" />  
    </bean>  
    <!-- PTP jms模板 -->  
    <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">  
        <property name="connectionFactory" ref="queueConnectionFactory"></property>  
        <property name="defaultDestination" ref="dest" />  
        <property name="messageConverter" ref="messageConvertForSys" />  
        <property name="pubSubDomain" value="false" />  
    </bean>  
      
    <!-- 消息转换器 -->  
    <bean id="messageConvertForSys" class="com.normandy.tech.test.MessageConvertForSys"></bean>  
      
    <!-- 消息发送方 -->  
    <bean id="messageSender" class="com.normandy.tech.test.MessageSender"></bean>  
    <!-- 消息接收方 -->  
    <bean id="messageReceiver" class="com.normandy.tech.test.MessageReceiver"></bean>  
      
    <!-- 消息监听容器 -->  
    <bean id="listenerContainer"    
        class="org.springframework.jms.listener.DefaultMessageListenerContainer">    
        <property name="connectionFactory" ref="queueConnectionFactory" />    
        <property name="destination" ref="dest" />    
        <property name="messageListener" ref="messageReceiver" />    
    </bean>  
</beans>

第二种:PUB/SUB方式的配置

我们配置两个消息订阅者,分别订阅不同的消息,这样用于判断是否成功执行了消息的发布和消息的订阅

    <?xml version="1.0" encoding="UTF-8"?>  
    <beans xmlns="http://www.springframework.org/schema/beans"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"  
        xmlns:context="http://www.springframework.org/schema/context"  
        xmlns:aop="http://www.springframework.org/schema/aop"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans   
        http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
        http://www.springframework.org/schema/context   
        http://www.springframework.org/schema/context/spring-context-2.5.xsd "  
        default-autowire="byName">  
          
        <!-- JMS TOPIC MODEL -->  
        <!-- TOPIC链接工厂 -->  
        <bean id="topicSendConnectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory">  
            <property name="brokerURL" value="tcp://127.0.0.1:61616" />  
            <property name="useAsyncSend" value="true" />  
        </bean>  
          
        <bean id="topicListenConnectionFactory" class="org.apache.activemq.spring.ActiveMQConnectionFactory">  
            <property name="brokerURL" value="tcp://127.0.0.1:61616" />  
        </bean>  
          
        <!-- 定义主题 -->  
        <bean id="myTopic"  class="org.apache.activemq.command.ActiveMQTopic">  
          <constructor-arg value="normandy.topic"/>  
        </bean>  
          
        <bean id="myTopic2"  class="org.apache.activemq.command.ActiveMQTopic">  
          <constructor-arg value="normandy.topic2"/>  
        </bean>  
          
        <!-- 消息转换器 -->  
        <bean id="messageConvertForSys" class="com.normandy.tech.test.MessageConvertForSys"></bean>  
          
        <!-- TOPIC send jms模板 -->  
        <bean id="topicSendJmsTemplate" class="org.springframework.jms.core.JmsTemplate">  
            <property name="connectionFactory" ref="topicSendConnectionFactory"></property>  
            <property name="defaultDestination" ref="myTopic" />  
            <property name="messageConverter" ref="messageConvertForSys" />  
            <!-- 开启订阅模式 -->  
            <property name="pubSubDomain" value="true"/>  
        </bean>  
          
        <!-- 消息发送方 -->  
        <bean id="topicMessageSender" class="com.normandy.tech.test.MessageSender">  
            <property name="jmsTemplate" ref="topicSendJmsTemplate"></property>  
        </bean>  
          
        <!-- 消息接收方 -->  
        <bean id="topicMessageReceiver" class="com.normandy.tech.test.MessageReceiver">  
        </bean>  
          
        <!-- 主题消息监听容器 -->  
        <bean id="listenerContainer"    
            class="org.springframework.jms.listener.DefaultMessageListenerContainer">    
            <property name="connectionFactory" ref="topicListenConnectionFactory" />    
            <property name="pubSubDomain" value="true"/><!-- default is false -->  
            <property name="destination" ref="myTopic" />  <!-- listen topic: myTopic -->  
            <property name="subscriptionDurable" value="true"/>  
            <property name="clientId" value="clientId_001"/>  
            <property name="messageListener" ref="topicMessageReceiver" />    
        </bean>  
          
        <!-- 主题消息监听容器2 -->  
        <bean id="listenerContainer2"    
            class="org.springframework.jms.listener.DefaultMessageListenerContainer">    
            <property name="connectionFactory" ref="topicListenConnectionFactory" />    
            <property name="pubSubDomain" value="true"/><!-- default is false -->  
            <property name="destination" ref="myTopic2" />  <!-- listen topic: myTopic2 -->  
            <property name="subscriptionDurable" value="true"/>  
            <property name="clientId" value="clientId_002"/>  
            <property name="messageListener" ref="topicMessageReceiver" />    
        </bean>  
    </beans>  

单元测试代码:

    public class TechTest extends TestCase {  
        ApplicationContext ptpApplicationContext;  
        ApplicationContext topicApplicationContext;  
          
        @Override  
        protected void setUp() throws Exception {  
            super.setUp();  
            ptpApplicationContext = new ClassPathXmlApplicationContext(  
                    "com/normandy/tech/test/ptpContext.xml");  
            topicApplicationContext = new ClassPathXmlApplicationContext(  
                    "com/normandy/tech/test/topicContext.xml");  
        }  
          
        protected Object getPtpBean(String name) {  
            return ptpApplicationContext.getBean(name);  
        }  
          
        protected Object getTopicBean(String name) {  
            return topicApplicationContext.getBean(name);  
        }  
    }  

public class JmsQueueTest extends TechTest {  
      
    /** 
     * 测试消息发送 
     */  
    public void testQueueSend() {  
        long beginTime = System.currentTimeMillis();  
        // PTP  
//      MessageSender messageSender = (MessageSender) getPtpBean("messageSender");  
//      messageSender.sendMessage();  
          
        // TOPIC  
        MessageSender messageSender = (MessageSender) getTopicBean("topicMessageSender");  
        messageSender.sendMessage();  
        System.out.println("cost time:"+ (System.currentTimeMillis() - beginTime));  
    }  
}  

测试结果执行便可。

在这里,消息系统我们采用的是activemq,试想一个问题,如果消息过多,这个时候发生了宕机,消息是否会丢失?

这里就涉及到了一个新问题,即消息持久化。

activemq的消息持久化分成两种:文件和数据库(支持MySQL/oracle)。可以再其配置文件中进行配置,activemq配置文件采用的是spring的方式,所以配置起来非常的方便。

通常下载了activemq后,会有一系列的配置文件demo,可以参照其中的样例修改即可。

这里我们使用mysql作为消息持久化的数据库服务器。

将mysql的驱动包,拷贝到activemq的lib目录,配置如下:

conf/activemq.xml

    <persistenceAdapter>  
                <!--<kahaDB directory="${activemq.base}/data/kahadb"/>-->  
                 <jdbcPersistenceAdapter dataDirectory="${activemq.base}/data" dataSource="#mysql-ds"/>  
            </persistenceAdapter>  

<bean id="mysql-ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">  
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>  
        <property name="url" value="jdbc:mysql://localhost/activemq?relaxAutoCommit=true"/>  
        <property name="username" value="root"/>  
        <property name="password" value="root"/>  
        <property name="maxActive" value="200"/>  
        <property name="poolPreparedStatements" value="true"/>  
    </bean> 

特别注意的是,这里指定的数据库名称,需要事先在mysql中创建好schema。

运行activemq,可以发现自动创建了三张表:

activemq_acks

activemq_lock

activemq_msgs


JMS 消息传送模式、消息签收以及spring jmsTemplate配置

Activemq支持两种消息传送模式:PERSISTENT (持久消息)和 NON_PERSISTENT(非持久消息)

从字面意思就可以了解,这是两种正好相反的模式。

1、PERSISTENT 持久消息

是activemq默认的传送方式,此方式下的消息在配合activemq.xml中配置的消息存储方式,会被存储在特定的地方,直到有消费者将消息消费或者消息过期进入DLQ队列,消息生命周期才会结束。

此模式下可以保证消息只会被成功传送一次和成功使用一次,消息具有可靠性。在消息传递到目标消费者,在消费者没有成功应答前,消息不会丢失。所以很自然的,需要一个地方来持久性存储。

如果消息消费者在进行消费过程发生失败,则消息会被再次投递。

 

2、NON_PERSISTENT 非持久消息

非持久的消息适用于不重要的,可以接受消息丢失的哪一类消息,这种消息只会被投递一次,消息不会在持久性存储中存储,也不会保证消息丢失后的重新投递。

在spring提供的JmsTemplate中,同样提供了针对于当前功能的配置选项:

<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">  
        <property name="connectionFactory" ref="cachingConnectionFactory"></property>  
        <property name="defaultDestination" ref="dest" />  
        <property name="messageConverter" ref="messageConverter" />  
        <property name="pubSubDomain" value="false" />  
        <property name="explicitQosEnabled" value="true" /> <!-- deliveryMode, priority, timeToLive 的开关,要生效,必须配置为true,默认false-->  
        <property name="deliveryMode" value="1" /> <!-- 发送模式  DeliveryMode.NON_PERSISTENT=1:非持久 ; DeliveryMode.PERSISTENT=2:持久-->  
    </bean> 

消息的签收模式:

客户端成功接收一条消息的标志是一条消息被签收,成功应答。

消息的签收情形分两种:

1、带事务的session

 如果session带有事务,并且事务成功提交,则消息被自动签收。如果事务回滚,则消息会被再次传送。

2、不带事务的session

 不带事务的session的签收方式,取决于session的配置。

Activemq支持一下三种模式:

  Session.AUTO_ACKNOWLEDGE  消息自动签收
  Session.CLIENT_ACKNOWLEDGE  客户端调用acknowledge方法手动签收
  Session.DUPS_OK_ACKNOWLEDGE 不必必须签收,消息可能会重复发送。在第二次重新传递消息的时候,消息头的JmsDelivered会被置为true标示当前消息已经传送过一次,客户端需要进行消息的重复处理控制。

spring提供的JmsTemplate中的配置方式:

    <!-- PTP jms模板 -->  
        <bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">  
            <property name="connectionFactory" ref="cachingConnectionFactory"></property>  
            <property name="defaultDestination" ref="dest" />  
            <property name="messageConverter" ref="messageConverter" />  
            <property name="pubSubDomain" value="false" />  
            <property name="sessionAcknowledgeMode" value="1" />   
    <!-- 消息应答方式  
            Session.AUTO_ACKNOWLEDGE  消息自动签收  
            Session.CLIENT_ACKNOWLEDGE  客户端调用acknowledge方法手动签收  
            Session.DUPS_OK_ACKNOWLEDGE 不必必须签收,消息可能会重复发送  
            -->  
        </bean>  


相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
7月前
|
负载均衡 监控 Java
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
本文详细介绍了 Spring Cloud Gateway 的核心功能与实践配置。首先讲解了网关模块的创建流程,包括依赖引入(gateway、nacos 服务发现、负载均衡)、端口与服务发现配置,以及路由规则的设置(需注意路径前缀重复与优先级 order)。接着深入解析路由断言,涵盖 After、Before、Path 等 12 种内置断言的参数、作用及配置示例,并说明了自定义断言的实现方法。随后重点阐述过滤器机制,区分路由过滤器(如 AddRequestHeader、RewritePath、RequestRateLimiter 等)与全局过滤器的作用范围与配置方式,提
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
|
7月前
|
Java 关系型数据库 MySQL
Spring Boot自动配置:魔法背后的秘密
Spring Boot 自动配置揭秘:只需简单配置即可启动项目,背后依赖“约定大于配置”与条件化装配。核心在于 `@EnableAutoConfiguration` 注解与 `@Conditional` 系列条件判断,通过 `spring.factories` 或 `AutoConfiguration.imports` 加载配置类,实现按需自动装配 Bean。
|
人工智能 Java Serverless
【MCP教程系列】搭建基于 Spring AI 的 SSE 模式 MCP 服务并自定义部署至阿里云百炼
本文详细介绍了如何基于Spring AI搭建支持SSE模式的MCP服务,并成功集成至阿里云百炼大模型平台。通过四个步骤实现从零到Agent的构建,包括项目创建、工具开发、服务测试与部署。文章还提供了具体代码示例和操作截图,帮助读者快速上手。最终,将自定义SSE MCP服务集成到百炼平台,完成智能体应用的创建与测试。适合希望了解SSE实时交互及大模型集成的开发者参考。
14797 60
|
7月前
|
人工智能 Java 开发者
【Spring】原理解析:Spring Boot 自动配置
Spring Boot通过“约定优于配置”的设计理念,自动检测项目依赖并根据这些依赖自动装配相应的Bean,从而解放开发者从繁琐的配置工作中解脱出来,专注于业务逻辑实现。
2528 0
|
9月前
|
Java Spring
Spring Boot配置的优先级?
在Spring Boot项目中,配置可通过配置文件和外部配置实现。支持的配置文件包括application.properties、application.yml和application.yaml,优先级依次降低。外部配置常用方式有Java系统属性(如-Dserver.port=9001)和命令行参数(如--server.port=10010),其中命令行参数优先级高于系统属性。整体优先级顺序为:命令行参数 &gt; Java系统属性 &gt; application.properties &gt; application.yml &gt; application.yaml。
1265 0
|
缓存 Java API
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
本文介绍了在Spring Boot中配置Swagger2的方法。通过创建一个配置类,添加`@Configuration`和`@EnableSwagger2`注解,使用Docket对象定义API文档的详细信息,包括标题、描述、版本和包路径等。配置完成后,访问`localhost:8080/swagger-ui.html`即可查看接口文档。文中还提示了可能因浏览器缓存导致的问题及解决方法。
1280 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
|
6月前
|
前端开发 Java 应用服务中间件
《深入理解Spring》 Spring Boot——约定优于配置的革命者
Spring Boot基于“约定优于配置”理念,通过自动配置、起步依赖、嵌入式容器和Actuator四大特性,简化Spring应用的开发与部署,提升效率,降低门槛,成为现代Java开发的事实标准。
|
安全 Java API
深入解析 Spring Security 配置中的 CSRF 启用与 requestMatchers 报错问题
本文深入解析了Spring Security配置中CSRF启用与`requestMatchers`报错的常见问题。针对CSRF,指出默认已启用,无需调用`enable()`,只需移除`disable()`即可恢复。对于`requestMatchers`多路径匹配报错,分析了Spring Security 6.x中方法签名的变化,并提供了三种解决方案:分次调用、自定义匹配器及降级使用`antMatchers()`。最后提醒开发者关注版本兼容性,确保升级平稳过渡。
1406 2
|
7月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
1336 5