当前位置: 首页 > news >正文

图片网站建设广东网站建设哪家专业

图片网站建设,广东网站建设哪家专业,公司做网站是做什么账务处理,怎样做淘宝联盟网站6、秒杀优化 6.0 压力测试 目的#xff1a;测试1000个用户抢购优惠券时秒杀功能的并发性能~ ①数据库中创建1000用户 这里推荐使用开源工具#xff1a;https://www.sqlfather.com/ #xff0c;导入以下配置即可一键生成模拟数据 {dbName:hmdp,…6、秒杀优化 6.0 压力测试 目的测试1000个用户抢购优惠券时秒杀功能的并发性能~ ①数据库中创建1000用户 这里推荐使用开源工具https://www.sqlfather.com/ 导入以下配置即可一键生成模拟数据 {dbName:hmdp,tableName:tb_user,tableComment:用户表,mockNum:100,fieldList:[{fieldName:id,fieldType:bigint(20),defaultValue:null,notNull:true,comment:主键id,primaryKey:true,autoIncrement:true,mockType:递增,mockParams:2,onUpdate:null},{fieldName:phone,fieldType:varchar(33),defaultValue:null,notNull:false,comment:null,primaryKey:false,autoIncrement:false,mockType:随机,mockParams:手机号,onUpdate:null},{fieldName:password,fieldType:varchar(384),defaultValue:null,notNull:false,comment:null,primaryKey:false,autoIncrement:false,mockType:随机,mockParams:字符串,onUpdate:null},{fieldName:nick_name,fieldType:varchar(96),defaultValue:null,notNull:false,comment:null,primaryKey:false,autoIncrement:false,mockType:规则,mockParams:user_\\w{10}$,onUpdate:null},{fieldName:icon,fieldType:varchar(765),defaultValue:null,notNull:false,comment:null,primaryKey:false,autoIncrement:false,mockType:固定,mockParams:/imgs/blogs/blog1.jpg,onUpdate:null},{fieldName:create_time,fieldType:timestamp,defaultValue:null,notNull:false,comment:null,primaryKey:false,autoIncrement:false,mockType:固定,mockParams:2023-01-01 00:00:00,onUpdate:null},{fieldName:update_time,fieldType:timestamp,defaultValue:null,notNull:false,comment:null,primaryKey:false,autoIncrement:false,mockType:固定,mockParams:2023-01-01 00:00:01,onUpdate:null}]}②将1000个用户处于登录状态(本质就是为1000个用户生成token并保存到Redis中) /*** 在Redis中保存1000个用户信息并将其token写入文件中方便测试多人秒杀业务*/Testvoid testMultiLogin() throws IOException {List User userList userService.lambdaQuery().last(limit 1000).list();for (User user : userList) {String token UUID.randomUUID().toString(true);UserDTO userDTO BeanUtil.copyProperties(user, UserDTO.class);Map String,Object userMap BeanUtil.beanToMap(userDTO,new HashMap (),CopyOptions.create().ignoreNullValue().setFieldValueEditor((fieldName,fieldValue) - fieldValue.toString()));String tokenKey RedisConstants.LOGIN_USER_KEY token;stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);stringRedisTemplate.expire(tokenKey, 60,TimeUnit.MINUTES);}Set String keys stringRedisTemplate.keys(RedisConstants.LOGIN_USER_KEY *);Cleanup FileWriter fileWriter new FileWriter(System.getProperty(user.dir) \\tokens.txt);Cleanup BufferedWriter bufferedWriter new BufferedWriter(fileWriter);assert keys ! null;for (String key : keys) {String token key.substring(RedisConstants.LOGIN_USER_KEY.length());String text token \n;bufferedWriter.write(text);}}③在Jmeter中进行压力测试1000个线程请求接口观察结果 这接口被Leader发现估计要被骂死~ 6.1 秒杀优化-异步秒杀思路 我们来回顾一下下单流程 当用户发起请求此时会请求nginxnginx会访问到tomcat而tomcat中的程序会进行串行操作分成如下几个步骤 1、查询优惠卷 2、判断秒杀库存是否足够 3、查询订单 4、校验是否是一人一单 5、扣减库存 6、创建订单 在这六步操作中又有很多操作是要去操作数据库的而且还是一个线程串行执行 这样就会导致我们的程序执行的很慢所以我们需要异步程序执行那么如何加速呢 在这里笔者想给大家分享一下课程内没有的思路看看有没有小伙伴这么想比如我们可以不可以使用异步编排来做或者说我开启N多线程N多个线程一个线程执行查询优惠卷一个执行判断扣减库存一个去创建订单等等然后再统一做返回这种做法和课程中有哪种好呢答案是课程中的好因为如果你采用我刚说的方式如果访问的人很多那么线程池中的线程可能一下子就被消耗完了而且你使用上述方案最大的特点在于你觉得时效性会非常重要但是你想想是吗并不是比如我只要确定他能做这件事然后我后边慢慢做就可以了我并不需要他一口气做完这件事所以我们应当采用的是课程中类似消息队列的方式来完成我们的需求而不是使用线程池或者是异步编排的方式来完成这个需求 **优化方案**我们将耗时比较短的逻辑判断放入到redis中比如是否库存足够比如是否一人一单这样的操作只要这种逻辑可以完成就意味着我们是一定可以下单完成的我们只需要进行快速的逻辑判断根本就不用等下单逻辑走完我们直接给用户返回成功 再在后台开一个线程后台线程慢慢的去执行queue里边的消息这样程序不就超级快了吗而且也不用担心线程池消耗殆尽的问题因为这里我们的程序中并没有手动使用任何线程池。当然这里边有两个难点 第一个难点是我们怎么在redis中去快速校验一人一单还有库存判断 第二个难点是由于我们校验和tomct下单是两个线程那么我们如何知道到底哪个单他最后是否成功或者是下单完成为了完成这件事我们在redis操作完之后我们会将一些信息返回给前端同时也会把这些信息丢到异步queue中去后续操作中可以通过这个id来查询我们tomcat中的下单逻辑是否完成了。【饭店的运营流程】 我们现在来看看整体思路当用户下单之后判断库存是否充足只需要到redis中去根据key找对应的value是否大于0即可如果不充足则直接结束如果充足继续在redis中判断用户是否可以下单如果set集合中没有这条数据说明他可以下单如果set集合中没有这条记录则将userId和优惠卷存入到redis中并且返回0整个过程需要保证是原子性的我们可以使用lua来操作 当以上判断逻辑走完之后我们可以判断当前redis中返回的结果是否是0 如果是0则表示可以下单则将之前说的信息存入到到queue中去然后返回然后再来个线程异步的下单前端可以通过返回的订单id来判断是否下单成功。 6.2 秒杀优化-Redis完成秒杀资格判断 需求 新增秒杀优惠券的同时将优惠券信息保存到Redis中 基于Lua脚本判断秒杀库存、一人一单决定用户是否抢购成功 如果抢购成功将优惠券id和用户id封装后存入阻塞队列 开启线程任务不断从阻塞队列中获取信息实现异步下单功能 VoucherServiceImpl Override Transactional public void addSeckillVoucher(Voucher voucher) {// 保存优惠券save(voucher);// 保存秒杀信息SeckillVoucher seckillVoucher new SeckillVoucher();seckillVoucher.setVoucherId(voucher.getId());seckillVoucher.setStock(voucher.getStock());seckillVoucher.setBeginTime(voucher.getBeginTime());seckillVoucher.setEndTime(voucher.getEndTime());seckillVoucherService.save(seckillVoucher);// 保存秒杀库存到Redis中stringRedisTemplate.opsForValue().set(SECKILL_STOCK_KEY voucher.getId(), voucher.getStock().toString()); }完整lua表达式 -- 1.参数列表 -- 1.1 优惠券Id local voucherId ARGV[1] -- 1.2 用户id local userId ARGV[2]-- 2.数据key -- 2.1 库存key local stockKey seckill:stock: .. voucherId -- 2.2 订单key local orderKey seckill:order: .. voucherId-- 3.脚本业务 -- 3.1 判断库存是否充足 get stockKey if (tonumber(redis.call(get, stockKey)) 0) then-- 3.1.2 库存不足,返回1return 1 end-- 3.2 判断用户是否已经下过单 if (redis.call(sismember, orderKey, userId) 1) then-- 3.2.2 存在说明重复下单返回2return 2 end-- 3.3 扣库存 incrby stockKey -1 redis.call(incrby, stockKey, -1)-- 3.4 下单(保存用户) sadd orderKey userId redis.call(sadd, orderKey, userId)-- 3.5 用户有下单资格返回0 return 0当以上lua表达式执行完毕后剩下的就是根据步骤3,4来执行我们接下来的任务了 VoucherOrderServiceImpl /*** 使用Lua脚本消息队列实现秒杀下单** param voucherId* return*/ Override public Result seckillVoucher(Long voucherId) {// 获取用户idLong userId UserHolder.getUser().getId();// 1.执行Lua脚本Long result stringRedisTemplate.execute(SECKILL_SCRIPT,Collections.emptyList(),voucherId.toString(),userId.toString());// 2.判断结果是否为0if (result ! 0) {// 2.1 不为0代表没有购买资格return Result.fail(result 1 ? 库存不足 : 不能重复下单);}//TODO 保存阻塞队列// 3.返回订单idreturn Result.ok(orderId); }**压力测试**因为目前前两步骤做完后面的加入阻塞队列执行时间就很短了~ 可以看到并发性能大大提升请求响应值在0.1s左右吞吐量可达到1500/sec~ 速度飞起 6.3 秒杀优化-基于阻塞队列实现秒杀优化 VoucherOrderServiceImpl 修改下单动作现在我们去下单时是通过lua表达式去原子执行判断逻辑如果判断我出来不为0 则要么是库存不足要么是重复下单返回错误信息如果是0则把下单的逻辑保存到队列中去然后异步执行 /*** 将代理对象声明成全局*/private IVoucherOrderService proxy;/*** 存放任务的阻塞队列* 特点当一个线程尝试从队列中获取元素没有元素线程就会被阻塞直到队列中有元素线程才会被唤醒并去获取元素*/private BlockingQueue VoucherOrder orderTasks new ArrayBlockingQueue (1024 * 1024);/*** 思考一个问题为什么要使用线程池呢而不是直接创建一个线程* 其实直接创建一个线程也行但是创建一个线程开销很大的用阻塞队列线程池的形式实现了线程的的复用*/private static final ExecutorService SECKILL_ORDER_EXECUTOR Executors.newSingleThreadExecutor();/*** 由于用户秒杀的时间可能是随时的所以需要我们项目已启动 线程池就应该从消息队列获取任务然后工作...** PostConstruct类初始花后立刻执行*/PostConstructprivate void init() {SECKILL_ORDER_EXECUTOR.submit(new VoucherOrderHandler());}private class VoucherOrderHandler implements Runnable {Overridepublic void run() {while (true) {try {// 1.获取队列中的订单信息// take()获取和删除该队列的头部如果没有则阻塞等待直到有元素可用。所以使用该方法如果有元素线程就工作没有线程就阻塞卡在这里不用担心CPU会空转~VoucherOrder voucherOrder orderTasks.take();// 2.创建订单handleVoucherOrder(voucherOrder);} catch (Exception e) {log.error(处理订单异常, e);}}}}/*** 创建订单** param voucherOrder*/private void handleVoucherOrder(VoucherOrder voucherOrder) {/*** 其实这里可以不加锁了:方式一* ①:前面的Lua脚本已经进判断过库存和一人一单了并且也可以保证执行的原子性一次只有一个线程执行。* ②:此时线程池中只有一个线程,是单线程哦~* ③:之后从消息队列取任务执行并不需要保证其原子性因为就不存在并发安全问题了* 加锁算是一种兜底~*/// 方式一加分布式锁再创建订单// // 1.获取用户// // 注意这里userId不能从UserHolder中去取因为当前并不是主线程而是子线程无法拿到父线程ThreadLocal中的数据// Long userId voucherOrder.getUserId();// // 2.获取分布式锁// RLock lock redissonClient.getLock(lock:order: userId);// boolean isLock lock.tryLock();// // 3.判断是否获取锁成功// if (!isLock) {// // 获取锁失败返回错误和重试// log.error(不允许重复下单~);// }// try {// // 获取代理对象只有通过代理对象调用方法事务才会生效// // 注意这里直接通过以下方式获取肯定是不行的。因为方法底层也是基于ThreadLocal获取的子线程是无法获取父线程ThreadLocal中的对象的// // 解决办法在seckillVoucher中提前获取然后通过消息队列传入或者声明成全局变量从而就可以使用了// // IVoucherOrderService proxy (IVoucherOrderService) AopContext.currentProxy();// proxy.createVoucherOrder(voucherOrder.getVoucherId());// } finally {// lock.unlock();// }// 方式二直接创建订单proxy.createVoucherOrder(voucherOrder);}// RedisScript需要加载seckill.lua文件为了避免每次释放锁时都加载我们可以提前加载好。否则每次读取文件就会产生IO效率很低static {SECKILL_SCRIPT new DefaultRedisScript ();SECKILL_SCRIPT.setLocation(new ClassPathResource(seckill.lua));SECKILL_SCRIPT.setResultType(Long.class);}/*** 使用Lua脚本消息队列实现秒杀下单** param voucherId* return*/Overridepublic Result seckillVoucher(Long voucherId) {// 获取用户idLong userId UserHolder.getUser().getId();// 1.执行Lua脚本Long result stringRedisTemplate.execute(SECKILL_SCRIPT,Collections.emptyList(),voucherId.toString(),userId.toString());// 2.判断结果是否为0if (result ! 0) {// 2.1 不为0代表没有购买资格return Result.fail(result 1 ? 库存不足 : 不能重复下单);}// 2.2 为0有购买资格把下单信息保存到消息队列// 2.3 创建订单VoucherOrder voucherOrder new VoucherOrder();// 2.4 订单idlong orderId redisIdWorker.nextId(order);voucherOrder.setId(orderId);// 2.5 用户idvoucherOrder.setUserId(userId);// 2.6代金券idvoucherOrder.setVoucherId(voucherId);// 2.7放入阻塞队列【理论上只要放入消息队列就有购买资格】orderTasks.add(voucherOrder);// 3.获取代理对象proxy (IVoucherOrderService) AopContext.currentProxy();// 4. 返回订单idreturn Result.ok(orderId);}Overridepublic void createVoucherOrder(VoucherOrder voucherOrder) {//注意因为我们在Lua中已经校验过库存和一人一单了这里就不需要校验拉~// 1.扣减库存boolean success seckillVoucherService.update().setSql(stock stock - 1).eq(voucher_id, voucherOrder.getVoucherId()).gt(stock, 0).update();//这里其实不判断也是OK的因为Lua脚本中校验过了所以一定是充足的if (!success) {log.error(库存不足);}// 2.保存订单this.save(voucherOrder);}并发测试 可以看出平均每个请求40ms并发达到1000/sec速度非常快。 小总结 秒杀业务的优化思路是什么 先利用Redis完成库存余量、一人一单判断完成抢单业务再将下单业务放入阻塞队列利用独立线程异步下单基于阻塞队列的异步秒杀存在哪些问题 内存限制问题因为我们使用的是JDK的阻塞队列它使用的是内存。不加以限制的时候在高并发的情况下无数订单进入队列可能导致内存溢出。所以我们在创建队列的时候设置了上限。另外如果此时队列已经存满了又有新的任务忘里面塞就放不进去了。数据安全问题目前是基于内存来保存这些订单信息的 ①如果内存突然宕机那么内存中所有的订单信息都丢失了。从而就可能出现用户下单成功但是数据库里面并没有订单记录造成数据不一致的问题。②如果有一个线程从队列中取出了下单的任务即将执行的时候发生了严重的事故异常等那么这个任务就没有执行而且因为这个任务已经取出队列了以后就再也不会执行了。从而这个任务就丢失了再次出现数据不一致的问题。
http://www.hkea.cn/news/14354596/

