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

分布式环境下定时任务扫描时间段模板创建可预订时间段

🎯 本文详细介绍了场馆预定系统中时间段生成的实现方案。通过设计场馆表、时间段模板表和时间段表,系统能够根据场馆的提前预定天数生成未来可预定的时间段。为了确保任务执行的唯一性和高效性,系统采用分布式锁机制和定时任务,避免重复生成时间段。通过流式查询优化大数据处理,减少内存占用和网络延迟。同时,使用唯一复合索引保证时间段生成的幂等性,避免重复插入。为提高系统性能,引入二级缓存和Redis管道技术,加速数据查询和缓存预热,确保用户在预定时间段时获得快速响应。整体方案兼顾了系统的稳定性、高效性和可扩展性。

文章目录

  • 简介
  • 数据表设计
  • 定时任务
  • 根据时间段模板创建时间段
    • MySQL流式查询
    • 时间段生成如何保证幂等性
    • 二级缓存
    • Redis管道
    • 代码实现

简介

在场馆管理员创建了时间段模板之后,需要使用定时任务,每天定时生成未来可接受预定的时间段

数据表设计

【场馆表】

该表存储了 提前可预定天数(advance_booking_day),为什么要存储这个,是因为生成时间段的时候,需要参考该字段生成多少天内的时间段

DROP TABLE IF EXISTS `venue`;
CREATE TABLE `venue`(`id` bigint NOT NULL COMMENT 'ID',`create_time` datetime,`update_time` datetime,`is_deleted` tinyint default 0 COMMENT '逻辑删除 0:没删除 1:已删除',`organization_id` bigint NOT NULL COMMENT '所属机构ID',`name` varchar(30) NOT NULL COMMENT '场馆名称',`type` char(4) NOT NULL COMMENT '场馆类型 1:篮球馆(场) 2:足球场 3:羽毛球馆(场) 4:排球馆(场)100:体育馆 1000:其他',`address` varchar(255) NOT NULL COMMENT '场馆地址',`description` varchar(255) DEFAULT '' COMMENT '场馆描述,也可以说是否提供器材等等',`open_time` varchar(2000) NOT NULL COMMENT '场馆营业时间',`phone_number` varchar(11) NULL DEFAULT '' COMMENT '联系电话',`status` tinyint NOT NULL COMMENT '场馆状态 0:关闭 1:开放 2:维护中',`is_open` tinyint NOT NULL COMMENT '是否对外开放 0:否 1:是 如果不对外开放,需要相同机构的用户才可以预定',`advance_booking_day` int NOT NULL COMMENT '提前可预定天数,例如设置为1,即今天可预订明天的场',`start_booking_time` time NOT NULL COMMENT '开放预订时间',PRIMARY KEY (`id`) USING BTREE
)

【时间段模板表】

在这里需要使用 已生成到的日期(last_generated_date)来记录已经生成到的日期,避免重复生成时间段。比如说advance_booking_day=7,在 1月1 的时候,其实就已经生成了 [1月2, 1月8] 的时间段数据,那 1月2 的时候,其实只需要生成 1月9 的时间段即可

DROP TABLE IF EXISTS `time_period_model`;
CREATE TABLE `time_period_model`( `id` bigint NOT NULL COMMENT 'ID',`create_time` datetime,`update_time` datetime,`is_deleted` tinyint default 0 COMMENT '逻辑删除 0:没删除 1:已删除',`price` decimal(10,2) NOT NULL COMMENT '该时间段预订使用价格(元)',`partition_id` bigint NOT NULL COMMENT '场区id',`begin_time` time NOT NULL COMMENT '时间段开始时间HH:mm(不用填日期)',`end_time` time NOT NULL COMMENT '时间段结束时间HH:mm(不用填日期)', `effective_start_date` date NOT NULL COMMENT '生效开始日期', `effective_end_date` date NOT NULL COMMENT '生效结束日期', `last_generated_date` date COMMENT '已生成到的日期',`status` tinyint default 0 COMMENT '0:启用;1:停用', PRIMARY KEY (`id`) USING BTREE,INDEX `idx_partition_id` (`partition_id`)
);

【时间段表】

之所以要创建唯一复合索引,是因为怕重复生成时间段,为什么会出现重复生成时间段的情况,看到后面就明白了

DROP TABLE IF EXISTS `time_period`;
CREATE TABLE `time_period`( `id` bigint NOT NULL COMMENT 'ID',`create_time` datetime,`update_time` datetime,`is_deleted` tinyint default 0 COMMENT '逻辑删除 0:没删除 1:已删除',`partition_id` bigint NOT NULL COMMENT '场区id',`price` decimal(10,2) NOT NULL COMMENT '该时间段预订使用价格(元)',`stock` int NOT NULL COMMENT '库存',`booked_slots` bigint unsigned NOT NULL DEFAULT 0 COMMENT '已预订的场地(位图表示)',`period_date` date NOT NULL COMMENT '预定日期', `begin_time` time NOT NULL COMMENT '时间段开始时间HH:mm(不用填日期)',`end_time` time NOT NULL COMMENT '时间段结束时间HH:mm(不用填日期)', PRIMARY KEY (`id`) USING BTREE,INDEX `idx_partition_id` (`partition_id`),UNIQUE INDEX `idx_unique_partition_period_time` (`partition_id`, `period_date`, `begin_time`, `end_time`)
);

