1, Theoretical basis
1.1 front end optimization
- Use dynamic and static separation to store static resources in a third-party file server to achieve cdn acceleration, so as to reduce the bandwidth of second kill and rush to buy
- When the user clicks the seckill button, the button should be disabled to prevent repeated submission
- Using complex graphic verification code to prevent machine simulation
- Seckill details page, use timer to query seckill results according to user information
- Using nginx+lua+openresty to realize static page of product details page
1.2 gateway
- ratelimter, nginx, hystrix and redis implement the current limit token pain + copper leakage algorithm to limit the current and protect the service of the user's seckill request.
- User blacklist and whitelist interception
1.3 seckill interface
- Service degradation, isolation and fusing
- Get the token of seckill from redis (if you can get the token, you can succeed in seckill, otherwise you will fail in seckill!)
- Asynchronous use of MQ to modify inventory
- Provide an interface to query seckill results based on user information
1.4 project deployment
- Nginx+lvs for high service availability and clustering
- Rush purchase by time (12306 in use, noon and afternoon)
1.5 at the same time, there are 100000 requests to realize seckill, and only 100 commodity inventories. To achieve this, only 100 inventory modifications are needed
Scheme implementation process:
The corresponding tokens (100 tokens) are generated in advance from the corresponding commodity inventory, that is, token bucket. In 100000 requests, as long as anyone can get the token, he can kill in seconds. After getting the token, the mq asynchronous implementation is used to modify and subtract the library
2, Code implementation
Schematic diagram:
Note: in the company level seckill service, the seckill producers and consumers should be placed in different services respectively, so as to avoid that the producers hang up and the consumers hang up
2.1 producers
(1) MQ related configuration: RabbitmqConfig
@Component public class RabbitmqConfig { // Add modify inventory queue public static final String MODIFY_INVENTORY_QUEUE = "modify_inventory_queue"; // Switch name private static final String MODIFY_EXCHANGE_NAME = "modify_exchange_name"; // 1. Add switch queue @Bean public Queue directModifyInventoryQueue() { return new Queue(MODIFY_INVENTORY_QUEUE); } // 2. Define switches @Bean DirectExchange directModifyExchange() { return new DirectExchange(MODIFY_EXCHANGE_NAME); } // 3. Modify inventory queue binding switch @Bean Binding bindingExchangeintegralDicQueue() { return BindingBuilder.bind(directModifyInventoryQueue()).to(directModifyExchange()).with("modifyRoutingKey"); } }
(2) Producer send message: spike commodityproducer
@Component @Slf4j public class SpikeCommodityProducer implements RabbitTemplate.ConfirmCallback { @Autowired private RabbitTemplate rabbitTemplate; @Transactional public void send(JSONObject jsonObject) { String jsonString = jsonObject.toJSONString(); System.out.println("jsonString:" + jsonString); String messAgeId = UUID.randomUUID().toString().replace("-", ""); // Encapsulation message Message message = MessageBuilder.withBody(jsonString.getBytes()) .setContentType(MessageProperties.CONTENT_TYPE_JSON).setContentEncoding("utf-8").setMessageId(messAgeId) .build(); // Data returned by build callback (message id) this.rabbitTemplate.setMandatory(true); this.rabbitTemplate.setConfirmCallback(this); CorrelationData correlationData = new CorrelationData(jsonString); rabbitTemplate.convertAndSend("modify_exchange_name", "modifyRoutingKey", message, correlationData); } // When the producer sends a message to the server, the producer uses the reply mechanism @Override public void confirm(CorrelationData correlationData, boolean ack, String cause) { String jsonString = correlationData.getId(); System.out.println("news id:" + correlationData.getId()); if (ack) { log.info(">>>Use MQ Message confirmation mechanism ensures that messages must be delivered to MQ Success in China"); return; } JSONObject jsonObject = JSONObject.parseObject(jsonString); // If producer message delivery fails, recursive retry mechanism is adopted send(jsonObject); log.info(">>>Use MQ Message confirmation mechanism delivered to MQ Failure in"); } }
2.2 consumers
StockConsumer :
@Component @Slf4j public class StockConsumer { @Autowired private SeckillMapper seckillMapper; @Autowired private OrderMapper orderMapper; @RabbitListener(queues = "modify_inventory_queue") @Transactional public void process(Message message, @Headers Map<String, Object> headers, Channel channel) throws IOException { String messageId = message.getMessageProperties().getMessageId(); String msg = new String(message.getBody(), "UTF-8"); log.info(">>>messageId:{},msg:{}", messageId, msg); JSONObject jsonObject = JSONObject.parseObject(msg); // 1. Get seckill id Long seckillId = jsonObject.getLong("seckillId"); SeckillEntity seckillEntity = seckillMapper.findBySeckillId(seckillId); if (seckillEntity == null) { log.warn("seckillId:{},Product information does not exist!", seckillId); return; } Long version = seckillEntity.getVersion(); int inventoryDeduction = seckillMapper.inventoryDeduction(seckillId, version); if (!toDaoResult(inventoryDeduction)) { log.info(">>>seckillId:{}Failed to modify inventory>>>>inventoryDeduction Return to{} Second kill failed!", seckillId, inventoryDeduction); return; } // 2. Add seckill order OrderEntity orderEntity = new OrderEntity(); String phone = jsonObject.getString("phone"); orderEntity.setUserPhone(phone); orderEntity.setSeckillId(seckillId); orderEntity.setState(1l); int insertOrder = orderMapper.insertOrder(orderEntity); if (!toDaoResult(insertOrder)) { return; } log.info(">>>Modify inventory succeeded seckillId:{}>>>>inventoryDeduction Return to{} Seckill success", seckillId, inventoryDeduction); } // Call database layer judgment public Boolean toDaoResult(int result) { return result > 0 ? true : false; } }
2.3. Query seckill record according to mobile phone number and commodity inventory id
(1)OrderSeckillService :
public interface OrderSeckillService { @RequestMapping("/getOrder") public BaseResponse<JSONObject> getOrder(String phone, Long seckillId); }
(2)OrderSeckillServiceImpl
@RestController public class OrderSeckillServiceImpl extends BaseApiService<JSONObject> implements OrderSeckillService { @Autowired private OrderMapper orderMapper; @Override public BaseResponse<JSONObject> getOrder(String phone, Long seckillId) { if (StringUtils.isEmpty(phone)) { return setResultError("Mobile number cannot be empty!"); } if (seckillId == null) { return setResultError("Commodity inventory id Can not be empty!"); } OrderEntity orderEntity = orderMapper.findByOrder(phone, seckillId); if (orderEntity == null) { return setResultError("Queuing....."); } return setResultSuccess("Congratulations on your success!"); } }