Single function in a highly concurrent design: the actual assessment interview

Functional Requirements: Design a spike system

The initial program

Product Design Table: selling goods available to the user spike, an initial inventory.

@Entity
public class SecKillGoods implements Serializable{
    @Id
    private String id;

    /**
     * 剩余库存
     */
    private Integer remainNum;

    /**
     * 秒杀商品名称
     */
    private String goodsName;
}

Spike Orders table design: record spike successful orders

@Entity
public class SecKillOrder implements Serializable {
    @Id
    @GenericGenerator(name = "PKUUID", strategy = "uuid2")
    @GeneratedValue(generator = "PKUUID")
    @Column(length = 36)
    private String id;

    //用户名称
    private String consumer;

    //秒杀产品编号
    private String goodsId;

    //购买数量
    private Integer num;
}

Dao design: The main is a method to reduce inventory, other CRUD method that comes with JPA

public interface SecKillGoodsDao extends JpaRepository<SecKillGoods,String>{

    @Query("update SecKillGoods g set g.remainNum = g.remainNum - ?2 where g.id=?1")
    @Modifying(clearAutomatically = true)
    @Transactional
    int reduceStock(String id,Integer remainNum);

}

Provide operational data initialization and save orders:

@Service
public class SecKillService {

    @Autowired
    SecKillGoodsDao secKillGoodsDao;

    @Autowired
    SecKillOrderDao secKillOrderDao;

    /**
     * 程序启动时:
     * 初始化秒杀商品,清空订单数据
     */
    @PostConstruct
    public void initSecKillEntity(){
        secKillGoodsDao.deleteAll();
        secKillOrderDao.deleteAll();
        SecKillGoods secKillGoods = new SecKillGoods();
        secKillGoods.setId("123456");
        secKillGoods.setGoodsName("秒杀产品");
        secKillGoods.setRemainNum(10);
        secKillGoodsDao.save(secKillGoods);
    }

    /**
     * 购买成功,保存订单
     * @param consumer
     * @param goodsId
     * @param num
     */
    public void generateOrder(String consumer, String goodsId, Integer num) {
        secKillOrderDao.save(new SecKillOrder(consumer,goodsId,num));
    }
}

Here is the design of the controller layer

@Controller
public class SecKillController {

    @Autowired
    SecKillGoodsDao secKillGoodsDao;
    @Autowired
    SecKillService secKillService;

    /**
     * 普通写法
     * @param consumer
     * @param goodsId
     * @return
     */
    @RequestMapping("/seckill.html")
    @ResponseBody
    public String SecKill(String consumer,String goodsId,Integer num) throws InterruptedException {
        //查找出用户要买的商品
        SecKillGoods goods = secKillGoodsDao.findOne(goodsId);
        //如果有这么多库存
        if(goods.getRemainNum()>=num){
            //模拟网络延时
            Thread.sleep(1000);
            //先减去库存
            secKillGoodsDao.reduceStock(num);
            //保存订单
            secKillService.generateOrder(consumer,goodsId,num);
            return "购买成功";
        }
        return "购买失败,库存不足";
    }

}

Above all, the basis of preparation, a unit test using the following method to simulate high concurrency, a lot of people to buy the same situation a hot commodity.

@Controller
public class SecKillSimulationOpController {

    final String takeOrderUrl = "http://127.0.0.1:8080/seckill.html";

    /**
     * 模拟并发下单
     */
    @RequestMapping("/simulationCocurrentTakeOrder")
    @ResponseBody
    public String simulationCocurrentTakeOrder() {
        //httpClient工厂
        final SimpleClientHttpRequestFactory httpRequestFactory = new SimpleClientHttpRequestFactory();
        //开50个线程模拟并发秒杀下单
        for (int i = 0; i < 50; i++) {
            //购买人姓名
            final String consumerName = "consumer" + i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    ClientHttpRequest request = null;
                    try {
                        URI uri = new URI(takeOrderUrl + "?consumer=consumer" + consumerName + "&goodsId=123456&num=1");
                        request = httpRequestFactory.createRequest(uri, HttpMethod.POST);
                        InputStream body = request.execute().getBody();
                        BufferedReader br = new BufferedReader(new InputStreamReader(body));
                        String line = "";
                        String result = "";
                        while ((line = br.readLine()) != null) {
                            result += line;//获得页面内容或返回内容
                        }
                        System.out.println(consumerName+":"+result);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
        return "simulationCocurrentTakeOrder";
    }

}

Access localhost: 8080 / simulationCocurrentTakeOrder, you can test the
expected case: Because we only spike goods (123456) initialized 10, ideally of course, is to reduce to zero inventory, order form and only 10 records.

Reality: Orders table records

Single function in a highly concurrent design: the actual assessment interview

Commodity table records

Single function in a highly concurrent design: the actual assessment interview

Following analysis of why super stocks will happen:
because multiple requests to access, just use dao query a database has no inventory, but more bad situation is that many people have found the stock, this time because of the treatment program delay, there is no time to reduce inventory, it appeared dirty read. How to avoid it in the design? Most stupid way to do synchronization seckill method SecKillController, the only one who can place orders. But to affect performance, the single became a synchronous operation.

 @RequestMapping("/seckill.html")
 @ResponseBody
 public synchronized String SecKill

improve proposals

The multi-threaded programming specification, promoting the shared resource locking, most likely thinking of the case of concurrent competition plus sync blocks appear. It should be only one thread at a time to reduce inventory. But here is the best option, is to use Oracle, Mysql row-level locks - the same time only one thread can operate on the same line records, SecKillGoodsDao transformation:

public interface SecKillGoodsDao extends JpaRepository<SecKillGoods,String>{

    @Query("update SecKillGoods g set g.remainNum = g.remainNum - ?2 where g.id=?1 and g.remainNum>0")
    @Modifying(clearAutomatically = true)
    @Transactional
    int reduceStock(String id,Integer remainNum);

}

And just adding a, has caused great changes, int return value represents a number of rows affected, the corresponding controller to make the appropriate determination.

@RequestMapping("/seckill.html")
    @ResponseBody
    public String SecKill(String consumer,String goodsId,Integer num) throws InterruptedException {
        //查找出用户要买的商品
        SecKillGoods goods = secKillGoodsDao.findOne(goodsId);
        //如果有这么多库存
        if(goods.getRemainNum()>=num){
            //模拟网络延时
            Thread.sleep(1000);
            if(goods.getRemainNum()>0) {
                //先减去库存
                int i = secKillGoodsDao.reduceStock(goodsId, num);
                if(i!=0) {
                    //保存订单
                    secKillService.generateOrder(consumer, goodsId, num);
                    return "购买成功";
                }else{
                    return "购买失败,库存不足";
                }
            }else {
                return "购买失败,库存不足";
            }
        }
        return "购买失败,库存不足";
    }

In a look at the operation

Single function in a highly concurrent design: the actual assessment interview

Orders table:

Single function in a highly concurrent design: the actual assessment interview

In the case of the spike high concurrency problems, even if the network delay exists, it is also guaranteed.

Common progress, learn to share

I welcome everyone's attention [number of public calm as code ], mass Java-related articles, learning materials will be updated on the inside, finishing materials will be on the inside.

I feel good to write on a point of praise, plus followers chant! Point of attention, do not get lost, continuously updated! ! !

Massive interview, information-sharing architecture

Single function in a highly concurrent design: the actual assessment interview

Guess you like

Origin blog.51cto.com/14570694/2483731