JavaWeb_LeadNews_Day5-文章定时发布

news2025/1/14 19:54:40

JavaWeb_LeadNews_Day5-文章定时发布

  • 延时服务
    • 概述
      • DelayQueue
      • RabbitMQ(常用)
      • Redis(常用)
    • redis延迟服务
      • 实现思路
        • 总思路
        • 添加任务
        • 取消任务
        • 拉取任务
        • 未来数据定时刷新
        • redis解决集群抢占
      • 具体实现
        • 乐观锁
        • docker运行redis
        • 项目集成redis
        • 添加任务
        • 取消任务
        • 拉取任务
        • 未来数据定时刷新
        • redis解决集群抢占
        • 数据库定时同步
        • 远程接口
        • 发布文章添加延迟任务
        • 拉取任务
      • 总结
        • redis延迟服务
        • 数据库
        • 未来数据定时刷新
          • redis key值匹配
  • 来源
  • Gitee

延时服务

概述

DelayQueue

  • JDK自带DelayQueue 是一个支持延时获取元素的阻塞队列,内部采用优先队列 PriorityQueue 存储元素,同时元素必须实现 Delayed 接口
  • 在创建元素时可以指定多久才可以从队列中获取当前元素,只有在延迟期满时才能从队列中提取元素
  • 使用DelayQueue作为延迟任务,如果程序挂掉之后,任务都是放在内存,消息会丢失,如何保证数据不丢失

RabbitMQ(常用)

  • TTL: Time To Live(消息存活时间)
  • 死信队列: Dead Letter Exchange(死信交换机), 当消息成为Dead message后, 可以重新发送另一个交换机(死信交换机)

Redis(常用)

  • zset数据类型的去重有序(分数排序)特点进行延迟. 例如: 时间戳(毫秒值)作为score进行排序

redis延迟服务

实现思路

总思路

添加任务

  1. 添加任务到数据库
  2. 添加任务到redis
    1. 如果任务的执行时间小于等于当前时间存入list
    2. 如果任务的执行时间大于当前时间, 小于等于预设时间(未来5分钟)存入zset

取消任务

  • 场景: 第三方接口网络不通, 使用延迟任务进行重试, 达到阈值后, 取消任务
  1. 根据taskId删除任务, 修改任务日志状态为2(取消)
  2. 删除redis中对应的任务数据, 包括listzset

拉取任务

  1. 消费任务, 参数为: 任务的类型和优先级
  2. 从redis的list中pop数据, pop: 取出数据并删除
  3. 删除任务和更新任务日志

未来数据定时刷新

  1. 每分钟获取未来数据的keys
  2. 按照分值查询zset, 判断数据是否到期
  3. 到期数据同步到list中

redis解决集群抢占

  • 分布式锁: 控制分布式系统有序的去对共享资源进行操作, 通过互斥来保证数据的一致性
  • 解决方案
    方案说明
    数据库基于表的唯一索引
    zookeeper根据zookeep中的临时有序节点排序
    redis使用setnx命令完成
  • redis分布式锁: setnx(set if not exists)命令在指定的key不存在时, 为key设置指定的值

具体实现

乐观锁

// @Version注解
public class TaskinfoLogs implements Serializable {
    ...
    /**
     * 版本号,用乐观锁
     */
    @Version
    private Integer version;
    ...
}

// Interceptor
@Bean
public MybatisPlusInterceptor optimisticLockerInterceptor(){
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return interceptor;
}

docker运行redis

// 拉取镜像
docker pull redis

// 创建运行容器
docker run -d --name redis -p 6379:6379 redis --requirepass "leadnews"

项目集成redis

  • 依赖
    <!--spring data redis & cache-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <!-- redis依赖commons-pool 这个依赖一定要添加 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
    
  • 配置
    spring:
      redis:
        host: 192.168.174.133
        password: leadnews
        port: 6379
    
  • 工具类
    // 太长了, 从仓库获取
    
    // 配置spring.factories
    
  • 测试类
    @SpringBootTest(classes = ScheduleApplication.class)
    @RunWith(SpringRunner.class)
    public class RedisTest {
    
        @Autowired
        private CacheService cacheService;
    
        @Test
        public void testList(){
    
            //在list的左边添加元素
          //cacheService.lLeftPush("list_001","hello,redis");
    
            //在list的右边获取元素,并删除
            String list_001 = cacheService.lRightPop("list_001");
            System.out.println(list_001);
        }
    
        @Test
        public void testZset(){
            //添加数据到zset中  分值
            cacheService.zAdd("zset_key_001","hello zset 001",1000);
            cacheService.zAdd("zset_key_001","hello zset 002",8888);
            cacheService.zAdd("zset_key_001","hello zset 003",7777);
            cacheService.zAdd("zset_key_001","hello zset 004",999999);
    
            //按照分值获取数据
            Set<String> zset_key_001 = cacheService.zRangeByScore("zset_key_001", 0, 8888);
            System.out.println(zset_key_001);
    
    
        }
    }
    

