【微服务】限流、熔断和降级(持续更新中~)

1、限流

1.1 什么是限流

        限流(Rate Limiting)是一种常用的技术手段,用于控制系统对资源的访问速率,确保系统的稳定性和可靠性。在分布式系统、Web服务、API接口等场景中,限流尤为重要。通过限制请求的频率或数量,可以有效防止因突发流量导致系统过载、崩溃或资源耗尽等问题。限流不仅保护了服务提供者的利益,也提升了用户体验。

1.2 限流的常见场景

  1. API接口保护防止恶意用户或爬虫程序对API进行高频访问,消耗大量服务器资源。
  2. 分布式系统在微服务架构中,限制各服务间的调用频率,避免服务间的级联失败。
  3. 数据库访问限制对数据库的查询或写入操作,防止数据库因压力过大而响应缓慢或崩溃。
  4. 网络带宽控制在网络设备或服务器上实施带宽限制,确保关键业务的网络传输质量。

1.3 限流策略

  1. 基于IP的限流对每个访问者的IP地址进行限流,防止单个IP的恶意访问。
  2. 基于用户的限流对注册用户进行限流,根据用户身份或角色分配不同的请求额度。
  3. 基于接口的限流对不同的API接口分别设置限流规则,根据接口的重要性和访问频率进行调整。
  4. 基于时间段的限流:在特定的时间段内对请求进行限流,如高峰期限制请求频率,低峰期放宽限制。

1.4 限流算法

  1. 固定窗口算法:将时间划分为固定的窗口,每个窗口内统计请求数量,超过阈值则拒绝服务。该算法实现简单,但可能存在临界问题,即窗口切换时突然允许大量请求通过。

  2. 滑动窗口算法:相对于固定窗口,滑动窗口算法允许窗口在时间轴上滑动,通过两个固定窗口(当前窗口和上一个窗口)的叠加来统计请求数量,解决了临界问题,但实现复杂度稍高。

  3. 漏桶算法(Leaky Bucket):将请求视为水滴,以固定速率从桶中漏出。如果水滴到达速率超过漏出速率,则桶满后多余的水滴将被丢弃。该算法平滑了请求的突发流量,但可能导致低优先级请求长时间等待。

  4. 令牌桶算法(Token Bucket):与漏桶算法类似,但令牌桶以固定速率向桶中添加令牌(代表服务容量)。当请求到达时,如果桶中有足够的令牌,则请求被处理并消耗一个令牌;否则,请求被限流。该算法既能应对突发流量,又能保证请求的公平性。

2、熔断

2.1 什么是熔断

       熔断(Circuit Breaker)模式是一种用于处理分布式系统中因服务调用失败而可能导致系统雪崩效应的保护机制。它借用了电路中的“熔断器”概念,当电流过大时,熔断器会自动切断电路,以保护整个电路系统不被烧毁。在分布式系统中,熔断器用于监控服务调用的健康状况,并在检测到异常(如服务调用失败率过高、响应时间过长等)时,自动切断对该服务的调用,从而防止故障在系统中蔓延,保障系统的整体稳定性和可用性。

2.2 熔断的目的

  1. 防止系统雪崩当某个服务出现故障时,如果没有有效的隔离措施,故障可能会迅速扩散到整个系统,导致系统雪崩。熔断机制能够在服务故障时及时切断调用链,防止故障扩散。
  2. 提升系统弹性通过熔断机制,系统能够在面对服务故障时保持一定的弹性,即能够在故障恢复后快速恢复正常服务,减少因故障导致的服务中断时间。
  3. 优化用户体验在熔断期间,系统可以返回预设的降级响应(如默认值、缓存数据或错误信息),减少用户等待时间和错误率,提升用户体验。

2.3 熔断的常见场景

  1. 服务调用超时:当下游服务的响应时间超过预设的阈值时,如果继续调用可能会导致上游服务资源耗尽,影响整体系统性能。此时,熔断机制可以切断对下游服务的调用,避免超时问题进一步恶化。

  2. 服务调用失败率过高:如果某个服务的调用失败率达到一定阈值,说明该服务可能存在问题,继续调用可能会浪费系统资源并影响用户体验。熔断机制可以在此时介入,切断对该服务的调用,并返回降级响应。

  3. 系统资源紧张:在高峰期或系统资源不足时,为了保障核心功能的稳定运行,可以通过熔断机制对部分非核心服务进行降级处理,释放系统资源给关键服务使用。

  4. 硬件故障或网络问题:当下游服务所在的服务器或网络出现故障时,可能导致服务不可用。熔断机制可以及时发现并切断对故障服务的调用,防止故障扩散。

  5. 缓存击穿或穿透:在缓存服务中,如果大量请求同时访问未命中缓存的数据(缓存击穿)或不存在的数据(缓存穿透),可能会直接对数据库造成压力。此时,可以通过熔断机制对这类请求进行限流或降级处理。

