Chapter VII buffer stock

Question: single operations require multiple calls to the database, query product information, user information, modify inventory data, resulting in performance bottlenecks.

Optimization direction: to read data read from the cache, to modify the inventory data in the modification data to modify the cache database with the asynchronous message queue. You can use asynchronous transactional message rocketmq to ensure redis database and data synchronization, anomalies in the cache can be used to restore database data.

1. Verify transaction optimization

(1) verify the existence of commodities, instead cache

Prior is to obtain product data directly from the database according to itemId

Redis now changed to obtain from the cache, do not get in from the database

     @Override
	public ItemModel getItemByIdInCache(Integer id) {
		ItemModel itemModel = (ItemModel)redisTemplate.opsForValue().get("item_validate_"+id);
		if(itemModel == null) {
			itemModel = this.getItemById(id);
			redisTemplate.opsForValue().set("item_validate_"+id, itemModel);
		}
		return itemModel;
	}

(2) checking whether there is user information, to the cache

Before the user data is acquired directly from the database according to the userId

Redis now changed to obtain from the cache, do not get in from the database

    @Override
	public UserModel getUserByIdInCache(Integer id) {
		UserModel userModel = (UserModel)redisTemplate.opsForValue().get("user_"+id);
		if(userModel == null) {
			userModel = this.getUserById(id);
			redisTemplate.opsForValue().getAndSet("user_"+id, userModel);
		}
		return userModel;
	}

2. Modify the inventory data changed to modify cache

Stock modify table data because itemId for the stock table's primary key, so there will row locks to prevent concurrency, but will affect the efficiency

So when buying activity released into inventory redis, first redis modify cache data when the next one, and then modify the synchronization rocketmq inventory to ensure the reliability of

a. launches, inventory cache   

    // launch event, this should be the operation and maintenance operations 
  @RequestMapping (value = "/ publishpromo" , method = {RequestMethod.GET}) @ResponseBody public CommonReturnType publisPromo(Integer id) { promoService.publishPromo(id); return CommonReturnType.create(null); }
 

  @Override
  public void publishPromo (Integer promoId) {
    // event information acquired by the event ID
    the Promodo the Promodo = promoDOMapper.selectByPrimaryKey (promoId);
    IF (. PromoDO.getItemId the Promodo == null || () intValue () == 0) {
      return;
    }

    itemModel itemModel = itemService.getItemById (promoDO.getItemId ());
    // redis synchronized to the inventory
    redisTemplate.opsForValue () getAndSet ( "promo_item_stock _ " + promoDO.getItemId (), itemModel.getStock ()).;
  }

b. The Save single stock

@Override
    @Transactional
    public boolean decreaseStock(Integer itemId, Integer amount) throws BusinessException {
        /*int affectedRow =  itemStockDOMapper.decreaseStock(itemId,amount);
        if(affectedRow > 0){
            // update inventory success
            return true;
        }else{
            // update inventory fails
            return false;
        }*/
    	Long result = redisTemplate.opsForValue().increment("promo_item_stock_"+itemId, amount.intValue()*-1);
    	// send message queue modification inventory database 
     boolean sendResult = producer.asyncReduceStock (itemId, amount ); // remaining data if(result > 0 && sendResult){ // update inventory success return true; }else { // update inventory fails redisTemplate.opsForValue().increment("promo_item_stock_"+itemId, amount.intValue()); return false; } }

The producer transmits the message queue

@Component
public class MQProducer {
	Log log = LogFactory.getLog(getClass());
	@Value("${mq.nameserver.addr}")
	private String nameServer;
	
	@Value("${mq.topicname}")
	private String topicName;
	
	DefaultMQProducer producer;
	@PostConstruct
	public void init() throws MQClientException {
		producer = new DefaultMQProducer("producer");
		producer.setNamesrvAddr(nameServer);
		producer.start();
	}
	
	// synchronization deductions stock news
	public boolean asyncReduceStock(Integer itemId, Integer amount) {
		Map<String,Object> bodyMap = new HashMap<String,Object>();
		bodyMap.put("itemId", itemId);
		bodyMap.put("amount", amount);
		Message msg = new Message(topicName,"increase", 
				JSON.toJSON(bodyMap).toString().getBytes(Charset.forName("UTF-8")));
		try {
			log.error("begin to send msg");
			producer.send(msg);
			log.error("finish send msg");
		} catch (MQClientException e) {
			e.printStackTrace ();
			return false;
		} catch (RemotingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace ();
			return false;
		} catch (MQBrokerException e) {
			// TODO Auto-generated catch block
			e.printStackTrace ();
			return false;
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace ();
			return false;
		}
		return true;
	}
}

 Consumers reduced inventory database message queue 

@Component
public class MQConsumer {
	@Value("${mq.nameserver.addr}")
	private String nameServer;
	
	@Value("${mq.topicname}")
	private String topicName;
	
	DefaultMQPushConsumer consumer;
	@Autowired
    private ItemStockDOMapper itemStockDOMapper;
	
	private Log log = LogFactory.getLog(getClass());
	
	@PostConstruct
	public void init() throws MQClientException {
		consumer = new DefaultMQPushConsumer("stock_consumer_group");
		consumer.setNamesrvAddr(nameServer);
		consumer.subscribe(topicName, "*");
		consumer.registerMessageListener(new MessageListenerConcurrently() {

			@Override
			public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
				log.error("begin to cosume msg");
				Message msg = msgs.get(0);
				String jsonStr = new String(msg.getBody());
				Map<String,Object> map = JSON.parseObject(jsonStr, Map.class);
				Integer itemId= (Integer)map.get("itemId");
				Integer amount= (Integer)map.get("amount");
				log.error("itemId:"+itemId+",amount:"+amount);
				int affectedRow =  itemStockDOMapper.decreaseStock(itemId,amount);
				if(affectedRow > 0){
		                  // update inventory success
					return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
		             }else{
		                // update inventory fails
		        	  return ConsumeConcurrentlyStatus.RECONSUME_LATER;
		             }
			}
             });
		consumer.start();
	}
}

  

Guess you like

Origin www.cnblogs.com/t96fxi/p/12082884.html