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

打造Spring Boot接口护盾:防重提交与限流秘籍

打造Spring Boot接口护盾防重提交与限流秘籍Spring Boot 接口那些 “糟心事”在当今高并发的互联网应用场景下Spring Boot 作为主流的 Java 开发框架被广泛应用于构建各类后端服务。然而随着业务的不断发展和用户量的增长接口面临的挑战也日益严峻其中接口被重复提交和遭遇高并发流量便是常见的 “糟心事”。以电商下单场景为例当用户点击 “提交订单” 按钮时由于网络延迟、前端交互设计不完善等原因可能会导致用户多次点击该按钮。如果接口没有防重提交机制这将导致在数据库中生成多条重复的订单记录。这不仅会占用数据库资源还会给后续的订单处理、库存管理等环节带来极大的困扰甚至可能引发业务逻辑错误比如超卖现象严重影响用户体验和企业的正常运营。再看支付接口当用户进行支付操作时一旦接口被重复触发就会造成用户重复扣费。这对于用户来说是极其糟糕的体验可能会导致用户对平台产生信任危机进而流失用户。而对于企业而言不仅要处理用户的投诉和退款事宜还可能面临法律风险。在高并发流量场景下问题同样不容忽视。当大量用户同时访问接口时如果没有合理的限流措施接口可能会因为负载过高而响应变慢甚至直接崩溃。例如在一些热门活动期间如电商的 “双 11” 大促、限时抢购等瞬间涌入的大量请求可能会使接口不堪重负。这不仅会导致正常用户的请求无法得到及时处理还可能引发连锁反应使整个系统陷入瘫痪状态造成巨大的经济损失。由此可见接口防护对于保障系统的稳定运行、提升用户体验以及维护企业的利益至关重要。它是我们在开发 Spring Boot 应用时必须要重视和解决的问题接下来我们就一起深入探讨如何实现接口的防重提交和限流。防重提交给接口穿上 “防重铠甲”一问题剖析重复提交的 “破坏力”在电商系统中重复下单的问题尤为突出。当用户快速连续点击提交订单按钮时若接口没有防重机制数据库中会瞬间生成多条相同的订单记录。这不仅会使库存数据混乱导致商品超卖或库存显示错误还会让订单处理流程陷入混乱增加人工核对和处理的成本严重影响用户体验和商家的正常运营。支付接口的重复提交则会引发更为严重的后果。用户在支付过程中可能因网络波动等原因多次触发支付请求。如果接口不能有效防重用户账户将被重复扣费。这不仅会导致用户资金损失引发用户的不满和投诉还会对平台的信誉造成极大损害使用户对平台的安全性和可靠性产生质疑进而流失大量用户。二传统方案的 “短板”前端防重是一种常见的方式通常是在用户点击提交按钮后通过 JavaScript 代码禁用按钮或添加 loading 状态防止用户再次点击。然而这种方式存在明显的局限性。对于一些熟悉技术的用户或者恶意攻击者来说他们可以通过修改前端代码、使用浏览器插件等方式绕过前端的防重限制直接向服务器发送重复请求从而导致防重机制失效。Token 标识方案则相对复杂一些。它的原理是在服务器端生成一个唯一的 Token并将其发送给前端。前端在每次请求时携带这个 Token服务器接收到请求后验证 Token 的有效性并将其销毁。如果再次收到相同 Token 的请求则判定为重复提交。这种方式虽然安全性较高但它高度依赖前端的配合。如果前端代码出现漏洞未能正确处理 Token或者 Token 在传输过程中被窃取都可能导致防重失败。此外Token 的生成、验证和管理流程也增加了系统的复杂度需要额外的代码和资源来维护。三哈希算法我们的 “秘密武器”原理揭秘我们提出的基于哈希算法的防重方案通过对请求路径、方法和参数进行处理生成一个唯一的哈希值。这个哈希值就像请求的 “指纹”能够准确标识请求的唯一性。当请求到达服务器时首先计算其哈希值然后查询缓存中是否已存在相同的哈希值。如果存在说明该请求是重复提交直接拒绝如果不存在则将哈希值存入缓存并处理该请求。这种方式实现了后端无依赖防重无需前端进行特殊处理大大提高了防重机制的可靠性和稳定性。代码实战首先我们定义一个自定义防重注解PreventDuplicate通过该注解可以灵活配置防重时间、参与生成哈希的字段以及提示信息等。importjava.lang.annotation.*;importjava.util.concurrent.TimeUnit;Target(ElementType.METHOD)Retention(RetentionPolicy.RUNTIME)publicinterfacePreventDuplicate{// 防重复提交时间单位秒intexpire()default3;// 时间单位默认秒TimeUnittimeUnit()defaultTimeUnit.SECONDS;// 可选指定参与生成哈希的主要字段String[]field()default{};// 提示信息Stringmessage()default请勿重复提交;}接下来利用 AOP 切面实现防重逻辑。在切面中获取请求的相关信息拼接成唯一签名源计算 SHA-256 哈希值并与缓存中的值进行比对。importcn.hutool.crypto.digest.DigestUtil;importcom.example.demo.annotation.PreventDuplicate;importcom.example.demo.storage.DuplicateStorage;importcom.example.demo.storage.DuplicateStorageFactory;importcom.example.demo.util.RequestParameterUtils;importjakarta.servlet.http.HttpServletRequest;importlombok.RequiredArgsConstructor;importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.springframework.stereotype.Component;AspectComponentRequiredArgsConstructorpublicclassPreventDuplicateAspect{privatefinalHttpServletRequestrequest;privatefinalDuplicateStorageFactorystorageFactory;Around(annotation(preventDuplicate))publicObjecthandle(ProceedingJoinPointjoinPoint,PreventDuplicatepreventDuplicate)throwsThrowable{// 获取请求方法Stringmethodrequest.getMethod();// 获取请求URIStringurirequest.getRequestURI();// 获取请求参数StringparamsRequestParameterUtils.getAllParamsAsString(joinPoint,preventDuplicate.field());// 拼接唯一签名源StringsignSourcemethod:uri:params;// 计算哈希值StringkeyDigestUtil.sha256Hex(signSource);DuplicateStoragestoragestorageFactory.getStorage();if(storage.exists(key)){thrownewRuntimeException(preventDuplicate.message());}storage.put(key,preventDuplicate.expire(),preventDuplicate.timeUnit());returnjoinPoint.proceed();}}在上述代码中Around(annotation(preventDuplicate))表示对标记了PreventDuplicate注解的方法进行环绕增强。在增强逻辑中首先获取请求的方法、URI 和参数拼接成唯一的签名源然后使用DigestUtil.sha256Hex方法计算签名源的 SHA-256 哈希值。接着从DuplicateStorageFactory获取DuplicateStorage实例检查缓存中是否已存在该哈希值。如果存在抛出异常提示用户请勿重复提交如果不存在将哈希值存入缓存并放行请求执行目标方法。我们还需要设计缓存存储抽象与实现支持 Redis 和 Caffeine 等多种缓存方式以满足不同的业务需求。importjava.util.concurrent.TimeUnit;publicinterfaceDuplicateStorage{booleanexists(Stringkey);voidput(Stringkey,intexpire,TimeUnittimeUnit);}importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.stereotype.Component;importjavax.annotation.Resource;importjava.util.concurrent.TimeUnit;ComponentpublicclassRedisStorageimplementsDuplicateStorage{ResourceprivateRedisTemplateString,ObjectredisTemplate;Overridepublicbooleanexists(Stringkey){returnBoolean.TRUE.equals(redisTemplate.hasKey(key));}Overridepublicvoidput(Stringkey,intexpire,TimeUnittimeUnit){redisTemplate.opsForValue().set(key,1,expire,timeUnit);}}importcom.github.benmanes.caffeine.cache.Cache;importcom.github.benmanes.caffeine.cache.Caffeine;importorg.springframework.stereotype.Component;importjava.util.concurrent.TimeUnit;ComponentpublicclassCaffeineStorageimplementsDuplicateStorage{privatefinalCacheString,StringcacheCaffeine.newBuilder().expireAfterWrite(3,TimeUnit.SECONDS).maximumSize(10000).build();Overridepublicbooleanexists(Stringkey){returncache.getIfPresent(key)!null;}Overridepublicvoidput(Stringkey,intexpire,TimeUnittimeUnit){cache.put(key,1);}}在缓存存储抽象与实现部分首先定义了DuplicateStorage接口其中包含exists和put两个方法分别用于检查缓存中是否存在指定键的缓存值以及将键值对存入缓存并设置过期时间。然后实现了RedisStorage类通过注入RedisTemplate来操作 Redis 缓存利用redisTemplate.hasKey方法检查键是否存在使用redisTemplate.opsForValue().set方法将键值对存入 Redis并设置过期时间。同时实现了CaffeineStorage类使用 Caffeine 缓存库创建一个本地缓存实例cache通过cache.getIfPresent方法检查缓存中是否存在指定键的值通过cache.put方法将键值对存入缓存。性能验证为了验证哈希算法防重方案的性能我们搭建了一个模拟高并发的测试环境使用专业的性能测试工具模拟大量用户同时发送请求。测试结果显示即使在高并发场景下生成哈希值的耗时极短几乎可以忽略不计对接口的响应时间和吞吐量影响极小。同时由于哈希值的唯一性能够准确识别重复请求有效避免了重复提交问题的发生证明了该方案在性能和可靠性方面的出色表现。限流为接口流量装上 “调节阀”一限流的 “必要性”在高并发场景下系统面临的流量压力是巨大的。以电商 “双 11” 大促为例在活动开始的瞬间大量用户同时涌入平台对商品查询、下单、支付等接口发起请求流量可能会瞬间达到平时的数十倍甚至数百倍。如果没有限流措施这些接口可能会因为无法承受如此巨大的流量而响应变慢甚至直接崩溃。一旦接口崩溃不仅会导致用户无法正常使用平台功能造成糟糕的用户体验还会给企业带来巨大的经济损失。此外大量的无效请求或恶意请求也可能会耗尽系统资源影响正常业务的运行。因此限流对于保护系统资源、防止系统因流量过大而崩溃具有至关重要的作用。二常见限流算法 “大比拼”固定窗口算法固定窗口算法是一种简单直观的限流算法。它将时间划分为固定长度的窗口例如 1 分钟在每个窗口内统计请求数量。当请求到达时检查当前窗口内的请求数是否超过设定的阈值。如果未超过则允许请求通过并将请求数加 1如果超过则拒绝请求。例如设定每分钟的请求阈值为 100在第一个窗口内前 99 个请求都能正常通过当第 100 个请求到达时该窗口内的请求数达到阈值后续请求将被拒绝直到下一个窗口开始。这种算法的优点是实现简单易于理解和部署。然而它存在明显的缺陷当请求集中在窗口切换的临界点时可能会出现两倍流量的突发情况。例如在 0:59:59 到 1:00:00 这一瞬间可能会有两个窗口的请求同时到达导致系统在极短时间内承受巨大的流量压力。滑动窗口算法滑动窗口算法是对固定窗口算法的改进。它将时间窗口划分为多个更小的子窗口每个子窗口都有自己的计数器。随着时间的推移窗口像幻灯片一样向前滑动每当有新的子窗口进入当前窗口范围就将其计数器加入总请求数统计同时移除过期子窗口的计数器。例如将 1 分钟的时间窗口划分为 60 个 1 秒的子窗口每个子窗口记录该秒内的请求数。当时间从 1 秒滑动到 2 秒时将第 2 秒的子窗口计数器加入总请求数并移除第 1 秒的子窗口计数器。这样可以更精确地控制流量避免固定窗口算法在临界点的双倍流量问题。滑动窗口算法的优点是限流精度高能够更平滑地控制流量有效避免突发流量对系统的冲击。但它的实现相对复杂需要维护多个子窗口的计数器对系统资源的消耗也相对较大。漏桶算法漏桶算法的原理类似于一个底部有小孔的水桶。请求就像水一样流入漏桶而漏桶以固定的速率将水请求流出。当桶满时新流入的水请求将被丢弃。例如设定漏桶的容量为 100流出速率为每秒 10 个请求。当请求以每秒 20 个的速率流入时漏桶将以每秒 10 个的速率处理请求多余的 10 个请求将被丢弃。这种算法的优点是能够严格控制流量的输出速率保证系统处理请求的稳定性适用于对流量稳定性要求较高的场景如数据库写入速率控制、网络传输速率限制等。然而它的缺点是无法应对突发流量因为无论请求流量如何变化漏桶始终以固定速率处理请求可能会导致突发流量下的请求被大量丢弃影响用户体验。令牌桶算法令牌桶算法是目前应用较为广泛的一种限流算法。系统以固定的速率向令牌桶中生成令牌每个请求在处理之前需要从桶中获取一个令牌。如果桶中有足够的令牌则请求可以通过并消耗一个令牌如果桶中没有令牌则请求被拒绝。例如设定令牌生成速率为每秒 10 个令牌桶容量为 100。当请求到达时先检查桶中是否有令牌若有则获取一个令牌并处理请求若没有则拒绝请求。令牌桶算法的优点是允许一定程度的突发流量因为在桶中有足够令牌的情况下请求可以快速通过。同时它也能保证系统在长期内的平均请求处理速率符合设定的限制适用于大多数需要限制流量的场景如 API 接口限流、用户行为控制等。不过它的实现相对复杂一些需要维护令牌桶的状态和令牌的生成逻辑。三基于 AOP 和 Redis 的限流实现技术选型理由选择 AOP面向切面编程实现限流逻辑是因为它能够以一种低侵入式的方式将限流功能切入到业务代码中。通过定义切面我们可以在不修改业务方法核心逻辑的前提下对方法的调用进行统一的限流处理。这使得限流逻辑与业务逻辑分离提高了代码的可维护性和可扩展性。例如当我们需要调整限流策略时只需在切面中修改相关代码而无需在每个业务方法中进行修改。选择 Redis 实现分布式环境下的原子性计数和状态存储主要是基于 Redis 的高性能和原子操作特性。在分布式系统中多个服务实例可能同时处理请求需要一个可靠的分布式存储来保证限流计数的准确性和一致性。Redis 的单线程模型保证了原子操作的特性例如 INCR原子递增和 EXPIRE设置过期时间等命令能够确保在高并发场景下对限流计数器的操作不会出现竞态条件。同时Redis 的高可用性和集群模式也能够满足分布式系统对稳定性和扩展性的要求。代码实现步骤首先定义自定义限流注解RateLimiter通过该注解可以灵活配置限流的相关参数如限流唯一标识、时间窗口、允许请求次数等。importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;Retention(RetentionPolicy.RUNTIME)Target(ElementType.METHOD)publicinterfaceRateLimiter{// 限流唯一标识支持SpELStringkey();// 时间窗口秒inttime()default60;// 允许请求次数intcount()default10;}接下来实现 AOP 切面类RateLimiterAspect在切面中通过Around注解拦截标记了RateLimiter注解的方法。在拦截逻辑中首先解析 SpEL 表达式生成动态的限流 Key然后使用 Redis 的opsForValue().increment方法对限流计数器进行原子递增操作。如果是首次访问计数器为 1则设置该 Key 的过期时间以实现时间窗口的限流。如果当前请求数超过了设定的允许请求次数则抛出限流异常拒绝请求否则放行请求执行目标方法。importorg.aspectj.lang.ProceedingJoinPoint;importorg.aspectj.lang.annotation.Around;importorg.aspectj.lang.annotation.Aspect;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.expression.ExpressionParser;importorg.springframework.expression.spel.standard.SpelExpressionParser;importorg.springframework.expression.spel.support.StandardEvaluationContext;importorg.springframework.stereotype.Component;AspectComponentpublicclassRateLimiterAspect{AutowiredprivateStringRedisTemplateredisTemplate;AutowiredprivateExpressionParserparser;Around(annotation(rateLimiter))publicObjectaround(ProceedingJoinPointjoinPoint,RateLimiterrateLimiter)throwsThrowable{// 解析SpEL动态生成Key例如user_123StringkeyparseSpEL(joinPoint,rateLimiter.key());inttimerateLimiter.time();intcountrateLimiter.count();// Redis计数器自增LongcurrentredisTemplate.opsForValue().increment(key,1);if(current1){// 首次设置过期时间redisTemplate.expire(key,time,TimeUnit.SECONDS);}if(currentcount){thrownewRateLimitException(请求过于频繁请稍后再试);}returnjoinPoint.proceed();}privateStringparseSpEL(ProceedingJoinPointjoinPoint,StringspEL){// 解析方法参数、注解等生成动态KeyStandardEvaluationContextcontextnewStandardEvaluationContext();Object[]argsjoinPoint.getArgs();context.setVariable(args,args);returnparser.parseExpression(spEL).getValue(context,String.class);}}在上述代码中Around(annotation(rateLimiter))表示对标记了RateLimiter注解的方法进行环绕增强。在增强逻辑中parseSpEL方法用于解析 SpEL 表达式根据方法参数生成动态的限流 Key。然后通过redisTemplate.opsForValue().increment(key, 1)对限流 Key 对应的计数器进行原子递增操作并返回递增后的计数值。如果计数值为 1说明是首次访问使用redisTemplate.expire(key, time, TimeUnit.SECONDS)设置该 Key 的过期时间为time秒实现时间窗口的限流。最后判断当前计数值是否超过允许的请求次数count如果超过则抛出RateLimitException异常提示用户请求过于频繁否则通过joinPoint.proceed()放行请求执行目标方法。为了进一步优化性能减少 Redis 的网络开销和保证操作的原子性我们使用 Lua 脚本实现令牌桶算法。以下是 Lua 脚本的示例代码-- KEYS[1]: 限流Key-- ARGV[1]: 时间窗口秒-- ARGV[2]: 允许的最大请求数localcurrentredis.call(GET,KEYS[1])ifcurrentfalsethenredis.call(SET,KEYS[1],1,EX,ARGV[1])return1elselocalnewCountredis.call(INCR,KEYS[1])iftonumber(newCount)tonumber(ARGV[2])thenreturn-1-- 超出限制elsereturnnewCountendend在 Java 中调用 Lua 脚本的代码如下importorg.springframework.data.redis.core.DefaultRedisScript;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.scripting.support.ResourceScriptSource;importorg.springframework.stereotype.Component;importjava.util.Collections;ComponentpublicclassLuaRateLimiter{privatefinalStringRedisTemplateredisTemplate;privatestaticfinalDefaultRedisScriptLongRATE_LIMIT_SCRIPTnewDefaultRedisScript();static{RATE_LIMIT_SCRIPT.setScriptSource(newResourceScriptSource(newClassPathResource(rate_limiter.lua)));RATE_LIMIT_SCRIPT.setResultType(Long.class);}publicLuaRateLimiter(StringRedisTemplateredisTemplate){this.redisTemplateredisTemplate;}publicbooleantryAcquire(Stringkey,inttime,intcount){LongresultredisTemplate.execute(RATE_LIMIT_SCRIPT,Collections.singletonList(key),String.valueOf(time),String.valueOf(count));returnresult!nullresult!-1;}}在上述代码中首先定义了一个LuaRateLimiter组件用于加载和执行 Lua 脚本。在静态代码块中通过RATE_LIMIT_SCRIPT.setScriptSource方法加载位于类路径下的rate_limiter.lua脚本文件并设置脚本的返回结果类型为Long。tryAcquire方法用于尝试获取令牌它通过redisTemplate.execute方法执行 Lua 脚本传入限流 Key、时间窗口和允许的最大请求数作为参数。如果脚本执行结果不为-1说明请求未超出限制可以获取令牌返回true否则返回false表示请求被限流。注意事项和优化方向在实际应用中限流维度的选择非常重要。我们可以根据业务需求选择按 IP 地址、用户 ID、接口路径等维度进行限流。例如对于一些公共接口为了防止恶意用户频繁访问可以按 IP 地址进行限流对于一些需要保护用户隐私的接口可以按用户 ID 进行限流。同时要确保限流维度的唯一性避免出现误判或绕过限流的情况。缓存的高可用性也是需要关注的问题。在分布式环境中Redis 可能会出现单点故障。为了提高缓存的可用性可以采用 Redis 集群、哨兵模式或 Redis Cluster 等方案。这些方案能够实现自动故障转移当主节点出现故障时从节点能够自动升级为主节点保证系统的正常运行。还可以考虑结合监控系统对限流情况进行实时监控以便及时发现和处理限流异常。通过监控可以了解系统的实际流量情况根据业务需求动态调整限流阈值提高系统的性能和稳定性。例如在业务高峰期可以适当提高限流阈值以满足用户的需求在业务低谷期可以降低限流阈值节省系统资源。总结与展望在构建 Spring Boot 应用的过程中接口防护是保障系统稳定运行、提升用户体验的关键环节。通过深入剖析接口重复提交和高并发流量带来的问题我们探索了一系列行之有效的解决方案。基于哈希算法的防重提交方案利用其对请求路径、方法和参数的独特处理方式生成唯一的哈希值实现了后端无依赖防重为接口穿上了一层坚固的 “防重铠甲”有效避免了重复提交导致的数据不一致和业务逻辑混乱等问题。而限流机制则为接口流量装上了 “调节阀”。从常见的限流算法如固定窗口算法、滑动窗口算法、漏桶算法和令牌桶算法的对比分析中我们了解到它们各自的优缺点和适用场景。基于 AOP 和 Redis 的限流实现结合了 AOP 的低侵入性和 Redis 的高性能、原子操作特性通过自定义注解和切面编程灵活且高效地实现了接口限流保护系统资源防止系统因流量过大而崩溃。在实际项目中我们应根据业务需求和系统架构合理选择和应用这些接口防护技术。同时随着技术的不断发展未来接口防护技术有望在智能化、自动化和精细化方向取得更大突破。例如借助人工智能和机器学习技术实现对接口流量的实时预测和动态调整限流策略通过自动化工具和平台简化接口防护的配置和管理流程针对不同的业务场景和用户行为提供更加精细化的防重提交和限流方案进一步提升接口的安全性和稳定性。希望大家在今后的开发中重视接口防护让我们的 Spring Boot 应用更加健壮和可靠。

相关文章:

打造Spring Boot接口护盾:防重提交与限流秘籍

打造Spring Boot接口护盾:防重提交与限流秘籍 Spring Boot 接口那些 “糟心事” 在当今高并发的互联网应用场景下,Spring Boot 作为主流的 Java 开发框架,被广泛应用于构建各类后端服务。然而,随着业务的不断发展和用户量的增长&a…...

【JAVA学习思维导图】黑马程序员Java+AI智能辅助编程day03- 结构-Xmind思维导图

...

JunZi Music 2.0.5 | 聚合网易云和酷狗双音源,支持超清母带下载

JunZi Music是一款特色鲜明的听歌软件,整合了网易云音乐和酷狗音乐双音源。它允许用户导入网易云和酷狗的歌单,不过歌单属于本地存储,卸载软件后会消失;同时具备收藏歌曲功能,该功能通过服务器保存,可跟随账…...

2026年论文AI率从85%降到8%全记录:踩了3个坑才搞定

2026年论文AI率从85%降到8%全记录:踩了3个坑才搞定 改了三遍,AI率从45%涨到了62%。 没错,越改越高。因为方向错了——我当时在用手动改写的方式,每段都在调措辞换说法,结果反而让文本特征变得更像AI生成的。后来换了…...

从实验室到码头:精仪智检的技术迭代与海洋监测精度革新路径

风暴潮会对海岸造成冲击。海浪会对船舶航行构成威胁。每一次海洋灾害的发生,都与传统监测技术的局限性密切相关。传统浮子式验潮仪的测量误差普遍达到10cm。这一误差可能导致灾害预警延迟数小时。延迟预警会造成数亿元的经济损失。在这样的背景下,福州大…...

【C语言程序设计】第27篇:递归函数原理与实例分析

1 引言考虑计算阶乘的问题&#xff1a;n! n (n-1) ... 2 1。我们可以用循环实现&#xff1a;cint factorial(int n) {int result 1;for (int i 1; i < n; i) {result * i;}return result; }但也可以换一种思路&#xff1a;n! n (n-1)!&#xff0c;即阶乘可以用自身…...

零基础上手:5分钟搭建第一个智能体——用Coze零代码实战(2026智能体开发系列·第3篇)

> 本文适合:AI智能体零基础新手、非技术从业者、想快速体验智能体开发的爱好者 > 阅读难度:🌟(全程零代码、图文步骤,复制模板即可上手,5分钟搞定) > 系列衔接:承接第2篇《AI Agent核心概念拆解》,将“感知、工具调用”等概念落地到实操,用Coze零代码搭建…...

湘潭品牌设计公司权威推荐榜单

在当今竞争激烈的市场环境中&#xff0c;品牌已成为企业最核心的资产之一。一个专业、系统、富有战略性的品牌设计&#xff0c;不仅能提升企业的市场辨识度&#xff0c;更能成为驱动业务增长的强大引擎。对于湘潭及周边地区的企业而言&#xff0c;选择一家专业、可靠的品牌设计…...

【Spring框架】别再死记硬背!AOP 原来这么简单

一、核心定义 AOP&#xff08;Aspect-Oriented Programming&#xff0c;面向切面编程&#xff09;是一种编程范式&#xff0c;核心思想是&#xff1a;将与业务核心逻辑无关&#xff0c;但多个模块都需要的通用功能&#xff08;如日志、事务、权限校验&#xff09;抽离出来&…...

Kafka消息幂等性实战指南

Kafka 通过 生产者端机制 与 消费者端应用设计 协同保障消息处理的幂等性&#xff08;即重复操作不影响最终结果&#xff09;。需注意&#xff1a;Kafka 本身不提供“端到端全自动幂等”&#xff0c;需结合配置与业务逻辑实现。核心方案如下&#xff1a;&#x1f512; 一、生产…...

基于大数据的学习资源推送系统的设计与实现-

目录需求分析与规划数据采集与处理推荐算法设计系统架构实现测试与优化部署与维护项目技术支持可定制开发之功能创新亮点源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作需求分析与规划 明确系统目标&#xff0c;如个性化推荐、资源分类、用…...

筋膜提拉一般多少钱

在面部年轻化领域&#xff0c;筋膜提升手术始终是公认的“终极抗衰方案”。它打破传统表皮提拉的局限&#xff0c;通过精准切除面部松弛冗余的皮肤&#xff0c;同时对SMAS筋膜层这一深层支撑结构进行复位、提升与紧致&#xff0c;从衰老根源重塑清晰面部轮廓&#xff0c;让皱纹…...

单链表应用:双指针【快慢指针】

[2019] &#xff08;15 分&#xff09;已知一个带有表头节点的单链表&#xff0c;节点结构为datalink假设该链表只给出了头指针 list。在不改变链表的前提下&#xff0c;请设计一个尽可能高效的算法&#xff0c;查找链表中倒数第 k 个位置上的结点&#xff08;k 为正整数&#…...

2026微信抢红包终极秘籍:从0.01元专业户到手气王锦鲤

2026年抢红包&#xff0c;早已不是“拼手速”那么简单&#xff01;微信红包的分配背后藏着明确的算法逻辑&#xff0c;掌握这套逻辑再搭配实用技巧&#xff0c;就能从“陪跑选手”逆袭成“红包锦鲤”。据统计&#xff0c;2026年除夕夜用户共抢到50.8亿个微信红包&#xff0c;但…...

鸿蒙HarmonyOS开发从入门到实战:一份完整的布局与组件学习路线图

最近整理了一份《鸿蒙HarmonyOS深度探索》的学习资料&#xff0c;涵盖了从UI布局到基础组件的完整知识体系&#xff0c;特别适合想要系统性入门HarmonyOS应用开发的同学。 鸿蒙HarmonyOS深度探索 &#x1f4da; 内容体系概览 这份资料不是简单的概念堆砌&#xff0c;而是按照…...

Hi3519芯片开发过程笔记:四、Uboot环境变量nand_env.bin镜像生成方法(默认环境变量设置方法)

Hi3519的sdk里面&#xff0c;默认的分区方式中&#xff0c;有一个分区是专门用来存放Uboot的env环境变量的。如图所示&#xff0c;Hi3519的SDK可以生成可烧写的uboot环境变量.bin镜像文件。具体的生成方法为&#xff1a;在目录&#xff1a;/Hi3519dv500_sdk_new/Hi3519DV500_SD…...

甩掉API硬编码包袱:2026桌面级办公智能体选型指南及实在Agent等主流工具横评

2026年&#xff0c;企业自动化已全面从“对话框时代”跨入“行动代理时代”。 选型逻辑不再是单纯对比模型参数&#xff0c;而是考验智能体&#xff08;Agent&#xff09;对复杂办公环境的系统级操纵能力与安全合规边界。 本文将深度拆解当前市场主流方案&#xff0c;通过多维R…...

ADRC优于PID?真相揭秘

ADRC与PID控制对比分析&#xff1a;为何经典PID仍占主导地位 1. 控制算法基本原理对比 1.1 PID控制核心原理 PID&#xff08;比例-积分-微分&#xff09;控制器是控制领域最经典的算法&#xff0c;其基本结构包含三个核心环节&#xff1a; // PID控制器基本实现 float PID_…...

迦娃餐馆点餐系统的设计与实现小程序

目录需求分析技术选型原型设计开发与测试部署上线运营维护关键代码示例&#xff08;微信小程序&#xff09;项目技术支持可定制开发之功能创新亮点源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作需求分析 明确迦娃餐馆点餐系统的核心功能需…...

SQL Server 学习笔记:从 MySQL 到 SQL Server

作为一名在大学项目 中经常使用MySQL 数据库的开发者&#xff0c;最近因项目需要开始学习 SQL Server。本文记录核心差异点和学习要点&#xff0c;方便有相同背景的同学快速上手以及我的事后回顾。一、基础概念对比 特性 MySQL SQL Server 所属公司 Oracle&#xff08…...

答题卡检测

答题卡识别评分代码完整讲解1. 答题卡处理流程图1) 读取答题卡图像并进行灰度化、模糊处理和边缘检测&#xff1b;2) 定位答题卡区域并进行透视变换&#xff1b;3) 通过阈值处理和轮廓分析检测填涂的选项泡泡&#xff1b;4) 将检测结果与标准答案对比计算得分。系统支持自定义参…...

