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

瑞吉外卖项目----(2)缓存优化

1 缓存优化

1.0 问题说明

1.1 环境搭建

将项目推送到远程仓库里,教程在git

提交远程仓库前建议取消代码检查
在这里插入图片描述

创建新的分支v1.0(用于实现缓存优化)并推送到远程仓库
在这里插入图片描述

1.1.1 maven坐标

导入spring-data-redis的maven坐标:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

1.1.2 配置文件

在application.yml中加入redis相关配置:

spring:redis:host: localhostport: 6379#password: root@123456database: 0

1.1.3 配置类

在项目中加入RedisConfig

/*** Redis配置类*/
@Configuration
public class RedisConfig extends CachingConfigurerSupport{@Beanpublic RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();//默认的Key序列化器为:JdkSerializationRedisSerializerredisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setHashKeySerializer(new StringRedisSerializer());redisTemplate.setConnectionFactory(connectionFactory);return redisTemplate;}}

1.2 缓存短信验证码

1.2.1 实现思路

前面已经实现了移动端手机验证码登录,之前随机生成的验证码是保存在HttpSession中的。现在需要改造为将验证码缓存在Redis中,具体的实现思路如下:

  • 在服务端UserController中注入RedisTemplate对象,用于操作Redis
  • 在服务端UserController的sendMsg方法中,将随机生成的验证码缓存到Redis中,并设置有效期为5分钟
  • 在服务端UserController的login方法中,从Redis中 获取缓存的验证码,如果登录成功则删除Redis中的验证码

1.2.2 代码改造

在这里插入图片描述在这里插入图片描述在这里插入图片描述
测试验证:
在这里插入图片描述

1.3 缓存菜品数据

1.3.1 实现思路

前面已经实现了移动端菜品查看功能,对应的服务端方法为DishController的list方法,此方法会根据前端提交的查询条件进行数据库查询操作。在高并发的情况下,频繁查询数据库会导致系统性能下降,服务端响应时间增长。 现在需要对此方法进行缓存优化,提高系统的性能。
具体的实现思路如下:

  1. 改造DishController的list方法 ,先从Redis中获取菜品数据,
    如果有则直接返回,无需查询数据库;
    如果没有则查询数据库,并将查询到的菜品数据放入Redis,有效期1小时。
  2. 改造DishController的save和update方法,加入清理缓存的逻辑

注意事项:
在使用缓存过程中,要注意保证数据库中的数据和缓存中的数据一致,如果数据库中的数据发生变化,需要及时清理缓存数据。

1.3.2 代码改造

@GetMapping("/list")
public R<List<DishDto>> list(Dish dish){List<DishDto> dishDtoList = null;//动态构造keyString key = "dish_"+dish.getCategoryId()+"_"+dish.getStatus(); //dish_xxxx_1//从redis中获取缓存数据dishDtoList = (List<DishDto>) redisTemplate.opsForValue().get(key);if (dishDtoList != null) {//如果存在,直接返回,无需查询数据库return R.success(dishDtoList);}//构造查询条件LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(dish.getCategoryId() != null ,Dish::getCategoryId,dish.getCategoryId());//添加条件,查询状态为1(起售状态)的菜品queryWrapper.eq(Dish::getStatus,1);//添加排序条件queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime);List<Dish> list = dishService.list(queryWrapper);dishDtoList = list.stream().map((item) -> {DishDto dishDto = new DishDto();BeanUtils.copyProperties(item,dishDto);Long categoryId = item.getCategoryId();//分类id//根据id查询分类对象Category category = categoryService.getById(categoryId);if(category != null){String categoryName = category.getName();dishDto.setCategoryName(categoryName);}//当前菜品的idLong dishId = item.getId();LambdaQueryWrapper<DishFlavor> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.eq(DishFlavor::getDishId,dishId);//SQL:select * from dish_flavor where dish_id = ?List<DishFlavor> dishFlavorList = dishFlavorService.list(lambdaQueryWrapper);dishDto.setFlavors(dishFlavorList);return dishDto;}).collect(Collectors.toList());//如果不存在,需要查询数据库,将查询到的菜品数据缓存到redis中redisTemplate.opsForValue().set(key,dishDtoList,60, TimeUnit.MINUTES);return R.success(dishDtoList);
}@PostMapping
public R<String> save(@RequestBody DishDto dishDto){log.info(dishDto.toString());dishService.saveWithFlavor(dishDto);//清理所有菜品的缓存数据//Set keys = redisTemplate.keys("dish_*");//redisTemplate.delete(keys);//清理某个分类下的菜品缓存数据String key = "dish_"+dishDto.getCategoryId()+"_1";redisTemplate.delete(key);return R.success("新增菜品成功");
}@PutMapping
public R<String> update(@RequestBody DishDto dishDto){log.info(dishDto.toString());dishService.updateWithFlavor(dishDto);//清理所有菜品的缓存数据//Set keys = redisTemplate.keys("dish_*");//redisTemplate.delete(keys);//清理某个分类下的菜品缓存数据String key = "dish_"+dishDto.getCategoryId()+"_1";redisTemplate.delete(key);return R.success("修改菜品成功");
}

1.4 合并分支

开发完代码并且测试成功后要及时的将代码提交并且推送到远程仓库
然后合并分支:先切换到master分支,然后选择分支合并
在这里插入图片描述

1.5 Spring Cache

1.5.1 介绍