2.4 熔断策略

  1. 基于失败率的熔断策略:当服务调用的失败率达到预设的阈值时,触发熔断。这种策略适用于对服务稳定性要求较高的场景,可以确保在服务出现问题时及时切断调用链。

  2. 基于响应时间的熔断策略:当服务调用的响应时间超过预设的阈值时,触发熔断。这种策略适用于对服务响应时间有严格要求的场景,如在线交易系统、实时数据处理等。

  3. 基于异常比例的熔断策略:当单位统计时长内异常请求(如抛出异常、返回错误码等)的比例超过设定的阈值时,触发熔断。这种策略适用于服务出现不稳定情况、异常情况较多的场景。

  4. 基于异常数的熔断策略:当单位统计时长内异常请求的数量超过设定的阈值时,触发熔断。这种策略适用于对异常请求数量敏感的场景,如高并发的Web服务中突然出现大量异常请求的情况。

  5. 滑动窗口熔断策略:通过滑动窗口来记录一段时间内的请求情况,并根据窗口内的请求失败率或响应时间等指标来判断是否触发熔断。这种策略可以更加灵活地应对系统负载的变化。

  6. 半开状态策略:在熔断器处于开启状态一段时间后,进入半开状态,允许部分请求通过以测试服务是否已恢复正常。如果测试请求成功,则熔断器关闭;如果测试请求失败,则熔断器重新进入开启状态并延长休眠时间。这种策略有助于在保障系统稳定性的同时,尽快恢复对服务的调用。

2.5 熔断工作流程

  1. 闭合状态熔断器处于正常工作状态,允许服务调用通过。此时,系统会监控服务调用的健康状况,如失败率、响应时间等。
  2. 开启状态当服务调用满足熔断条件(如失败率达到阈值)时,熔断器进入开启状态,自动切断对该服务的调用。此时,所有对该服务的调用都将直接返回降级响应,不再实际执行。
  3. 半开启状态熔断器在开启一段时间后(称为“休眠时间”),会进入半开启状态。在此状态下,系统会允许少量的服务调用通过,以测试服务是否已恢复正常。如果测试调用成功,熔断器将重新进入闭合状态;如果测试调用失败,熔断器将再次进入开启状态,并延长休眠时间。

3、降级

3.1 什么是降级

        降级(Degrade)是分布式系统和高可用架构设计中的一个重要概念,旨在系统资源紧张或发生故障时,通过牺牲部分非核心业务功能或降低服务性能的方式,来保障系统整体的可用性和稳定性。降级是一种自我保护机制,它允许系统在面对压力时,主动减少负载,从而避免系统全面崩溃或资源耗尽。   

3.2 降级的常见场景

  1. 服务依赖故障:当系统依赖的外部服务(如数据库、缓存、第三方API等)出现故障或响应缓慢时,可以通过降级策略来减少对这些服务的依赖,使用本地缓存、备用数据源或模拟数据等方式来保障核心功能的运行。

  2. 系统资源不足:在高峰期或系统资源紧张时,通过降级非核心功能来释放资源,确保核心功能的稳定运行。例如,在电商大促期间,可以降级非交易相关的功能(如商品评论、推荐系统等),以保障交易系统的流畅运行。

  3. 服务版本不兼容:在微服务架构中,不同服务之间可能存在版本不兼容的问题。当某个服务升级后,与其他服务存在兼容性问题时,可以通过降级策略来回退到旧版本,以保障系统的整体稳定性。

  4. 安全或合规性要求:在某些情况下,为了满足安全或合规性要求,可能需要降级部分功能或数据。例如,在发现数据泄露风险时,可以降级涉及敏感数据的服务,以防止数据进一步泄露。

  5. 功能优先级调整:在业务需求发生变化时,可能需要重新评估各功能的优先级。通过降级非优先功能,可以确保有限的资源被用于支持最重要的业务场景。

  6. 性能测试和故障演练:在进行性能测试或故障演练时,为了模拟真实环境下的系统压力,可以主动对部分服务进行降级处理,以观察系统的表现和恢复能力。

