Redis 优惠卷秒杀(二) 异步秒杀、基于Stream的消息队列处理

目录

基于Stream的消息队列

Redis优化秒杀

登录头 

改进秒杀业务,调高并发性能

Redis消息队列实现异步秒杀

​编辑基于List结构模拟消息队列 

基于PuSub的消息队列

 ​编辑

  基于Stream的消息队列

Redis消息队列


基于Stream的消息队列

Redis优化秒杀

登录头 

 

改进秒杀业务,调高并发性能


@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {

    @Resource
    private ISeckillVoucherService seckillVoucherService;

    @Resource
    private RedisIdWorker redisIdWorker;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private RedissonClient redissonClient;

    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }
//    阻塞队列
    private BlockingQueue<VoucherOrder> orederTasks = new ArrayBlockingQueue(1024*1024);
//创建线程池
    private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();

    @PostConstruct
    private void init(){
        //TODO 类初始化的时候执行线程池
        SECKILL_ORDER_EXECUTOR.submit(new VocherOrderHandler());

    }
    private class VocherOrderHandler implements Runnable{

        @Override
        public void run() {
            while (true) {
                try {
                    //1.获取队列中的订单信息
                    VoucherOrder voucherOrder = orederTasks.take();
//                    2.创建订单
                    handleVocherOrder(voucherOrder);
                }
                catch (Exception e) {
//                    e.printStackTrace();
                    log.error("处理订单异常" ,e);
                }
            }
        }
    }

    private void handleVocherOrder(VoucherOrder voucherOrder) {
        Long userId = voucherOrder.getUserId();
//        Long userId = UserHolder.getUser().getId();
//        synchronized锁还是有线程问题
//        synchronized(userId.toString().intern()) {//intern返回字符串的规范表示
        //TODO 分布锁
        //TODO 创建锁对象
//        SimpleRedisLock lock = new SimpleRedisLock("order" + userId, stringRedisTemplate);
//        TODO 使用工具Redisson
        RLock lock = redissonClient.getLock("lock:order" + userId);
        //获取锁
        //TODO
        boolean isLock = lock.tryLock();
        if ( !isLock ){
            //获取锁失败,返回错误或重试
            log.error("不允许重复下单");
            return ;

        }
        try {
            //获取代理对象(事务)事务生效
            //TODO 获取锁之后再创建事务
//            IVoucherOrderService proxy = (IVoucherOrderService)AopContext.currentProxy();
            //TODO 事务提交完再释放锁
            proxy.createVoucherOrderTwo(voucherOrder);//事务能够
            //TODO 可以避免事务没提交就释放锁的安全问题
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    private IVoucherOrderService proxy;
    //    TODO
    @Override
    public Result seckillVoucher(Long voucherId) {
        Long userId = UserHolder.getUser().getId();
        //1.执行lua脚本
        Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),
                voucherId.toString(), userId.toString());
        //2.判断结果为0
        int r = result.intValue();
        if ( r!=0 ) {
            //2.1不为0,代表没有购买资格
            return Result.fail(r==1?"库存不足":"不能重复下单");

        }
        //TODO 2.2为0, 有购买资格,把下单信息保存到阻塞队列
        long orderId = redisIdWorker.nextId("order");
        //2.3.创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        //2.3订单di
        voucherOrder.setId(orderId);
        //2.4用户id
//        Long userId = UserHolder.getUser().getId();
        voucherOrder.setUserId(userId);
        //2.5代金卷id
        voucherOrder.setVoucherId(voucherId);
        //TODO 2.6 订单放入堵塞队列
        orederTasks.add(voucherOrder);

        //TODO 3.获取代理对象 创建全局变量其他地方才可以用
         proxy = (IVoucherOrderService)AopContext.currentProxy();

        //7.返回订单
        //TODO 保存阻塞队列

        //3.返回订单id

        return Result.ok(orderId);
    }
  /*  @Override
    public Result seckillVoucher(Long voucherId) {
        //1.查询优惠卷
        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
        //2.判断秒杀是否开始
        if ( voucher.getBeginTime().isAfter(LocalDateTime.now()) ) {
//            秒杀尚未开始
            return Result.fail ("秒杀尚未开始!");
        }

        //3.判断秒杀是否已经结束
        if ( voucher.getEndTime().isBefore(LocalDateTime.now()) ) {
            return Result.fail ("秒杀已结束!");
        }
        //4.判断库存是否充足
        if ( voucher.getStock()<1 ) {
            //库存不足
            return Result.fail("库存不足");
        }
//        -------
        Long userId = UserHolder.getUser().getId();
//        synchronized锁还是有线程问题
//        synchronized(userId.toString().intern()) {//intern返回字符串的规范表示
        //TODO 分布锁
        //TODO 创建锁对象
//        SimpleRedisLock lock = new SimpleRedisLock("order" + userId, stringRedisTemplate);
//        TODO 使用工具Redisson
        RLock lock = redissonClient.getLock("lock:order" + userId);
        //获取锁
        //TODO
        boolean isLock = lock.tryLock();
        if ( !isLock ){
            //获取锁失败,返回错误或重试
            return Result.fail("不允许重复下单");

        }
        try {
            //获取代理对象(事务)事务生效
            //TODO 获取锁之后再创建事务
            IVoucherOrderService proxy = (IVoucherOrderService)AopContext.currentProxy();
            //TODO 事务提交完再释放锁
            return proxy.createVoucherOrder(voucherId);//事务能够
            //TODO 可以避免事务没提交就释放锁的安全问题
        } finally {
            //释放锁
            lock.unlock();
        }
//
//        }
    }
*/
  @Transactional
  public void createVoucherOrderTwo(VoucherOrder voucherOrder) {
      //TODO 6.一人一单
//      TODO 不是在主线程了,在线程池了
      Long userId = voucherOrder.getUserId();
//        userId.toString()底层代码是创建对象,所以有可能还是不一样的
//        synchronized(userId.toString().intern()) {//intern返回字符串的规范表示


      //TODO 6.1查询订单
      Integer count = query().eq("user_id", userId).eq("voucher_id", voucherOrder.getVoucherId()).count();
      //TODO 6.2判断是否存在
      if ( count > 0 ) {
          //用户已经买过了
          log.error("用户已经买过了一次了");
          return ;
      }


      //5.扣减库存
      boolean sucess = seckillVoucherService.update()
              .setSql("stock = stock-1")//set stock = stock-1
              .gt("stock", "0")//可以解决超卖 where id ?and stock >0
              .eq("voucher_id", voucherOrder.getVoucherId()).update();//
//                .eq("stock",voucher.getStock()).update();//where id ?and stock = ?

      if ( !sucess ) {
          //扣减不足
          log.error("库存不足");
          return ;

      }
      //7.0创建订单
      save(voucherOrder);

  }
    @Transactional//加入事务
    public   Result createVoucherOrder(Long voucherId) {
        //TODO 6.一人一单
        Long userId = UserHolder.getUser().getId();
//        userId.toString()底层代码是创建对象,所以有可能还是不一样的
//        synchronized(userId.toString().intern()) {//intern返回字符串的规范表示


            //TODO 6.1查询订单
            Integer count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
            //TODO 6.2判断是否存在
            if ( count > 0 ) {
                //用户已经买过了
                return Result.fail("用户已经买过了一次了");
            }


            //5.扣减库存
            boolean sucess = seckillVoucherService.update()
                    .setSql("stock = stock-1")//set stock = stock-1
                    .gt("stock", "0")//可以解决超卖 where id ?and stock >0
                    .eq("voucher_id", voucherId).update();//
//                .eq("stock",voucher.getStock()).update();//where id ?and stock = ?

            if ( !sucess ) {
                //扣减不足
                return Result.fail("库存不足");

            }


            //6.创建订单
            VoucherOrder voucherOrder = new VoucherOrder();
            //6.1订单di
            long orderId = redisIdWorker.nextId("order");
            voucherOrder.setId(orderId);
            //6.2用户id
//        Long userId = UserHolder.getUser().getId();
            voucherOrder.setUserId(userId);
            //6.3代金卷id
            voucherOrder.setVoucherId(voucherId);
            save(voucherOrder);
            //7.返回订单
            return Result.ok(orderId);

    }


}