  • Spring Cache是一个框架, 实现了基于注解的缓存功能,只需要简单地加一个注解, 就能实现缓存功能。

  • Spring Cache提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓存技术。

  • CacheManager是Spring提供的各种缓存技术抽象接口。

  • 针对不同的缓存技术需要实现不同的CacheManager:
    在这里插入图片描述

1.5.2 常用注解

在这里插入图片描述
在spring boot项目中,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在启动类上使用@EnableCaching开启缓存支持即可。

例如,使用Redis作为缓存技术,只需要导入Spring data Redis的maven坐标即可。

1.5.3 简单案例

  1. 导入坐标
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.5</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.itheima</groupId><artifactId>cache_demo</artifactId><version>1.0-SNAPSHOT</version><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><scope>compile</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.20</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency><dependency><groupId>commons-lang</groupId><artifactId>commons-lang</artifactId><version>2.6</version></dependency><!--<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.2</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.23</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>2.4.5</version></plugin></plugins></build>
</project>
  • 如果只是使用基本的功能这些坐标已经够了,spring cache相关的api在spring-context包里面(spring-boot-start-web坐标下)
  • 如果后面需要使用到redis作为缓存还需要再导入spring-boot-start-cache包
  1. 配置yml文件
server:port: 8080
spring:application:#应用的名称,可选name: cache_demodatasource:druid:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/cache_demo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=trueusername: rootpassword: root
mybatis-plus:configuration:#在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:db-config:id-type: ASSIGN_ID
  1. 开启缓存:启动类上@EnableCaching
@Slf4j
@SpringBootApplication
@EnableCaching
public class CacheDemoApplication {public static void main(String[] args) {SpringApplication.run(CacheDemoApplication.class,args);log.info("项目启动成功...");}
}
  1. 在Controller的方法上加入@CachePut、@Cacheable、@CacheEvict等注解,进行缓存操作
    注解@CachePut
/*** CachePut:将方法返回值放入缓存(将保存的User对象存到Map里)* value:缓存的名称,每个缓存名称下可以有多个key* key:缓存的key*/
@CachePut(value = "userCache",key = "#user.id")
@PostMapping
public User save(User user){userService.save(user);return user;
}

注解@CacheEvict

/*** CacheEvict:清理指定缓存* value:缓存的名称,每个缓存名称下可以有多个key* key:缓存的key*/
@CacheEvict(value = "userCache",key = "#id")//"#p0" or "#root.args[0]"
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id){userService.removeById(id);
}

注解@Cacheable

