Spring Boot 生产消费模式示例

4b8ab9b15b78885cdc22e08ac8f7d4a1.png

生产者消费者模型

在并发编程中 使用生产者和消费者模式能够解决绝大多数并发问题。

该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。

1.什么是生产者消费者模式?

    生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

2.为什么要使用生产者和消费者模式?

在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。

在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。

如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。

为了解决这个问题,就在生产者和消费者之间通过队列,增加缓冲,避免了生产者和消费者 之间的交互。于是引入了生产者和消费者模式。

9f1cfb1324aa8c21b2c56e6dc1d2871d.png

一、示例准备

在分布式和微服务架构中,生产消费模式是一种常见的模式,通常用于解耦系统、提高系统的吞吐量和响应速度。本示例将演示如何使用Spring Boot框架,结合线程池、异步执行、MyBatis和MySQL数据库,实现一个每1秒生产1000条数据,而消费者每10秒批量存储一次的生产消费模式。

二、环境准备

Spring Boot: 快速搭建Web应用的基础框架。

MyBatis: 优秀的持久层框架,支持定制化SQL、存储过程以及高级映射。

MySQL: 关系型数据库管理系统。

三、项目结构

生产者(Producer):负责数据的生产,每1秒生成1000条数据,并将数据放入队列或缓冲区。

消费者(Consumer):负责从队列或缓冲区中取出数据,每10秒批量存储到MySQL数据库。

线程池(ThreadPool):用于异步执行消费者的存储操作。

四、实现步骤

1. 创建Spring Boot项目

使用Spring Initializr(https://start.spring.io/)创建一个新的Spring Boot项目,添加必要的依赖spring-boot-starter-web、mybatis-spring-boot-starter、mysql-connector-java等。

2. 配置数据库连接

在application.properties中配置MySQL数据库连接信息。

spring.datasource.url=jdbc:mysql://localhost:3306/weijishu_db  
spring.datasource.username=root  
spring.datasource.password=123456  
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

3. 创建数据模型(Entity)和Mapper

根据业务需求创建数据模型和对应的MyBatis Mapper接口。

c308a3d46285af543c283da1b6cf0371.png

4. 实现生产者(Producer)

创建一个生产者类,使用@Scheduled注解实现每1秒执行一次的定时任务,生成数据并放入队列。

@Component  
public class Producer {  


    @Autowired  
    private BlockingQueue<Data> dataQueue;  


    @Scheduled(fixedRate = 1000) // 每1秒执行一次  
    public void produceData() {  
        for (int i = 0; i < 1000; i++) {  
            Data data = new Data(); // 假设Data是数据模型  
            // ... 初始化数据  
            dataQueue.offer(data);  
        }  
    }  
}

5. 实现消费者(Consumer)

使用@Async注解实现异步执行,并使用线程池。消费者从队列中取出数据,并每10秒批量存储。

@Component  
public class Consumer {  


    @Autowired  
    private DataMapper dataMapper; // 假设DataMapper是MyBatis的Mapper接口  


    @Autowired  
    private ThreadPoolTaskExecutor taskExecutor; // 注入线程池  


    @Scheduled(fixedDelay = 10000) // 每10秒执行一次  
    public void consumeData() {  
        List<Data> dataList = new ArrayList<>();  
        Data data;  
        while ((data = dataQueue.poll()) != null) {  
            dataList.add(data);  
            if (dataList.size() >= 1000 || dataQueue.isEmpty()) { // 可以设置其他批量条件  
                taskExecutor.execute(() -> {  
                    // 异步批量存储  
                    dataMapper.insertDataList(dataList);  
                    dataList.clear(); // 清空列表以便下次使用  
                });  
            }  
        }  
    }  
}

注意:在DataMapper中需要实现insertDataList方法,用于批量插入数据。

6. 配置线程池(ThreadPool)

在Spring Boot的配置类中配置线程池。

@Configuration  
@EnableAsync  
public class AsyncConfig {  


    @Bean(name = "taskExecutor")  
    public Executor taskExecutor() {  
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
        executor.setCorePoolSize(5); // 核心线程数  
        executor.setMaxPoolSize(10); // 最大线程数  
        executor.setQueueCapacity(25); // 队列容量  
        executor.setKeepAliveSeconds(60); // 线程空闲时间  
        executor.setThreadNamePrefix("Async-Service-"); // 线程名称前缀  
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略
        executor.initialize();
        return executor;
    }
}

注意:@EnableAsync注解启用异步方法执行。

7. 启动类  

启动类上添加@SpringBootApplication注解和@EnableScheduling注解来启用定时任务。  

@SpringBootApplication  
@EnableScheduling  
public class Application {  


    public static void main(String[] args) {  
        SpringApplication.run(Application.class, args);  
    }  
}

3bea7765e76fad212557beff67d6eefd.png

五、测试与验证

启动Spring Boot应用。

检查MySQL数据库,确保数据表结构已经创建好。

等待一段时间后,检查数据库中的数据,验证是否每10秒批量插入了数据。

六、说明

本示例演示了如何使用Spring Boot结合MyBatis、MySQL、线程池和异步执行实现生产消费模式。生产者通过定时任务每1秒生成1000条数据并放入队列,消费者则每10秒从队列中取出数据并批量存储到MySQL数据库中。使用线程池和异步执行可以提高系统的吞吐量和响应速度。

在实际应用中,可能还需要考虑数据的去重、错误处理、队列的容量管理、事务处理等问题,以确保系统的稳定性和可用性。此外,也可以考虑使用消息中间件(如RabbitMQ、Kafka等)来实现更可靠、更灵活的生产消费模式。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是 Spring Boot 整合 RabbitMQ 的详细示例: 1. 引入依赖 在 pom.xml 文件中引入以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> ``` 2. 配置 RabbitMQ 在 application.properties 文件中配置 RabbitMQ: ```properties spring.rabbitmq.host=localhost spring.rabbitmq.port=5672 spring.rabbitmq.username=guest spring.rabbitmq.password=guest ``` 3. 创建生产者 创建一个生产者(Producer)类,用于发送消息到 RabbitMQ: ```java @Component public class Producer { @Autowired private RabbitTemplate rabbitTemplate; public void send(String message) { rabbitTemplate.convertAndSend("myExchange", "myRoutingKey", message); } } ``` 4. 创建消费者 创建一个消费者(Consumer)类,用于从 RabbitMQ 接收消息: ```java @Component public class Consumer { @RabbitListener(queues = "myQueue") public void receive(String message) { System.out.println("Received message: " + message); } } ``` 5. 创建队列、交换机、绑定关系 使用 RabbitAdmin 类创建队列、交换机、绑定关系: ```java @Configuration public class RabbitConfig { @Autowired private RabbitTemplate rabbitTemplate; @PostConstruct public void setup() { RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitTemplate.getConnectionFactory()); Queue queue = new Queue("myQueue", true); DirectExchange exchange = new DirectExchange("myExchange", true, false); rabbitAdmin.declareQueue(queue); rabbitAdmin.declareExchange(exchange); rabbitAdmin.declareBinding(BindingBuilder.bind(queue).to(exchange).with("myRoutingKey")); } } ``` 6. 测试 在控制器(Controller)中调用生产者发送消息,测试消费者是否成功接收消息: ```java @RestController public class TestController { @Autowired private Producer producer; @GetMapping("/test") public String test() { producer.send("Hello, RabbitMQ!"); return "Message sent."; } } ``` 以上就是 Spring Boot 整合 RabbitMQ 的详细示例,希望能对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值