添加任务

// Service
@Service
@Transactional
public class TaskServiceImpl implements TaskService {

    @Override
    public long addTask(Task task) {
        // 1. 添加任务到数据库中
       boolean success = addTaskToDb(task);
        // 2. 添加任务到redis
        if(success){
            addTaskToCache(task);
        }
        return task.getTaskId();
    }

    @Autowired
    private CacheService cacheService;

    private void addTaskToCache(Task task) {

        String key = task.getTaskType()+"_"+task.getPriority();

        // 获取5分钟后的时间, 毫秒值
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE, 5);
        long nextScheduleTime = calendar.getTimeInMillis();

        if(task.getExecuteTime() <= System.currentTimeMillis()){
            // 2.1 如果任务的执行时间小于等于当前时间, 存入list
            cacheService.lLeftPush(ScheduleConstants.TOPIC+key, JSON.toJSONString(task));
        }else if(task.getExecuteTime() <= nextScheduleTime){
            // 2.2 如果任务的执行时间大于当前时间 && 小于等于预设时间(未来5分钟), 存入zset
            cacheService.zAdd(ScheduleConstants.FUTURE+key, JSON.toJSONString(task), task.getExecuteTime());
        }


    }


    @Autowired
    private TaskinfoMapper taskinfoMapper;

    @Autowired
    private TaskinfoLogsMapper taskinfoLogsMapper;

    private boolean addTaskToDb(Task task) {

        boolean flag = false;

        try {
            // 保存任务表
            Taskinfo taskinfo = new Taskinfo();
            BeanUtils.copyProperties(task, taskinfo);
            taskinfo.setExecuteTime(new Date(task.getExecuteTime()));
            taskinfoMapper.insert(taskinfo);

            // 设置taskId
            task.setTaskId(taskinfo.getTaskId());

            // 保存任务日志数据
            TaskinfoLogs taskinfoLogs = new TaskinfoLogs();
            BeanUtils.copyProperties(taskinfo, taskinfoLogs);
            taskinfoLogs.setVersion(1);
            taskinfoLogs.setStatus(ScheduleConstants.SCHEDULED);
            taskinfoLogsMapper.insert(taskinfoLogs);

            flag = true;
        } catch (BeansException e) {
            e.printStackTrace();
        }

        return flag;
    }
}

// Test
@SpringBootTest(classes = ScheduleApplication.class)
@RunWith(SpringRunner.class)
public class TaskServiceImplTest {

    @Autowired
    private TaskService taskService;

    @Test
    public void addTask() {

        Task task = new Task();
        task.setTaskType(100);
        task.setExecuteTime(new Date().getTime()+500000);
        task.setParameters("Hello Task!".getBytes());
        task.setPriority(50);

        long taskId = taskService.addTask(task);
        System.out.println(taskId);
    }
}

取消任务

// Service
public boolean cancelTask(Long taskId) {

    boolean flag = false;

    // 删除任务, 修改任务日志状态为2(取消)
    Task task = updateDb(taskId, ScheduleConstants.CANCELLED);

    // 删除redis中的数据
    if(task != null) {
        removeTaskFromCache(task);
        flag = true;
    }
    return flag;
}

/**
 * 删除redis中的数据
 * @param task
 */
private void removeTaskFromCache(Task task) {
    String key = task.getTaskType()+"_"+task.getPriority();
    if(task.getExecuteTime() <= System.currentTimeMillis()){
        cacheService.lRemove(ScheduleConstants.TOPIC+key, 0, JSON.toJSONString(task));
    }else{
        cacheService.zRemove(ScheduleConstants.FUTURE+key, JSON.toJSONString(task));
    }
}