/*** Cacheable:* value:缓存的名称,每个缓存名称下可以有多个key* key:缓存的key* condition:满足条件时才缓存数据(不能使用返回值result)* unless:满足条件则不缓存*/
@Cacheable(value = "userCache",key = "#id",unless = "#result == null")
@GetMapping("/{id}")
public User getById(@PathVariable Long id){User user = userService.getById(id);return user;
}

1.5.4 使用方式(redis)

在Spring Boot项目中使用Spring Cache的操作步骤(使用redis缓存技术)

  1. 导入maven坐标
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  1. 配置application.yml
spring:redis:host: localhostport: 6379#password: root@123456database: 0cache:redis:time-to-live: 1800000 #设置缓存过期时间
  1. 在启动类上加入@EnableCaching注解,开启缓存注解功能
  2. 在Controller的方法上加入@Cacheable、@CacheEvict等注解,进行缓存操作

1.6 缓存套餐数据

1.6.1 实现思路

前面我们已经实现了移动端套餐查看功能,对应的服务端方法为SetmealController的list方法,此方法会根据前端提交的查询条件进行数据库查询操作。在高并发的情况下,频繁查询数据库会导致系统性能下降,服务端响应时间增长。
现在需要对此方法进行缓存优化,提高系统的性能。

具体的实现思路如下:

1、 导入Spring Cache和Redis相关maven坐标

2、 在application.yml中 配置缓存数据的过期时间

3、在启动类.上加入@EnableCaching注解,开启缓存注解功能

4、在SetmealController的list方法.上加入@Cacheable注解

5、在SetmealController的save和delete方法上加入@CacheEvict注解

1.6.2 代码改造

  1. 在pom.xml文件中导入坐标
    在这里插入图片描述
  2. 在application.yml中配置缓存数据过期时间
    在这里插入图片描述
  3. 在启动类上加入@EnableCaching注解,开启缓存
    在这里插入图片描述
  4. 在SetmealController的list方法上加入@Cacheable注解
    在这里插入图片描述
    问题:返回的R没有序列化
    在这里插入图片描述
    解决:在这里插入图片描述
  5. 在SetmealController的save方法和delete方法上加入@CacheEvict注解
    在这里插入图片描述
    在这里插入图片描述
  6. 完成后经测试代码没问题后,将代码提交到Git仓库并合并到主分支

2 读写分离

2.0 问题分析

在这里插入图片描述
解决方案:读写分离

在这里插入图片描述

2.1 MySQL主从复制

2.1.1 介绍

MySQL主从复制是一个异步的复制过程,底层是基于Mysql数据库自带的二进制日志功能。就是一台或多台MySQL数据库(slave, 即从库)从另一台MySQL数据库(master, 即主库)进行日志的复制然后再解析日志并应用到自身,最终实现从库的数据和主库的数据保持一致。 MySQL主从复制是MySQL数据库自带功能,无需借助第三方工具。

MySQL复制过程分成三步:
● master将 改变记录到二进制日志(binary log )
● slave将master的binary 1og拷贝到它的中继日志(relay log )
● slave重做中继日志中的事件,将改变应用到自己的数据库中
在这里插入图片描述

2.2 读写分离案例

2.2.1 配置-前置条件

提前准备好两台服务器,分别安装Mysql并启动服务成功

建议主从库都用虚拟机,跟自己Windows环境的数据库区分开。VMware 虚拟机克隆

2.2.2 主库master

  1. 修改MySQL数据库的配置文件, vim /etc/my.cnf
[mysqld]
log-bin=mysql-bin #[必须]启用二进制日志
server-id=100 #[必须] 服务器唯一id
  1. 重启mysql服务systemctl restart mysqld

  2. 登录MySQL数据库mysql -uroot -proot,执行以下SQL

GRANT REPLICATION SLAVE ON *.* to 'xiaoming'@'%' identified by 'Root@123456';
  1. 登录mysql数据库,执行sql:show master status; 记录结果中的FIle和Position的值
    在这里插入图片描述

注意:上面SQL的作用是查看Master的状态,执行完上面SQL语句不要再执行任何操作

2.2.3 从库slave

  1. 修改MySQL数据库的配置文件, vim /etc/my.cnf
