【使用redisson完成延迟队列的功能】使用redisson配合线程池完成异步执行功能,延迟队列和不需要延迟的队列

news2024/12/27 13:47:28

1. 使用redisson完成延迟队列的功能

引入依赖

spring-boot-starter-actuator是Spring Boot提供的一个用于监控和管理应用程序的模块
用于查看应用程序的健康状况、审计信息、指标和其他有用的信息。这些端点可以帮助你监控应用程序的运行状态、性能指标和健康状况。
已经有了其他的监控和管理工具,不需要使用Spring Boot Actuator提供的功能。

<!-- redisson -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </exclusion>
    </exclusions>
</dependency>

1.1 延时队列工具类

添加延迟队列时使用,监测扫描时也会用这个工具类进行获取消息

package cn.creatoo.common.redis.queue;

import cn.creatoo.common.core.utils.StringUtils;
import org.redisson.api.RBlockingDeque;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 分布式延时队列工具类
 * @author
 */
@Component
@ConditionalOnBean({RedissonClient.class})
public class RedisDelayQueueUtil {

    private static final Logger log = LoggerFactory.getLogger(RedisDelayQueueUtil.class);

    @Resource
    private RedissonClient redissonClient;

    /**
     * 添加延迟队列
     *
     * @param value     队列值
     * @param delay     延迟时间
     * @param timeUnit  时间单位
     * @param queueCode 队列键
     * @param <T>
     */
    public <T> boolean addDelayQueue(@NonNull T value, @NonNull long delay, @NonNull TimeUnit timeUnit, @NonNull String queueCode) {
        if (StringUtils.isBlank(queueCode) || Objects.isNull(value)) {
            return false;
        }
        try {
            RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque(queueCode);
            RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
            delayedQueue.offer(value, delay, timeUnit);
            //delayedQueue.destroy();
            log.info("(添加延时队列成功) 队列键:{},队列值:{},延迟时间:{}", queueCode, value, timeUnit.toSeconds(delay) + "秒");
        } catch (Exception e) {
            log.error("(添加延时队列失败) {}", e.getMessage());
            throw new RuntimeException("(添加延时队列失败)");
        }
        return true;
    }

    /**
     * 获取延迟队列
     *
     * @param queueCode
     * @param <T>
     */
    public <T> T getDelayQueue(@NonNull String queueCode) throws InterruptedException {
        if (StringUtils.isBlank(queueCode)) {
            return null;
        }
        RBlockingDeque<Map> blockingDeque = redissonClient.getBlockingDeque(queueCode);
        RDelayedQueue<Map> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
        T value = (T) blockingDeque.poll();
        return value;
    }
    /**
     * 删除指定队列中的消息
     *
     * @param o 指定删除的消息对象队列值(同队列需保证唯一性)
     * @param queueCode 指定队列键
     */
    public boolean removeDelayedQueue(@NonNull Object o, @NonNull String queueCode) {
        if (StringUtils.isBlank(queueCode) || Objects.isNull(o)) {
            return false;
        }
        RBlockingDeque<Object> blockingDeque = redissonClient.getBlockingDeque(queueCode);
        RDelayedQueue<Object> delayedQueue = redissonClient.getDelayedQueue(blockingDeque);
        boolean flag = delayedQueue.remove(o);
        //delayedQueue.destroy();
        return flag;
    }
}

1.2 延迟队列执行器

package cn.creatoo.system.handler;

/**
 * 延迟队列执行器
 */
public interface RedisDelayQueueHandle<T> {

    void execute(T t);

}

1.3 实现队列执行器

实现队列执行器接口,在这里写延迟要做的业务逻辑

package cn.creatoo.system.handler.impl;

import cn.creatoo.common.core.domain.vo.WaterVo;
import cn.creatoo.system.api.RemoteFileService;
import cn.creatoo.system.handler.RedisDelayQueueHandle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component("exposeLinkCloudDelay")
public class ExposeLinkCloudDelay implements RedisDelayQueueHandle<Map> {
    @Autowired
    private RemoteFileService remoteFileService;

    @Override
    public void execute(Map map) {
        long dataId = Long.parseLong(map.get("dataId").toString());
        WaterVo waterVo = new WaterVo();
        waterVo.setFileLink(map.get("fileLink").toString());
        waterVo.setType(Integer.parseInt(map.get("type").toString()));
        waterVo.setDataId(dataId);
        remoteFileService.waterLink(waterVo);
    }
}

1.4 延迟队列业务枚举类