3.3 降级策略

  1. 开关降级:通过配置开关来启用或禁用某些功能或服务,实现快速降级。
  2. 限流降级:当系统流量超过预设阈值时,对部分请求进行限流或拒绝服务,以减少系统负载。
  3. 熔断降级当服务调用失败率达到一定阈值时,自动触发熔断机制,将后续请求直接返回降级响应,避免对下游服务造成更大压力。
  4. 资源隔离降级通过隔离不同服务的资源(如线程池、数据库连接等),防止单个服务的故障影响整个系统。当某个服务资源耗尽时,对其进行降级处理,以保障其他服务的正常运行。
  5. 超时降级当服务调用超过预设的超时时间时,自动返回降级响应,防止长时间等待导致的资源浪费和用户体验下降。

3.4 降级和熔断的区别

       降级和熔断都是系统保护机制,但侧重点不同。熔断侧重于在故障发生时快速切断服务调用链,防止故障扩散;而降级则是在系统资源紧张或故障发生时,通过牺牲部分非核心功能来保障系统整体可用性和稳定性。两者往往结合使用,形成更完善的系统保护体系。 

4、 Sentinel 实现方案

        Sentinel 提供了 @SentinelResource 注解用于定义资源,并提供了 AspectJ 的扩展用于自动定义资源、处理 BlockException 等。使用 Sentinel Annotation AspectJ Extension 的时候需要引入以下依赖:

		<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-core</artifactId><version>1.8.8</version></dependency><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-annotation-aspectj</artifactId><version>1.8.8</version></dependency>

4.1 官方示例

public class TestService {// 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 static 函数.@SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})public void test() {System.out.println("Test");}// 原函数@SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")public String hello(long s) {return String.format("Hello at %d", s);}// Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.public String helloFallback(long s) {return String.format("Halooooo %d", s);}// Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.public String exceptionHandler(long s, BlockException ex) {// Do some log here.ex.printStackTrace();return "Oops, error occurred at " + s;}
}

需要通过配置的方式将 SentinelResourceAspect 注册为一个 Spring Bean:

@Configuration
public class SentinelAspectConfiguration {@Beanpublic SentinelResourceAspect sentinelResourceAspect() {return new SentinelResourceAspect();}
}

官方文档:https://sentinelguard.io/zh-cn/docs/introduction.html

官方案例:Sentinel/sentinel-demo at master · alibaba/Sentinel · GitHub

4.2 流量控制(自测)

一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:

  • resource:资源名,即限流规则的作用对象
  • count: 限流阈值
  • grade: 限流阈值类型,QPS 或线程数
  • strategy: 根据调用关系选择策略

基于QPS/并发数的流量控制

FlowRule.grade 字段控制了统计QPS还是统计并发数。

1、定义流量限制规则

@Component
public class SentinelRuleInitializer {@PostConstructprivate static void initFlowQpsRule() {List<FlowRule> rules = new ArrayList<FlowRule>();FlowRule rule1 = new FlowRule();//定义资源名称rule1.setResource("flowControl");//限流阈值rule1.setCount(1);//限流阈值类型 0-并发数 1-QPSrule1.setGrade(RuleConstant.FLOW_GRADE_QPS);//超出阈值后处理手段 0-直接拒绝 1-冷启动 2-匀速器 3-rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);//应用来源限制rule1.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);rules.add(rule1);FlowRuleManager.loadRules(rules);}
}

2、注入bean

@Configuration
public class SentinelAspectConfiguration {@Beanpublic SentinelResourceAspect sentinelResourceAspect() {return new SentinelResourceAspect();}
}

3、实现接口

注意!!!一开始没注意到踩了坑~

// Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.

// Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.

@Slf4j
@RestController
@RequestMapping("/sentinel")
public class SentinelController {private Integer count = 0;@SneakyThrows@GetMapping("/flow/control")@SentinelResource(value = "flowControl", blockHandler = "handleBlock", fallback = "exceptHandler")public String flowControl() {return "hello flow control!" + count++;}// 发生异常时 进入该方法public String exceptHandler(BlockException ex) {log.info("发生异常{}", count);return "发生异常";}// sentinel发生异常时 BlockException 进入该方法public String handleBlock(BlockException ex) {log.info("发生限流、降级和熔断{}", count);return "发生降级、降级和熔断" + ex;}
}

4、测试

限流规则是 QPS为1,即1秒最多请求1次,请求正常结果如下:

当快速点击后,请求结果如下:

4.2 熔断降级(自测)

1. 自定义熔断规则

