2024年网络安全最新微服务高并发秒杀实战_基于令牌桶实现库存(1),2024金九银十面试季

给大家的福利

零基础入门

对于从来没有接触过网络安全的同学,我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线,大家跟着这个大的方向学习准没问题。

同时每个成长路线对应的板块都有配套的视频提供:

在这里插入图片描述

因篇幅有限,仅展示部分资料

网络安全面试题

绿盟护网行动

还有大家最喜欢的黑客技术

网络安全源码合集+工具包

所有资料共282G,朋友们如果有需要全套《网络安全入门+黑客进阶学习资源包》,可以扫描下方二维码领取(如遇扫码问题,可以在评论区留言领取哦)~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化资料的朋友,可以点击这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

@Transactional
public BaseResponse<JSONObject> spike(String phone, Long GoodsId) {
    GoodsDto goodsDto = seckillMapper.getByGoodsId(GoodsId);
    if (goodsDto == null) {
        return setResultError("商品信息不存在!");
    }
    // 用户频率限制 setnx 如果key存在话
    Boolean reusltNx = redisUtil.setNx(phone, seckillId + "", 10l);
    if (!reusltNx) {
        return setResultError("访问次数过多,10秒后再实现重试!");// 直接return,无需执行下面的version++操作
    }
    Long version = goodsDto.getVersion();// 先获取版本号
    int row = seckillMapper.inventoryDeduction(GoodsId, version);// 减库存
    // 后续可以生成订单到订单表。。。
}

@Component
public class RedisUtil {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    // 如果key存在的话返回fasle 不存在的话返回true(原生redis的setNx命令会返回0或1)
    public Boolean setNx(String key, String value, Long timeout) {
	Boolean setIfAbsent = stringRedisTemplate.opsForValue().setIfAbsent(key, value);
	if (timeout != null) {
	    stringRedisTemplate.expire(key, timeout, TimeUnit.SECONDS);
	}
	return setIfAbsent;
    }

    public void setList(String key, List<String> listToken) {
        stringRedisTemplate.opsForList().leftPushAll(key, listToken);
    }
}

    

3. 基于库存令牌桶 + MQ 实现异步修改库存并提交订单

在高并发情况下,如果有1万个用户同时秒杀某一商品,对数据库频繁的IO操作,可能会产生数据库崩溃问题(分表分库,读写分离治标不治本),解决方法:基于MQ+库存令牌桶实现。

同时有1万个请求实现秒杀,商品库存只有100个, 实现只需要修改库存100次就可以了:

方案实现流程:商品库存是多少,就提前在redis生成多少对应的库存令牌(这里即为100),在1万个请求中,只要谁能够获取到令牌谁就能够秒杀成功, 获取到秒杀令牌后,在使用mq异步实现修改减去库存。

代码实现:

① 编写生成100个令牌桶接口

    

② 编写获取token代码,并用mq异步发送

RabbitMQ消费者

@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");
	JSONObject jsonObject = JSONObject.parseObject(msg);
	// 1.获取秒杀id
	Long goodsId = jsonObject.getLong("seckillId");
	SeckillEntity seckillEntity = seckillMapper.findBySeckillId(goodsId);
	if (seckillEntity == null) {
	    log.warn("goodsId:{},商品信息不存在!", goodsId);
	    return;
	}
	Long version = seckillEntity.getVersion();
        // 2.减库存
	int inventoryDeduction = seckillMapper.inventoryDeduction(goodsId, version);
	if (!toDaoResult(inventoryDeduction)) {
	    log.info(">>>seckillId:{}修改库存失败", goodsId);
	    return;
	}
	// 3.添加订单
	OrderEntity orderEntity = new OrderEntity();
	String phone = jsonObject.getString("phone");
	orderEntity.setUserPhone(phone);
	orderEntity.setSeckillId(goodsId);
	orderEntity.setState(1l);
	int insertOrder = orderMapper.insertOrder(orderEntity);
	if (!toDaoResult(insertOrder)) {
	    return;
	}
	log.info(">>>成功消费seckillId:{},秒杀成功!", goodsId);
    }
    // 调用数据库层判断
    public Boolean toDaoResult(int result) {
        return result > 0 ? true : false;
    }
}

③  提供一个根据用户信息查询秒杀结果接口(实际开发中,也可以根据userId)

