Question 1: Request an order can keep the brush caused by cattle as well as pressure on the server through a script
Such a restriction can be done only to get a user in the process of logical tokens in the token issuer spike
Question 2: Write a single logical interfaces with a single spike and the lower, of the redundant strong spike. Even if the activity does not start, it can be used as general merchandise orders. Trading system would cause no associated load
Resolution: introducing token spike, the spike into the single logical token generation here, so easy to separate after deployment.
1. to use the access token to avoid large single
Wind spike control and to manage the token authentication, the user avoid a large flow of operations performed at a single
Generating a token general than some of the inventory, such as double
First call / generatePromoToken, generate promoToken, then carry promoToken to the next one / createorder
(1) generating a token spike
public String generateSecondKillTocken(Integer itemId, Integer userId, Integer promoId, Integer amount) {
// 1. check a single state, single commodity exists and whether the user is legitimate and whether the correct number of purchase
ItemModel itemModel = itemService.getItemByIdInCache(itemId);
if(itemModel == null){
return null;
}
UserModel userModel = userService.getUserByIdInCache(userId);
if(userModel == null){
return null;
}
if(amount <= 0 || amount > 99){
return null;
}
// check event information
if(promoId != null){
// (1) verify the existence of this correspondence activity for what goods
if(promoId.intValue() != itemModel.getPromoModel().getId()){
return null;
// (2) check whether the activities in progress
}else if(itemModel.getPromoModel().getStatus().intValue() != 2) {
return null;
}
}
// generate buying token, and store reids
String token = UUID.randomUUID().toString().replace("-", "");
redisTemplate.opsForValue().set("promo_token_"+promoId+"_"+itemId+"_"+userId, token);
redisTemplate.expire("promo_token_"+promoId+"_"+itemId+"_"+userId, 5, TimeUnit.MINUTES);
return token;
}
(2) single authentication token
// single package request
@RequestMapping(value = "/createorder",method = {RequestMethod.POST},consumes={CONTENT_TYPE_FORMED})
@ResponseBody
public CommonReturnType createOrder(@RequestParam(name="itemId")Integer itemId,
@RequestParam(name="amount")Integer amount,
@RequestParam(name="promoId",required = false)Integer promoId,
@RequestParam(name="token",required = false)String token,
@RequestParam(name="promoToken",required = false)String promoToken) throws BusinessException {
UserModel userModel = (UserModel) redisTemplate.opsForValue().get(token);
// spike token verification, compared with the value of redis
if(promoId!=null) {
if(promoToken!=null) {
String inRedisPromoToken = (String) redisTemplate.opsForValue().get("promo_token_"+promoId+"_"+itemId+"_"+userModel.getId());
if(!promoToken.equals(inRedisPromoToken)) {
throw new BusinessException (EmBusinessError.PARAMETER_VALIDATION_ERROR, "spike token validation failed");
}
} else {
throw new BusinessException (EmBusinessError.PARAMETER_VALIDATION_ERROR, "spike token validation failed");
}
}
// add water init state commodity stocks
String stockLogId = itemService.initStockLog(itemId, amount);
//OrderModel orderModel = orderService.createOrder(userModel.getId(),itemId,promoId,amount);
// transactional message driven by a single, simultaneous or rollback message to determine the transmission state in accordance with callback
boolean mqResult = mqProducer.transactionAsyncReduceStock(userModel.getId(),itemId,promoId,amount, stockLogId);
if(!mqResult) {
throw new BusinessException (EmBusinessError.UNKNOWN_ERROR, "single failure");
}
return CommonReturnType.create(null);
}
Question 3: Achieving spike token is defective, it can generate unlimited, so if there are over one hundred million users, generating affect system performance, but also can not grab a token of goods
Resolution: introducing spike gates, according to issue stock number of tokens corresponding to the flow rate control gates
(1) at the time of launch event, save the library to redis, the number of gates will also be saved to redis
public void publishPromo(Integer promoId) {
。。。。。。。。。。。。。。。。。
// inventory in sync to redis
redisTemplate.opsForValue().getAndSet("promo_item_stock_"+promoDO.getItemId(), itemModel.getStock());
// Save the number to spike gates redis
redisTemplate.opsForValue().set("promo_door_count_"+promoId, itemModel.getStock().intValue()*5);
}
(2) the token is generated before the number to check whether there are gates spike
@Override public String generateSecondKillTocken(Integer itemId, Integer userId, Integer promoId, Integer amount) { // check whether the inventory is sold out if(redisTemplate.hasKey("promo_item_stock_invalid_"+itemId)) { return null; } // 1. check a single state, single commodity exists and whether the user is legitimate and whether the correct number of purchase 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。 // Get the number of gates spike long result = redisTemplate.opsForValue().increment("promo_door_count_"+promoId, -1); if(result<0) { return null; } // generate buying token, and store reids String token = UUID.randomUUID().toString().replace("-", ""); redisTemplate.opsForValue().set("promo_token_"+promoId+"_"+itemId+"_"+userId, token); redisTemplate.expire("promo_token_"+promoId+"_"+itemId+"_"+userId, 5, TimeUnit.MINUTES); return token; }
Question 4: token can not cope with the influx of traffic surges, such as inventory itself is very large. In addition multiple inventories, limit the ability of multi-commodity token weak
Solution: the introduction of flood queue, the task will be submitted to the thread pool, the thread pool threads covered with executable tasks will be placed in the waiting queue, to do so would be tantamount to restricting the flow of concurrent users, making it waiting in the thread pool queue queuing process. Then the future is to enable the use of front-end user after the call controller can synchronize the results obtained
1 queuing times faster than the concurrent, if the wait for the lock, the thread exits. Another thread scheduling CPU, CPU context switching loss
For example: redis is single-threaded, but soon. Because redis is a memory operation, and there is no thread switch overhead on single thread
private ExecutorService executorService;
@PostConstruct
public void init() {
executorService = Executors.newFixedThreadPool(20);
}
// single package request
@RequestMapping(value = "/createorder",method = {RequestMethod.POST},consumes={CONTENT_TYPE_FORMED})
@ResponseBody
public CommonReturnType createOrder(@RequestParam(name="itemId")Integer itemId,
@RequestParam(name="amount")Integer amount,
@RequestParam(name="promoId",required = false)Integer promoId,
@RequestParam(name="token",required = false)String token,
@RequestParam(name="promoToken",required = false)String promoToken) throws BusinessException {
。。。。。。。。。。。。。。。。。。。。。。。。。。。。
// synchronous call the submit method thread pool
// congestion window to the waiting queue 20, a queue to flood, more than 20 of the queue to wait
Future<Object> future = executorService.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
// add water init state commodity stocks
String stockLogId = itemService.initStockLog(itemId, amount);
//OrderModel orderModel = orderService.createOrder(userModel.getId(),itemId,promoId,amount);
// transactional message driven by a single, simultaneous or rollback message to determine the transmission state in accordance with callback
boolean mqResult = mqProducer.transactionAsyncReduceStock(userModel.getId(),itemId,promoId,amount, stockLogId);
if(!mqResult) {
throw new BusinessException (EmBusinessError.UNKNOWN_ERROR, "single failure");
}
return null;
}
});
try {
// get method to get the results, the method will block until the task returns the result.
future.get();
} catch (InterruptedException | ExecutionException e) {
throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);
}
return CommonReturnType.create(null);
}