sentinel熔断降级

熔断降级

Slot 责任链上的最后一环:熔断降级 DegradeSlot,熔断降级作为保护系统的一种强大手段,可以根据慢调用、异常比例和异常数进行熔断,并自定义持续时间以实现系统保护

规则配置

规则类中属性解析

与控制面板对应

// 其中资源名称在 AbstractRule 里。
public class DegradeRule extends AbstractRule {/*熔断策略 (0: 平均RT,1: 异常比例,2: 异常计数)*/private int grade = RuleConstant.DEGRADE_GRADE_RT;// 阈值计数, 含义取决于所选择的熔断策略private double count;// 熔断器断开后恢复时间(单位秒), 超时后, 熔断器转换成半开状态, 允许部分请求通过private int timeWindow;// 触发熔断最低请求数private int minRequestAmount = RuleConstant.DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT;// RT模式下慢请求比例的阈值private double slowRatioThreshold = 1.0d;// 间隔统计持续时间 (毫秒)private int statIntervalMs = 1000;
}
  • grade:熔断降级规则的类型,取值范围为

    • 慢调用比例: RuleConstant.DEGRADE_GRADE_RT=0, 默认值就是慢比例
    • 异常比例: RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO=1
    • 异常数: RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT=2
  • count:熔断降级的阈值,具体含义取决于 grade 字段的值

    • 慢调用比例: count 表示慢调用比例阈值
    • 异常比例: count 表示异常比例阈值
    • 异常数: count 表示异常数阈值
  • timeWindow: 熔断降级发生后的降级持续时间(单位:秒),在这段时间内,对应的资源将被降级, 超时后, 熔断器转换成半开状态, 允许部分请求通过, 如果这部分请求还是不通过, 那么熔断器转换成开状态, 继续熔断, 如果通过, 那么熔断器转换成关状态

  • minRequestAmount: 熔断降级统计周期内的最小请求总数。仅当周期内的请求总数达到此值时,才会根据 gradecount 进行熔断降级。

    • 默认值为 RuleConstant.DEGRADE_DEFAULT_MIN_REQUEST_AMOUNT=5
  • slowRatioThreshold:慢调用比例阈值,仅当 grade 为慢调用比例时生效。取值范围为 0 到 1 之间的小数,表示慢调用请求占总请求的比例, 默认值为1

  • statIntervalMs:熔断降级统计周期(单位:毫秒)。在这个周期内,Sentinel 会对请求进行统计,以判断是否需要进行熔断降级。默认值为 1000 毫秒(1 秒)

熔断器

不同的策略底层使用的算法不一样, 我们可以通过if-else进行, 也可以通过switch进行, 但是都不够优雅, 更加优雅的做法是, 使用策略模式, sentinel底层就是采用策略模式实现的

什么是策略模式不在赘述, 见这个链接策略模式 | 菜鸟教程 (runoob.com)

CircuitBreaker 是一个熔断器接口, 用于实现 Sentinel 的熔断降级功能。它定义了一些关键方法和一个内部枚举类型 State

public interface CircuitBreaker {DegradeRule getRule();boolean tryPass(Context context);State currentState();void onRequestComplete(Context context);enum State {OPEN, // 开启HALF_OPEN, // 半开CLOSED // 关闭}
}
  1. DegradeRule getRule():获取当前熔断器所对应的熔断降级规则。
  2. boolean tryPass(Context context):尝试通过熔断器
    • 关闭状态(CLOSED): 则允许请求通过
    • 打开状态(OPEN): 则拒绝请求
    • 半开状态(HALF_OPEN): 则根据规则允许部分请求通过。
  3. State currentState():获取当前熔断器的状态(OPEN, HALF_OPEN, CLOSED)
  4. void onRequestComplete(Context context):在请求完成后调用此方法,用于更新熔断器的统计数据。

内部枚举类型 State