@GetMapping("/checkSpike")
public BaseResponse<JSONObject> getOrder(String phone, Long goodsId) {
    if (StringUtils.isEmpty(phone)) {
	return setResultError("手机号码不能为空!");
    }
    if (goodsId== null) {
    	return setResultError("商品库存id不能为空!");
    }
    OrderEntity orderEntity = orderMapper.findByOrder(phone, goodsId);
    if (orderEntity == null) {
    	return setResultError("正在排队中.....");// 要么还没被mq消费,要么没抢到token令牌
    }
    return setResultSuccess("恭喜你秒杀成功!");
}
public interface OrderMapper {
    @Select("SELECT goods_id,user_phone,stateFROM order_table WHERE USER_PHONE=#{phone} and goods_id=#{goodsId} AND STATE=1")
    OrderEntity findByOrder(@Param("phone")String phone, @Param("goodsId")Long goodsId);
}

前端需要写一个定时器,用于查询秒杀成功状态:

前端调用秒杀接口spike,如果秒杀成功的话,返回正在排队中。。。

前端写一个定时器调用checkSpike接口,使用手机号/userId + 商品id查询是否秒杀成功。

(如果调用spike返回售罄,则前端不用写定时器)

    

三. 网关优化

1. 基于Google的guava实现限流(基于令牌桶)

令牌桶实现原理: 以规定的速率往令牌桶中存入Token,用户请求必须获取到令牌中的Token才可以处理 请求,如果没有从令牌桶中获取到令牌则丢失该请求。 例如:令牌桶中最多只能存放50个Token,以规定速率存入Token实现在高并发情况下限流 。

Google的Guava工具包中就提供了一个限流工具类——RateLimiter,本文也是通过使用该工具类来实现限流功能。RateLimiter是基于“令牌通算法”来实现限流的,具体步骤如下:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
</dependency>

在zuul网关过滤器的run方法里,加入以下代码:

@Component
@Slf4j
public class GatewayFilter extends ZuulFilter {
    // 每秒存入令牌中token数为1
    private static final RateLimiter rateLimiter = RateLimiter.create(1);
    public Object run() throws ZuulException {
        /** 这里省略一系列的验证token,黑名单白名单等... */
        /* 
            默认超时时间是0,意思是拿不到就立即返回false,如果想修改超时时间,采用该代码:
            boolean tryAcquire = rateLimiter.tryAcquire(0, TimeUnit.SECONDS);
        */
        boolean tryrateLimiter.tryAcquire(); //阻塞等待超时时间,这里设为0,如果没有拿到令牌,直接拒绝访问,无需等待      
	    if (!tryAcquire) {// 返回false,表示没有获取到令牌,直接return
	        resultError(500, ctx, "现在抢购的人数过多,请稍等一下下哦!");
	        return;
	    }
        // 否则,获取到令牌,放行,继续执行后续逻辑,待所有过滤都通过,则直接访问秒杀接口...
   }
}

2. 使用Hystrix实现服务线程池隔离

默认情况下,上面的秒杀接口spike和查询秒杀结果接口checkSpike,都在一个线程池;在高并发场景,秒杀接口的压力会非常大,当一秒内用户全部请求秒杀接口,线程池都去处理秒杀接口,没有空闲线程去处理查询秒杀结果接口,这时候会产生延迟等待问题(默认tomcat只有一个线程池去处理所有请求,一旦线程池满了,导致其他线程无法访问)。

同时请求spike和checkSpike接口,打印日志如下:

>>>>>秒杀接口线程池名称:http-nio-9800-exec-1
>>>>>查询秒杀结果线程名称:http-nio-9800-exec-2

可以看到,两个接口处于同一个线程池,下面引入Hystrix实现服务降级,隔离:

<!-- 引入hystrix依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>


**先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7**

**深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

**因此收集整理了一份《2024年最新网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。**
![img](https://img-blog.csdnimg.cn/img_convert/c12e6e964b33dfa8a98fa373a93ea235.png)
![img](https://img-blog.csdnimg.cn/img_convert/a77384b772a1477081bf237593031f2b.png)
![img](https://img-blog.csdnimg.cn/img_convert/9fc2bc050848a76c90d36ea64484fe7a.png)
![img](https://img-blog.csdnimg.cn/img_convert/db5dad658fbc97068b2ec12c179d5625.png)
![img](https://img-blog.csdnimg.cn/img_convert/00ff536c81be3e75b8027decfcc91f4b.png)
![img](https://img-blog.csdnimg.cn/img_convert/a3ce4f605d726bac56ec62c8db06f5c9.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上网络安全知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

15499936356)]
[外链图片转存中...(img-4VJfYxmh-1715499936356)]
[外链图片转存中...(img-CW2N1F4Z-1715499936357)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上网络安全知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值