SpringBoot 商品下单放进RabbitMQ 和数据用使用redis缓存,提高吞吐量和响应速度。

SpringBoot 商品下单放进RobbitMQ 和数据用使用redis缓存,提高吞吐量和响应速度。

  • 这是一个简单的集成rabbitMQ和redis的例子
    • 项目结构
    • pom依赖配置
    • 代码
    • 压力测试
    • 总结

这是一个简单的集成rabbitMQ和redis的例子

项目结构

SpringBoot 商品下单放进RabbitMQ 和数据用使用redis缓存,提高吞吐量和响应速度。_第1张图片

pom依赖配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.tomato</groupId>
	<artifactId>super-shopping</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>super-shopping</name>
	<description>super-shopping</description>

	<properties>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-amqp</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>
		<!--<dependency>-->
			<!--<groupId>org.springframework.boot</groupId>-->
			<!--<artifactId>spring-boot-starter-security</artifactId>-->
		<!--</dependency>-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.amqp</groupId>
			<artifactId>spring-rabbit-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

代码

启动类 SuperShoppingApplication .java

@SpringBootApplication
@EnableCaching
public class SuperShoppingApplication {

	private static final Logger LOGGER = LoggerFactory.getLogger(SuperShoppingApplication.class);

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

}

Rabbit配置类
RabbitMqConfig

@Configuration
public class RabbitMqConfig {
    public static final String topicExchangeName = "shopping-order-exchange";
    public static final String queueName = "shopping-order";

    @Bean
    Queue queue(){
        return new Queue(queueName,false);
    }

    @Bean
    TopicExchange topicExchange(){
        return new TopicExchange(topicExchangeName);
    }

    @Bean
    Binding binding(Queue queue,TopicExchange topicExchange){
        return BindingBuilder.bind(queue).to(topicExchange).with("shopping.#");
    }

    @Bean
    MessageListenerAdapter rabbitListenerAdapter(RabbitReceiver rabbitReceiver) {
        return new MessageListenerAdapter(rabbitReceiver, "receiverMessage");
    }

    @Bean
    SimpleMessageListenerContainer rabbitContainer(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter){
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setMessageListener(listenerAdapter);
        container.setQueueNames(queueName);
        return container;
    }

    @Bean
    RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        return new RabbitTemplate(connectionFactory);
    }


}

Rabbit MQ 消息接受处理类

@Component
public class RabbitReceiver {
    //private CountDownLatch countDownLatch = new CountDownLatch(1);
    private static final Logger LOGGER = LoggerFactory.getLogger(RabbitReceiver.class);

    @Autowired
    OrderService orderService;

    public void receiverMessage(ShoppingOrder order){
        LOGGER.info(".......MQ receiverMessage.......");
        orderService.save(order);
    }

//    public CountDownLatch getCountDownLatch() {
//        return countDownLatch;
//    }
}

Redis配置
RedisConfig

@Configuration
public class RedisConfig {
    @Bean
    CacheManager cacheManager(RedisConnectionFactory connectionFactory){
        return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(connectionFactory).build();
    }

    @Bean
    RedisTemplate template(RedisConnectionFactory connectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }
}

数据库dao层 JPA实现
OrderRepository

@Cacheable(cacheNames = "shopping-order")
public interface OrderRepository extends CrudRepository<ShoppingOrder,String> {
    @Cacheable(key="#p0",condition = "#p0.indexOf('mygood')!=-1")
    List<ShoppingOrder> findAllByGoods(String goods);

    @Cacheable(key="#p0")
    ShoppingOrder findOrderById(String id);

    @CachePut(value = "shopping-order",key="#p0.id")//每次都会执行方法,并将结果存入指定的缓存中
    @Override
    <S extends ShoppingOrder> S save(S s);

    //cacheNames 放在类上对所有方法都适用,也可以放在方法上只对某个方法作用
    //key 设置主键 #p0是指第一个参数,也可以 #参数名 比如 #id
    //condition 过滤条件 过滤掉不需要缓存的调用
    // @CacheEvict是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。
}

service层 接口和实现类

public interface OrderService {
    List<ShoppingOrder> queryAllOrder(String goodsName);
    ShoppingOrder findOrderById(String id);
    ShoppingOrder save(ShoppingOrder order);
}

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    OrderRepository repository;

    @Override
    public List<ShoppingOrder> queryAllOrder(String goodsName) {
        return repository.findAllByGoods(goodsName);
    }

    @Override
    public ShoppingOrder findOrderById(String id) {
        return repository.findOrderById(id);
    }

    @Override
    public ShoppingOrder save(ShoppingOrder order) {
        return repository.save(order);
    }
}

控制层controller实现

@RequestMapping("/order")
@Controller
public class OrderController {
    private static final Logger LOGGER = LoggerFactory.getLogger(OrderController.class);

    @Autowired
    OrderService orderService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @GetMapping("/")
    public String OrderPage(){
        return "order";
    }

    @PostMapping
    public @ResponseBody String addOrder(@RequestBody ShoppingOrder shoppingOrder){
        LOGGER.info("order is ",shoppingOrder);
        String uuid = UUID.randomUUID().toString();
        LOGGER.info("request coming.. uuid is "+uuid);
        shoppingOrder.setId(uuid);
        shoppingOrder.setCreateDate(new Date());
        //mq 缓存订单
        rabbitTemplate.convertAndSend(RabbitMqConfig.topicExchangeName, "shopping.order", shoppingOrder);
        //orderService.save(shoppingOrder);
        return "success";
    }
}

ShoppingOrder 订单实体类
实现Serializable接口给redis缓存使用


/**
 * 订单实体类
 * create by tomato
 * date 2020年5月30日
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class ShoppingOrder implements Serializable {
    @Id
    private String id;
    private Date createDate;
    private double price;
    private String goods;
    private int amount;
}

压力测试

1.安装Jmeter压力测试工具,具体使用方法百度
2.创建http请求
SpringBoot 商品下单放进RabbitMQ 和数据用使用redis缓存,提高吞吐量和响应速度。_第2张图片
这里我只是配置了测试10000次,可以测试多点
SpringBoot 商品下单放进RabbitMQ 和数据用使用redis缓存,提高吞吐量和响应速度。_第3张图片
3.测试结果
使用MQ明显能提高吞吐量和响应速度。因为他加入队列是非常快的,后续在做集体业务处理。使用MQ我觉得需要前端提供一个响应接口来反馈业务处理的结果,这里我就没有写了。

使用MQ测试结果
SpringBoot 商品下单放进RabbitMQ 和数据用使用redis缓存,提高吞吐量和响应速度。_第4张图片
关闭MQ,测试结果
SpringBoot 商品下单放进RabbitMQ 和数据用使用redis缓存,提高吞吐量和响应速度。_第5张图片
关闭MQ和redis缓存测试结果
SpringBoot 商品下单放进RabbitMQ 和数据用使用redis缓存,提高吞吐量和响应速度。_第6张图片
事实上,redis对于下订单接口没有提高吞吐量和响应速度的帮助,使用redis的作用是这些操作的数据都缓存在了redis当再次用到的时候就不需要读取数据库,对于高并发读取数据库是很有帮助的,但需要考虑下缓存和数据库的数据一直性的问题。

总结

我只是简单的测试了下,还有很多细节和原理也还没弄明白。继续努力中…

你可能感兴趣的:(springboot,rabbitmq,redis,java,redis,spring,boot,rabbitmq,spring)