Spring+RabbitMQ+Redis的秒杀实现

软件安装

1.Reids安装教程:https://www.runoob.com/redis/redis-install.html
2.RabbitMQ安装教程:https://blog.csdn.net/zhuzhezhuzhe1/article/details/80464291

代码实现

1.引入依赖

	<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-amqp</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-logging</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>${fastjson.version}</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-feign</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.5</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>${mybatis.spring-boot.version}</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <!--pagehelper -->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper-spring-boot-starter</artifactId>
        <version>${tk.mybatis.version}</version>
    </dependency>

    <!-- tk通用mybatis与springboot整合 -->
    <dependency>
        <groupId>tk.mybatis</groupId>
        <artifactId>mapper-spring-boot-starter</artifactId>
        <version>${tk.mybatis.version}</version>
    </dependency>

    <!--springfox-swagger2 -->
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>${swagger.version}</version>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>${druid.version}</version>
    </dependency>
    <!-- alibaba的druid数据库连接池 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.9</version>
    </dependency>

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.7.0</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

2.配置文件 application.yml

server:
	port: 8089
spring:
	rabbitmq:
 		virtual-host: /
 		host: localhost
 		username: guest
 		password: guest
	application:
		name: concurrency-project
	redis:
  		host: localhost
 		port: 6379
  		jedis:
    		pool:
      		max-active: 1024
      		max-wait: -1s
      		max-idle: 200
   		password: 123456
	datasource:
     	name: db_concurrency
     	type: com.alibaba.druid.pool.DruidDataSource
     	druid:
       		driver-class-name: com.mysql.jdbc.Driver
       		url: jdbc:mysql://localhost:3306/db_concurrency
       		username: root
       		password:
       		initial-size: 1
       		min-idle: 1
       		max-active: 20
            #获取连接等待超时时间
       		max-wait: 60000
       		#间隔多久进行一次检测,检测需要关闭的空闲连接
       		time-between-eviction-runs-millis: 60000
			#一个连接在池中最小生存的时间
       		min-evictable-idle-time-millis: 300000
       		validation-query: SELECT 'x'
       		test-while-idle: true
       		test-on-borrow: false
       		test-on-return: false
			#打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
       		pool-prepared-statements: false
       		max-pool-prepared-statement-per-connection-size: 20
mybatis:
	mapper-locations: mapper/*.xml
	type-aliases-package: com.concurrency.concurrencyproject.model

pagehelper:
 	helper-dialect: mysql

mapper:
	mappers: com.concurrency.concurrencyproject.base.service.GenericMapper
	not-empty: false
	identity: MYSQL

3.主要代码实现
MyRabbitMQConfig

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;


@Configuration
public class MyRabbitMQConfig {

//库存交换机
public static final String STORY_EXCHANGE = "STORY_EXCHANGE";

//订单交换机
public static final String ORDER_EXCHANGE = "ORDER_EXCHANGE";

//库存队列
public static final String STORY_QUEUE = "STORY_QUEUE";

//订单队列
public static final String ORDER_QUEUE = "ORDER_QUEUE";

//库存路由键
public static final String STORY_ROUTING_KEY = "STORY_ROUTING_KEY";

//订单路由键
public static final String ORDER_ROUTING_KEY = "ORDER_ROUTING_KEY";

@Bean
public MessageConverter messageConverter() {
    return new Jackson2JsonMessageConverter();
}


//创建库存交换机
@Bean
public Exchange getStoryExchange() {
    return ExchangeBuilder.directExchange(STORY_EXCHANGE).durable(true).build();
}

//创建库存队列
@Bean
public Queue getStoryQueue() {
    return new Queue(STORY_QUEUE);
}

//库存交换机和库存队列绑定
@Bean
public Binding bindStory() {
    return BindingBuilder.bind(getStoryQueue()).to(getStoryExchange()).with(STORY_ROUTING_KEY).noargs();
}

//创建订单队列
@Bean
public Queue getOrderQueue() {
    return new Queue(ORDER_QUEUE);
}

//创建订单交换机
@Bean
public Exchange getOrderExchange() {
    return ExchangeBuilder.directExchange(ORDER_EXCHANGE).durable(true).build();
}

//订单队列与订单交换机进行绑定
@Bean
public Binding bindOrder() {
    return BindingBuilder.bind(getOrderQueue()).to(getOrderExchange()).with(ORDER_ROUTING_KEY).noargs();
}
}

RedisConfig

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Configuration
@PropertySource(value = { “classpath:redis-config.properties” })
public class RedisConfig {
/**
* 连接池配置信息
* @return
/
@Bean
@ConfigurationProperties(prefix = “spring.redis.pool”)
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig config = new JedisPoolConfig();
return config;
}
/
*
* 2.创建RedisConnectionFactory:配置redis 链接信息
*/
@Bean
@ConfigurationProperties(prefix = “spring.redis”)
public RedisConnectionFactory redisConnectionFactory(JedisPoolConfig config) {

    RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
    JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcf = (JedisClientConfiguration.JedisPoolingClientConfigurationBuilder) JedisClientConfiguration
            .builder();
    // 修改我们的连接池配置
    jpcf.poolConfig(config);
    // 通过构造器来构造jedis客户端配置
    JedisClientConfiguration jedisClientConfiguration = jpcf.build();
    return new JedisConnectionFactory(redisStandaloneConfiguration);
}