[mysqld]
server-id=101 #[必须] 服务器唯一id
  1. 重启mysql服务systemctl restart mysqld
  2. 登录mysql数据库,执行下面SQL
change master to
master_host= '192.168.1.189',master_user='xiaoming',master_password='Root@123456', master_log_file= 'mysql-bin.000002',master_log_pos=154;
start slave;
  1. 查询从数据库的状态show slave status\G; 在这里插入图片描述

2.3 项目实现读写分离

  1. 导入maven依赖
<dependency><groupId>org.apache.shardingsphere</groupId><artifactId> sharding-jdbc-spring-boot-starter</artifactId><version>4.0.0-RC1</version>
</dependency>
  1. 在配置文件中配置读写分离规则并允许bean定义覆盖配置项
server:port: 8080
spring:application:#应用的名称,可选name: reggie_take_outshardingsphere:datasource:names:master,slave# 主数据源master:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.1.189:3306/reggie?characterEncoding=utf-8&useSSL=falseusername: rootpassword: 123456# 从数据源slave:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.1.190:3306/reggie?characterEncoding=utf-8&useSSL=falseusername: rootpassword: 123456masterslave:# 读写分离配置load-balance-algorithm-type: round_robin #轮询# 最终的数据源名称name: dataSource# 主库数据源名称master-data-source-name: master# 从库数据源名称列表,多个逗号分隔slave-data-source-names: slaveprops:sql:show: true #开启SQL显示,默认falsemain:allow-bean-definition-overriding: trueredis:host: localhostport: 6379#password: root@123456database: 0cache:redis:time-to-live: 1800000 #设置缓存数据的过期时间
mybatis-plus:configuration:#在映射实体或者属性时,将数据库中表名和字段名中的下划线去掉,按照驼峰命名法映射map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImplglobal-config:db-config:id-type: ASSIGN_ID
reggie:path: D:\img\

3 前后端分离

3.1 问题说明

  • 开发人员同时负责前端和后端代码开发,分工不明确
  • 开发效率低
  • 前后端代码混合在一个工程中,不便于管理
  • 对开发人员要求高,人员招聘困难

3.2 前后端分离开发

3.2.1 介绍

前后端分离开发,就是在项目开发过程中,对于前端代码的开发由专门的前端开发人员负责,后端代码则由后端开发人员负责,这样可以做到分工明确、各司其职,提高开发效率,前后端代码并行开发,可以加快项目开发进度。
目前,前后端分离开发方式已经被越来越多的公司所采用,成为当前项目开发的主流开发方式。
前后端分离开发后,从工程结构上也会发生变化,即前后端代码不再混合在同一个maven工程中,而是分为前端工程(部署到Nginx)和后端工程(部署到Tomcat)。
在这里插入图片描述

3.2.2 开发流程

前后端分离开发后,面临一个问题,就是前端开发人员和后端开发人员如何进行配合来共同开发一个项目?
可以按照如下流程进行:
在这里插入图片描述

接口(API接口):一个http的请求,主要就是去定义:请求路径、请求方式、请求参数、响应数据

接口举例:
在这里插入图片描述

3.2.3 前端技术栈

  • 开发工具
    • Visual Studio Code
    • hbuilder .
  • 技术框架
    • nodejs
    • VUE
    • ElementUI
    • mock(测试数据)
    • webpack (前端打包工具)

3.3 Yapi

3.3.1 介绍

  • YApi是高效、易用、功能强大的api管理平台,旨在为开发、产品、测试人员提供更优雅的接口管理服务。可以帮助开发者轻松创建、发布、维护API,YApi 还为用户提供了优秀的交互体验,开发人员只需利用平台提供的接口数据写入工具以及简单的点击操作就可以实现接口的管理。
  • YApi让接口开发更简单高效,让接口的管理更具可读性、可维护性,让团队协作更合理。
  • 要使用YApi,需要自己进行部署。部署参考文章:YAPI 部署从零到一
  • 源码地址:http://github.com/YMFE/yapi

3.3.2 使用

使用YApi可以执行操作:添加项目,添加分类,添加接口,编辑接口,查看接口