相关文章:

  • 黄村网站建设国家企业信用公示网全国上海
  • 做公司网站的模板如何修改网页上的内容
  • 企业网站 自助建站网站多页面布局
  • 基于php技术的个人网站设计深圳市交易服务中心
  • wordpress 网站域名广东工程建设监理协会网站
  • 做类似起点的网站wordpress站点演示
  • 中国空间站扩建POS机网站怎么做
  • 跟我学seoseo排名优化收费
  • 做网站是不是要域名费足球比赛直播平台
  • 济南网站开发哪家好浦东新区手机网站设计
  • 福建网站建设开发深圳做网站哪家公司好
  • 做本地网站赚钱吗?域名注册服务商网站
  • 动态素材网站网站域名怎么查询
  • 济南网站建设服务公司北京搜索关键词优化
  • 苏州网站建设制作网页设计实训内容及过程
  • 查询网站开发语言排网站搭建论文
  • 广西建设执业资格注册中心网站网站构成
  • 域名到网站上线WordPress这新手
  • 网站制作软件是什么电商网页设计理念
  • 网站备案个人信息科技有限公司属于什么行业
  • 慈溪企业网站建设刚做的网站怎么在百度搜到
  • 域名推广技巧安徽网站关键词优化
  • 做网站v赚钱wordpress简约企业主题
  • 西安网站建设推广专家ngrok 群晖wordpress
  • 自己做的网站如何在网络上展示太原建站公司有哪些
  • 个人网站模板建站软件技术适合女生学吗大专
  • 金华品牌网站建设wordpress 建企业网站
  • 手机ppt在哪个网站做招聘wordpress
  • 网站开发基础语言龙采网站建设资源分享平台
  • 设计类素材网站网站服务器名是什么