package cn.creatoo.common.core.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
 * 延迟队列业务枚举类
 * @author shang tf
 * @data 2024/3/21 14:52
 */
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum FileRedisDelayQueueEnum {
    EXPOSE_LINK_DELAY("EXPOSE_LINK_DELAY","资源链接处理","exposeLinkDelay"),
    EXPOSE_LINK_CLOUD_DELAY("EXPOSE_LINK_CLOUD_DELAY","资源链接处理","exposeLinkCloudDelay"),
    COMPRESSED_LINK_DELAY("COMPRESSED_LINK_DELAY","文件压缩处理","compressedLinkDelay"),
    UPLOAD_TO_CLOUD_DELAY("UPLOAD_TO_CLOUD_DELAY","资源上传消费端","uploadToCloudDelay"),
    GET_HASHCODE_DELAY("GET_HASHCODE_DELAY","资源hash值获取","getHashcodeDelay"),
    UPLOAD_FILE_TO_CABINET("UPLOAD_FILE_CABINET","异步添加文件到数据柜","uploadFileCabinet");

    /**
     * 延迟队列 Redis Key
     */
    private String code;

    /**
     * 中文描述
     */
    private String name;

    /**
     * 延迟队列具体业务实现的 Bean
     * 可通过 Spring 的上下文获取
     */
    private String beanId;
}

1.5 启动延迟队列监测扫描

package cn.creatoo.system.handler.impl;

import cn.creatoo.common.core.enums.FileRedisDelayQueueEnum;
import cn.creatoo.common.redis.queue.RedisDelayQueueUtil;
import cn.creatoo.system.handler.RedisDelayQueueHandle;
import com.alibaba.fastjson2.JSON;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author shang tf
 * @data 2024/3/14 10:45
 * 启动延迟队列监测扫描
 * 文件处理的延迟队列线程池
 */
@Slf4j
@Component
public class FileRedisDelayQueueRunner implements CommandLineRunner {
    @Autowired
    private RedisDelayQueueUtil redisDelayQueueUtil;
    @Autowired
    private ApplicationContext context;
    @Autowired
    private ThreadPoolTaskExecutor ptask;

    @Value("${file-thread-pool.core-pool-size:1}")
    private int corePoolSize;

    @Value("${file-thread-pool.maximum-pool-size:1}")
    private int maximumPoolSize;

    private ThreadPoolExecutor executorService;

    /**
     * 程序加载配置文件后,延迟创建线程池
     */
    @PostConstruct
    public void init() {
        executorService = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 30, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(1000), new ThreadFactoryBuilder().setNameFormat("delay-queue-%d").build());
    }
    @Override
    public void run(String... args) {
        ptask.execute(() -> {
            while (true) {
                try {
                    FileRedisDelayQueueEnum[] queueEnums = FileRedisDelayQueueEnum.values();
                    for (FileRedisDelayQueueEnum queueEnum : queueEnums) {
                        Object value = redisDelayQueueUtil.getDelayQueue(queueEnum.getCode());
                        if (value != null) {
                            System.out.println("----------------value:" + JSON.toJSONString(value));
                            RedisDelayQueueHandle<Object> redisDelayQueueHandle = (RedisDelayQueueHandle<Object>) context.getBean(queueEnum.getBeanId());
                            executorService.execute(() -> {
                                redisDelayQueueHandle.execute(value);
                            });
                        }
                    }
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    log.error("(FileRedission延迟队列监测异常中断) {}", e.getMessage());
                }
            }
        });
        log.info("(FileRedission延迟队列监测启动成功)");
    }
}

1.6 使用延迟队列

使用时在需要延时的地方。
通过注入RedisDelayQueueUtil,使用addDelayQueue方法进行添加延迟任务。

Map<String, String> map = new HashMap<>();
map.put("dataId", examineVo.getId().toString());
map.put("fileLink", resourceLink);
map.put("type", resourceType.toString());
map.put("remark", "资源链接处理");
// 5秒后执行exposeLinkCloudDelay中的方法
redisDelayQueueUtil.addDelayQueue(map, 5, TimeUnit.SECONDS, FileRedisDelayQueueEnum.EXPOSE_LINK_CLOUD_DELAY.getCode());

在这里插入图片描述

2. 使用redisson完成不延时队列的功能

2.1 分布式队列工具类

package cn.creatoo.common.redis.queue;

import cn.creatoo.common.core.utils.StringUtils;
import org.redisson.api.RBoundedBlockingQueue;
import org.redisson.api.RQueue;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * 分布式队列工具类
 */