Redis消息队列实现异步秒杀

基于List结构模拟消息队列 

 

基于PuSub的消息队列

 

 

  基于Stream的消息队列

 

 

Redis消息队列


@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {

    @Resource
    private ISeckillVoucherService seckillVoucherService;

    @Resource
    private RedisIdWorker redisIdWorker;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private RedissonClient redissonClient;

    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;
    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }
//    TODO
//创建线程池
    private static final ExecutorService SECKILL_ORDER_EXECUTOR = Executors.newSingleThreadExecutor();

    @PostConstruct
    private void init(){
        //TODO 类初始化的时候执行线程池
        SECKILL_ORDER_EXECUTOR.submit(new VocherOrderHandler());

    }
    private class VocherOrderHandler implements Runnable{
        String querName = "stream.orders";

        @Override
        public void run() {
            while (true) {
                try {
                    //1.获取消息队列中的订单信息
//                    XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS stream.order >
                    List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
                            Consumer.from("g1", "c1"),
                            StreamReadOptions.empty().count(1).block(Duration.ofSeconds(2)),
                            StreamOffset.create(querName, ReadOffset.lastConsumed())
                    );
                    //判断消息获取是否成功
                    if ( list == null || list.isEmpty() ) {
                        //2.1如果获取失败,说明没有消息,继续下一次循环
                        continue;

                    }
                    //3.解析消息中的订单信息
                    MapRecord<String, Object, Object> record = list.get(0);
                    Map<Object, Object> value = record.getValue();
                    VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);
                    //4.如果获取成功,可以下单
                    handleVocherOrder(voucherOrder);
                    //ACK确认 SACK stream.orders g1 id
                    stringRedisTemplate.opsForStream().acknowledge(querName,"g1",record.getId());
                }
                catch (Exception e) {
//                    e.printStackTrace();
                    log.error("处理订单异常" ,e);
                    handlePendingList();
                }
            }
        }

        private void handlePendingList() {
            while (true) {
                try {
                    //1.获取pending-list中的订单信息
//                    XREADGROUP GROUP g1 c1 COUNT 1 BLOCK 2000 STREAMS stream.order 0
                    List<MapRecord<String, Object, Object>> list = stringRedisTemplate.opsForStream().read(
                            Consumer.from("g1", "c1"),
                            StreamReadOptions.empty().count(1),
                            StreamOffset.create(querName, ReadOffset.from("0"))
                    );
                    //判断消息获取是否成功
                    if ( list == null || list.isEmpty() ) {
                        //2.1如果获取失败,说明pending-list没有异常消息,结束循环
                        continue;

                    }
                    //3.解析消息中的订单信息
                    MapRecord<String, Object, Object> record = list.get(0);
                    Map<Object, Object> value = record.getValue();
                    VoucherOrder voucherOrder = BeanUtil.fillBeanWithMap(value, new VoucherOrder(), true);
                    //4.如果获取成功,可以下单
                    handleVocherOrder(voucherOrder);
                    //ACK确认 SACK stream.orders g1 id
                    stringRedisTemplate.opsForStream().acknowledge(querName,"g1",record.getId());
                }
                catch (Exception e) {
//                    e.printStackTrace();
                    log.error("处理pending-list订单异常" ,e);
                    try {
                        Thread.sleep(20);
                    }
                    catch (InterruptedException interruptedException) {
                        interruptedException.printStackTrace();
                    }
                }
            }

        }
    }
   /*
    //    阻塞队列
    private BlockingQueue<VoucherOrder> orederTasks = new ArrayBlockingQueue(1024*1024);
    private class VocherOrderHandler implements Runnable{

        @Override
        public void run() {
            while (true) {
                try {
                    //1.获取队列中的订单信息
                    VoucherOrder voucherOrder = orederTasks.take();
//                    2.创建订单
                    handleVocherOrder(voucherOrder);
                }
                catch (Exception e) {
//                    e.printStackTrace();
                    log.error("处理订单异常" ,e);
                }
            }
        }
    }
    */

    private void handleVocherOrder(VoucherOrder voucherOrder) {
        Long userId = voucherOrder.getUserId();
//        Long userId = UserHolder.getUser().getId();
//        synchronized锁还是有线程问题
//        synchronized(userId.toString().intern()) {//intern返回字符串的规范表示
        //TODO 分布锁
        //TODO 创建锁对象
//        SimpleRedisLock lock = new SimpleRedisLock("order" + userId, stringRedisTemplate);
//        TODO 使用工具Redisson
        RLock lock = redissonClient.getLock("lock:order" + userId);
        //获取锁
        //TODO
        boolean isLock = lock.tryLock();
        if ( !isLock ){
            //获取锁失败,返回错误或重试
            log.error("不允许重复下单");
            return ;

        }
        try {
            //获取代理对象(事务)事务生效
            //TODO 获取锁之后再创建事务
//            IVoucherOrderService proxy = (IVoucherOrderService)AopContext.currentProxy();
            //TODO 事务提交完再释放锁
            proxy.createVoucherOrderTwo(voucherOrder);//事务能够
            //TODO 可以避免事务没提交就释放锁的安全问题
        } finally {
            //释放锁
            lock.unlock();
        }
    }

    private IVoucherOrderService proxy;

    //TODO
    @Override
    public Result seckillVoucher(Long voucherId) {
//        获取用户
        Long userId = UserHolder.getUser().getId();
//        获取订单id
        long orderId = redisIdWorker.nextId("order");

        //1.执行lua脚本
        Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),
                voucherId.toString(), userId.toString(),String.valueOf(orderId));
        //2.判断结果为0
        int r = result.intValue();
        if ( r!=0 ) {
            //2.1不为0,代表没有购买资格
            return Result.fail(r==1?"库存不足":"不能重复下单");

        }
        //TODO 2.2为0, 有购买资格,把下单信息保存到阻塞队列

        //TODO 3.获取代理对象 创建全局变量其他地方才可以用
        proxy = (IVoucherOrderService)AopContext.currentProxy();

        //7.返回订单
        //TODO 保存阻塞队列

        //3.返回订单id

        return Result.ok(orderId);
    }
    //    TODO
   /* @Override
    public Result seckillVoucher(Long voucherId) {
        Long userId = UserHolder.getUser().getId();
        //1.执行lua脚本
        Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),
                voucherId.toString(), userId.toString());
        //2.判断结果为0
        int r = result.intValue();
        if ( r!=0 ) {
            //2.1不为0,代表没有购买资格
            return Result.fail(r==1?"库存不足":"不能重复下单");

        }
        //TODO 2.2为0, 有购买资格,把下单信息保存到阻塞队列
        long orderId = redisIdWorker.nextId("order");
        //2.3.创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        //2.3订单di
        voucherOrder.setId(orderId);
        //2.4用户id
//        Long userId = UserHolder.getUser().getId();
        voucherOrder.setUserId(userId);
        //2.5代金卷id
        voucherOrder.setVoucherId(voucherId);
        //TODO 2.6 订单放入堵塞队列
        orederTasks.add(voucherOrder);

        //TODO 3.获取代理对象 创建全局变量其他地方才可以用
         proxy = (IVoucherOrderService)AopContext.currentProxy();

        //7.返回订单
        //TODO 保存阻塞队列

        //3.返回订单id

        return Result.ok(orderId);
    }
    */
  /*  @Override
    public Result seckillVoucher(Long voucherId) {
        //1.查询优惠卷
        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
        //2.判断秒杀是否开始
        if ( voucher.getBeginTime().isAfter(LocalDateTime.now()) ) {
//            秒杀尚未开始
            return Result.fail ("秒杀尚未开始!");
        }

        //3.判断秒杀是否已经结束
        if ( voucher.getEndTime().isBefore(LocalDateTime.now()) ) {
            return Result.fail ("秒杀已结束!");
        }
        //4.判断库存是否充足
        if ( voucher.getStock()<1 ) {
            //库存不足
            return Result.fail("库存不足");
        }
//        -------
        Long userId = UserHolder.getUser().getId();
//        synchronized锁还是有线程问题
//        synchronized(userId.toString().intern()) {//intern返回字符串的规范表示
        //TODO 分布锁
        //TODO 创建锁对象
//        SimpleRedisLock lock = new SimpleRedisLock("order" + userId, stringRedisTemplate);
//        TODO 使用工具Redisson
        RLock lock = redissonClient.getLock("lock:order" + userId);
        //获取锁
        //TODO
        boolean isLock = lock.tryLock();
        if ( !isLock ){
            //获取锁失败,返回错误或重试
            return Result.fail("不允许重复下单");

        }
        try {
            //获取代理对象(事务)事务生效
            //TODO 获取锁之后再创建事务
            IVoucherOrderService proxy = (IVoucherOrderService)AopContext.currentProxy();
            //TODO 事务提交完再释放锁
            return proxy.createVoucherOrder(voucherId);//事务能够
            //TODO 可以避免事务没提交就释放锁的安全问题
        } finally {
            //释放锁
            lock.unlock();
        }
//
//        }
    }
*/
  @Transactional
  public void createVoucherOrderTwo(VoucherOrder voucherOrder) {
      //TODO 6.一人一单
//      TODO 不是在主线程了,在线程池了
      Long userId = voucherOrder.getUserId();
//        userId.toString()底层代码是创建对象,所以有可能还是不一样的
//        synchronized(userId.toString().intern()) {//intern返回字符串的规范表示


      //TODO 6.1查询订单
      Integer count = query().eq("user_id", userId).eq("voucher_id", voucherOrder.getVoucherId()).count();
      //TODO 6.2判断是否存在
      if ( count > 0 ) {
          //用户已经买过了
          log.error("用户已经买过了一次了");
          return ;
      }


      //5.扣减库存
      boolean sucess = seckillVoucherService.update()
              .setSql("stock = stock-1")//set stock = stock-1
              .gt("stock", "0")//可以解决超卖 where id ?and stock >0
              .eq("voucher_id", voucherOrder.getVoucherId()).update();//
//                .eq("stock",voucher.getStock()).update();//where id ?and stock = ?

      if ( !sucess ) {
          //扣减不足
          log.error("库存不足");
          return ;

      }
      //7.0创建订单
      save(voucherOrder);

  }
    @Transactional//加入事务
    public   Result createVoucherOrder(Long voucherId) {
        //TODO 6.一人一单
        Long userId = UserHolder.getUser().getId();
//        userId.toString()底层代码是创建对象,所以有可能还是不一样的
//        synchronized(userId.toString().intern()) {//intern返回字符串的规范表示


            //TODO 6.1查询订单
            Integer count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
            //TODO 6.2判断是否存在
            if ( count > 0 ) {
                //用户已经买过了
                return Result.fail("用户已经买过了一次了");
            }


            //5.扣减库存
            boolean sucess = seckillVoucherService.update()
                    .setSql("stock = stock-1")//set stock = stock-1
                    .gt("stock", "0")//可以解决超卖 where id ?and stock >0
                    .eq("voucher_id", voucherId).update();//
//                .eq("stock",voucher.getStock()).update();//where id ?and stock = ?

            if ( !sucess ) {
                //扣减不足
                return Result.fail("库存不足");

            }


            //6.创建订单
            VoucherOrder voucherOrder = new VoucherOrder();
            //6.1订单di
            long orderId = redisIdWorker.nextId("order");
            voucherOrder.setId(orderId);
            //6.2用户id
//        Long userId = UserHolder.getUser().getId();
            voucherOrder.setUserId(userId);
            //6.3代金卷id
            voucherOrder.setVoucherId(voucherId);
            save(voucherOrder);
            //7.返回订单
            return Result.ok(orderId);

    }


}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/37532.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 142.环形链表II

