Java fresh business platform - the core technology of high concurrency combat orders and inventory

Java fresh business platform - the core technology of high concurrency combat orders and inventory

 

First, the problem

Only 100 stocks of a commodity, there are 1000 or more users to buy, while each user plans to purchase a range of several commodities.

How to ensure that inventory in a highly concurrent scenarios is safe?

(1) not made

(2) a lot of hair

 

 

 

Second, the single step

(1) single

(2) orders while camped stock

(3) pay

(4) pay real success deductions stock

(5) to cancel the order

(6) fallback camped stock

Third, what time to camp inventory?

(1) Option One: When adding a shopping cart to the camp-stock

(2) Option II: Order of time to camp on stock

(3) Option Three: When the pre-paid account to inventory

Fourth, analysis

(1) Option One : Add to Cart does not mean that users will buy, if the time to start camp on stock, will lead to want to buy can not be added to cart. And people do not want to buy the stock has been occupied. Obviously, this approach is not desirable.

(2) Option Two : After the goods into the shopping cart, choose the next single, this time to camp on inventory. Users choose to pay illustrate, users desire to buy is more than a program to be strong. Orders also have a prescription, such as half an hour. After more than half an hour, the system will automatically cancel the order, Fallback camped inventory.

(3) Option Three : single success to pay to camp on time inventory. Only 100 users can pay for success, 900 users fail to pay. The user experience is not good, as you go up a bright road, all the way open, and suddenly here was told not to pass. And the payment process is a more complex process, reduce inventory, and if put together, will become more complicated.

So sum up: Choose Option II reasonable.

V. repeat orders issue

(1) a user clicks too quickly resubmit twice

(2) the network delay, the user clicks refresh or single resubmit under

(3) a repeat request frame network, some network frame, automatic repeat request will be delayed in the case of relatively high

(4) a malicious user behavior

Sixth, the solution

In the UI interception, click the button gray and can not continue to click, to prevent users, click on continuous repeat orders caused.

1, before the next one gets a unique token of a single, one-time needs under this token. Background check system this token is valid only proceed under a single operation.

/**     

* Mr. Cheng token to save Redis     

* Token as a key, and set the length of time the expiration time based on mission requirements     

* Value to determine whether the digital increment used *     

* @param user     

* @return   

*/

public String createToken(User user) {

String key = "placeOrder:token:" + user.getId();

String token = UUID.randomUUID().toString();

// save to Redis

redisService.set(key + token, 0, 1000L); return token; }

/**     

* Check under the single token is valid     

* @param user     

* @param token     

* @return     

*/

public Boolean checkToken(User user, String token) {

String key = "placeOrder:token:" + user.getId();

if (null != redisService.get(key + token)) {

long times = redisService.increment(key + token, 1);

if (times == 1) {

// increment using the token determines whether atomicity whether

return true;

} else {

// already used the

}

//delete

redisService.remove(key + token);

}

return false; }

2, how to secure deductions inventory?

A single user or multiple users at the same time buying a commodity, how we do concurrent security deductions stock?

(1) Goods Inventory database operations

/**  * Created by Administrator on 2017/9/8.  */

public interface ProductDao extends JpaRepository<Product,Integer>{

/ ** * @param pid product ID      

* @Param num Quantity      

* @return      

*/

@Transactional

@Modifying

@Query("update Product set availableNum = availableNum - ?2 , reserveNum = reserveNum + ?2 where id = ?1")intreduceStock1(Integerpid,Integernum);

/**     

 * @Param pid product ID     

 * @Param num Quantity     

 * @return      

*/

@Transactional

@Modifying

@Query("update Product set availableNum = availableNum - ?2 , reserveNum = reserveNum + ?2 where id = ?1 and  availableNum - ?2 >= 0")intreduceStock2(Integerpid,Integernum);

}

 

(2) single

/**     

* 1 * single operation     

* @param req     

*/

private int place(PlaceOrderReq req) {

User user = userDao.findOne(req.getUserId());

Product product = productDao.findOne(req.getProductId());

// number of orders

Req.getNum whether = integer ();

// available inventory

Integer availableNum = product.getAvailableNum();

// book available

if (availableNum >= num) {

// reduce inventory

int count = productDao.reduceStock1(product.getId(), num);

if (count == 1) {

//Generate orders

createOrders(user, product, num);

} else {

logger.info ( "Inventory less than 3");

}

return 1;

} else {

logger.info ( "stock is less than 4");

return -1;

}

}

/ ** * 2 * single operation     

* @param req     

*/

private int place2(PlaceOrderReq req) {

User user = userDao.findOne(req.getUserId());

Product product = productDao.findOne(req.getProductId());

// number of orders Integer num = req.getNum ();

// available inventory

Integer availableNum = product.getAvailableNum();

// book available

if (availableNum >= num) {

// reduce inventory

int count = productDao.reduceStock2(product.getId(), num);

if (count == 1) {

//Generate orders

createOrders(user, product, num);

} else {

logger.info ( "Inventory less than 3");

}

return 1;

} else {

logger.info ( "stock is less than 4");

return -1;

}

}