  • OPEN:表示熔断器处于打开状态,此时会拒绝所有请求
  • HALF_OPEN:表示熔断器处于半开状态,此时允许部分请求通过,以检测系统是否已经恢复正常
  • CLOSED:表示熔断器处于关闭状态,此时允许所有请求通过

在这里插入图片描述

熔断器接口实现类图如下

在这里插入图片描述

AbstractCircuitBreaker完成一些功能的基础功能

public abstract class AbstractCircuitBreaker implements CircuitBreaker {}

具体的策略实现类会继承该抽象类完成一些独有的逻辑

public class ExceptionCircuitBreaker extends AbstractCircuitBreaker {}public class ResponseTimeCircuitBreaker extends AbstractCircuitBreaker {}

规程初始化

熔断降级规则的初始化也是通过监听器模式来完成的。监听器就像是一个基础框架,所有的规则都是基于这套框架来实现的

规则的存储和转换

两个Map用户存储熔断策略熔断规则

public final class DegradeRuleManager {// 熔断策略private static volatile Map<String, List<CircuitBreaker>> circuitBreakers = new HashMap<>();// 熔断规则private static volatile Map<String, Set<DegradeRule>> ruleMap = new HashMap<>();
}

将调用者传入的 List<DegradeRule> 转换为上述两个 HashMap

private synchronized void reloadFrom(List<DegradeRule> list) {// List<DegradeRule> 转 List<CircuitBreaker>Map<String, List<CircuitBreaker>> cbs = buildCircuitBreakers(list);// 将熔断器策略Map<String, List<DegradeRule>> rules = buildCircuitBreakerRules(cbs);circuitBreakers.updateRules(cbs);ruleMap.updateRules(rules);
}

buildCircuitBreakers

这里使用了策略模式, 并使用swtich进行分发

/*
List<DegradeRule> 转 List<CircuitBreaker>
*/
private Map<String, List<CircuitBreaker>> buildCircuitBreakers(List<DegradeRule> list) {// cbMap用于存储CircuitBreakerMap<String, List<CircuitBreaker>> cbMap = new HashMap<>(8);// 非空判断if (list == null || list.isEmpty()) {return cbMap;}// 遍历列表for (DegradeRule rule : list) {// 非法校验if (!isValidRule(rule)) {RecordLog.warn("[DegradeRuleManager] Ignoring invalid rule when loading new rules: {}", rule);continue;}// 如果规则的limitApp为空,则将其设置为默认值if (StringUtil.isBlank(rule.getLimitApp())) {rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);}// 根据规则获取已存在的CircuitBreaker或创建新的CircuitBreakerCircuitBreaker cb = getExistingSameCbOrNew(rule);if (cb == null) {RecordLog.warn("[DegradeRuleManager] Unknown circuit breaking strategy, ignoring: {}", rule);continue;}// 使用规则的资源名作为键,将CircuitBreaker添加到cbMap对应的列表中,如果cbMap中不存在该键则先创建空列表String resourceName = rule.getResource();List<CircuitBreaker> cbList = cbMap.get(resourceName);if (cbList == null) {cbList = new ArrayList<>();cbMap.put(resourceName, cbList);}cbList.add(cb);}return cbMap;
}/*
获取与给定降级规则相同的现有断路器或创建新的断路器
*/
private static CircuitBreaker getExistingSameCbOrNew(/*@Valid*/ DegradeRule rule) {// 根据给定的降级规则获取所有断路器的列表List<CircuitBreaker> cbs = getCircuitBreakers(rule.getResource());// 非空校验if (cbs == null || cbs.isEmpty()) {return newCircuitBreakerFrom(rule);}// 遍历熔断器for (CircuitBreaker cb : cbs) {// 果找到与给定降级规则相同的断路器,则重用该断路器并返回if (rule.equals(cb.getRule())) {// Reuse the circuit breaker if the rule remains unchanged.return cb;}}// 执行到这里说明, 没有找到相同的熔断器, 根据给定的规则创建新的熔断器, 并返回return newCircuitBreakerFrom(rule);
}/*
根据指定的降级规则创建一个熔断器
*/
private static CircuitBreaker newCircuitBreakerFrom(/*@Valid*/ DegradeRule rule) {// 根据熔断器策略进行分发switch (rule.getGrade()) {// RT响应时间case RuleConstant.DEGRADE_GRADE_RT:return new ResponseTimeCircuitBreaker(rule);// 异常比例, 异常数case RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO:case RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT:return new ExceptionCircuitBreaker(rule);default:return null;}
}

核心流程

熔断验证
public class DegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> {@Overridepublic void entry(...) throws Throwable {// 熔断验证逻辑performChecking(...);// 放行fireEntry(context, resourceWrapper, node, count, prioritized, args);}@Overridepublic void exit(...) {}
}

可以看到核心熔断验证逻辑在performChecking(), 那么它做了什么事