3.4 Swagger

3.4.1 介绍

使用Swagger你只需要按照它的规范去定义接口及接口相关的信息,再通过Swagger衍生出来的一系列项目和工具,就可以做到生成各种格式的接口文档,以及在线接口调试页面等等。
官网:https://swagger.io/
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。

<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.2</version>
</dependency>

3.4.2 使用方式

操作步骤:

  1. 导入knife4j的maven坐标
<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId><version>3.0.2</version>
</dependency>
  1. 导入knife4j相关配置类-WebMvcConfig
@Slf4j
@Configuration
@EnableSwagger2
@EnableKnife4j
public class WebMvcConfig extends WebMvcConfigurationSupport {/*** 设置静态资源映射* @param registry*/@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {log.info("开始进行静态资源映射...");registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");}/*** 扩展mvc框架的消息转换器* @param converters*/@Overrideprotected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {log.info("扩展消息转换器...");//创建消息转换器对象MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();//设置对象转换器,底层使用Jackson将Java对象转为jsonmessageConverter.setObjectMapper(new JacksonObjectMapper());//将上面的消息转换器对象追加到mvc框架的转换器集合中converters.add(0,messageConverter);}@Beanpublic Docket createRestApi() {// 文档类型return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().apis(RequestHandlerSelectors.basePackage("com.zs.reggie.controller")).paths(PathSelectors.any()).build();}private ApiInfo apiInfo() {return new ApiInfoBuilder().title("瑞吉外卖").version("1.0").description("瑞吉外卖接口文档").build();}
}
  1. 设置静态资源,否则接口文档页面无法访问-WebMvcConfig
/*** 设置静态资源映射* @param registry*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {log.info("开始进行静态资源映射...");registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
}
  1. 在LoginCheckFilter中设置不需要处理的请求路径
    在这里插入图片描述

3.4.3 常用注解

在这里插入图片描述
加入注解后的swagger文档:
在这里插入图片描述

3.5 项目部署

3.5.1 部署架构

在这里插入图片描述

3.5.2 部署环境说明

  • 服务器A:192.168.1.189

    • Nginx:部署前端项目、配置反向代理
    • Mysql:主从复制结构中的主库
  • 服务器B:192.168.1.190

    • jdk:运行Java项目
    • git:版本控制工具
    • maven:项目构建工具
    • jar:Spring Boot项目打成jar包基于内置Tomcat运行
    • Mysql:主从复制结构中的从库
  • 服务器C:127.0.0.1

    • Redis:缓存中间件

3.5.3 部署前端项目

  1. 在服务器A中安装Nginx,将dist目录上传到Nginx的html目录下,dist目录是前端打包后的目录
    在这里插入图片描述在这里插入图片描述
  2. 修改Nginx配置文件nginx.conf
    在这里插入图片描述
    访问192.168.1.189,点击登录:
    在这里插入图片描述
    访问的 192.168.112.100
    反向代理的结果 :192.168.112.100/api/employee/login
    反向代理URL重写 :/api/employee/login ---->/employee/login
    后端正常访问是 192.168.137.1:8080/employee/login

3.5.4 部署后端项目

  1. 在服务器B中安装jdk、git、 maven、MySQL,使用git clone命令将git远程仓库的代码克隆下来
    在这里插入图片描述
  2. 将reggiesStart.sh文件上传到服务器B,通过chmod命令设置执行权限
    在这里插入图片描述
  3. 执行脚本文件,自动部署项目

注意:如果在Linux下部署记得修改一下yml文件下配置的图片的位置为path: /usr/local/img/

相关文章:

瑞吉外卖项目----(2)缓存优化

1 缓存优化 1.0 问题说明 1.1 环境搭建 将项目推送到远程仓库里&#xff0c;教程在git 提交远程仓库前建议取消代码检查 创建新的分支v1.0&#xff08;用于实现缓存优化&#xff09;并推送到远程仓库 1.1.1 maven坐标 导入spring-data-redis的maven坐标&#xff1a; &l…...

c++ http url encode decode

在C++中,可以使用以下方法对URL进行编码和解码: URL编码:#include <iostream> #include <string> #include <sstream> #include <iomanip>std::string urlEncode...

@vue/composition-api功能介绍

前言 vue/composition-api 是通过一个插件的方式,为 Vue2&#xff08;2.7自带&#xff0c;2.6及以下可用&#xff09; 提供类似 Vue3 composition API 的函数式编程能力。它的实现思路主要有: 1、提供组合式函数,在函数内部追踪响应性依赖。 2、将组合产生的响应式状态保存到…...

WebSocket整合直播

由于浏览器不支持对于rtmp协议推拉流&#xff0c;所以需要后台对传输的数据进行处理&#xff0c;将数据转接&#xff0c;为了实现其实时性&#xff0c;使用websocket将数据传输 先使用obs和vlc测试正常的推拉流是否正常 然后在跑本地后台传输视频 使用JavaCV技术传输音视频 …...

【Linux】IO 篇:文件调用原理,文件描述符,FILE的内涵,解析重定向,理解缓冲区

文章目录 一、系统调用接口二、文件调用1. 文件描述符 fd2. 文件调用原理3. FILE 三、重定向dup2 四、缓冲区简易 FILE 的代码实现 文件被加载之前&#xff0c;被存在磁盘上&#xff0c;操作文件&#xff0c;文件的部分内容则会被调度到 内存中。 要分析文件&#xff0c;我们也…...

力扣:47. 全排列 II(Python3)

题目&#xff1a; 给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列。 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 链接&#xff1a;力扣 示例&#xff1a; 示例 1&#xff1a; 输入&#xff1a;nums [1,1,2] 输出&#xff1a;[…...

Android uart-修改串口节点名

需求: 应客户软件的需求,需要将Android系统里面的/dev/ttyS3节点名称修改为/dev/ttyS9; 实现: 1、判断 driver->name是否为"ttyS",index是否为3,如果是的话替换为ttyS9; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c old mode 100644 new …...

【HarmonyOS】键盘遮挡输入框时,实现输入框显示在键盘上方

【关键字】 harmonyOS、键盘遮挡input&#xff0c;键盘高度监听 【写在前面】 在使用API6、API7开发HarmonyOS应用时&#xff0c;常出现页面中需要输入input&#xff0c;但是若input位置在页面下方&#xff0c;在input获取焦点的时候&#xff0c;会出现软键盘挡住input情况&a…...

day19-二叉树的最大最小深度

二叉树的最大/最小深度 给定一个二叉树 root &#xff0c;返回其最大/小深度。 二叉树的 最大/小深度 是指从根节点到最远/近叶子节点的最长路径上的节点数。 思路 求最大深度比较简单&#xff0c;我们先解决最大深度。 最大深度 递归 class Solution { public:int maxD…...

Ansible-roles

Ansible-roles 一、roles作用 把playbook剧本里的各个play看作为角色&#xff0c;将各个角色的tasks任务、vars变量、templates模板、files文件等内容放置到角色的目录中统一管理&#xff0c;需要的时候可在playbook中直接使用roles调用&#xff0c;所以roles可以实现playboo…...

NullPointerException导致手机重启案例分析

和你一起终身学习&#xff0c;这里是程序员Android 经典好文推荐&#xff0c;通过阅读本文&#xff0c;您将收获以下知识点: 一、 Framework 层对象 空指针导致手机重启。二、解决方案&#xff0c;规避空指针三、Telecom APK 控制导致的重启举例 一、 Framework 层对象 空指针导…...

JAVA 反编译工具

Releases deathmarine/Luyten GitHub 安装exe 打开拖入文件即可...

(AcWing)分组背包问题

有 N 组物品和一个容量是 V 的背包。 每组物品有若干个&#xff0c;同一组内的物品最多只能选一个。 每件物品的体积是 vij&#xff0c;价值是 wij&#xff0c;其中 i 是组号&#xff0c;j 是组内编号。 求解将哪些物品装入背包&#xff0c;可使物品总体积不超过背包容量&…...

JSP项目国际化词条统计

国际化字条匹配并导出为excel格式 需求 将jsp页面里的key值&#xff0c;就是<spring:message code"gsyezer_Single_crystal"/>里的gsyezer_Single_crystal。和对应的字条对应上&#xff0c;并以excel表格形式输出。 jsp页面key值示例 <label for"&…...

Java课题笔记~ MyBatis缓存

为了减少重复查询给数据库带来的压力&#xff0c;MyBatis提供了缓存机制&#xff0c;这种机制能够缓存查询的结果&#xff0c;避免重复的查询。 MyBatis提供了两种缓存方式&#xff1a; 一种为针对于SqlSession的缓存【默认开启】 另一种为针对于全局的缓存【手动开启】 一…...

数据结构--循环队列、链队

基础知识 //循环队列数据结构 typedef struct { QElemType data[MaxQSize];//数据域 int front,rear; //队头队尾指针 }SqQueue; //链队结点数据结构 typedef struct QNode { int data;//数据域 struct QNode* next;//指针域 }QNode, * QueuePtr; typedef struct { struct Q…...

hbuilderx主题色分享-github风格

效果 步骤 hbuilderx总共有三种主题&#xff0c;绿柔主题Default,酷黑主题Monokai,雅黑主题Atom One Dark,修改主题色是基于三种主题之一的&#xff0c;不能直接创建一个新主题&#xff0c;比如下方配置是基于Atom One Dark(对象名为[Atom One Dark])&#xff0c;则当前hbuild…...

【C++】类与对象(1)

文章目录 前言一、什么是类1.类的定义2.类的访问限定符3.类的作用域 二、类的实例化三、类对象的存储方式四、this指针总结 前言 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题。C是基于面向对象的&#x…...

Java课题笔记~ MyBatis核心配置

一、核心配置文件概览 MyBatis配置文件中有MyBatis框架的核心配置&#xff0c;负责对MyBatis进行全局管理。它包含许多控制MyBatis功能的重要元素。 <configuration><!--设置配置文件--><properties><property name"" value""/>…...

从0开始自学网络安全(黑客)

前言 黑客技能是一项非常复杂和专业的技能&#xff0c;需要广泛的计算机知识和网络安全知识。你可以参考下面一些学习步骤&#xff0c;系统自学网络安全。 在学习之前&#xff0c;要给自己定一个目标或者思考一下要达到一个什么样的水平&#xff0c;是学完找工作&#xff08;…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

深度学习水论文:mamba+图像增强

&#x1f9c0;当前视觉领域对高效长序列建模需求激增&#xff0c;对Mamba图像增强这方向的研究自然也逐渐火热。原因在于其高效长程建模&#xff0c;以及动态计算优势&#xff0c;在图像质量提升和细节恢复方面有难以替代的作用。 &#x1f9c0;因此短时间内&#xff0c;就有不…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

[USACO23FEB] Bakery S

题目描述 Bessie 开了一家面包店! 在她的面包店里&#xff0c;Bessie 有一个烤箱&#xff0c;可以在 t C t_C tC​ 的时间内生产一块饼干或在 t M t_M tM​ 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC​,tM​≤109)。由于空间…...

密码学基础——SM4算法

博客主页&#xff1a;christine-rr-CSDN博客 ​​​​专栏主页&#xff1a;密码学 &#x1f4cc; 【今日更新】&#x1f4cc; 对称密码算法——SM4 目录 一、国密SM系列算法概述 二、SM4算法 2.1算法背景 2.2算法特点 2.3 基本部件 2.3.1 S盒 2.3.2 非线性变换 ​编辑…...

基于stm32F10x 系列微控制器的智能电子琴(附完整项目源码、详细接线及讲解视频)

注&#xff1a;文章末尾网盘链接中自取成品使用演示视频、项目源码、项目文档 所用硬件&#xff1a;STM32F103C8T6、无源蜂鸣器、44矩阵键盘、flash存储模块、OLED显示屏、RGB三色灯、面包板、杜邦线、usb转ttl串口 stm32f103c8t6 面包板 …...