/**
 *  RedisTemplate(或StringRedisTemplate)虽然已经自动配置,但是不灵活(第一没有序列化,第二泛型为<Object, Object>不是我们想要的类型)
 *  所以自己实现RedisTemplate或StringRedisTemplate)
 */
@Bean(name = "redisTemplate")
public RedisTemplate<String,Object> redisTemplate(JedisConnectionFactory factory) {
    RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(factory);
    setRedisTemplate(redisTemplate);
    redisTemplate.afterPropertiesSet();
    return redisTemplate;
}


public void setRedisTemplate(RedisTemplate redisTemplate) {
    //        JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
    GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
    // 设置值(value)的序列化采用FastJsonRedisSerializer。
    redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
    //        redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
    // 设置键(key)的序列化采用StringRedisSerializer。
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    redisTemplate.afterPropertiesSet();
}


// 4配置redisCacheManager
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    // 生成一个默认配置,通过config对象即可对缓存进行自定义配置
    RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
    config = config.entryTtl(Duration.ofMinutes(1)) // 设置缓存的默认过期时间,也是使用Duration设置
            .disableCachingNullValues(); // 不缓存空值

    // 设置一个初始化的缓存空间set集合  可以指定需要缓存的内容缓存在那个空间下
    Set<String> cacheNames = new HashSet<>();
    cacheNames.add("timeGroup");
    cacheNames.add("emp");

    // 对每个缓存空间应用不同的配置
    Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
    configMap.put("timeGroup", config);
    configMap.put("emp", config.entryTtl(Duration.ofSeconds(120)));

    RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory) // 使用自定义的缓存配置初始化一个cacheManager
            .initialCacheNames(cacheNames) // 注意这两句的调用顺序,一定要先调用该方法设置初始化的缓存名,再初始化相关的配置
            .withInitialCacheConfigurations(configMap).build();
    return cacheManager;
}
}

SecController

import com.concurrency.concurrencyproject.config.MyRabbitMQConfig;
import com.concurrency.concurrencyproject.model.Order;
import com.concurrency.concurrencyproject.service.OrderService;
import com.concurrency.concurrencyproject.service.RedisService;
import com.concurrency.concurrencyproject.service.StockService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class SecController {

private Logger LOGGER = LoggerFactory.getLogger(getClass());

@Autowired
private RabbitTemplate rabbitTemplate;

@Autowired
private RedisService redisService;

@Autowired
private OrderService orderService;

@Autowired
private StockService stockService;

/**
 * 使用redis+消息队列进行秒杀实现
 *
 * @param username
 * @param stockName
 * @return
 */
@RequestMapping("/sec")
@ResponseBody
public String sec(@RequestParam(value = "username") String username, @RequestParam(value = "stockName") String stockName) {
    LOGGER.info("参加秒杀的用户是:{},秒杀的商品是:{}", username, stockName);
    String message = null;
    //调用redis给相应商品库存量减一
    Long decrByResult = redisService.decrBy(stockName);
    if (decrByResult >= 0) {
        /**
         * 说明该商品的库存量有剩余,可以进行下订单操作
         */
        LOGGER.info("用户:{}秒杀该商品:{}库存有余,可以进行下订单操作", username, stockName);
        //发消息给库存消息队列,将库存数据减一
        rabbitTemplate.convertAndSend(MyRabbitMQConfig.STORY_EXCHANGE, MyRabbitMQConfig.STORY_ROUTING_KEY, stockName);

        //发消息给订单消息队列,创建订单
        Order order = new Order();
        order.setOrder_name(stockName);
        order.setOrder_user(username);
        rabbitTemplate.convertAndSend(MyRabbitMQConfig.ORDER_EXCHANGE, MyRabbitMQConfig.ORDER_ROUTING_KEY, order);
        message = "用户" + username + "秒杀" + stockName + "成功";
    } else {
        /**
         * 说明该商品的库存量没有剩余,直接返回秒杀失败的消息给用户
         */
        LOGGER.info("用户:{}秒杀时商品的库存量没有剩余,秒杀结束", username);
        message = username + "商品的库存量没有剩余,秒杀结束";
    }
    return message;
}

/**
 * 实现纯数据库操作实现秒杀操作
 *
 * @param username
 * @param stockName
 * @return
 */
@RequestMapping("/secDataBase")
@ResponseBody
public String secDataBase(@RequestParam(value = "username") String username, @RequestParam(value = "stockName") String stockName) {
    LOGGER.info("参加秒杀的用户是:{},秒杀的商品是:{}", username, stockName);
    String message = null;
    //查找该商品库存
    Integer stockCount = stockService.selectByExample(stockName);
    LOGGER.info("用户:{}参加秒杀,当前商品库存量是:{}", username, stockCount);
    if (stockCount > 0) {
        /**
         * 还有库存,可以进行继续秒杀,库存减一,下订单
         */
        //1、库存减一
        stockService.decrByStock(stockName);

        //2、下订单
        Order order = new Order();
        order.setOrder_user(username);
        order.setOrder_name(stockName);
        orderService.createOrder(order);
        LOGGER.info("用户:{}.参加秒杀结果是:成功", username);
        message = username + "参加秒杀结果是:成功";
    } else {
        LOGGER.info("用户:{}.参加秒杀结果是:秒杀已经结束", username);
        message = username + "参加秒杀活动结果是:秒杀已经结束";
    }
    return message;
}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值