    @PostConstructprivate static void initDegradeRule() {List<DegradeRule> rules = new ArrayList<>();DegradeRule rule = new DegradeRule("flowControl")//熔断策略,支持 0-慢调用比例/ 1-异常比例/ 2-异常数策略.setGrade(CircuitBreakerStrategy.SLOW_REQUEST_RATIO.getType())//慢调用比例模式下为慢调用临界 响应时间(超出该值计为慢调用);异常比例/异常数模式下为对应的阈值.setCount(5)// 熔断时长,单位为 s.setTimeWindow(10)//慢调用比例阈值,仅慢调用比例模式有效(1.8.0 引入).setSlowRatioThreshold(0.6)// 熔断触发的最小请求数,请求数小于该值时即使异常比率超出阈值也不会熔断(1.7.0 引入).setMinRequestAmount(1)//统计时长(单位为 ms),如 60*1000 代表分钟级(1.8.0 引入).setStatIntervalMs(20000);rules.add(rule);DegradeRuleManager.loadRules(rules);}

2. controller 增加10s时间

 @SneakyThrows@GetMapping("/flow/control")@SentinelResource(value = "flowControl", blockHandler = "handleBlock", fallback = "exceptHandler")public String flowControl() {Thread.sleep(10);return "hello flow control!" + count++;}

3. 访问页面,由于每次请求都超出5s,所以5s后第二次请求一定是发生熔断,熔断时间10s,即第一次请求15s后才能再次请求(前5秒是可以发起请求的)

可以看到报错的类已经从FlowException 变成 DegradeException

4.3 Sentinel 控制台

官方 jar包下载:https://github.com/alibaba/Sentinel/releases

下载后使用如下命令启动,注意更改最后的文件名sentinel-dashboard.jar

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

应用绑定sentinel dashboard

spring.cloud.sentinel.transport.dashboard = localhost:8081

打开本地页面(我这里更改端口为8081,默认8080):http://localhost:8081/#/login

默认用户名和密码都是 sentinel

进入后即可配置限流、熔断规则等。

4.5 源码解析(未完成)

比较重要的类有

  1. Entry 接口:这是 Sentinel 流量控制的一个核心概念。在 Sentinel 中,几乎所有的资源访问(如 HTTP 请求、数据库调用等)都需要通过创建 Entry 实例来进行。Entry 的创建和退出代表了一次资源访问的开始和结束,Sentinel 会根据这些 Entry 的创建和退出情况来统计流量,并进行相应的流量控制。
  2. SphU(Strictly Priority Hub)类:这是一个常用的工具类,用于快速访问资源,并自动进行流量控制。开发者可以通过调用 SphU.entry(resourceName) 来尝试访问一个资源,如果流量控制规则允许,则返回一个 Entry 对象,表示资源访问被允许;如果不允许,则根据配置进行限流处理(如抛出异常、返回默认值等)。
  3. FlowRuleManager 类:这个类管理着流量控制规则(FlowRule)。在 Sentinel 中,流量控制规则定义了资源的访问阈值、控制策略等。FlowRuleManager 负责加载、更新和查询这些规则,以便在运行时进行流量控制。
  4. ContextUtil 类:这个类用于管理当前线程的上下文信息,如当前线程的调用链信息、来源信息等。这些信息对于 Sentinel 进行精确的流量控制和统计非常重要。
  5. DefaultSlotChainBuilder 和 SlotChain 类:Sentinel 的处理逻辑被封装在一系列插槽(Slot)中,这些插槽按照一定的顺序排列,形成了一个插槽链(SlotChain)。DefaultSlotChainBuilder 负责构建这个插槽链,而 SlotChain 则负责按照顺序执行这些插槽中的逻辑。
/**
* 每次调用 SphU#entry() 都会返回一个 Entry类。这个类保存了当前调用的信息:
*
*   createTime,这个条目的创建时间,用于响应时间(RT)统计。
*   curNode,即当前上下文中资源的统计信息。
*   originNode,即特定来源的统计信息。通常,来源可以是服务消费者的应用名称,参见
*   ContextUtil#enter(String name, String origin)
*   ResourceWrapper即资源名称。
*   如果在同一个 Context中多次调用 SphU#entry(),则会创建一个调用树,因此这个类可能会持有父条*      目或子条目以形成树状结构。由于 Context 总是持有调用树中的当前条目,因此每次调用 
*      Entry#exit() 时都应该修改
*/
public abstract class Entry implements AutoCloseable {protected static final Object[] OBJECTS0 = new Object[0];private final long createTimestamp;private long completeTimestamp;private Node curNode;/*** 特定来源的Node,通常是服务消费者.*/private Node originNode;private Throwable error;private BlockException blockError;protected final ResourceWrapper resourceWrapper;protected final int count;protected final Object[] args;public Entry(ResourceWrapper resourceWrapper) {this(resourceWrapper, 1, OBJECTS0);}public Entry(ResourceWrapper resourceWrapper, int count, Object[] args) {this.resourceWrapper = resourceWrapper;this.createTimestamp = TimeUtil.currentTimeMillis();this.count = count;this.args = args;}public ResourceWrapper getResourceWrapper() {return resourceWrapper;}/*** Complete the current resource entry and restore the entry stack in context.* Do not need to carry count or args parameter, initialization does* @throws ErrorEntryFreeException if entry in current context does not match current entry*/public void exit() throws ErrorEntryFreeException {exit(count, args);}public void exit(int count) throws ErrorEntryFreeException {exit(count, args);}/*** Equivalent to {@link #exit()}. Support try-with-resources since JDK 1.7.** @since 1.5.0*/@Overridepublic void close() {exit();}/*** Exit this entry. This method should invoke if and only if once at the end of the resource protection.** @param count tokens to release.* @param args extra parameters* @throws ErrorEntryFreeException, if {@link Context#getCurEntry()} is not this entry.*/public abstract void exit(int count, Object... args) throws ErrorEntryFreeException;/*** Exit this entry.** @param count tokens to release.* @param args extra parameters* @return next available entry after exit, that is the parent entry.* @throws ErrorEntryFreeException, if {@link Context#getCurEntry()} is not this entry.*/protected abstract Entry trueExit(int count, Object... args) throws ErrorEntryFreeException;/*** Get related {@link Node} of the parent {@link Entry}.** @return*/public abstract Node getLastNode();public long getCreateTimestamp() {return createTimestamp;}public long getCompleteTimestamp() {return completeTimestamp;}public Entry setCompleteTimestamp(long completeTimestamp) {this.completeTimestamp = completeTimestamp;return this;}public Node getCurNode() {return curNode;}public void setCurNode(Node node) {this.curNode = node;}public BlockException getBlockError() {return blockError;}public Entry setBlockError(BlockException blockError) {this.blockError = blockError;return this;}public Throwable getError() {return error;}public void setError(Throwable error) {this.error = error;}/*** Get origin {@link Node} of the this {@link Entry}.** @return origin {@link Node} of the this {@link Entry}, may be null if no origin specified by* {@link ContextUtil#enter(String name, String origin)}.*/public Node getOriginNode() {return originNode;}public void setOriginNode(Node originNode) {this.originNode = originNode;}/*** Like {@code CompletableFuture} since JDK 8, it guarantees specified handler* is invoked when this entry terminated (exited), no matter it's blocked or permitted.* Use it when you did some STATEFUL operations on entries.* * @param handler handler function on the invocation terminates* @since 1.8.0*/public abstract void whenTerminate(BiConsumer<Context, Entry> handler);}

5、学习实践(未完成)

5.1 限流-令牌桶

注意:令牌桶算法不能与另外一种常见算法“漏桶算法(Leaky Bucket)”相混淆。这两种算法的主要区别在于“漏桶算法”能够强行限制数据的传输速率,而“令牌桶算法”在能够限制数据的平均传输速率外,还允许某种程度的突发传输。在“令牌桶算法”中,只要令牌桶中存在令牌,那么就允许突发地传输数据直到达到用户配置的门限,因此它适合于具有突发特性的流量。

令牌桶工作分为三步:

  1. 产生令牌:周期性的以速率CIR/EIR向令牌桶中增加令牌,桶中的令牌不断增多。如果桶中令牌数已到达CBS/EBS,则丢弃多余令牌。
  2. 消耗令牌:输入数据包会消耗桶中的令牌。在网络传输中,数据包的大小通常不一致。大的数据包相较于小的数据包消耗的令牌要多。
  3. 判断是否通过:输入数据包经过令牌桶后的结果包括输出的数据包和丢弃的数据包。当桶中的令牌数量可以满足数据包对令牌的需求,则将数据包输出,否则将其丢弃。

以下模仿在微服务中,1台客户端实例不断请求,3台服务端实例使用令牌桶进行限流的场景。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

每天五分钟计算机视觉:人脸识别网络FaceNet

本文重点 在前面的课程中,为了解决人脸识别的问题,我们学习了Siamese神经网络。本文我们学习另外一种人脸识别网络模型FaceNet。 论文 FaceNet: A Unified Embedding for Face Recognition and Clustering FaceNet概述 FaceNet是谷歌在CVPR 2015上提出的一种深度学习模型,…

【Redis】Redis 持久化 AOF、RDB—(七)

目录 一、AOF 日志二、RDB 内存快照 Redis 一旦服务器宕机&#xff0c;内存中的数据将全部丢失&#xff0c;从后端数据库恢复这些数据&#xff0c;对数据库压力很大&#xff0c;且性能肯定比不上从 Redis 中读取&#xff0c;会拖慢应用程序。所以&#xff0c;对 Redis 来说&…

Linux awk案例

目录 1. 查询时间超过2000毫秒的请求2. 查询指定列组合出现的次数3. 统计所有文件的大小4. 获取大于指定大小的文件名&#xff0c;并按照从大到小排序5. grep指定字段后&#xff0c;使用awk列转行6. 查询第四个字段等于指定值的内容 1. 查询时间超过2000毫秒的请求 ✅log: 202…

初等数学几百年重大错误:N各元n的对应n+1的全体是N的真子集N+——百年病态集论的症结

黄小宁 数学图可是“离散”的点组成的点集N&#xff5b;0&#xff0c;1&#xff0c;2&#xff0c;…&#xff0c;n&#xff0c;…0&#xff5d;&#xff08;各数是点的坐标&#xff09;。设本文所说集合往往是元不少于两个的集。定义&#xff1a;若数&#xff08;点&#xff09…

实现一个能设置MaxLine的LayoutManager

实现一个能设置MaxLine的LayoutManager 有时候&#xff0c;我们会遇到这种需求&#xff1a;一个线性的列表布局&#xff0c;当item量很少的时候&#xff0c;就是wrap_content直接展示完所有item&#xff0c;但是当item数量超过某个数时就要固定高度&#xff0c;让其变成可滑动…

jmeter如何把一个请求的响应中部分字段提取出来便于下个请求用

jmeter如何把一个请求的响应中部分字段提取出来便于下个请求用&#xff0c;可以通过json提取器提取&#xff0c;如果提取多个&#xff0c;就设置多个json提取。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/dd5afb1fca3f4e31b636e17e11e8dfc3.png

15年让爱轮回

15年前&#xff0c;运巧的命运齿轮因一位记者的稿件悄然转动&#xff0c;运巧这个名字&#xff0c;真的是命运的巧合&#xff0c;把她和邦尔骨科连接在了一起&#xff0c;她的人生轨迹因一家医院的善举发生了改变。那时的她&#xff0c;面临生活的重重困境&#xff0c;求学之路…

python实战实例:矩阵加法乘法转置

1.矩阵加法—题目描述 输入两个 n行 m 列的矩阵 A 和 B&#xff0c;输出它们的和 AB&#xff0c;矩阵加法的规则是两个矩阵中对应位置的值进行加和&#xff0c;具体参照样例。 输入格式 第一行包含两个整数 n 和 m&#xff0c;表示矩阵的行数和列数。 接下来 n 行&#xff…

Git安装及配置

Git安装 在你开始使用 Git 前,需要将它安装在你的计算机上。 即便已经安装,最好将它升级到最新的版本。 你可以通过软件包或者其它安装程序来安装,或者下载源码编译安装。 下面,我们将会介绍不同操作系统上 Git 的安装方法。 在 Windows 上安装 在 Windows 上安装 Git 的…

STM32的寄存器详解

目录 前言 一、 STM32 单片机寄存器概述 1.寄存器的作用 2.寄存器的分类 二、STM32 内核寄存器 1.程序计数器&#xff08;PC&#xff09; 2.堆栈指针&#xff08;SP&#xff09; 3.链接寄存器&#xff08;LR&#xff09; 4.控制寄存器&#xff08;CONTROL&#xff09;…

智能分拣投递机器人

产品介绍 自研智能分拣投递机器人&#xff0c;专注于物流行业“NC小件”的分拣与投递&#xff0c;机器人运行稳定、分拣效率高&#xff0c;搭配智能分拣投递系统单台机器人最大作业效率可达400件/H&#xff0c;投递效率相较于传统“小黄人“提升了30%-50%&#xff0c;可替代“…

【3.8】贪心算法-解无重叠区间

一、题目 给定一个区间的集合 intervals &#xff0c;其中 intervals[i] [starti, endi] 。返回 需要移除区间的最小数量&#xff0c;使剩余区间互不重叠 。 示例 1: 输入: intervals [[1,2],[2,3],[3,4],[1,3]] 输出: 1 解释: 移除 [1,3] 后&#xff0c;剩下的区间没有重叠…

EtherCAT 转 ModbusTCP 网关

设备简介 本产品是 EtherCAT 和 Modbus TCP 网关&#xff0c;使用数据映射方式工作。 本产品在 EtherCAT 侧作为 EtherCAT 从站&#xff0c;接 TwinCAT 、 CodeSYS 、 PLC等&#xff1b;在 ModbusTCP 侧做为 ModbusTCP 主站&#xff08; Client &#xff09;或从站…

奉加微PHY6233开门狗;超时时间对不上;好像应用不需要喂狗只需要开启定时器就行;底层是通过空闲任务喂狗的

超时时间对不上 这里设置看门狗超时时间为WDG_16S: hal_watchdog_config(WDG_16S);但是我测试到复位时间却是34秒: 然后我设置时间为WDG_2S的话实际间隔是6秒: 我很无语,被逼无奈只能够认了,最小设置是WDG_2S也就是说时间为6秒,这时候2秒喂狗一次: #define

计算机视觉基础. 1 学习导论

1 .引言 学习的目的是从过去的经验中吸取教训&#xff0c;以解决未来的问题。通常&#xff0c;这涉及搜索解决问题过去实例的算法。然后&#xff0c;该算法可以应用于该问题的未来实例。 过去和未来不一定指日历日期&#xff1b;相反&#xff0c;它们指的是学习者之前看到的内…

使用python导出Excel表格中的lua配置

背景&#xff1a;游戏开发中&#xff0c; 策划使用Excel配置游戏中的参数数据&#xff0c;写一个工具用于导出这些配置 工具选择使用 python来开发&#xff0c;这样Windows、macOS、Linux平台都可以使用&#xff0c;而且有丰富的第三方模块。 本机先安装python&#xff0c;我…

二叉树相关练习

二叉树相关oj题&#xff1a; 对称二叉树 解题思路&#xff1a;判断一棵树是否轴对称&#xff0c;先判断左右子树结构是否相同&#xff0c;结构相同的情况下再判断对应的val是否轴对称&#xff0c;判断根节点的左右子树&#xff0c;再判断根节点的左右子树的左右子树是否轴对称…

CAD二次开发IFoxCAD框架系列(25)- 自动加载和初始化的使用

自动加载&#xff0c;意思就是我们不需要每次重启都得要去输入netload加载软件&#xff0c;这个我们该怎么解决&#xff0c;CAD给我们提供了注册表的方式来进行加载&#xff0c;IFoxCAD给我们提供了非常便捷的操作注册表的方法。 namespace ifoxgse.Core.System;public static…

【Python系列】text二进制方式写入文件

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

C语言 | Leetcode C语言题解之第376题摆动序列

题目&#xff1a; 题解&#xff1a; int wiggleMaxLength(int* nums, int numsSize) {if (numsSize < 2) {return numsSize;}int prevdiff nums[1] - nums[0];int ret prevdiff ! 0 ? 2 : 1;for (int i 2; i < numsSize; i) {int diff nums[i] - nums[i - 1];if ((…