程序员如何利用自然语言处理技术

程序员如何利用自然语言处理技术关键词&#xff1a;程序员、自然语言处理、技术应用、算法原理、实战案例摘要&#xff1a;本文旨在全面探讨程序员如何利用自然语言处理&#xff08;NLP&#xff09;技术。从自然语言处理的背景知识入手&#xff0c;详细阐述其核心概念、算法原理…...

简单的c语言分析 汇编代码

1、STR是ARM汇编中的内存访问指令&#xff1a;表示字数据写入&#xff0c;用于将一个32位的字数据写入到指令中指定的内存单元。 比如STR R0, [R1, #0x100]; 表示将R0中的字数据保存到内存单元&#xff08;R10x100&#xff09;中。2、 BL 指令BL 指令的格式为&#xff1a; BL{条…...

ALS(Approximate Logic Synthesis) 综述| Approximate Logic Synthesis: A Survey

记一下ALS的综述笔记。Introduction 讲近似电路计算的两种分类&#xff0c;大致介绍了怎么对误差建模。Method for error estimation 讲如何计算近似电路和精确电路的误差。包括&#xff1a; A. error matrix hamming distance (max & average)error rateapproximate effic…...

keil+Arm Visual Hardware(AVH)入门

1.准备 下载keil5,最新版本5.37&#xff08;早期版本没有AVH&#xff09;,激活professional版本&#xff0c;plus和Essential版本不行 2.安装好keil后&#xff0c;建立VHT工程&#xff0c;此处以arm cortex-M4为例&#xff0c;按照下图选择设备3.配置标准接口&#xff0c;下面以…...