定时任务

由于项目是微服务架构,在开发定时任务的时候需要考虑任务执行的唯一性,否则集群的所有机器都会执行同一个任务,浪费计算机算力,可以通过直接加一个分布式锁,只让集群中的一台机器执行整个定时任务,但是如果这样的话,其他机器处于空闲状态。为了提高机器的利用率和定时任务的执行效率,这里将不同表的时间段模板扫描工作交与不同机器实现

【实现思路】

  • 第一次定时任务:机器通过对表加锁,如果可以加锁成功,则扫描时间段模板进行时间段生成。注意,加锁时设置过期时间为2小时,如果任务执行完成,将锁状态设置1,同时过期时间设置更长
  • 第二次定时任务:用来兜底,避免第一次定时任务执行时有机器宕机,其负责的任务并没有完成。第二次扫描就是找出没有执行完成的任务,重新执行一遍。在第二次定时任务时,要么任务已经执行完成,其任务状态被设置为1,要么没有执行,其锁状态已经过期
package com.vrs.config.scheduled;import com.vrs.constant.RedisCacheConstant;
import com.vrs.service.TimePeriodModelService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;/*** @Author dam* @create 2024/11/17 16:44*/
@Component
@RequiredArgsConstructor
@Slf4j
public class TimePeriodScheduledTasks {private final TimePeriodModelService timePeriodModelService;private final StringRedisTemplate stringRedisTemplate;private int tableNum = 16;/*** 在每天凌晨1点执行* 扫描数据库的时间段模板,生成可预定的时间段*/@Scheduled(cron = "0 0 1 * * ?")public void timePeriodGenerator1() {for (int i = 0; i < tableNum; i++) {timePeriodGenerate(i);}}/*** 兜底一次,保证时间段都生成成功了* 扫描数据库的时间段模板,生成可预定的时间段*/@Scheduled(cron = "0 0 4 * * ?")public void timePeriodGenerator2() {for (int i = 0; i < tableNum; i++) {// 获取当前表的状态String status = stringRedisTemplate.opsForValue().get(String.format(RedisCacheConstant.VENUE_TIME_PERIOD_GENERATE_KEY, i));// 如果状态为 "1",说明任务已经完成,跳过if ("1".equals(status)) {continue;}// 如果还没有完成,尝试获取锁并执行任务timePeriodGenerate(i);}}/*** 时间段生成* @param tableIndex*/private void timePeriodGenerate(int tableIndex) {// 0状态设置2小时就过期,方便没有执行完成的任务在兜底时可以重新执行boolean isSuccess = stringRedisTemplate.opsForValue().setIfAbsent(String.format(RedisCacheConstant.VENUE_TIME_PERIOD_GENERATE_KEY, tableIndex),"0", 2, TimeUnit.HOURS).booleanValue();if (isSuccess) {try {// --if-- 设置键成功,说明集群中的其他机器还没有扫描当前表,由当前机器执行// 执行时间段生成timePeriodModelService.generateTimePeriodByModel(tableIndex);// 时间段生成成功,设置状态为1,过期时间段也设置长一点,方便兜底时检测stringRedisTemplate.opsForValue().set(String.format(RedisCacheConstant.VENUE_TIME_PERIOD_GENERATE_KEY, tableIndex),"1", 6, TimeUnit.HOURS);} catch (Exception e) {// 如果任务执行失败,删除锁,以便其他机器可以重试stringRedisTemplate.delete(String.format(RedisCacheConstant.VENUE_TIME_PERIOD_GENERATE_KEY, tableIndex));log.error("时间段生成失败,表编号:{},错误信息:{}", tableIndex, e.getMessage(), e);}} else {// 如果锁已被占用,检查任务是否已完成String currentStatus = stringRedisTemplate.opsForValue().get(String.format(RedisCacheConstant.VENUE_TIME_PERIOD_GENERATE_KEY, tableIndex));if (!"1".equals(currentStatus)) {log.warn("表编号:{} 的任务未完成,但锁已被占用,可能由其他机器处理中", tableIndex);}}}
}

根据时间段模板创建时间段

MySQL流式查询

首先要解决的事情是:如何高效扫描时间段模板表?

很容易想到的是,分页查询时间段模板,一批一批进行时间段生成,还可以套一个多线程,并行执行不同批次的任务。但分页查询有一个最大的缺点,在处理大数据时,每次分页都需要执行完整的查询并跳过前面的记录,尤其是深分页时,查询效率非常低下。

为了优化这个问题,本文使用流式查询来处理。

流式查询是一种处理和传输查询结果的方式,它允许客户端逐行接收来自数据库的数据,而不是一次性获取整个结果集。这种方式可以显著减少内存占用和网络延迟,优化资源使用,并且能够更快地响应用户请求,特别适合处理大规模数据集或实时性要求高的应用场景。通过流式查询,即使面对海量数据,应用程序也能保持高效和流畅的用户体验。相较于分页查询,它效率高的一个原因是,它每次查询都是从上一条数据开始,而不是每次从头来过

时间段生成如何保证幂等性

由于在扫描时间段模板生成时间段时,服务器可能发生宕机,在兜底时,可能该任务再次被集群中的其他机器执行。但是由于部分时间段模板其实已经被第一台机器扫描过,相应的时间段也已经创建完成,在兜底时,如何保证已创建的时间段不会再被重复生成,最简单的一种实现方式:给partition_id, period_date, begin_time, end_time生成唯一复合索引,这样重复创建时间段时,插入数据库就会失败。

还有一个问题,为了提高数据插入效率,使用了批量插入策略,即累积一定的数据量才进行插入。但 Mybatis Plus 提供的saveBatch函数是原子性的,要么全部插入成功,要么全部插入失败。由于部分时间段可能已经被第一台机器创建完成,兜底时插入数据库可能出现唯一索引异常,这样会导致其他没有创建过的时间段也插入失败。因此我们需要自己实现一段SQL,让批量插入时,及时部分数据插入异常,也不影响其他数据

<insert id="insertBatchIgnore">INSERT IGNORE INTO time_period (id,create_time,update_time,is_deleted,partition_id,price,stock,booked_slots,period_date,begin_time,end_time) VALUES<foreach collection="timePeriodDOList" item="item" separator=",">(#{item.id},NOW(),NOW(),0,#{item.partitionId},#{item.price},#{item.stock},#{item.bookedSlots},#{item.periodDate},#{item.beginTime},#{item.endTime})</foreach>
</insert>

二级缓存

在生成未来时间段时,需要查询advance_booking_day才知道要生成未来多少天的时间段,由于很多时间段模板可能都来源于同一个分区、同一个场馆,为了加速这个查询,可以使用缓存来提高效率,即第一次查询之后将分区ID对应的场馆信息存储起来,第二次获取就很快了。

但如果每次从Redis缓存中加载数据,需要多次网络I/O,为了进一步的效率提升,可以使用本地缓存 HashMap 来进一步优化,即每次先从本地缓存中查询,查询不到再去 Redis 缓存中加载。

二级缓存策略:

  • 首先使用本地缓存(如 HashMap)存储分区ID对应的场馆信息,以实现极快的查询速度;
  • 当本地缓存未命中时,再从Redis缓存中加载数据。Redis作为第二级缓存,提供了比数据库更快的数据访问速度,并且能够跨多个实例共享缓存数据,确保了数据的一致性和高可用性。

Redis管道

在创建时间段时,还需要做的一件事是缓存预热,即将相应的时间段库存、时间段信息添加到 Redis 缓存中,保证用户在预定时间段时有较快的响应速度,而不是预定时再去数据库中查询放到缓存中。

由于需要添加大量时间段的缓存,如果每个数据都单独提交给 Redis,会导致大量的网络 I/O 操作,从而降低效率。因此,是否有一种类似于数据库批量插入的方式来优化这一过程?

Redis 管道(Pipeline)是一种用于优化客户端与 Redis 服务器之间通信的技术,它允许客户端一次性发送多个命令给服务器,并在所有命令执行完毕后一次性接收所有的回复。这种方式减少了客户端与服务器之间的往返时间,尤其是在需要执行大量命令时,能够显著提高性能。

/*** 使用管道来批量将数据存储到Redis中** @param timePeriodDOList*/
@Override
public void batchPublishTimePeriod(List<TimePeriodDO> timePeriodDOList) {if (timePeriodDOList == null || timePeriodDOList.size() == 0) {return;}/// 将时间段存放到数据库中
//        this.saveBatch(timePeriodDOList);baseMapper.insertBatchIgnore(timePeriodDOList);/// 将时间段信息放到缓存中// 创建一个管道回调RedisCallback<Void> pipelineCallback = connection -> {// 开始管道connection.openPipeline();for (TimePeriodDO timePeriodDO : timePeriodDOList) {// 时间段开始时间long timePeriodStartMill = DateUtil.combineLocalDateAndLocalTimeToDateTimeMill(timePeriodDO.getPeriodDate(), timePeriodDO.getBeginTime());// 计算从现在到时间段开始还有多少毫秒 + 余量(86400000表示一天)//todo 待确认 cacheTimeSecond 是否一定为正数long cacheTimeSecond = (timePeriodStartMill - System.currentTimeMillis() + 86400000) / 1000;// 时间段信息connection.setEx(String.format(RedisCacheConstant.VENUE_TIME_PERIOD_KEY, timePeriodDO.getId()).getBytes(),cacheTimeSecond,JSON.toJSONString(timePeriodDO).getBytes());// 库存connection.setEx(String.format(RedisCacheConstant.VENUE_TIME_PERIOD_STOCK_KEY, timePeriodDO.getId()).getBytes(),cacheTimeSecond,JSON.toJSONString(timePeriodDO.getStock()).getBytes());}// 执行管道中的所有命令connection.closePipeline();return null;};// 使用StringRedisTemplate执行管道回调stringRedisTemplate.execute(pipelineCallback);
}

代码实现

package com.vrs.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vrs.constant.RedisCacheConstant;
import com.vrs.convention.exception.ClientException;
import com.vrs.convention.page.PageResponse;
import com.vrs.convention.page.PageUtil;
import com.vrs.domain.dto.req.TimePeriodModelListReqDTO;
import com.vrs.domain.entity.PartitionDO;
import com.vrs.domain.entity.TimePeriodDO;
import com.vrs.domain.entity.TimePeriodModelDO;
import com.vrs.domain.entity.VenueDO;
import com.vrs.mapper.TimePeriodModelMapper;
import com.vrs.service.PartitionService;
import com.vrs.service.TimePeriodModelService;
import com.vrs.service.TimePeriodService;
import com.vrs.service.VenueService;
import com.vrs.utils.DateUtil;
import com.vrs.utils.SnowflakeIdUtil;
import groovy.util.logging.Slf4j;
import lombok.Cleanup;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;import javax.sql.DataSource;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;/*** @author dam* @description 针对表【time_period_model_0】的数据库操作Service实现* @createDate 2024-11-17 14:29:46*/
@Service
@RequiredArgsConstructor
@Slf4j
public class TimePeriodModelServiceImpl extends ServiceImpl<TimePeriodModelMapper, TimePeriodModelDO>implements TimePeriodModelService {private final DataSource dataSource;private final TimePeriodService timePeriodService;private final StringRedisTemplate stringRedisTemplate;private final PartitionService partitionService;private final VenueService venueService;/*** 流式处理*/@Override@SneakyThrowspublic void generateTimePeriodByModel(int tableIndex) {// 获取 dataSource Bean 的连接@Cleanup Connection conn = dataSource.getConnection();@Cleanup Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);stmt.setFetchSize(Integer.MIN_VALUE);long start = System.currentTimeMillis();// 查询sql,只查询关键的字段String sql = "SELECT id,price,partition_id,begin_time,end_time,effective_start_date,effective_end_date,last_generated_date FROM time_period_model_" + tableIndex + " where is_deleted = 0 and status = 0";@Cleanup ResultSet rs = stmt.executeQuery(sql);HashMap<Long, Integer> partitionIdAndAdvanceBookingDayMap = new HashMap<>();List<TimePeriodDO> timePeriodDOInsertBatch = new ArrayList<>();List<TimePeriodModelDO> timePeriodDOModelUpdateBatch = new ArrayList<>();int batchSize = 2000;SimpleDateFormat sdf = new SimpleDateFormat("HH:mm");// 每次获取一行数据进行处理,rs.next()如果有数据返回true,否则返回falsewhile (rs.next()) {// 获取数据中的属性long id = rs.getLong("id");long partitionId = rs.getLong("partition_id");Date beginTime = sdf.parse(rs.getString("begin_time"));Date endTime = sdf.parse(rs.getString("end_time"));BigDecimal price = rs.getBigDecimal("price");Date effectiveStartDate = rs.getDate("effective_start_date");Date effectiveEndDate = rs.getDate("effective_end_date");// 上次生成到的日期Date lastGeneratedDate = rs.getDate("last_generated_date");int advanceBookingDay = getAdvanceBookingDayByPartitionId(partitionIdAndAdvanceBookingDayMap, partitionId);PartitionDO partitionDO = partitionService.getPartitionDOById(partitionId);if (partitionDO == null) {continue;}// 如果当前分区存在可预订分区的缓存,这里进行删除,因为生成了新的,需要重新查询数据库stringRedisTemplate.delete(String.format(RedisCacheConstant.VENUE_TIME_PERIOD_BY_PARTITION_ID_KEY,partitionId));// 这里其实不需要每天定时任务,都把advanceBookingDay都生成一遍,例如今天已经生成了未来七天的时间段了,那么明天其实只需要生成第八天的时间段即可Date generateDate = null;for (int i = 1; i <= advanceBookingDay; i++) {// 获取要生成的日期generateDate = new Date(System.currentTimeMillis() + i * 24 * 60 * 60 * 1000);if (lastGeneratedDate != null && generateDate.before(lastGeneratedDate)) {// 如果对应日期的时间段已经被生成过了,直接跳过continue;}// 检查明天的日期是否在这个范围内boolean isInDateRange = generateDate.after(effectiveStartDate) && generateDate.before(effectiveEndDate);if (isInDateRange) {TimePeriodDO timePeriodDO = TimePeriodDO.builder().partitionId(partitionId).price(price).stock(partitionDO.getNum()).bookedSlots(0L).periodDate(DateUtil.dateToLocalDate(generateDate)).beginTime(DateUtil.dateToLocalTime(beginTime)).endTime(DateUtil.dateToLocalTime(endTime)).build();timePeriodDO.setId(SnowflakeIdUtil.nextId());timePeriodDOInsertBatch.add(timePeriodDO);if (timePeriodDOInsertBatch.size() >= batchSize) {// --if-- 数据量够了,存储数据库timePeriodService.batchPublishTimePeriod(timePeriodDOInsertBatch);timePeriodDOInsertBatch.clear();}}}if (generateDate != null) {// 批量更新时间段模板的最新生成日期TimePeriodModelDO timePeriodModelDO = new TimePeriodModelDO();timePeriodModelDO.setId(id);timePeriodModelDO.setPartitionId(partitionId);timePeriodModelDO.setLastGeneratedDate(generateDate);timePeriodDOModelUpdateBatch.add(timePeriodModelDO);if (timePeriodDOModelUpdateBatch.size() >= batchSize) {// --if-- 数据量够了,修改数据库this.updateLastGeneratedDateBatch(timePeriodDOModelUpdateBatch);timePeriodDOModelUpdateBatch.clear();}}}// 处理最后一波数据if (timePeriodDOInsertBatch.size() >= 0) {// 将时间段存储到数据库timePeriodService.batchPublishTimePeriod(timePeriodDOInsertBatch);timePeriodDOInsertBatch.clear();}if (timePeriodDOModelUpdateBatch.size() >= 0) {// --if-- 数据量够了,修改数据库this.updateLastGeneratedDateBatch(timePeriodDOModelUpdateBatch);timePeriodDOModelUpdateBatch.clear();}log.debug("流式生成时间段花费时间:" + ((System.currentTimeMillis() - start) / 1000));}private void updateLastGeneratedDateBatch(List<TimePeriodModelDO> timePeriodDOModelUpdateBatch) {if (timePeriodDOModelUpdateBatch == null || timePeriodDOModelUpdateBatch.size() == 0) {return;}baseMapper.updateLastGeneratedDateBatch(timePeriodDOModelUpdateBatch);}/*** 获取分区的提前预定时间* 使用二级缓存,本地缓存找不到,再去Redis中找,还找不到的话,去数据库中找** @param partitionIdAndAdvanceBookingDayMap* @param partitionId* @return*/private int getAdvanceBookingDayByPartitionId(HashMap<Long, Integer> partitionIdAndAdvanceBookingDayMap, long partitionId) {if (partitionIdAndAdvanceBookingDayMap.containsKey(partitionId)) {return partitionIdAndAdvanceBookingDayMap.get(partitionId);}VenueDO venueDO = venueService.getVenueDOByPartitionId(partitionId);partitionIdAndAdvanceBookingDayMap.put(partitionId, venueDO.getAdvanceBookingDay());return venueDO.getAdvanceBookingDay();}
}

相关文章:

分布式环境下定时任务扫描时间段模板创建可预订时间段

&#x1f3af; 本文详细介绍了场馆预定系统中时间段生成的实现方案。通过设计场馆表、时间段模板表和时间段表&#xff0c;系统能够根据场馆的提前预定天数生成未来可预定的时间段。为了确保任务执行的唯一性和高效性&#xff0c;系统采用分布式锁机制和定时任务&#xff0c;避…...

谷粒商城-高级篇完结-Sleuth+Zipkin 服务链路追踪

1、基本概念和整合 1.1、为什么用 微服务架构是一个分布式架构&#xff0c;它按业务划分服务单元&#xff0c;一个分布式系统往往有很多个服务单元。由于服务单元数量众多&#xff0c;业务的复杂性&#xff0c;如果出现了错误和异常&#xff0c;很难去定位 。主要体现在&#…...

GraphRAG:LLM之Graphrag接入milvus

前言 微软目前的graphrag更像个demo&#xff0c;数据量大的时候不是很友好的啊&#xff0c;所以将milvus接入了graphrag&#xff0c;看完这篇文章&#xff0c;其他数据库接入应该也没问题 注&#xff1a;这篇文章只是在search的时候接入进来&#xff0c;index过程或者说整个流…...

flink cdc oceanbase(binlog模式)

接上文&#xff1a;一文说清flink从编码到部署上线 环境&#xff1a;①操作系统&#xff1a;阿里龙蜥 7.9&#xff08;平替CentOS7.9&#xff09;&#xff1b;②CPU&#xff1a;x86&#xff1b;③用户&#xff1a;root。 预研初衷&#xff1a;现在很多项目有国产化的要求&#…...

【算法】算法初步

要学好数据结构和算法的设计与分析&#xff0c;请务必先打好C语言基础&#xff0c;因为C语言中的数据存储、内存映射、指针等等概念最接近计算机的底层原理&#xff0c;数据结构是数据在内存空间当中的组织形式&#xff0c;而算法则是提供了解决某个问题的一种思路&#xff0c;…...

Eureka原理

my: 服务注册与发现 心跳 自我保护 故障转移 Eureka 原理 Eureka 是一个由 Netflix 开源的服务注册与发现框架&#xff0c;广泛用于微服务架构中&#xff0c;尤其是 Spring Cloud 中的服务注册与发现。Eureka 的主要作用是管理和协调分布式系统中的服务实例&#xff0c;使…...

关于linux的ld.so.conf.d

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 源码指引&#xff1a;github源…...

Linux Shell 脚本编程基础知识篇—awk的条件判断(3)

ℹ️大家好&#xff0c;我是练小杰&#xff0c;今天周五了&#xff0c;又是一周过去了&#x1f606; 本文是有关Linux shell脚本编程的awk命令的条件语句&#xff0c;后续我会不断增加相关内容 ~~ 回顾:【awk字符串函数和内置变量】 更多Linux 相关内容请点击&#x1f449;【Li…...

安装Cockpit服务,使用Web页面管理你的Linux服务器

说起管理 Linux 服务器&#xff0c;大家首先想到的使用 SecureCRT、Xshell、MobaXterm 等工具远程到服务器&#xff0c;然后使用命令行管理服务器。今天给大家介绍一个好玩的工具&#xff0c;名字叫Cockpit&#xff0c; Cockpit 是一个免费开源的基于 web 的 Linux 服务器管理…...

基于微信小程序的面部动作检测

目录 引言系统架构概述前端实现细节后端实现细节防止欺骗与误导的措施前后端数据交互详细细节关键技术选型关键技术框架与算法详细说明优化与注意事项总结 引言 微信小程序的面部动作检测的核心功能包括检测用户的左右转头、眨眼和张嘴动作&#xff0c;并根据检测结果逐步引导…...

使用npm 插件[mmdc]将.mmd时序图转换为图片

使用npm 插件[mmdc]将.mmd时序图转换为图片 1. 安装 mmdc2. 转换为图片 可以使用 mmdc &#xff08;Mermaid CLI&#xff09;这个工具来将 .mmd 时序图&#xff08;Mermaid语法描述的时序图&#xff09;转换为图片&#xff0c;以下是使用步骤&#xff1a; 1. 安装 mmdc 确保…...

服务端错误的处理和web安全检测

文章目录 I 服务端错误的处理业务返回码处理前端处理业务返回码nginx处理http状态码II web安全检测区分服务器类型主机扫漏III 使用 micro_httpd 搭建一个PHP站点步骤下载micro_httpd 并安装它配置micro_httpd 来服务PHP文件I 服务端错误的处理 服务端发生错误时,返回给前端的…...

周记-Repeater中的children和item区别

Repeater中的children和item 在开发qml的界面时&#xff0c;用到了Repeater&#xff0c;表头需要根据Repeater是否存在显示的项&#xff0c;来进行显示。 repeater.children[i] repeater.itemAt(i)如果判断有没有存在显示的项&#xff0c;可以用下面的代码 function is_exis…...

JVM实战—8.如何分析jstat统计来定位GC

大纲 1.使用jstat了解线上系统的JVM运行状况 2.使用jmap和jhat了解线上系统的对象分布 3.如何分析JVM运行状况并合理优化 4.使用jstat分析模拟的BI系统JVM运行情况 5.使用jstat分析模拟的计算系统JVM运行情况 6.问题汇总 1.使用jstat了解线上系统的JVM运行状况 (1)JVM的…...

halcon三维点云数据处理(五)创建代表工具和机器人底座的3D模型

目录 一、gen_robot_tool_and_base_object_model_3d 函数调用二、gen_arrow_object_model_3d 函数调用 首先说明一下这部分代码在find_box_3d这个例程中&#xff0c;非常好用的一个坐标系生成函数。 一、gen_robot_tool_and_base_object_model_3d 函数调用 RobotToolSize : 0.…...

React Router 向路由组件传state参数浏览器回退历史页面显示效果问题

昨天在看尚硅谷张天禹老师讲的 React教程p90&#xff0c;老师讲到 React路由的 replace模式和push模式&#xff0c;老师的演示效果与自己本地操作不太一样。 老师的效果&#xff1a;点击查看消息1&#xff0c;消息2&#xff0c;消息3 再点回退&#xff0c;可以依次查看到 消息…...

LabVIEW四旋翼飞行器姿态监测系统

四旋翼飞行器姿态监测系统是一个集成了高度、速度、俯仰角与滚转角数据采集与分析的系统&#xff0c;提高飞行器在复杂环境中的操作精确度与安全性。系统利用LabVIEW平台与硬件传感器相结合&#xff0c;实现实时数据处理与显示&#xff0c;有效地提升了四旋翼飞行器的监测与控制…...

HTML——66.单选框

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>单选框</title></head><body><!--input元素的type属性&#xff1a;(必须要有)--> <!--单选框:&#xff08;如所住省会&#xff0c;性别选择&…...

av1学习笔记(二):sequence_header_obu

av1学习笔记&#xff08;二&#xff09;&#xff1a;sequence_header_obu 目录 av1学习笔记&#xff08;二&#xff09;&#xff1a;sequence_header_obu1&#xff0c;图片的编解码方式1.1 seq_profile:1.2 still_picture1.3 reduced_still_picture_header1.4 编解码图像的宽高…...

嵌入式驱动开发详解8(阻塞/非阻塞/异步通信)

文章目录 前言阻塞非阻塞异步通知后续 前言 首先来回顾一下“中断”&#xff0c;中断是处理器提供的一种异步机制&#xff0c;我们配置好中断以后就 可以让处理器去处理其他的事情了&#xff0c;当中断发生以后会触发我们事先设置好的中断服务函数&#xff0c; 在中断服务函数…...

CSS进阶和SASS

目录 一、CSS进阶 1.1、CSS变量 1.2、CSS属性值的计算过程 1.3、做杯咖啡 1.4、下划线动画 1.5、CSS中的混合模式(Blending) 二、SASS 2.1、Sass的颜色函数 2.2、Sass的扩展(extend)和占位符(%)、混合(Mixin) 2.3、Sass的数学函数 2.4、Sass的模块化开发 2.5、Sass…...

求交错序列前N项和(PTA)C语言

本题要求编写程序&#xff0c;计算交错序列 1-2/33/5-4/75/9-6/11... 的前N项之和。 输入格式: 输入在一行中给出一个正整数N。 输出格式: 在一行中输出部分和的值&#xff0c;结果保留三位小数。 输入样例: 5输出样例: 0.917 代码&#xff1a; #include<stdio.h&g…...

【Delphi】创建COM服务器供浏览器(WebView)使用的操作步骤

首先创建一个VCL程序在程序的主界面放置WebView浏览器控件新增Automation Object&#xff08;ActiveX 页面中&#xff09;&#xff0c;重点&#xff0c;注意WebView只支持IDisptcher接口然后根据这个提示实现实现其函数功能在浏览器中众注册&#xff1a; Delphi程序 procedure …...

Spring Security(maven项目) 3.0.2.3版本

前言 通过实践而发现真理&#xff0c;又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识&#xff0c;又从理性认识而能动地指导革命实践&#xff0c;改造主观世界和客观世界。实践、认识、再实践、再认识&#xff0c;这种形式&#xff0c;循环往复以至无穷&a…...

如何删除 Docker 中的悬虚镜像?

在 Docker 中&#xff0c;悬虚镜像&#xff08;Dangling Images&#xff09;是指那些没有 标签 且没有被任何容器使用的镜像。这些镜像通常是由于构建过程中生成的中间层镜像或未正确清理的镜像残留。删除悬虚镜像可以释放磁盘空间并保持 Docker 环境的整洁。 1. 列出悬虚镜像…...

【JMeter】配置元件Config Element

1.配置元件 作用&#xff1a; 通用接口配置&#xff0c;协议&#xff0c;IP, 端口等&#xff0c;减少重复工作量 元件的分类 HTTP Request Defaults即HTTP请求默认值 作用&#xff1a; 可以配置成通用请求行的信息&#xff0c;可复用 ​​​​​​​ ​​​​​​​ JDBC Co…...

【Vue】分享一个快速入门的前端框架以及如何搭建

先上效果图: 登录 菜单: 下载地址: 链接&#xff1a;https://pan.baidu.com/s/1m-ZlBARWU6_2n8jZil_RAQ 提取码&#xff1a;ui20 … 主要是可以自定义设置token,更改后端请求地址较为方便。 应用设置: 登录与token设置: 在这里设置不用登录,可以请求的接口: request.js i…...

搭建开源版Ceph分布式存储

系统&#xff1a;Rocky8.6 三台2H4G 三块10G的硬盘的虚拟机 node1 192.168.2.101 node2 192.168.2.102 node3 192.168.2.103 三台虚拟机环境准备 1、配置主机名和IP的映射关系 2、关闭selinux和firewalld防火墙 3、配置时间同步且所有节点chronyd服务开机自启 1、配置主机名和…...

运动相机拍摄的视频打不开怎么办

3-10 GoPro和大疆DJI运动相机的特点&#xff0c;小巧、高清、续航长、拍摄稳定&#xff0c;很多人会在一些重要场合用来拍摄视频&#xff0c;比如可以用来拿在手里拍摄快速运动中的人等等。 但是毕竟是电子产品&#xff0c;有时候是会出点问题的&#xff0c;比如意外断电、摔重…...

信号处理-消除趋势项

matlab 版本 python 版本 import numpy as np import matplotlib.pyplot as plt from matplotlib import rcParams# 设置中文字体 rcParams[font.sans-serif] [SimHei] # 设置默认字体为黑体 rcParams[axes.unicode_minus] False # 解决负号显示问题def compute_time(n, f…...

民宿酒店预订系统小程序+uniapp全开源+搭建教程

一.介绍 一.系统介绍 基于ThinkPHPuniappuView开发的多门店民宿酒店预订管理系统&#xff0c;快速部署属于自己民宿酒店的预订小程序&#xff0c;包含预订、退房、WIFI连接、吐槽、周边信息等功能。提供全部无加密源代码&#xff0c;支持私有化部署。 二.搭建环境 系统环境…...

Java(1)入门基础

1. Java简介 1.1 什么是Java Java 是一款由Sun Microsystems公司&#xff08;现为甲骨文公司Oracle Corporation的一部分&#xff09;的James Gosling及其团队在1995年发布的高级编程语言。同时&#xff0c;Java 是一种面向对象的语言&#xff0c;这意味着它允许开发者通过创…...

大风车excel:怎么把题库导入excel?题库导入excel

高效管理试题库&#xff1a;如何批量导入试题到 Excel&#xff1f; 在教育培训、学校管理以及在线学习平台中&#xff0c;试题库的管理是核心工作之一。如何快速、准确地将试题导入到 Excel 表格中&#xff0c;成为许多教育工作者和开发者的迫切需求。本文将围绕“题库导入 Ex…...

rabbitmq——岁月云实战笔记

1 rabbitmq设计 生产者并不是直接将消息投递到queue&#xff0c;而是发送给exchange&#xff0c;由exchange根据type的规则来选定投递的queue&#xff0c;这样消息设计在生产者和消费者就实现解耦。 rabbitmq会给没有type预定义一些exchage&#xff0c;而实际我们却应该使用自己…...

更改IP地址能提高網路速度嗎?

IP地址是由ISP分配給連接設備的唯一數字字串&#xff0c;允許設備與互聯網上的網站&#xff0c;伺服器和其他設備進行通信。因此&#xff0c;IP地址是訪問互聯網的基本條件之一。 IP 地址如何工作&#xff1f; 線上流覽時&#xff0c;網路請求都會使用IP地址從設備發送&#…...

Backend - C# asp .net core API(使用swagger)

目录 1. 安装Swagger组件 2. 查看swagger套件&#xff08;包&#xff09; 3. 配置Swagger中间件(Swagger服务) 4. 启动Swagger中间件 5. 接口文档配置为项目首页 6. swagger中的接口添加注释 1. 安装Swagger组件 工具栏“工具” > NuGet套件管理员 > 管理方案的NuGet套件…...

linux系统(ubuntu,uos等)连接鸿蒙next(mate60)设备

以前在linux上是用adb连接&#xff0c;现在升级 到了鸿蒙next&#xff0c;adb就不好用了。得用Hdc来了&#xff0c;在windows上安装了hisuit用的好好的&#xff0c;但是到了linux(ubuntu2204)下载安装了 下载中心 | 华为开发者联盟-HarmonyOS开发者官网&#xff0c;共建鸿蒙生…...

Unity-Mirror网络框架-从入门到精通之网络组件介绍

文章目录 前言网络组件Network AnimatorNetwork AuthenticatorNetwork DiscoveryNetwork IdentityNetwork ManagerNetwork Manager HUDNetwork Ping DisplayNetwork RigidbodyNetwork Room ManagerNetwork Room PlayerNetwork Start PositionNetwork StatisticsNetwork Transfo…...

nginx反向代理和负载均衡

nginx反向代理优点&#xff1a; 提高访问速度进行负载均衡保证后端服务安全&#xff08;将后端放到局域网&#xff09; 反向代理配置方式&#xff1a; nginx 负载均衡配置&#xff1a;...

Linux驱动开发学习准备(Linux内核源码添加到工程-Workspace)

Linux内核源码添加到VsCode工程 下载Linux-4.9.88源码&#xff1a; 没有处理同名文件的压缩包&#xff1a; https://pan.baidu.com/s/1yjIBXmxG9pwP0aOhW8VAVQ?pwde9cv 已把同名文件中以大写命名的文件加上_2后缀的压缩包&#xff1a; https://pan.baidu.com/s/1RIRRUllYFn2…...

2025.01.02 一月 | 充分地接受生活本身

一月 | 充分地接受生活本身 2025.01.02 吕方伊 课程导读 迎上去&#xff0c;抛却伤悲&#xff0c;放下消沉&#xff0c;坦然承载生命赋予的所有喜悦与不确定&#xff0c;真诚地拥抱最本真的自己。 英文原文 ▍今日一句 In order to understand the world, one has to turn…...

开源模型应用落地-qwen2-7b-instruct-LoRA微调合并-ms-swift-单机单卡-V100(十三)

一、前言 本篇文章将使用ms-swift去合并微调后的模型权重,通过阅读本文,您将能够更好地掌握这些关键技术,理解其中的关键技术要点,并应用于自己的项目中。 二、术语介绍 2.1. LoRA微调 LoRA (Low-Rank Adaptation) 用于微调大型语言模型 (LLM)。 是一种有效的自适应策略,…...

30、论文阅读:基于小波的傅里叶信息交互与频率扩散调整的水下图像恢复

Wavelet-based Fourier Information Interaction with Frequency Diffusion Adjustment for Underwater Image Restoration 摘要介绍相关工作水下图像增强扩散模型 论文方法整体架构离散小波变换与傅里叶变换频率初步增强Wide Transformer BlockSpatial-Frequency Fusion Block…...

uniapp H5页面实现懒加载

在 uniapp 中&#xff0c;要在小的 view 内实现列表懒加载&#xff0c;可以通过以下步骤来实现&#xff1a; 使用 scroll-view 组件来创建一个可滚动的区域。在 scroll-view内 部放置一个list组件&#xff0c;用于显示数据列表。监听 scroll-view 的滚动事件&#xff0c;当滚动…...

Linux(Centos 7.6)命令详解:mkdir

1.命令作用 如果目录还不存在&#xff0c;则创建目录(Create the DIRECTORY, if they do not already exist.) 2.命令语法 Usage: mkdir [OPTION]... DIRECTORY... 3.参数详解 OPTION: -m, --modeMODE&#xff0c;创建新目录同时设置权限模式-p, --parents&#xff0c;创…...

Freemarker模板进行判空

文章目录 freemarker判断对象是否为null使用 ?? 操作符使用 ?has_content 内建函数直接使用 ! 操作符取反 freemarker判断列表是否为空 freemarker判断对象是否为null 在 FreeMarker 模板引擎中&#xff0c;你可以使用内建的指令和条件判断来检测一个对象是否为 null。Free…...

20250103在Ubuntu20.04.5的Android Studio 2024.2.1.12中跑通Hello World

20250103在Ubuntu20.04.5的Android Studio 2024.2.1.12中跑通Hello World 2025/1/3 14:06 百度&#xff1a;android studio helloworld android studio hello world kotlin helloword kotlin 串口 no run configurations added android studio no run configurations added 1、…...

Infineon PSoC 4 CapSense ModusToolbox IDE - 系统生态篇

本文档说明了 ModusToolbox 软体环境的 4 个层面&#xff0c;该环境为 CapSense 设备和生态系统提供支援。本文是 Infineon PSoC 4 CapSense ModusToolbox IDE-系统介绍的延伸篇 (Infineon PSoC 4 CapSense ModusToolbox IDE -系统介绍篇 - 大大通(简体站))。 什么是ModusToolb…...

Backend - EF Core(C# 操作数据库 DB)

目录 一、EF Core 1. 使用的ORM框架&#xff08;对象关系映射&#xff09; 2. EFCore 常见两种模式 3. EFCore 提供程序 二、 EF 操作数据库&#xff08;Code First&#xff09; 1. 下载NuGet插件 2.创建模型类文件 3.创建DBContext文件 4.Programs.cs文件 5.appsettings.Devel…...

ETL处理工具Kettle入门

1. Kettle简介 Kettle&#xff08;现已更名为Pentaho Data Integration&#xff0c;简称PDI&#xff09;是一个开源的ETL工具&#xff0c;能够进行数据的抽取&#xff08;Extract&#xff09;、转换&#xff08;Transform&#xff09;和加载&#xff08;Load&#xff09;。它是…...