Redis的BitMap实现分布式布隆过滤器
布隆过滤器(Bloom Filter)是一种高效的概率型数据结构,用于判断一个元素是否属于一个集合。它通过使用哈希函数和位数组来存储和查询数据,具有较快的插入和查询速度,并且占用空间相对较少。
引入依赖
<!--切面-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</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><version>${mysql.version}</version>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version>
</dependency>
properties配置
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/itcast?serverTimezone=GMT%2B8&useUnicode=true&logger=Slf4JLogger&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root123
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.pool-name=HikariCPDatasource
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=180000
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.auto-commit=true
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.connection-test-query=SELECT 1
mybatis-plus.configuration.log-impl= org.apache.ibatis.logging.stdout.StdOutImpl
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.timeout=10s
spring.redis.password=123
自定义注解
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 此注解可作用于控制器方法,或者服务类方法** 使用示例,在目标方法上添加如下注解* <pre>* BitMap(key = "user", id = "#id")* </pre>**/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface BitMap {/*** <p>同一类集合的唯一标识符 商品表、订单表分别设置不同key</p>*/String key();/*** 支持{@code SPEL}表达式* 含义是以被调用方法参数<code>id</code>的值作为主键ID*/String id() default "#id";
}
切面
import com.example.demo.annotation.BitMap;
import com.example.demo.util.ParserUtils;
import com.example.demo.util.RedisBitMapUtils;
import com.example.demo.util.ResponseResult;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.TreeMap;/*** Redis BitMap AOP**/
@Aspect
@Component
public class BitMapAspect {private static final Logger logger = LoggerFactory.getLogger(BitMapAspect.class);@Resourceprivate RedisBitMapUtils redisBitMapUtils;@Pointcut("@annotation(com.example.demo.annotation.BitMap)")public void aspect() {}@Around("aspect()")public Object doAround(ProceedingJoinPoint point) throws Throwable {// 通过 point 对象获取方法签名信息。MethodSignature signature = (MethodSignature) point.getSignature();// 通过方法签名获取当前方法对象。Method method = signature.getMethod();// 获取当前方法上的 BitMap 注解。BitMap annotation = method.getAnnotation(BitMap.class);// 获取方法参数名和参数值的映射关系,并将结果保存到TreeMap中。TreeMap<String, Object> map = ParserUtils.createTreeMap(point, signature);// 从参数映射中获取 id 参数对应的值。String idString = ParserUtils.parse(annotation.id(), map);if (idString != null) {long id = Long.parseLong(idString);if (redisBitMapUtils.isPresent(annotation.key(), id)) {return point.proceed();} else {logger.info(String.format("当前主键ID{%d}不存在", id));return method.getReturnType().equals(ResponseResult.class) ? ResponseResult.okResult() : null;}}throw new RuntimeException("主键ID解析不正确,请按照参考格式书写");}}
RedisBitMap工具类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;/*** {@link RedisBitMapUtils}工具类*/
@Component
public class RedisBitMapUtils {private static final Logger logger = LoggerFactory.getLogger(RedisBitMapUtils.class);@Resourceprivate StringRedisTemplate stringRedisTemplate ;ValueOperations<String, String> opsForValue;@PostConstructpublic void init() {opsForValue= stringRedisTemplate.opsForValue();}/*** 该方法可以方便地将一个集合中的每个元素根据给定的映射函数进行转换,* 并返回一个新的列表。如果集合为空或为null,则返回一个空列表。* @param list* @param action* @return* @param <T>* @param <R>*/public <T, R> List<R> toList(final Collection<T> list, final Function<? super T, ? extends R> action) {Objects.requireNonNull(action);if (Objects.nonNull(list)) {return list.stream().map(action).collect(Collectors.toList());}return Collections.emptyList();}/*** <p>本方法在首次初始化以{@code key}为参数的BitMap时执行</p>* <p>首先删除Key 然后重新构建BitMap</p>** @param <T> 主键类型* @param key 每种业务分别对应不同的Key名称* @param ids 主键ID*/public <T extends Serializable> void init(String key, Collection<T> ids) {remove(key);setBit(key, ids);}/*** <p>本方法在首次初始化以{@code key}为参数的BitMap时执行</p>* <p>首先删除Key 然后重新构建BitMap</p>** @param key 每种业务分别对应不同的Key名称* @param list 实体类对象集合* @param action 主键列(方法引用表示)* @param <T> 实体类泛型* @param <R> 主键列泛型*/public <T, R extends Serializable> void init(String key, Collection<T> list, Function<T, R> action) {List<R> ids = toList(list, action);init(key, ids);}/*** 检查当前主键ID在Redis BitMap中是否存在 如果存在则执行函数式回调** @param key 每种业务分别对应不同的Key名称* @param id 主键ID* @return {@code R}实例*/public <T extends Serializable, R> R ifPresent(String key, T id, Function<T, R> action) {if (getBit(key, id)) {return action.apply(id);}return null;}/*** 检查当前主键ID在Redis BitMap中是否存在 如果存在则执行函数式回调** @param key 每种业务分别对应不同的Key名称* @param id 主键ID* @return {@code R}实例*/public <T extends Serializable, R> R ifPresent(String key, T id, Supplier<R> supplier) {if (getBit(key, id)) {return supplier.get();}return null;}/*** 检查当前主键ID在Redis BitMap中是否存在 如果存在则返回<code>true</code>** @param key 每种业务分别对应不同的Key名称* @param id 主键ID* @return 如果存在则返回<code>true</code>*/public <T extends Serializable> boolean isPresent(String key, T id) {return getBit(key, id);}/*** 检查当前主键ID在Redis BitMap中是否存在 如果存在则返回<code>true</code>* 本方法是{@link RedisBitMapUtils#getBit(String, Serializable)}的别名方法 方便对外调用** @param key 每种业务分别对应不同的Key名称* @param id 主键ID* @return 如果存在则返回<code>true</code>*/public <T extends Serializable> boolean checkId(String key, T id) {return getBit(key, id);}/*** 检查当前主键ID(集合)在Redis BitMap中是否存在 只返回存在的主键ID* 本方法是{@link RedisBitMapUtils#getBit(String, Serializable)}的别名方法 方便对外调用** @param key 每种业务分别对应不同的Key名称* @param ids 主键ID* @return 返回存在的主键ID*/public <T extends Serializable> List<T> checkIds(String key, Collection<T> ids) {return ids.stream().filter(e -> checkId(key, e)).collect(Collectors.toList());}/*** 向Redis BitMap中保存主键ID** @param key 每种业务分别对应不同的Key名称* @param id 主键ID*/public <T extends Serializable> void setBit(String key, T id) {ifOffsetValid(Objects.hash(id), e -> opsForValue.setBit(key, e, true));}/*** 向Redis BitMap中批量保存主键ID** @param <T> 主键类型* @param key 每种业务分别对应不同的Key名称* @param ids 主键ID*/public <T extends Serializable> void setBit(String key, Collection<T> ids) {ids.forEach(id -> ifOffsetValid(Objects.hash(id), e -> opsForValue.setBit(key, e, true)));}/*** 检查当前主键ID在Redis BitMap中是否存在 如果存在则返回<code>true</code>** @param key 每种业务分别对应不同的Key名称* @param id 主键ID* @return 如果存在则返回<code>true</code>*/public <T extends Serializable> boolean getBit(String key, T id) {return ifOffsetValid(Objects.hash(id), e -> opsForValue.getBit(key, e));}/*** 从Redis BitMap中删除当前主键ID** @param key 每种业务分别对应不同的Key名称* @param id 主键ID*/public <T extends Serializable> void removeBit(String key, T id) {ifOffsetValid(Objects.hash(id), e -> opsForValue.setBit(key, e, false));}/*** 从Redis BitMap中批量删除主键ID** @param key 每种业务分别对应不同的Key名称* @param ids 主键ID* @param <T> 主键类型*/public <T extends Serializable> void removeBit(String key, Collection<T> ids) {ids.forEach(id -> ifOffsetValid(Objects.hash(id), e -> opsForValue.setBit(key, e, false)));}/*** 将当前分类下的BitMap Key删除* 清空该Key下所有数据*/public void remove(String key) {stringRedisTemplate.delete(key);}/*** <p>检查偏移量是否合法</p>* <p>Redis字符串支持字符串最大长度512M,因此支持offset的最大值为(2^32)-1</p>** @param offset 偏移量* @param action 映射规则*/private static <N extends Number> Boolean ifOffsetValid(N offset, Function<N, Boolean> action) {Objects.requireNonNull(action);//如果ID用整型表示 那么正整数范围内所有的ID均有效 最大正整数值为2147483647 约为20亿long max = (1L << 32) - 1;if (offset.intValue() >= 0 && offset.intValue() < Integer.MAX_VALUE) {return action.apply(offset);} else {// 如果偏移量类型为长整型,或者整型范围内的最大值小于0且 offset 的值小于等于 maxif (Integer.MAX_VALUE >= 0 && offset.longValue() <= max) {return action.apply(offset);} else {logger.info(String.format("偏移量{%d}越界[0,%s],本次操作不成功!", offset.longValue(), max));return false;}}}
}
response工具类
import lombok.Data;import java.io.Serializable;
@Data
public class ResponseResult<T> implements Serializable {private Boolean success;private Integer code;private String msg;private T data;public ResponseResult() {this.success=true;this.code = HttpCodeEnum.SUCCESS.getCode();this.msg = HttpCodeEnum.SUCCESS.getMsg();}public ResponseResult(Integer code, T data) {this.code = code;this.data = data;}public ResponseResult(Integer code, String msg, T data) {this.code = code;this.msg = msg;this.data = data;}public ResponseResult(Integer code, String msg) {this.code = code;this.msg = msg;}public static ResponseResult errorResult(int code, String msg) {ResponseResult result = new ResponseResult();return result.error(code, msg);}public static ResponseResult okResult() {ResponseResult result = new ResponseResult();return result;}public static ResponseResult okResult(int code, String msg) {ResponseResult result = new ResponseResult();return result.ok(code, null, msg);}public static ResponseResult setHttpCodeEnum(HttpCodeEnum enums) {return okResult(enums.getCode(), enums.getMsg());}public static <T> ResponseResult<T> error(String message) {return new ResponseResult<T>(HttpCodeEnum.SYSTEM_ERROR.getCode(), message);}public ResponseResult<?> error(Integer code, String msg) {this.success=false;this.code = code;this.msg = msg;return this;}public ResponseResult<?> ok(Integer code, T data) {this.success=true;this.code = code;this.data = data;return this;}public ResponseResult<?> ok(Integer code, T data, String msg) {this.success=true;this.code = code;this.data = data;this.msg = msg;return this;}public static ResponseResult ok(Object data) {ResponseResult result = new ResponseResult();result.setData(data);return result;}}
controller
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.annotation.BitMap;
import com.example.demo.annotation.PreventRepeatSubmit;
import com.example.demo.mapper.StuMapper;
import com.example.demo.model.ResponseResult;
import com.example.demo.model.Student;
import com.example.demo.util.RedisBitMapUtils;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;@RestController
@RequestMapping("/test")
@Validated
public class TestController {@Resourceprivate StuMapper stuMapper;@Resourceprivate StringRedisTemplate stringRedisTemplate;private static final String BITMAP_STU="bitmap_stu";@Resourceprivate RedisBitMapUtils redisBitMapUtils;@GetMapping("init")public com.example.demo.util.ResponseResult init(){List<Student> studentList = stuMapper.selectList(new QueryWrapper<Student>());redisBitMapUtils.init(BITMAP_STU,studentList,Student::getId);return com.example.demo.util.ResponseResult.okResult();}/*** 编程式*/@GetMapping("selectStu1/{id}")@BitMap(key = BITMAP_STU,id = "#id")public com.example.demo.util.ResponseResult selectStu1(@PathVariable Integer id){return com.example.demo.util.ResponseResult.ok(stuMapper.selectById(id));}/*** 注解式*/@GetMapping("selectStu2/{id}")public com.example.demo.util.ResponseResult selectStu2(@PathVariable Integer id){if (redisBitMapUtils.getBit(BITMAP_STU,id)){return com.example.demo.util.ResponseResult.ok(stuMapper.selectById(id));}return com.example.demo.util.ResponseResult.okResult();}}
测试
初始化biemap数据,从数据库种获取所有id并导入redis的key为bitmap_stu的数据

测试数据库存在的数据id:1,走数据库获取数据

测试数据库的数据id:200,因为id为200不存在在数据库中,所以没有走数据库,减少了数据库压力
注解式也生效
达到了使用redis的bitmap实现分布式的布隆过滤器,过滤掉bitmap不存在的数据
相关文章:
Redis的BitMap实现分布式布隆过滤器
布隆过滤器(Bloom Filter)是一种高效的概率型数据结构,用于判断一个元素是否属于一个集合。它通过使用哈希函数和位数组来存储和查询数据,具有较快的插入和查询速度,并且占用空间相对较少。 引入依赖 <!--切面--&…...
【linux API分析】module_init
linux版本:4.19 module_init()与module_exit()用于驱动的加载,分别是驱动的入口与退出函数 module_init():内核启动时或动态插入模块时调用module_exit():驱动移除时调用 本篇文章介绍module_init() module_init() module_init…...
NSDT孪生编辑器助力智慧城市
技术有能力改变城市的运作方式,提高效率,为游客和居民提供更好的体验,实现更可持续的运营和更好的决策。 当今城市面临的主要挑战是什么,成为智慧城市如何帮助克服这些挑战? 我们生活在一个日益城市化的世界…...
如何优雅的实现接口统一调用
耦合问题 有些时候我们在进行接口调用的时候,比如说一个push推送接口,有可能会涉及到不同渠道的推送,以我目前业务场景为例,我做结算后端服务的,会与金蝶财务系统进行交互,那么我结算后端会涉及到多个结算…...
tomcat、nginx实现四层转发+七层代理+动静分离实验
实验环境: nginx1——20.0.0.11——客户端 静态页面: nginx2——20.0.0.21——代理服务器1 nginx3——20.0.0.31——代理服务器2 动态页面: tomcat1——20.0.0.12——后端服务器1 tomcat2——20.0.0.22——后端服务器2 实验步骤&…...
交通目标检测-行人车辆检测流量计数 - 计算机竞赛
文章目录 0 前言1\. 目标检测概况1.1 什么是目标检测?1.2 发展阶段 2\. 行人检测2.1 行人检测简介2.2 行人检测技术难点2.3 行人检测实现效果2.4 关键代码-训练过程 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 毕业设计…...
Java Excel转PDF,支持xlsx和xls两种格式, itextpdf【即取即用】
Java Excel转PDF itextpdf,即取即用 工具方法一、使用方式1、本地转换2、网络下载 二、pom依赖引入三、工具方法三、引文 本篇主要为工具方法整理,参考学习其他博主文章做了整理,方便使用。 工具方法 一、使用方式 1、本地转换 导入依赖创…...
重生奇迹mu宠物带来不一样的体验
重生奇迹mu宠物有什么作用? 全新版本中更是推出了各种宠物,在玩游戏时还可以带着宠物,一起疯狂的刷怪等等,可以为玩家带来非常不错的游戏体验,那么下面就来给大家说说各种宠物适合做什么事情。 1、强化恶魔适合刷怪 …...
【C++笔记】多态的原理、单继承和多继承关系的虚函数表、 override 和 final、抽象类、重载、覆盖(重写)、隐藏(重定义)的对比
1.final关键字 引出:设计一个不能被继承的类。有如下方法: class A { private:A(int a0):_a(a){} public:static A CreateOBj(int a0){return A(a);} protected:int _a; } //简介限制,子类构成函数无法调用父类构造函数初始化 //子类的构造…...
安装thinkphp6并使用多应用模式,解决提示路由不存在解决办法
1. 安装稳定版tp框架 composer create-project topthink/think tptp是安装完成的目录名称 ,可以根据自己需要修改。 如果你之前已经安装过,那么切换到你的应用根目录下面,然后执行下面的命令进行更新: composer update topthin…...
FPGA笔试
1、FPGA结构一般分为三部分:可编程逻辑块(CLB)、可编程I/O模块和可编程内部连线。 2 CPLD的内部连线为连续式布线互连结构,任意一对输入、输出端之间的延时是固定 ;FPGA的内部连线为分段式布线互连结构,各…...
Pytorch:cat、stack、squeeze、unsqueeze的用法
Pytorch:cat、stack、squeeze、unsqueeze的用法 torch.cat 在指定原有维度上链接传入的张量,所有传入的张量都必须是相同形状 torch.cat(tensors, dim0, *, outNone) → Tensor tensor:相同形状的tensor dim:链接张量的维度,不能超过传入张…...
聊聊HttpClient的RedirectStrategy
序 本文主要研究一下HttpClient的RedirectStrategy RedirectStrategy org/apache/http/client/RedirectStrategy.java public interface RedirectStrategy {/*** Determines if a request should be redirected to a new location* given the response from the target ser…...
【1day】复现宏景OA KhFieldTree接口 SQL注入漏洞
注:该文章来自作者日常学习笔记,请勿利用文章内的相关技术从事非法测试,如因此产生的一切不良后果与作者无关。 目录 一、漏洞描述 二、资产测绘 三、漏洞复现 四、漏洞修复 一、漏洞描述 宏景OA是一款基于...
同为科技TOWE智能PDU引领数据中心机房远控用电安全高效
随着数据中心的环境变得更加动态和复杂,许多数据中心都在对数据中心管理人员施加压力,要求提高可用性,同时降低成本,提升效率。新一代高密度服务器和网络设备的投入使用,增加了对更高密度机架的需求,并对整…...
支付成功后给指定人员发送微信公众号消息
支付成功后给指定人员(导购)发送微信公众号消息 微信openid已录入数据库表 调用后台接口发送消息接口调用代码如下: //----add by grj 20231017 start //订单支付成功发送微信公众号消息$.ajax({url:http://www.menggu100.com:7077/strutsJsp…...
漏洞复现--安恒明御安全网关文件上传
免责声明: 文章中涉及的漏洞均已修复,敏感信息均已做打码处理,文章仅做经验分享用途,切勿当真,未授权的攻击属于非法行为!文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直…...
简单的对称加密
异或 异或算法的好处便是数A和数B异或后,把结果再和数A异或便可得到B,或者和数B异或可重新得到数据A。利用异或的这个特性可简单实现数据的加密和解密算法。 恺撒密码 恺撒密码的替换方法是通过排列明文和密文字母表,密文字母表示通过将明…...
vue源码笔记之——响应系统
vue是一种声明式范式编程,使用vue者只需要告诉其想要什么结果,无需关心具体实现(vue内部做了,底层是利用命令式范式) 1. reactive为什么只能操作对象,对于基本数据类型,需要用ref? …...
Android Studio Giraffe | 2022.3.1
Android Gradle 插件和 Android Studio 兼容性 Android Studio 构建系统以 Gradle 为基础,并且 Android Gradle 插件 (AGP) 添加了几项专用于构建 Android 应用的功能。下表列出了各个 Android Studio 版本所需的 AGP 版本。 如果您的项目不受某个特定版本的 Andr…...
Unity性能优化实战:用Job System并行处理海量数据,告别主线程卡顿
Unity性能优化实战:用Job System并行处理海量数据,告别主线程卡顿 当你的游戏场景中出现成千上万的粒子在飞舞,或是数百个NPC同时进行复杂的AI决策时,是否经常遇到帧率骤降的困扰?作为Unity开发者,我们每天…...
Pixel Couplet Gen应用场景:微信小程序‘灵蛇贺岁’互动模块开发全解析
Pixel Couplet Gen应用场景:微信小程序灵蛇贺岁互动模块开发全解析 1. 项目背景与核心价值 在传统节日数字化呈现的浪潮下,我们开发了"灵蛇贺岁"微信小程序互动模块。这款基于ModelScope大模型的春联生成器,通过创新的像素游戏风…...
NVIDIA Profile Inspector终极指南:如何免费解锁显卡隐藏性能
NVIDIA Profile Inspector终极指南:如何免费解锁显卡隐藏性能 【免费下载链接】nvidiaProfileInspector 项目地址: https://gitcode.com/gh_mirrors/nv/nvidiaProfileInspector 想要让游戏运行更流畅、画面更清晰吗?NVIDIA显卡驱动中隐藏着大量可…...
C++ ODB ORM 实战指南
好的,这是一份关于在 C 中使用 ODB ORM 的指南,涵盖从基础概念到实际应用的各个方面。 1. ODB ORM 简介 对象关系映射 (ORM) 是一种编程技术,用于在面向对象的编程语言(如 C)和关系型数据库之间建立映射关系。它允许开…...
Simulink双矢量MPC实战:从郭磊磊论文到可运行的Matlab Function代码(调制模型预测控制详解)
Simulink双矢量MPC实战:从理论到代码的完整实现路径 当我在实验室第一次尝试复现郭磊磊老师那篇经典论文时,面对12种矢量组合和复杂的PWM生成逻辑,完全不知从何下手。经过三个月的反复试验和代码调试,终于摸清了从论文公式到可运行…...
RMBG-2.0镜像免配置部署:无需配置Python环境,开箱即用Web交互界面
RMBG-2.0镜像免配置部署:无需配置Python环境,开箱即用Web交互界面 你是不是也遇到过这样的烦恼?想用AI模型给图片换个背景,结果光是安装Python环境、配置依赖库就折腾了大半天,最后还可能因为版本冲突、CUDA不兼容等问…...
Python胶水代码变高性能引擎(Mojo原生编译实战手记)
第一章:Python胶水代码变高性能引擎(Mojo原生编译实战手记)Python 以其简洁语法和丰富生态成为数据科学与系统集成的“胶水语言”,但其解释执行机制常在数值计算、实时推理等场景遭遇性能瓶颈。Mojo 作为新兴的系统级编程语言&…...
自动驾驶开发必备:Vscode+Git双神器组合的隐藏技巧(含分支管理秘籍)
自动驾驶开发必备:VscodeGit双神器组合的隐藏技巧(含分支管理秘籍) 在自动驾驶开发领域,高效的代码管理和协作流程是项目成功的关键因素。随着代码库规模不断扩大,团队规模持续增长,传统的版本控制方式往往…...
vue3 diff算法中的-双端 Diff + 最长递增子序列 讲解
一句话总结 Vue3 Diff 双端比较(快速复用) 最长递增子序列(最小移动 DOM) 目的:在乱序节点中,只移动最少 DOM,实现最高效更新。1. 先搞懂:Vue3 对比 Vue2 差在哪? Vue2…...
AG-UI协议实战:构建智能体驱动的动态前端交互系统
1. AG-UI协议:智能体与前端交互的新范式 第一次听说AG-UI协议时,我正在为一个电商项目头疼——后台AI生成的商品推荐总需要手动同步到前端,代码里到处是setState和事件监听。直到发现这个协议,才明白原来智能体和前端可以像两个老…...