Google Earth Engine(GEE)——矢量数据集合和影像集合的连接join,给矢量集合添加到两景影像作为矢量集合的属性

本次教程主要是加载一个矢量集合,然后通过设定指定的时间条件和地理条件,和指定的时间窗口进行筛选应用于Landsat8 影像,最后将筛选出的影像加载到矢量集合中。 矢量数据集合: Feature Index Dy (Long) Hr (Long) Location N (String) Mo (Long) Year (Long) system:index…...

全球台锯:家具家装与建筑工程刚需驱动下的稳增扩容,2026-2032年CAGR3.8%,2032年规模9.6亿美元

在制造业与木工行业的蓬勃发展浪潮中&#xff0c;台锯作为核心工具&#xff0c;其市场表现备受瞩目。QYResearch调研显示&#xff0c;2025年全球台锯市场规模大约为7.41亿美元&#xff0c;预计2032年将达到9.6亿美元&#xff0c;2026-2032期间年复合增长率&#xff08;CAGR&…...

解决报错:ORA-12541:TNS:无监听程序

1.重新配置监听 找到监听程序配置&#xff0c;右键已管理员身份运行 选择第二个&#xff1a;重新配置 这个一般没什么好选的 默认选定的协议TCP&#xff0c;继续下一步 默认的否 继续下一步&#xff0c;完成监听重新配置 之后进行测试看能否连上 2.本地Net服务名配置 …...

QT(二):Qt相关控件的使用和设置,Qt对话框的使用,画图事件的创建和设置,线程和锁的创建利用,网络编程,TCP和UDP客户端及服务器的创建,SQLITE数据库,QTableWidget控件的使用

一、相关控件及操作配置1、QPushButton & QtoolButton&#xff08;按钮&#xff09;setText(QString) ---- 设置按钮上的内容setFixedSize(int w, int h) --- 设置固定大小setFixedHeight(int) --- 设置固定高度setFixedwidth(int) --- 设置固定宽度setMaximumSize(…...

AF350标记α-银环蛇d素,AF350-a-Bungarotoxin核心功能与应用场景

α-Bungarotoxin AF350&#xff0c;AF350标记α-银环蛇d素&#xff0c;AF350-a-Bungarotoxin&#xff0c;AF350-α-BTX&#xff0c;银环蛇d荧光标记一、试剂本质与结构解析α-Bungarotoxin, AF350&#xff08;以下简称“AF350-α-BTX”&#xff09;是一种由台湾银环蛇d液中提取…...