/**
 * 删除任务, 更新任务日志
 * @param taskId
 * @param cancelled
 * @return
 */
private Task updateDb(Long taskId, int cancelled) {
    Task task = null;
    try {
        // 删除任务
        taskinfoMapper.deleteById(taskId);

        // 更新任务日志
        TaskinfoLogs taskLog = taskinfoLogsMapper.selectById(taskId);
        taskLog.setStatus(cancelled);
        taskinfoLogsMapper.updateById(taskLog);

        task = new Task();
        BeanUtils.copyProperties(taskLog, task);
        task.setExecuteTime(taskLog.getExecuteTime().getTime());
    } catch (Exception e) {
        log.error("task cancel exception taskId={}", taskId);
    }

    return task;
}

拉取任务

public Task poll(Integer type, Integer priority) {

    Task task = null;
    try {
        String key = type+"_"+priority;
        // 从redis中拉取数据 pop
        String task_json = cacheService.lRightPop(ScheduleConstants.TOPIC + key);
        if(StringUtils.isNotBlank(task_json)){
            task = JSON.parseObject(task_json, Task.class);

            // 修改数据库信息
            updateDb(task.getTaskId(), ScheduleConstants.EXECUTED);
        }
    } catch (Exception e) {
        e.printStackTrace();
        log.error("poll task exception");
    }
    return task;
}

未来数据定时刷新

@Scheduled(cron = "0 */1 * * * ?")
public void refresh()
{
    log.info("未来数据定时刷新---定时任务");

    // 获取所有未来数据的集合key
    Set<String> futureKeys = cacheService.scan(ScheduleConstants.FUTURE + "*");
    for (String futureKey : futureKeys) {
        // 获取当前数据的key topic
        String topiKey = ScheduleConstants.TOPIC + futureKey.split(ScheduleConstants.FUTURE)[1];

        // 按照key和分值查询符合条件的数据
        Set<String> tasks = cacheService.zRangeByScore(futureKey, 0, System.currentTimeMillis());

        // 同步数据
        if(!tasks.isEmpty())
        {
            cacheService.refreshWithPipeline(futureKey, topiKey, tasks);
            log.info("成功的将"+futureKey+"刷新到了"+topiKey);
        }
    }
}

redis解决集群抢占

public String tryLock(String name, long expire) {
    name = name + "_lock";
    String token = UUID.randomUUID().toString();
    RedisConnectionFactory factory = stringRedisTemplate.getConnectionFactory();
    RedisConnection conn = factory.getConnection();
    try {

        //参考redis命令:
        //set key value [EX seconds] [PX milliseconds] [NX|XX]
        Boolean result = conn.set(
                name.getBytes(),
                token.getBytes(),
                Expiration.from(expire, TimeUnit.MILLISECONDS),
                RedisStringCommands.SetOption.SET_IF_ABSENT //NX
        );
        if (result != null && result)
            return token;
    } finally {
        RedisConnectionUtils.releaseConnection(conn, factory,false);
    }
    return null;
}

// use
String token = cacheService.tryLock("FUTURE_TASK_SYNC", 1000*30);
if(StringUtils.isNotBlank(token)) {
    ...
}

数据库定时同步

@PostConstruct // 初始化方法
@Scheduled(cron = "0 */5 * * * ?")
public void reloadData()
{
    // 清理缓存中的数据
    clearCache();
    // 查询符合条件的任务 小于未来5分钟的数据
    Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.MINUTE, 5);
    List<Taskinfo> taskinfoList = taskinfoMapper.selectList(Wrappers.<Taskinfo>lambdaQuery().lt(Taskinfo::getExecuteTime, calendar.getTime()));

    // 把任务添加到redis
    if(taskinfoList != null && taskinfoList.size() > 0){
        for (Taskinfo taskinfo : taskinfoList) {
            Task task = new Task();
            BeanUtils.copyProperties(taskinfo, task);
            task.setExecuteTime(taskinfo.getExecuteTime().getTime());
            addTaskToCache(task);
        }
    }
    log.info("数据库的数据同步到了redis");
}

/**
 * 清楚缓存中的数据
 */
public void clearCache()
{
    Set<String> futureKeys = cacheService.scan(ScheduleConstants.FUTURE + "*");
    Set<String> topicKeys = cacheService.scan(ScheduleConstants.TOPIC + "*");
    cacheService.delete(futureKeys);
    cacheService.delete(topicKeys);
}