@Component
@ConditionalOnBean({RedissonClient.class})
public class RedisBlockQueueUtil {

    private static final Logger log = LoggerFactory.getLogger(RedisBlockQueueUtil.class);

    @Resource
    private RedissonClient redissonClient;

    //
    public <T> boolean addQueue(@NonNull T value, @NonNull String queueCode) {
        if (StringUtils.isBlank(queueCode) || Objects.isNull(value)) {
            return false;
        }
        try {
            RBoundedBlockingQueue<T> queue = redissonClient.getBoundedBlockingQueue(queueCode);
            queue.trySetCapacity(10000);
            queue.put(value);
        } catch (Exception e) {
            throw new RuntimeException("(添加redisson队列失败)");
        }
        return true;
    }

    /**
     * 获取队列
     * @param queueCode
     * @param <T>
     */
    public <T> T getQueuePeek(@NonNull String queueCode) throws InterruptedException {
        if (StringUtils.isBlank(queueCode)) {
            return null;
        }
        RQueue<T> queue = redissonClient.getBoundedBlockingQueue(queueCode);
        T obj = (T) queue.peek();
        return obj;
    }

    public <T> T getQueueTake(@NonNull String queueCode) throws InterruptedException {
        if (StringUtils.isBlank(queueCode)) {
            return null;
        }
        RBoundedBlockingQueue<T> queue = redissonClient.getBoundedBlockingQueue(queueCode);
        T obj = (T) queue.take();
        return obj;
    }

}

2.2 队列业务枚举

package cn.creatoo.common.core.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
 * 队列业务枚举
 */
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum RedisQueueEnum {

    FLOW_RECORD("redissionQueue:FLOW_RECORD", "流量流水"),
    USER_LOGIN_RECORD("redissionQueue:USER_LOGIN_RECORD", "用户登录流水"),
    USER_REGISTER_RECORD("redissionQueue:USER_REGISTER_RECORD", "用户注册流水"),
    SMS_SEND_RECORD("redissionQueue:SMS_SEND_RECORD", "短信流水");

    /**
     * 队列 Redis Key
     */
    private String code;

    /**
     * 中文描述
     */
    private String name;


}

2.3 启动队列监测扫描

package cn.creatoo.system.handler.impl;

import cn.creatoo.common.core.enums.RedisQueueEnum;
import cn.creatoo.common.core.utils.StringUtils;
import cn.creatoo.common.mongodb.model.FlowStatistics;
import cn.creatoo.common.mongodb.model.MessageSendRecord;
import cn.creatoo.common.mongodb.model.UserLogin;
import cn.creatoo.common.mongodb.model.UserRegister;
import cn.creatoo.common.redis.queue.RedisBlockQueueUtil;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * description: 启动队列监测扫描
 */
@Slf4j
@Component
public class RedisQueueRunner implements CommandLineRunner {
    @Autowired
    private RedisBlockQueueUtil redisBlockQueueUtil;

    //@Autowired
    //private IBdStatcountService bdStatcountService;

    @Autowired
    private ThreadPoolTaskExecutor ptask;
    @Resource
    private MongoTemplate mongoTemplate;
    //@Autowired
    //private BdAdminHomeService bdAdminHomeService;


    @Value("${prodHost.mall}")
    private String mallHost;

    ThreadPoolExecutor executorService = new ThreadPoolExecutor(4, 8, 30, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(1000),new ThreadFactoryBuilder().setNameFormat("queue-%d").build());