  1. 获取熔断器
  2. 熔断相关的校验, 失败就抛出降级异常
/*
熔断检查
*/
void performChecking(Context context, ResourceWrapper r) throws BlockException {// 先根据资源name获取熔断器List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());if (circuitBreakers == null || circuitBreakers.isEmpty()) {return;}// 调用每个熔断器的 tryPass 方法进行验证for (CircuitBreaker cb : circuitBreakers) {// 验证失败则抛出异常进行熔断if (!cb.tryPass(context)) {throw new DegradeException(cb.getRule().getLimitApp(), cb.getRule());}}
}

可以看到真正判断是否触发熔断的是tryPass()

tryPass()做了什么事?

  1. 检查当前熔断器状态
    • 关闭: 不需要熔断, 放行
    • 打开: 继续往下执行
  2. 熔断器是打开状态, 判断当前系统时间大于等于下一次尝试恢复的时间
    • 是: 将熔断器状态更改成半开启
    • 否: 放行
/*
如果此次请求已经达到了熔断器恢复时间,并且将熔断器的状态从打开变为半开启(HALF_OPEN),则放行,反之拒绝
*/
@Override
public boolean tryPass(Context context) {// 熔断器为关闭状态if (currentState.get() == State.CLOSED) {return true;}// 熔断器为开启状态if (currentState.get() == State.OPEN) {// 如果此次请求已经达到了熔断器恢复时间并且将熔断器的状态从打开变为半开启(HALF_OPEN),则放行,反之拒绝return retryTimeoutArrived() && fromOpenToHalfOpen(context);}return false;
}

retryTimeoutArrived()

// nextRetryTimestamp:下一次尝试恢复的时间
protected boolean retryTimeoutArrived() {// 如果当前系统时间大于等于下一次尝试恢复的时间,也就是说已经到达了可以尝试恢复的时间,则返回 true,反之返回 falsereturn TimeUtil.currentTimeMillis() >= nextRetryTimestamp;
}

fromOpenToHalfOpen()

/*
尝试将熔断器的状态从打开(OPEN)更改为半开启(HALF_OPEN)。如果状态切换成功,返回 true 表示请求放行;否则返回 false 表示拒绝请求
*/
protected boolean fromOpenToHalfOpen(Context context) {if (currentState.compareAndSet(State.OPEN, State.HALF_OPEN)) {// 这里使用观察者模式, 通知观察者, 当前熔断器的状态从OPEN变成了HALF_OPENnotifyObservers(State.OPEN, State.HALF_OPEN, null);Entry entry = context.getCurEntry();entry.whenTerminate(new BiConsumer<Context, Entry>() {@Overridepublic void accept(Context context, Entry entry) {// Note: This works as a temporary workaround for https://github.com/alibaba/Sentinel/issues/1638// Without the hook, the circuit breaker won't recover from half-open state in some circumstances// when the request is actually blocked by upcoming rules (not only degrade rules).if (entry.getBlockError() != null) {// Fallback to OPEN due to detecting request is blockedcurrentState.compareAndSet(State.HALF_OPEN, State.OPEN);notifyObservers(State.HALF_OPEN, State.OPEN, 1.0d);}}});return true;}return false;
}

流程如下
在这里插入图片描述

熔断器开关时机

熔断开关时期应该是触发配置阈值时, 但是数据何时采集?

entry()为请求入口, 此时还没结束, 无法获取到异常数, RT相关信息, 而exit()请求出口, 此时请求已经结束, 可以获取到RT, 异常数相关信息, 所以数据再exit()中采集

代码如下

@Override
public void exit(Context context, ResourceWrapper r, int count, Object... args) {Entry curEntry = context.getCurEntry();if (curEntry.getBlockError() != null) {fireExit(context, r, count, args);return;}List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());if (circuitBreakers == null || circuitBreakers.isEmpty()) {fireExit(context, r, count, args);return;}// 如果没报错,那就调用 onRequestComplete 方法来计数if (curEntry.getBlockError() == null) {// 放行该请求// 这里使用for循环的原因: 因为一个资源的熔断器有多个, 完全可以对某个资源既按照慢调用比例进行熔断又按照异常数进行熔断for (CircuitBreaker circuitBreaker : circuitBreakers) {circuitBreaker.onRequestComplete(context);}}fireExit(context, r, count, args);
}

onRequestComplete的作用计数, 当请求结束时,会根据配置的熔断策略(异常比例或异常数)来更新计数器。如果达到阈值,熔断器状态将从 CLOSED 变为 OPEN ,

具体实现看ExceptionCircuitBreakerResponseTimeCircuitBreaker, 下边分析

ExceptionCircuitBreaker

异常数: errorCount异常数

异常比例: 额外使用totalCount记录请求总数, 异常比例 = errorCount / totalCount

public class ExceptionCircuitBreaker extends AbstractCircuitBreaker {/*请求数据统计*/@Overridepublic void onRequestComplete(Context context) {Entry entry = context.getCurEntry();if (entry == null) {return;}Throwable error = entry.getError();// 获取当前值SimpleErrorCounter counter = stat.currentWindow().value();// 如果此次请求报错了,则将 errorCount + 1if (error != null) {counter.getErrorCount().add(1);}// 将 totalCount 总数 + 1,用于计算异常比例counter.getTotalCount().add(1);// 根据当前请求的异常数/异常比例与设定阈值的关系,调用handleStateChangeWhenThresholdExceeded(error)方法来执行相应的状态变更操作handleStateChangeWhenThresholdExceeded(error);}/*熔断器开关变化逻辑处理*/private void handleStateChangeWhenThresholdExceeded(Throwable error) {// 如果当前熔断器已经打开了,则直接返回。if (currentState.get() == State.OPEN) {return;}// 如果是半开启状态if (currentState.get() == State.HALF_OPEN) {// 如果本次请求没出现异常,则代表可以关闭熔断器了,因此 fromHalfOpenToClose 关闭熔断器if (error == null) {// 这里面会通知各个观察者fromHalfOpenToClose();} else {// 如果本次请求还是异常,那就继续熔断,打开熔断器// 这里面会通知各个观察者fromHalfOpenToOpen(1.0d);}return;}// 执行到这里, 说明熔断器处于一个关的状态List<SimpleErrorCounter> counters = stat.values();// 异常数量long errCount = 0;// 请求总数long totalCount = 0;for (SimpleErrorCounter counter : counters) {errCount += counter.errorCount.sum();totalCount += counter.totalCount.sum();}// 如果请求总数没超过最小请求数,那直接放行if (totalCount < minRequestAmount) {return;}// curCount表示为当前配置熔断触发阈值, 配置熔断策略不同, 含义也不同double curCount = errCount;// 熔断策略为异常比例if (strategy == DEGRADE_GRADE_EXCEPTION_RATIO) {// 计算异常比例, 公式: 异常比例 = 异常数 / 总请求数curCount = errCount * 1.0d / totalCount;}// 当错误率或者错误数大于阈值,则打开熔断器if (curCount > threshold) {// 这里面会通知各个观察者transformToOpen(curCount);}}
}

异常数/异常比例的熔断降级了流程如下
在这里插入图片描述

ResponseTimeCircuitBreaker

这里主要统计的是慢比例调用数据,

慢比例计算公式如下

响应时间RT = 请求结束时间 - 请求开始时间

public class ResponseTimeCircuitBreaker extends AbstractCircuitBreaker {@Overridepublic void onRequestComplete(Context context) {// 获取滑动窗口统计的当前窗口内的慢请求计数器实例SlowRequestCounter counter = slidingCounter.currentWindow().value();Entry entry = context.getCurEntry();if (entry == null) {return;}long completeTime = entry.getCompleteTimestamp();if (completeTime <= 0) {completeTime = TimeUtil.currentTimeMillis();}// 计算请求响应时间(耗时),即完成时间减去创建时间long rt = completeTime - entry.getCreateTimestamp();// 判断响应时间是否超过最大允许响应时间,若超过则将慢请求计数加1if (rt > maxAllowedRt) {counter.slowCount.add(1);}// 不论请求是否为慢请求,都将总请求计数加1counter.totalCount.add(1);// 根据当前请求的响应时间与设定阈值的关系,调用handleStateChangeWhenThresholdExceeded(rt)方法来执行相应的状态变更操作handleStateChangeWhenThresholdExceeded(rt);}
}
降级

降级就是抛出异常, 抛出异常也是降级的一种手段,Slot 相当于过滤器链,过滤器阶段就给拦截了,就不会进入到主业务流程当中,也就不会去查询数据库等一系列业务逻辑。当然,你可以捕获这个异常做一些你想做的事情,这就是降级

总结

熔断器分类和原理