远程接口

@RestController
public class ScheduleClient implements IScheduleClient {

    @Autowired
    private TaskService taskService;

    /**
     * 添加任务
     */
    @PostMapping("/api/v1/task/add")
    public ResponseResult addTask(Task task){
        return ResponseResult.okResult(taskService.addTask(task));
    }

    /**
     * 取消任务
     */
    @GetMapping("/api/v1/task/{taskId}")
    public ResponseResult cancelTask(@PathVariable("taskId") Long taskId)
    {
        return ResponseResult.okResult(taskService.cancelTask(taskId));
    }

    /**
     * 按照类型和优先级拉取任务
     */
    @GetMapping("/api/v1/task/{type}/{priority}")
    public ResponseResult poll(@PathVariable("type") Integer type, @PathVariable("priority") Integer priority){
        return ResponseResult.okResult(taskService.poll(type, priority));
    }
}

发布文章添加延迟任务

  • 枚举类
    @Getter
    @AllArgsConstructor
    public enum TaskTypeEnum {
    
        NEWS_SCAN_TIME(1001, 1,"文章定时审核"),
        REMOTEERROR(1002, 2,"第三方接口调用失败,重试");
        private final int taskType; //对应具体业务
        private final int priority; //业务不同级别
        private final String desc; //描述信息
    }
    
  • ProtoStuff依赖
    <dependency>
        <groupId>io.protostuff</groupId>
        <artifactId>protostuff-core</artifactId>
        <version>1.6.0</version>
    </dependency>
    
    <dependency>
        <groupId>io.protostuff</groupId>
        <artifactId>protostuff-runtime</artifactId>
        <version>1.6.0</version>
    </dependency>
    
  • 添加任务
    @Override
    @Async
    public void addNewsToTask(Integer id, Date publishTime) {
        Task task = new Task();
        task.setExecuteTime(publishTime.getTime());
        task.setTaskType(TaskTypeEnum.NEWS_SCAN_TIME.getTaskType());
        task.setPriority(TaskTypeEnum.NEWS_SCAN_TIME.getPriority());
        WmNews wmNews = new WmNews();
        wmNews.setId(id);
        task.setParameters(ProtostuffUtil.serialize(wmNews));
    
        scheduleClient.addTask(task);
    }
    

拉取任务

@Scheduled(fixedDelay = 1000)
@Override
public void scanNewsByTask() {

    log.info("消费任务, 审核文章");

    ResponseResult responseResult = scheduleClient.poll(TaskTypeEnum.NEWS_SCAN_TIME.getTaskType(), TaskTypeEnum.NEWS_SCAN_TIME.getPriority());
    if(responseResult.getCode().equals(200) && responseResult.getData() != null){
        Task task = JSON.parseObject(JSON.toJSONString(responseResult.getData()), Task.class);
        WmNews wmNews = ProtostuffUtil.deserialize(task.getParameters(), WmNews.class);
        wmNewsAutoScanService.autoScanWmNews(wmNews.getId());
    }
}

总结

redis延迟服务

  • 为什么任务需要存储在数据库中?
    • 延迟任务是一个通用的服务,任何有延迟需求的任务都可以调用该服务, 内存数据库的存储是有限的,需要考虑数据持久化的问题,存储数据库中是一种数据安全的考虑
  • 为什么使用redis中的两种数据类型, list和zset?
    • list存储立即执行的任务, zset存储未来的数据
    • 任务量过大以后, zset的性能会下降, 将立即执行的任务存储在list中, 可以提高性能( list的lpush是O(1), zset的zadd是O(log(n)) )
  • 添加zset数据时, 为什么需要预加载?
    • 如果任务数据特别大, 为了防止阻塞, 只需要把未来几分钟要执行的数据放入缓存即可, 是一种优化的形式.

数据库

  • 数据库准备
    • mysql中, blob是一个二进制大型对象, 是一个可以存储大量数据的容器, longblob最大存储4G
  • 数据库锁
    • 悲观锁(Pessimistic Lock): 拿数据的时候认为别人会修改, 所以每次拿取数据都会上锁
    • 乐观锁(Optimistic Lock): 拿数据的时候认为别人不会修改, 所以不会上锁, 但是在更新的时候会判断一下在此期间别人有没有去更新数据, 可以使用版本号等机制

