当前位置: 主页 > JAVA语言

令牌桶算法 java实现-java实现银行家算法

发布时间:2023-03-24 11:11   浏览次数:次   作者:佚名

学习博客:如何设计一个秒杀系统(二)

为什么要使用漏斗算法或者令牌桶算法

用于接口限流。

lru算法java实现_java实现银行家算法_令牌桶算法 java实现

令牌桶算法 java实现_java实现银行家算法_lru算法java实现

通俗点来讲就是说,在调用实际业务之前,就对过来的线程请求数量做限制,防止大量的请求调用数据库产生极大的压力,也防止无用的请求对业务的调用,减轻服务器的压力令牌桶算法 java实现,把请求限制在最开始处。

lru算法java实现_java实现银行家算法_令牌桶算法 java实现

如何解决接口限流

java实现银行家算法_lru算法java实现_令牌桶算法 java实现

令牌桶算法和漏斗算法

令牌桶算法 java实现_lru算法java实现_java实现银行家算法

令牌桶算法 java实现_java实现银行家算法_lru算法java实现

漏斗算法

把所有发过来的请求放到一个漏斗里,然后漏斗一直以一个固定的速度分发请求,让请求处理业务,漏斗容量一定,等到漏斗中的请求数量超过漏斗容量,多余请求溢出令牌桶算法 java实现,也就是被抛弃。

问题

简单粗暴。如果此时商家可供买的商品有1000个,根据服务器性能等,设置漏斗的容量为500个,但是一下子来了

java实现银行家算法_lru算法java实现_令牌桶算法 java实现

5000个请求,那么超过漏斗容量的4500个请求都要被丢弃,商家的商品也没有卖出去。

令牌桶算法----------因更加灵活而被广泛使用

桶按着一定时间生成一定数量的令牌,所有的请求要进行秒杀的业务处理时,都先经过令牌桶,去令牌桶里拿令牌,如果拿到令牌了,那么就执行秒杀业务,如果拿不到令牌,两种策略,一种是线程被阻塞,一直向令牌桶请求拿令牌,直到拿到为止去执行秒杀业务,一种是设置超时时间,在规定的超时时间内,如果请求拿不到令牌,那么请求被抛弃。

使用令牌桶算法解决秒杀系统中的接口限流

令牌桶算法 java实现_lru算法java实现_java实现银行家算法

导入依赖

 
        
            com.google.guava
            guava
            28.2-jre
        

controller层设置令牌桶算法限流策略【使用的第二个策略】

@RestController
@Slf4j//lombok提供的日志
public class StockOrderController {
    @Autowired
    private StockOrderService stockOrderService;
    //创建令牌桶实例[每秒在令牌桶中放行10个请求]
    private RateLimiter rateLimiter = RateLimiter.create(10);
    /**
     * 令牌桶+乐观锁解决超卖
     * @param id
     * @return
     */
    @GetMapping("killToken")
    public String killToken(@RequestParam("id") Integer id){
        System.out.println("秒杀"+ id);
        //令牌桶算法,加入接口限流措施,在5s内拿到令牌可以进行秒杀,
        // 否则不能【意味着商品可能不会被全部卖出,因为如果业务处理时间过长,请
        // 求在5s之后也不会被拿到,则会把请求抛弃,但是正常,后续实际业务中,用户可能拿到有问题商品,进行更换】
        boolean tryAcquire = rateLimiter.tryAcquire(5, TimeUnit.SECONDS);
        if(!tryAcquire){
            System.out.println("当前请求被限流,直接抛弃,无法调用后续秒杀逻辑.....");
            return "抢购失败";
        }
        //根据秒杀商品id,调用秒杀业务
        try {
            int orderId = stockOrderService.kill(id);
            return "秒杀成功订单id为" + String.valueOf(orderId);
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }

令牌桶算法 java实现_java实现银行家算法_lru算法java实现

service.java

@Service
@Transactional
public class StockOrderServiceImpl implements StockOrderService {
    @Resource
    private StockDao stockDao;
    @Resource
    private StockOrderDao stockOrderDao;
    @Override
    public int kill(Integer id) {
        Stock stock = checkStock(id);
        updateSale(stock);
        int orderId = createOrder(stock);
        return orderId;
    }
    //校验库存
    public Stock checkStock(Integer id) {
        //根据商品id校验库存
        Stock stockEntity = stockDao.checkStock(id);
        if (stockEntity.getCount() == stockEntity.getSale()) {
            //不能再秒杀
            throw new RuntimeException("抢光了");
        }
        return stockEntity;
    }
    //扣除库存,
    public void updateSale(Stock stockEntity) {
        //继续秒杀,扣除库存
        //在sql层面完成销量的+1和版本号的+1,并且根据商品id和版本号同时查询更新的商品
//        stockEntity.setSale(stockEntity.getSale());
        int reslut = stockDao.updateSale(stockEntity);
        if(reslut == 0){
            throw new RuntimeException("抢购失败,请重试");
        }
    }
    //创建订单
    public int createOrder(Stock stockEntity) {
        //创建订单
        StockOrder stockOrder = new StockOrder();
        stockOrder.setSID(stockEntity.getId())
                .setCreateTime(new Date())
                .setName(stockEntity.getName());
        //mybatis可以自动把生成的主键放到此对象里
        stockOrderDao.createOrder(stockOrder);
        //得到数据库主键生成的Id
        return stockOrder.getId();
    }
}

更改库存的mapper.xml




    
    
        update stock
        
            
                sale = #{sale} + 1,
            
            
                version = #{version} + 1,
            
        
        
            
                id = #{id}
            
            
                and version = #{version}
            
        
    

结果

使用jmeter测试工具,进行测试,2000个请求,商品数量是100个,结果商品数量秒杀成功的有52个,其他的用户请求都被抛弃,正常。

如果要增大卖出的商品数量,那么就把令牌桶最开始的放行线程数量设置大一些,或者把超时时间设置长一些。