  1. 异常熔断器
    • 负责异常数/异常比例
    • 请求结束时统计异常数和请求总数, 判断是否达到阈值, 达到阈值更改熔断器状态
  2. RT熔断器:
    • 负责的是响应时间
    • 计算请求结束和请求开始的差值, 和阈值比较, 判断是否达到阈值, 达到阈值更改熔断器状态

熔断器大体流程

  1. 计数
  2. 对比阈值
  3. 熔断器验证

状态流转

  1. OPEN: 熔断器打开, 系统进入熔断状态
  2. HALF_OPEN: 熔断器半开, 系统放行部分请求, 如果请求通过, 熔断器切回关闭状态, 如果请求出现异常, 熔断器切回打开, 继续熔断
  3. CLAOSE: 熔断器关闭, 系统正常

如下图

HALF_OPEN像是一个中间态

在这里插入图片描述

参考资料

通关 Sentinel 流量治理框架 - 编程界的小學生

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

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

相关文章

章节2:单词本该这样记

为什么我们记不住单词&#xff1f; 单词不是被胡编乱造出来的&#xff0c;单词是有规律的&#xff0c;单词是符合人类的逻辑的。 单词实际意思结构意义历史文化 我们要怎么记单词&#xff1f; 掌握单词的结构规律了解与单词有关的历史文化灵活巧计&#xff0c;不要太拘泥于…

软考78-上午题-【面向对象技术3-设计模式】-结构型设计模式01

一、适配器模式 1-1、意图 个类的接口转换成客户希望的另外一个接口。 Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 1-2、结构 适配器模式分为&#xff1a; 1、适配器类模式&#xff1b; 2、适配器对象模式 类适配器使用多重继承对一个接口与另…

Java微服务分布式事务框架seata

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Search)

搜索框组件&#xff0c;适用于浏览器的搜索内容输入框等应用场景。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 无 接口 Search(options?: { value?: string, placeholder?: Reso…

使用 Jenkins 管道在 Docker Hub 中构建 Docker 镜像

Jenkins Pipeline 是一个强大的工具&#xff0c;可以自动执行部署。在各个阶段之间拆分的灵活和自定义操作是尝试此功能的一个很好的理由。 构建您自己的 Docker 镜像并将其上传到 Docker Hub 以保持存储库更新是了解 Jenkins Pipeline 如何改进您的工作方式的一个很好的示例。…

数据结构(三)——栈

三、栈、队列和数组 3.1 栈 3.1.1 栈的基本概念 线性表是具有相同数据类型的n&#xff08;n≥0&#xff09;个数据元素的有限 序列&#xff0c;其中n为表长&#xff0c;当n 0时线 性表是一个空表。若用L命名线性表&#xff0c;则其一般表示为 L (a1, a2, … , ai , ai1, ……

Java基础—2

1.面向对象 1.1 与面向过程区分 了解 Java是一种面向对象的编程语言&#xff0c;与面向过程的编程方式有明显的区别。 思维方式&#xff1a;面向对象编程&#xff08;OOP&#xff09;是基于对象的概念&#xff0c;强调将问题分解为对象&#xff0c;通过对象之间的交互来解决…

【GPT-SOVITS-02】GPT模块解析

说明&#xff1a;该系列文章从本人知乎账号迁入&#xff0c;主要原因是知乎图片附件过于模糊。 知乎专栏地址&#xff1a; 语音生成专栏 系列文章地址&#xff1a; 【GPT-SOVITS-01】源码梳理 【GPT-SOVITS-02】GPT模块解析 【GPT-SOVITS-03】SOVITS 模块-生成模型解析 【G…

Ansible管理主机的清单------------inventory

目录 一、 Ansible组成 二、inventory 主机清单 (1)主机变量使用 (2)inventory 中的变量含义 (3)组变量使用 (4)组嵌套使用 三、ansible命令 一、 Ansible组成 INVENTORY&#xff1a;Ansible管理主机的清单 /etc/ansible/hosts 需要管理的服务清单,(将你需要管理的主机 …

机器学习-绪论

机器学习致力于研究如何通过计算的手段、利用经验来改善系统自身的性能。在计算机系统中&#xff0c;“经验”通常以“数据”的形式存在&#xff0c;因此&#xff0c;机器学习所研究的主要内容&#xff0c;是关于在计算机上从数据中产生“模型”的算法&#xff0c;即“学习算法…

排序算法:快速排序(递归)

文章目录 一、创始人托尼霍尔的快速排序二、挖坑法三、前后指针法 所属专栏:C初阶 引言&#xff1a;这里所说的快速排序有三种&#xff0c;第一种是霍尔大佬自创的&#xff0c;还有一种叫做挖坑法&#xff0c;另外一种叫前后指针法 一、创始人托尼霍尔的快速排序 1.这里我们先…

VUE3 异步组件

概念 在大型项目中&#xff0c;我们可能需要拆分应用为更小的块&#xff0c;并仅在需要时再从服务器加载相关组件。Vue 提供了 defineAsyncComponent 方法来实现此功能&#xff1a; 使用 父组件 <template><div><asyncSon></asyncSon></div> <…

敏感信息泄露到接管云服务器

通过信息收集发现子域为xx.xx.com网站&#xff0c;打开先找功能点&#xff0c;测试登录&#xff0c;是微信扫描登录&#xff0c;自己太菜&#xff0c;测试一圈没测出来什么 指纹识别发现是js开发&#xff0c;如果登录或者找回密码不是扫码登录的话&#xff0c;八成是前端验证&a…

性能测试-Jmeter常用元件基础使用

一、Jmeter元件 #线程组 添加HTTP请求 #配置元件 配置元件内的元件都是用于进行初始化的东西 #监听器 监听器主要是用来获取我们使用取样器发送请求后的响应数据相关信息 #定时器 定时器主要用来控制我们多久后执行该取样器&#xff08;发送请求&#xff09; #前置处理器 前置处…

为什么手机和电视ip地址不一样

在数字化时代&#xff0c;我们每天都会与各种电子设备打交道&#xff0c;其中最常见的就是手机和电视。当我们连接到互联网时&#xff0c;这些设备都会被分配一个独特的IP地址&#xff0c;用于在网络上进行标识和通信。然而&#xff0c;您可能已经注意到&#xff0c;即使手机和…

1.MongoDB的特点与应用场景

什么是 MongoDB &#xff1f; MongoDB 是基于 C 开发的 NOSQL 开源文档数据库 &#xff0c;是最像关系型数据库的 nosql&#xff0c;功能也是最丰富的 nosql&#xff0c;它具有所以的可伸缩性&#xff0c;灵活性&#xff0c;高性能&#xff0c;高扩展性的优势。 大致有如下特…

【Stable Diffusion】入门-04:不同模型分类+代表作品+常用下载网站+使用技巧

目录 1 模型简介2 模型文件构成和加载位置2.1 存储位置2.2 加载模型 3 模型下载渠道3.1 HuggingFace3.2 Civitai 4 模型分类4.1 二次元模型4.2 写实模型4.3 2.5D模型 1 模型简介 拿图片给模型训练的这个过程&#xff0c;通常被叫做“喂图”。模型学习的内容不仅包括对具体事物…

云平台基本介绍 —— 什么是云原生及云服务器的购买和使用

云原生概述 在了解什么是云原生之前&#xff0c;我们先了解一下什么是云计算 什么是云计算 云计算是一种通过互联网提供计算资源和服务的模式。它允许用户通过网络访问虚拟化的计算资源&#xff0c;包括计算能力、存储空间和应用程序&#xff0c;而无需拥有实际的物理设备。…

uniapp h5 部署

uniapp 配置 服务器文件路径 打包文件结构 //nginx 配置 server {listen 8300;server_name bfqcwebsiteapp;charset utf-8;#允许跨域请求的域&#xff0c;* 代表所有add_header Access-Control-Allow-Origin *;#允许带上cookie请求add_header Access-Control-Allow-C…

WRF模型运行教程(ububtu系统)-- IV-1.模型相关文件参数说明【namelist.wps文件、namelist.input文件】

一、namelist.wps文件 文件位置&#xff1a;Build_WRF/WPS WPS模块有主要的三大程序geogrid.exe、ungrib.exe、metgrid.exe&#xff0c;namelist.wps文件是输入到这三大程序的配置文件。 namelist.wps文件一共包括四个部分&#xff1a;share, geogrid, ungrib和metgrid。 每个主…