未来数据定时刷新

redis key值匹配
  • keys模糊匹配

    keys的模糊匹配功能很方便也很强大,但是在生产环境需要慎用! 开发中使用keys的模糊匹配却发现redis的CPU使用率极高,所以公司的redis生产环境将keys命令禁用了!redis是单线程,会被堵塞.

  • scan
    SCAN 命令是一个基于游标的迭代器,SCAN命令每次被调用之后,都会向用户返回一个新的游标,用户在下次迭代时需要使用这个新游标作为SCAN命令的游标参数,以此来延续之前的迭代过程。

来源

黑马程序员. 黑马头条

Gitee

https://gitee.com/yu-ba-ba-ba/leadnews

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

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

相关文章

Jmeter入门之digest函数 jmeter字符串连接与登录串加密应用

登录请求中加密串是由多个子串连接&#xff0c;再加密之后传输。 参数连接&#xff1a;${var1}${var2}${var3} 加密函数&#xff1a;__digest &#xff08;函数助手里如果没有该函数&#xff0c;请下载最新版本的jmeter5.0&#xff09; 函数助手&#xff1a;Options > …

【Hystrix技术指南】(1)基本使用和配置说明

这世间许多事物皆因相信而存在&#xff0c;所以人们亲手捏出了泥菩萨&#xff0c;却选择坚定的去信仰它。 分布式系统的规模和复杂度不断增加&#xff0c;随着而来的是对分布式系统可用性的要求越来越高。在各种高可用设计模式中&#xff0c;【熔断、隔离、降级、限流】是经常被…

wordpress日主题Ripro9.0最新二开修正源码下载+美化包和插件

日主题Ripro9.0最新二开升级修正源码美化包和插件&#xff0c;RiPro9.0的二开版本新模板&#xff0c;包含2个美化包和全屏水印以及防复制插件。 模板和美化包路径:\wp-content\themes 插件路径&#xff1a;\wp-content\plugins&#xff0c;有兴趣的自行去体验吧

AOP-AspectJ 切面技术及使用MethodInterceptor实现切面

目录 一、简介 二、什么是AspectJ AOP 思想概述 AOP 实现方式 AOP 特性概念 切点指示器 通知类型 三、实现AOP的方式 四、配置说明 自定义注解作为切点 NeedCut MyAop GirlController 继承MethodInterceptor实现切面 MyMethodInterceptor AopConfig NeedCut 注解…

如何将安卓 Gradle 模块打包发布到本地 Maven 仓库

文章目录 具体流程 笔者的运行环境&#xff1a; Android Studio Flamingo | 2022.2.1 Android SDK 33 Gradle 8.0.1 JDK 17 Android 的 Gradle 项目与一般的 Gradle 项目是不同的&#xff0c;因此对将 Gradle 模块打包发布到本地 Maven 仓库来说&#xff0c;对普通 Gradle …

Python 模块 locust 性能测试

简介 locust 是 Python 的一个开源的负载测试工具&#xff0c;用于测试网络应用程序的性能和可伸缩性。它使用Python编写&#xff0c;并提供了一个简单易用的语法来定义和执行负载测试。locust模块允许用户模拟大量并发用户并观察系统在高负载下的响应情况。 目录 1. 基本用法…

vue中transition动画的使用

1.vue文件 说明&#xff1a;加name属性 <transition name"sort"><div class"sort" v-show"show"><div class"all-sort-list2" click"goSearch"><div class"item bo" v-for"(item1, in…

图像膨胀+滤波达到边缘外扩模糊效果

有一个扯淡需求, 根据某些格网值渲染对应的颜色, 我们做的实现方案是按照色代码渐变做颜色映射, 但是某些厂家不顾结果正确性与否, 应是为了好看做的好看, 将边界膨胀模糊, 一个非风场,力场类似场数据做了一个类似场的渲染效果, 也不知道说啥好, 例如原始图渲染如下 经过一系列…

初识http协议,简单实现浏览器和服务器通信

文章目录 认识urlhttp协议格式通信代码验证细节Util.hppprotocol.hppServer.hppServer.cc 结果分析 认识url 平时俗称的 “网址” 其实就是说的 URL&#xff0c;例如在百度上搜索一个C 可以看到这段网址前面有个 https 那么这个就代表着使用的是https协议&#xff0c;现在都是…