    @Override
    public void run(String... args) throws Exception {
        ptask.execute(() -> {
            while (true){
                try {
                    RedisQueueEnum[] queueEnums = RedisQueueEnum.values();
                    for (RedisQueueEnum queueEnum : queueEnums) {
                        Object value = redisBlockQueueUtil.getQueuePeek(queueEnum.getCode());
                        if (value != null) {
                            executorService.execute(() -> {
                                try {
                                    //System.out.println(value.toString());
                                    if(queueEnum.getCode().equals(RedisQueueEnum.FLOW_RECORD.getCode())){
                                        FlowStatistics flowStatistics = redisBlockQueueUtil.getQueueTake(queueEnum.getCode());
                                       /* if(flowStatistics!=null && StringUtils.isNotBlank(flowStatistics.getUrl())){
                                            mongoTemplate.insert(flowStatistics, "pv_" + new SimpleDateFormat("yyyy").format(new Date()));
                                            // 添加首页统计缓存
                                            bdAdminHomeService.addDetailCache(flowStatistics);
                                            if(StringUtils.isNotBlank(flowStatistics.getUrl())){
                                                bdStatcountService.browseByUrl(flowStatistics.getUrl());
                                            }
                                        }*/
                                    } else if (queueEnum.getCode().equals(RedisQueueEnum.USER_LOGIN_RECORD.getCode())) {
                                        UserLogin userLogin = redisBlockQueueUtil.getQueueTake(queueEnum.getCode());
                                        mongoTemplate.insert(userLogin, "user_login_" + new SimpleDateFormat("yyyy").format(new Date()));
                                    } else if (queueEnum.getCode().equals(RedisQueueEnum.USER_REGISTER_RECORD.getCode())) {
                                        UserRegister userRegister = redisBlockQueueUtil.getQueueTake(queueEnum.getCode());
                                        mongoTemplate.insert(userRegister, "user_register");
                                    } else if (queueEnum.getCode().equals(RedisQueueEnum.SMS_SEND_RECORD.getCode())) {
                                        MessageSendRecord sendRecord = redisBlockQueueUtil.getQueueTake(queueEnum.getCode());
                                        mongoTemplate.insert(sendRecord, "sms_send_" + new SimpleDateFormat("yyyy").format(new Date()));
                                    }
                                } catch (InterruptedException e) {
                                    log.error("(Redission队列监测异常中断) {}", e.getMessage());
                                }
                            });
                        }
                    }
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    log.error("(Redission队列监测异常中断) {}", e.getMessage());
                }
            }
        });
        log.info("(Redission队列监测启动成功)");
    }
}

2.4 使用

这个是直接执行,没有延迟的功能

   redisBlockQueueUtil.addQueue(userRegister, RedisQueueEnum.USER_REGISTER_RECORD.getCode());

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电子科技大学链时代工作室招新题C语言部分---题号H

1. 题目 最有操作的一道题&#xff0c;有利于对贪心算法有个初步了解。 这道题的开篇向我们介绍了一个叫汉明距离的概念。 汉明距离指的就是两个相同长度的字符串的不同字符的个数。 例如&#xff0c;abc和acd&#xff0c;b与c不同&#xff0c;c与d不同&#xff0c;所以这两个…

【Linux C | 多线程编程】线程的创建、线程ID、线程属性

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-03-22 0…

LiDAR 点云数据综合指南

LiDAR 是光探测和测距的缩写,它彻底改变了各个领域的数据采集。它生成密集 3D 点云的能力为我们的世界提供了无与伦比的洞察力。 但如此丰富的信息也带来了复杂性,特别是在理解不同类型的激光雷达点云数据时。本指南旨在成为您的一站式资源,阐明其中的细微差别,并使您能够…

python基础——对序列的通用操作【+和*、in、切片操作、separator.join(iterable)】

&#x1f4dd;前言&#xff1a; 我们已经学习了python数据容器中的列表&#xff0c;元组以及字符串。而他们都属于序列 &#xff08;序列是指&#xff1a;内容连续&#xff0c;有序&#xff0c;可以用下标索引访问的数据容器&#xff09; 在之前已经介绍了不少操作方法&#xf…

工作需求,Vue实现登录

加油&#xff0c;新时代打工人&#xff01; vue 2.x Element UI <template><div class"body" :style"{background-image: url(${require(/assets/images/login.png)})}"><el-form :rules"rules" ref"loginForm" :mode…

线性表:关于链表(主要以单链表为例)的相关理解和应用

多清澈这天空 晴雨相拥 同心逐梦&#xff01; 坚守我信心 一路出众&#xff01;&#xff01; 首先&#xff0c;按照惯例&#xff0c;欢迎大家边听歌边观看本博客 ▶ 紫荆花盛开 (163.com)&#xff08;建议复制链接&#xff0c;浏览器打开&#xff0c;csdn打开太慢了&#x…

mysql数据类型和常用函数

目录 1.整型 1.1参数signed和unsigned 1.2参数zerofill 1.3参数auto_increment 2.数字类型 2.1floor()向下取整 2.2随机函数rand() 2.3重复函数repeat() 3.字符串类型 3.1length()查看字节长度&#xff0c;char_length()查看字符长度 3.2字符集 3.2.1查看默认字符…

工程信号的去噪和(分类、回归和时序)预测

&#x1f680;【信号去噪及预测论文代码指导】&#x1f680; 还为小论文没有思路烦恼么&#xff1f;本人专注于最前沿的信号处理与预测技术——基于信号模态分解的去噪算法和深度学习的信号&#xff08;回归、时序和分类&#xff09;预测算法&#xff0c;致力于为您提供最精确、…