142. 环形链表 II - 力扣&#xff08;LeetCode&#xff09; /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ class Solution { public:ListNode *detectCycle(ListNode …

【微信小程序创作之路】- 小程序窗口整体配置(导航栏、标题)

【微信小程序创作之路】- 小程序窗口导航栏配置 第五章 微信小程序窗口导航栏配置 文章目录 【微信小程序创作之路】- 小程序窗口导航栏配置前言一、入口文件的配置二、页面配置三、全局默认窗口配置1.navigationBarTitleText&#xff1a;导航栏标题文字2.navigationBarBackgr…

【UE4 C++】02-编译、生成当前的C++程序

一、编译 编译快捷键&#xff1a; CtrlF7 如果不使用快捷键&#xff0c;可以点击顶部菜单栏中的下拉按钮&#xff0c;然后选择自定义 点击添加命令 点击“生成”&#xff0c;选择编译“”&#xff0c;点击“确定” 此时可以看到顶部菜单栏多了一个用于编译的按钮 二、生成 鼠…

TypeScript学习笔记

TypeScript学习笔记 1、运行环境 1.1、初始化 npm init -y npm i -D webpack webpack-cli webpack-dev-server typescript ts-loader html-webpack-plugin1.2、webpack.config.js const path require(path) const htmlWebpackPlugin require("html-webpack-plugin&…

Todo-List案例版本五

安装库npm i pubsub-js 消息的订阅与发布 src/App.vue <template><div class"app"><h1>{{ msg }}</h1><School/><Student/></div> </template><script> import Student from ./components/Student import …

【unity之IMGUI实践】单例模式管理数据存储【二】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;uni…

安卓UI:SearchView

目录 一、SearchView介绍 二、常用方法 &#xff08;一&#xff09;、监听器&#xff1a; (二)、常用方法&#xff1a; (三)、其他常用方法 三、例子&#xff1a; MainActivity2 &#xff1a; ChatListAdapter &#xff1a; item_people_view: activity_main2: 运行结果…

Java支付SDK接口远程调试 - 支付宝沙箱环境【公网地址调试】

文章目录 1.测试环境2.本地配置3. 内网穿透3.1 下载安装cpolar内网穿透3.2 创建隧道 4. 测试公网访问5. 配置固定二级子域名5.1 保留一个二级子域名5.2 配置二级子域名 6. 使用固定二级子域名进行访问 转载自cpolar极点云文章&#xff1a;Java支付宝沙箱环境支付&#xff0c;SD…

详解 ➾【FTP服务工作原理及连接模式】

详解 ➾【FTP服务工作原理及连接模式】 &#x1f53b; 前言&#x1f53b; 一、FTP服务简介&#x1f6a5; 1.1 FTP工作原理&#x1f6a5; 1.2 匿名用户访问的产生&#x1f6a5; 1.3 FTP服务的连接模式&#x1f6a5; 1.4 几种流行的FTP服务器软件 &#x1f53b; 总结—温故知新 &…

QTranslator语言转换

//appname的格式 例如通常为&#xff08;QQ为应用的名称&#xff09; QQ_en.ts或QQ_zh_CN.ts QString qmName"zh_CN"; QTranslator trans ; QString qm QString(":/translatoin/qt/appname_%1.qm").arg(qmName); auto ret trans.load(qm); Q_UNUSED(ret)…

WebSocket理解

WebSocket理解 WebSocket定义与HTTP关系相同点:不同点&#xff1a;联系总体过程 HTTP问题长轮询Ajax轮询 WebSocket特点 WebSocket 定义 本质上是TCP的协议 持久化的协议 实现了浏览器和服务器的全双工通信&#xff0c;能更好的节省服务器资源和带宽 与HTTP关系 相同点: 基于…

小白到运维工程师自学之路 第五十三集 (rsync+inotify备份)

一、概述 Rsync是一个用于在不同计算机之间同步文件和文件夹的工具。它可以在本地计算机和远程服务器之间复制、更新和备份文件。rsync通过比较源和目标文件的差异来最小化传输的数据量&#xff0c;从而提供高效的文件同步功能。 Inotify是Linux内核提供的一种机制&#xff0…

web中引入live2d的moc3模型

文章目录 前言下载官方sdk文件使用ide编译项目&#xff08;vsCode&#xff09;项目初始化使用vsCode项目树介绍使用live server运行index页面 演示导入自己的模型并显示modelDir文件resources文件夾案例模型修改modelDir然後重新打包項目運行 前言 先跟着官方sdk调试一遍&…

【状态估计】基于卡尔曼滤波器和扩展卡尔曼滤波器用于 INS/GNSS 导航、目标跟踪和地形参考导航研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

Windows VScode如何配置与使用git?

当我们在VScode中编写代码后&#xff0c;需要提交到git仓库时&#xff0c;但是我们又不想切换到git的命令行窗口&#xff0c;我们可以在VScode中配置git&#xff0c;然后就可以很方便快捷的把代码提交到仓库中。 1. 官网下载安装Git命令行工具 根据自己的电脑系统&#xff0c…

尚硅谷Docker实战教程-笔记12【高级篇,Docker-compose容器编排】

尚硅谷大数据技术-教程-学习路线-笔记汇总表【课程资料下载】视频地址&#xff1a;尚硅谷Docker实战教程&#xff08;docker教程天花板&#xff09;_哔哩哔哩_bilibili 尚硅谷Docker实战教程-笔记01【基础篇&#xff0c;Docker理念简介、官网介绍、平台入门图解、平台架构图解】…

3.2.17 什么是数组及应用

【分享成果&#xff0c;随喜正能量】人这一生&#xff0c;好不好都得自己走&#xff0c;累不累都得自己承受。每个人都有难言之苦&#xff0c;每个人都有无声的泪&#xff0c;岁月可曾放过谁&#xff1f;再风光的人&#xff0c;背后都有寒凉凄楚&#xff0c;再幸福的人&#xf…

[每周一更]-(第54期):Go的多版本管理工具

参考 https://zhuanlan.zhihu.com/p/611253641https://learnku.com/articles/78326 前文概要 Go语言从开始使用从1.13起步&#xff0c;随着泛型的支持&#xff0c;带领团队在转型Go的时候&#xff0c;做基础组件架构选型使用1.18&#xff0c;但是Go版本不断迭代想使用最新版本…

C++类相关概念

1. 函数形参默认值 &#xff08;1&#xff09; 建议函数&#xff08;不仅仅是构造函数&#xff09;形参默认值只在函数声明中指定&#xff1b; &#xff08;函数声明和定义写在同一个文件中&#xff0c;则函数声明、定义两者之一或两者都可指定形参默认值&#xff0c;两者都指…

中国移动光猫设置桥接

网上教程五花八门&#xff0c;有些坑有些行&#xff0c;我试成功了&#xff0c;记录一下方法。 一、流程简述 1. 使用超级管理员账号登录中国移动光猫&#xff0c;设置桥接&#xff0c;并重启 2. 用网线连接路由器和光猫&#xff0c;登录路由器&#xff0c;设置宽带拨号&…