0807hw

1. 2. (1)a100,b10; a10,b100;//值传递 (2)*a100,*b10; a100,b10;//地址传递 (3)*a100,*b10; a10,b100;

深入探索 Splashtop Enterprise 的潜力

在当今高度技术化的环境中&#xff0c;远程访问解决方案已成为无数组织的支柱。远程访问解决方案缩短了员工与工作之间的地理差距&#xff0c;提高了工作的效率和灵活性&#xff0c;促进形成了无缝的工作体验。在众多远程访问解决方案中&#xff0c;Splashtop Enterprise 作为远…

FFmpeg中AVIOContext的使用

通过FFmpeg对视频进行编解码时&#xff0c;如果输入文件存在本机或通过USB摄像头、笔记本内置摄像头获取数据时&#xff0c;可通过avformat_open_input接口中的第二个参数直接指定即可。但如果待处理的视频数据存在于内存块中时&#xff0c;该如何指定&#xff0c;可通过FFmpeg…

Linux初识网络基础

目录 网络发展 认识“协议 ” 网络协议 OSI七层模型&#xff1a; TCP/IP五层&#xff08;或四层&#xff09;模型 网络传输基本流程 网络传输流程图&#xff1a; 数据包封装和封用 网络中的地址 认识IP地址&#xff1a; 认识MAC地址&#xff1a; 网络发展 1.独立…

JAVA实用工具: 改良版雪花算法-分布式唯一ID神器

Seata内置了一个分布式UUID生成器,用于辅助生成全局事务ID和分支事务ID。具体如下特点: 高性能全局唯一趋势递增这个分布式UUID生成器是基于雪花算法进行改良的,本文针对改良的方法、目的等进行总结 改良点 改良版雪花算法的实现原理参考如下: Seata基于改良版雪花算法的…

redis安装(Windows+Linux)

redis安装 文章目录 redis安装一. windows下安装二.Linux环境下安装 一. windows下安装 下载地址(github): https://github.com/tporadowski/redis/releases (强烈推荐) https://github.com/MicrosoftArchive/redis/releases 选择安装包 下载完成后根据提示进行安装即可(这…

电源控制--品质因素Q值全解

什么是品质因素Q值&#xff1f; 在电源控制中&#xff0c;品质因素 Q 值通常用于描述电源滤波器的性能。电源滤波器用于减小电源中的噪声和干扰&#xff0c;以提供干净稳定的电源供应给电子设备。 品质因素 Q 值在电源滤波器中表示滤波器的带宽和中心频率之比&#xff0c;用于…

VS Code无法跳转,搜索也搜不到

1. 公司安装的加密软件&#xff0c;天锐绿盾&#xff08;绿盾&#xff09;导致VS Code无法使用 如果的绿盾的策略里面没有VS Code的话&#xff0c;就会导致VScode 无法使用&#xff0c;其它软件也是一样&#xff0c;出问题极有可能就是绿盾拦截了&#xff0c;像我的电脑使用wi…

springboot 集成 mybatis-plus 代码生成器

springboot 集成 mybatis-plus 代码生成器 一、导入坐标依赖二、配置快速代码生成器三、自定义代码生成器模板 一、导入坐标依赖 前置依赖&#xff0c;需要用到 mybatis,mysql驱动,lombok插件以及swapper.(因为后面接口测试文档&#xff0c;所以swapper也配了) <dependenc…

Python编程从入门到实践练习第七章:input输入和while循环

目录 一、input输入函数实例 二、while循环2.1 while结构练习题 2.2 使用while循环处理列表和字典2.2.1 在列表之间移动元素2.2.2 删除为特定值的多个列表元素2.2.3 使用用户输入来填充字典练习题 一、input输入函数 input( ) 方法&#xff1a;获取用户的输入信息&#xff0c;使…

【2.2】Java微服务:nacos的使用

✅作者简介&#xff1a;大家好&#xff0c;我是 Meteors., 向往着更加简洁高效的代码写法与编程方式&#xff0c;持续分享Java技术内容。 &#x1f34e;个人主页&#xff1a;Meteors.的博客 &#x1f49e;当前专栏&#xff1a; 深度学习 ✨特色专栏&#xff1a; 知识分享 &…