Method 1: do not consider the wording of inventory security

/ ** * * 1 * Save the available add inventory data is insecure camped * * * @param req * /   

@Override   

@Transactional   

public void placeOrder(PlaceOrderReq req) {       

place1(req);   

}

Analysis:  At high and the scene, it is assumed that only two stocks, two requests come in at the same time, change buying goods, the number is 2. A purchase request to acquire the stock at this time, just enough found stock, stock orders operation performed buckle . A request is completed in a time (uncommitted transactions), B can also request to acquire the stock at this time, there was found stock 2. The buckle also to perform inventory, orders operation. 2 inventory left, but sold four. The final database inventory quantity becomes -2, so the inventory is unsafe.

Method 2 : This operation ensures that inventory data is safe

/ * ** * Method 2 * Save Add available inventory data is insecure camped * * * @param req * /

@Override   

@Transactional   

public void placeOrder(PlaceOrderReq req) {

place2(req);

}

Analysis:  On the basis of the method of updating inventory statements, increasing the available inventory quantities greater than 0, availableNum - num> = 0 ; essence is the use of optimistic locking to control inventory database security, not a great situation in concurrency under you can do. But if it is a high spike, buy, instantaneous flow, then the pressure will have to the database, the database may collapse.

Method 3 : This method also ensures that the number of security stocks

/**   

* Method 3     

* Adopts Redis lock open time can only request a change the quantity of the same commodity     

* <p>     

* Concurrent drawback is not high, but a user can only seize operation, the user experience is not good! *     

* @param req     

*/

@Override   

public void placeOrder2(PlaceOrderReq req) {

String lockKey = "placeOrder:" + req.getProductId();

Boolean isLock = redisService.lock(lockKey);

if (!isLock) {

logger.info ( "system is busy try again later!");

return 2;

}

//place2(req); place1(req);

// These two methods can be

redisService.unLock(lockKey);

}

Analysis: using a distributed lock Redis forced control of the same goods, at the same time only a single processing request. Other requests return 'system is busy try again later! '; Force the serialization processing request, concurrent disadvantage is not high, the process is relatively slow, and the like are not suitable for buying scheme. The user experience is not good, it's clear to see inventory is adequate, is not strong. Scenario 2 compared to reduce the pressure on the database.

 

Method 4  : inventory can guarantee security, to meet the high concurrent processing, but relatively more complex

/**     

* Method 4     

* Number of commodities such as additional information to first save Redis     

* Check the stock and the stock is not reduced atomic to increment> 0 * prevail     

* @param req     

*/

@Override   

public void placeOrder3(PlaceOrderReq req) {

String key = "product:" + req.getProductId();

// check the adequacy of inventory

Integer num = (Integer) redisService.get(key);

if (num < req.getNum()) {

logger.info ( "stock is less than 1");

} else{

// here not Save single inventory, data or cause an unsafe situation similar to the method 1;

}

//decrease stock

long value = redisService.increment(key, -req.getNum().longValue());

//Adequate inventory

if (value >= 0) {

logger.info ( "buying success!");

// TODO real deduction operation inventory orders and other operations, these operations can be used in other ways or by MQ

place2(req);

} else {

// inventory shortage, just minus the need to increase inventory

redisService.increment(key, req.getNum().longValue());

logger.info ( "stock is less than 2");

}

}

Analysis : Redis increment of atomic operations to ensure the safety stock. Want to save in advance the number of inventory and other additional information to Redis, and to ensure that when the update inventory, update Redis.

Came in to get the number of the adequacy of inventory, and then execute increment. To increment> 0 prevail. Check inventory and reduce inventory is not atomic. Plenty of time to check inventory technology stocks nor orders; otherwise cause insecurity inventory, similar to the original method 1. increment is an atomic operation, it has been the subject.

redisService.increment (key, -req.getNum (). longValue ())> = 0 Description adequate inventory, orders can.

redisService.increment (key, -req.getNum (). longValue ()) <0 when not under orders, the number of insufficient inventory. And the need to increase the number of back stock just subtracted, otherwise it will lead to just the number of deductions has not bought out. Inventory database and cache inconsistencies.

The method can meet the high times and buy some other programs, real deduction inventory and orders can be executed asynchronously.

Prescription orders, cancel orders and other business interests in order to ensure, at the same time sell to people in need, after the success of single orders, there will always be a valid time. Over this time, cancel orders, inventory rollback.

After the order is canceled, you can use MQ fallback inventory.

Guess you like

Origin www.cnblogs.com/jurendage/p/11240172.html