MySql实战--深入浅出索引(下)

在开始这篇文章之前&#xff0c;我们先来看一下这个问题&#xff1a; 在下面这个表T中&#xff0c;如果我执行 select * from T where k between 3 and 5&#xff0c;需要执行几次树的搜索操作&#xff0c;会扫描多少行&#xff1f; 下面是这个表的初始化语句。 图1 InnoDB的索…

第 6 章 ROS-xacro练习(自学二刷笔记)

重要参考&#xff1a; 课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ 讲义链接:Introduction Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 6.4.3 Xacro_完整使用流程示例 需求描述: 使用 Xacro 优化 URDF 版的小车底盘模型实现 结果演示: 1.编写 X…

ChatGPT已经掌控了全局:不仅写论文的在用ChatGPT,同行评审也在用ChatGPT!

大家好&#xff0c;我是木易&#xff0c;一个持续关注AI领域的互联网技术产品经理&#xff0c;国内Top2本科&#xff0c;美国Top10 CS研究生&#xff0c;MBA。我坚信AI是普通人变强的“外挂”&#xff0c;所以创建了“AI信息Gap”这个公众号&#xff0c;专注于分享AI全维度知识…

分库分表场景下多维查询解决方案(用户+商户)

在采用分库分表设计时&#xff0c;通过一个PartitionKey根据散列策略将数据分散到不同的库表中&#xff0c;从而有效降低海量数据下C端访问数据库的压力。这种方式可以缓解单一数据库的压力&#xff0c;提升了吞吐量&#xff0c;但同时也带来了新的问题。对于B端商户而言&#…

【Python爬虫】网络爬虫:信息获取与合规应用

这里写目录标题 前言网络爬虫的工作原理网络爬虫的应用领域网络爬虫的技术挑战网络爬虫的伦理问题结语福利 前言 网络爬虫&#xff0c;又称网络爬虫、网络蜘蛛、网络机器人等&#xff0c;是一种按照一定的规则自动地获取万维网信息的程序或者脚本。它可以根据一定的策略自动地浏…

linux查看usb是3.0还是2.0

1 作为device cat /sys/devices/platform/10320000.usb30drd/10320000.dwc3/udc/10320000.dwc3/current_speed 如下 high-speed usb2.0 super-speed usb3.0 2 作为host linux下使用以下命令查看 &#xff0c;如果显示 速率为5G, 则为USB 3.0&#xff0c; USB2.0通常显示速率…

Day17|二叉树part04:110.平衡二叉树、257.二叉树的所有路径、404.左叶子之和、543: 二叉树的直径、124: 二叉树的最大路径和

之前的blog链接&#xff1a;https://blog.csdn.net/weixin_43303286/article/details/131982632?spm1001.2014.3001.5501 110.平衡二叉树 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a;一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。思路&#xff…

Matlab之已知2点绘制长度可定义的射线

目的&#xff1a;在笛卡尔坐标系中&#xff0c;已知两个点的位置&#xff0c;绘制过这两点的射线。同时射线的长度可以自定义。 一、函数的参数说明 输入参数&#xff1a; PointA&#xff1a;射线的起点&#xff1b; PointB&#xff1a;射线过的零一点&#xff1b; Length&…

AI PPT生成工具 V1.0.0

AI PPT是一款高效快速的PPT生成工具&#xff0c;能够一键生成符合相关主题的PPT文件&#xff0c;大大提高工作效率。生成的PPT内容专业、细致、实用。 软件特点 免费无广告&#xff0c;简单易用&#xff0c;快速高效&#xff0c;提高工作效率 一键生成相关主题的标题、大纲、…

【链表】Leetcode 138. 随机链表的复制【中等】

随机链表的复制 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点…

Linux - 应用层HTTPS、传输层TCP/IP模型中典型协议解析

目录 应用层&#xff1a;自定制协议实例 HTTP协议首行头部空行正文http服务器的搭建 HTTPS协议 传输层UDP协议TCP协议 应用层&#xff1a; 应用层负责应用程序之间的沟通—程序员自己定义数据的组织格式 应用层协议&#xff1a;如何将多个数据对象组织成为一个二进制数据串进行…

代码签名证书被吊销的原因及其后果是什么?

代码签名证书是确保软件代码完整性和可信度的关键工具&#xff0c;然而&#xff0c;在某些情况下&#xff0c;此类证书可能会被撤销。这意味着证书颁发机构&#xff08;CA&#xff09;不再认可该证书的有效性&#xff0c;并宣布其失效。本文将解析导致代码签名证书撤销的原因、…