浅谈微服务异步解决方案

导言

异步是一种设计思想,不是设计目的,因此不要为了异步而异步,要有所为,有所不为。 异步不是『银弹』, 避免试图套用一个『异步框架』解决所有问题, 需要根据不同的业务特点或要求,选择合适的设计实现方式

同步和异步问题是大型分布式系统中需要慎重等待的问题,然而笔者发现公司和部门内相关问题系统性讨论较少, 因此笔者试图通过本文展开交流讨论。

异步定义

在软件设计领域, 异步和同步是一对孪生的设计思想

同步是一种阻塞式且有严格执行时序的设计思想, 必须一件一件事做, 等前一件做完了才能做下一件事。

异步则是一种非阻塞的设计思想, 可以同时做多件事,没有严格的执行顺序。

同步调用方(客户端)在请求发起后会一直阻塞并等待被调用方(服务端)返回响应结果,且仅当调用方获取响应结果后才会继续或终止执行。 例如近期较为常见的『核酸采样亭』虽然采样亭内有多人协作,但是为了保证采样人管一致, 核酸采样队伍就是顺序行进的, 前一个没有录入完,也不会继续下一位。

异步调用方(发布方)无需等待被调用方(订阅方)执行完所有逻辑,就可继续执行后续事情。例如一些餐馆的『排队』场景,往往点餐区仅需要完成『点餐收银』动作, 之后订单会转到后厨进行制作, 此时点餐人无需继续在点餐去等待, 可以去找位置坐下等取餐提醒, 或者心急的话在取餐区排队等待。

同步

异步

特征

1)阻塞:调用发起方在请求提交后不会向系统交出控制权,而是持续等待被调用方返回响应结果。
2)强时序:由于阻塞式的交互,从而保证了调用发起方和被调用方交互的强时序。

1)非阻塞:任务提交后将控制权交予系统,系统可以进行其他任务的执行
2)无严格的执行时序: 调用发起方和被调用方执行逻辑无时序依赖

优点

1. 严格保证时序: 同步流程是最天然的控制过程顺序执行的方式, 因此对结果的处理始终和前文保持在一个上下文内。
2. 实时获取执行结果:调用方一定会等待被调用方返回执行结果
3. 异常处理方便:同步流程可以很容易捕获、处理异常。

1. 逻辑解耦:可在模块、服务、接口等不同粒度上实现解耦, 便于进行功能降级,提升系统稳定性
2. 故障隔离:在服务解耦的情况下, 异步执行的流程不会影响其他服务或流程, 避免出现雪崩的情况。
3. 降低接口延迟: 异步流程可以通过剥离非主流程逻辑, 尽快给调用方返回处理的结果。
4. 提升资源利用率:异步流程在执行的过程中,可以释放占用的线程等资源,避免阻塞,等到结果产生再重新获取线程处理。

缺点

1. 耦合度高: 调用方强依赖被调用方的状态
2. 资源利用率低: 由于同步流程是顺序串行执行的, 调用方在获取被调用方返回前系统资源大多处于等待状态, 调用方的吞吐率会受限于被调用方的吞吐率
3. 性能下降:一旦被调用方某个服务响应特别慢,那么整个调用链的性能都会受到影响。
4. 稳定性: 被调用方一旦失败,会导致调用方也跟着失败,从而引起调用链雪崩的问题

1. 数据一致性影响:由于调用发起方和被调用方通过异步进行了解耦, 数据一致性难以保证, 需要提供额外机制进行补偿。
2. 维护成本高: 异步执行没有严格的时序, 会增加问题排查难度
3. 冲击下游依赖系统稳定性: 异步设计在提升现有系统吞吐的同时,也会增加下游系统资源损耗, 如设计不当,可能会影响下游依赖系统稳定性。

设计要点

在异步设计过程中需要重点关注的指标: 数据一致性

  • 数据防丢失
  • 幂等

数据一致性需求

什么是数据一致性

  • 强一致性:也称为原子一致性,线性一致性。即任意时刻,所有节点中的数据应该是一样的。任何节点的读操作都能读取到某个数据的最近一次写的数据。关系数据库 的本地事务( ACID )来保证数据的强一致性。
  •  弱一致性:有很多种不同的实现方式。目前分布式系统中广泛实现的是最终一致性。最终一致性是弱一致性的一种特例,保证用户最终能够读取到某个数据的更新。BASE 来做数据的最终一致。 BASE: basically-available, soft-state, eventual consistency.

为什么会有一致性的需求

  • 不同的业务场景涉及到操作同一条数据,处理不当时,可能导致多次操作后数据丢失,不一致
  • 为了提高系统的吞吐量,对于数据实时性要求不高的场景,一般会考虑读写分离,从而衍生主从数据库一致性问题;
  • 为了提高系统的处理速度,当前都会使用各种缓存机制(localCache、redis 等等),从而衍生出数据库与各种缓存一致性问题;
  • 为了应用解耦、流量削峰,实现系统的高可用、可扩展等,当前都会使用各种消息队列,异步消息的处理也会衍生出数据一致性问题;

如何解决一致性问题

  • 强一致场景:
    • 数据库事务
    • 锁(悲观锁、乐观锁)
    • 分布式事务(正向 SAGA 保证最终一致、反向 SAGA 进行事务补偿)
  • 最终一致场景:
    • 幂等设计
    • 分布式对账

数据防丢失

为什么会出现数据丢失?

  • 分布式消息队列生产和消费:

  • Case 1: 消息发送环节,在消息生产方 Publisher 在业务逻辑处理完成后, 在步骤 1 没能正确发送到 MQ 导致消息丢失
  • Case 2: 消息接受处理环节, Subscriber 在消费逻辑没有正确处理前就像 MQ Server 发送 ACK, 导致消息丢失
  • 服务异常退出:
    • 主进程发送异步消息时没有记录当时的任务场景, 如主进程异常退出,会导致任务状态无法及时感知。

如何防止数据丢失?

  • 针对分布式消息队列生产和消费:
    • Case 1: 消息发送环节, 为了避免消息丢失
      • 延迟发送:将消息现在内存中缓存起来, 然后通过延迟队列进行发送(并增加重试机制)
    • Case 2: 消息消费环节
      • 在消息被正确响应后才对消息 ACK
      • 增加死信队列, 对处理异常的消息推送至死信队列, 然后对该队列消息进行单独处理。
  • 针对服务异常退出:
    • 在异步处理前,将现场必要的信息(例如异步任务对应的租户、任务 ID 等信息)进行持久化(通过 redis 或 mysql)进行存储。 并通过事务在发送迁进行持久化, 如果消息发送失败, 可通过持久化状态进行重试。

幂等

什么是幂等  idempotent )?

幂等性: 在计算机领域, 对于一个操作, 在输入相同时,如果它确保对我们关注的影响,在多次执行时和一次执行时相同, 那么这个操作对于我们来说就是幂等的。

在分布式环境下之所有强调幂等性是由于对通信链路的不信任,我们的请求可能由于网络问题或依赖服务的稳定性而出错进而需要做重试,而如果我们对应的接口没有做请求去重或进行幂等设计就可以导致重复处理引发数据错误。

什么场景需要考虑幂等问题

  • 网络波动: 因网络波动,由于网关没有及时获得响应,可能出现重复请求的问题
  • 分布式消息消费:

  • Case 1: 消息发送环节,如果 Publisher 没有收到来自 MQ Server 的 ACK 消息, 会根据重试设置进行重复生产消息
  • Case 2: 消息接受处理环节,如果 MQ Server 没有收到来自 Subscriber 的消息消费的 ACK 消息, 可能会重复投递消息
  • 用户重复操作: 用户在使用产品时,可能会误操作而触发多笔交易,或者因为长时间没有响应,而有意触发多笔交易。
  • 未关闭的重试机制: 技术人员人为的错误,因开发人员、测试人员或运维人员没有检查出来,而开启的重试机制(如 Nginx 重试、RPC 通信重试或业务层重试等)

如何解决幂等问题

  1. 识别产生幂等的场景唯一 key
  2. 通过接入侧去重或数据库兜底拦截的方式根据业务唯一 key 进行判断

适用场景

实现要点

实现方式

token 机制 (业务唯一标志)分布式锁

接口重放(攻击)用户主动的重复创建

Token 用于控制过滤重复动作,是指在动作流转过程中控制有效请求数量。 为每个请求分配一个具有业务含义的唯一 ID,例如组织架构调整过程中的 draftID 或是商品订单提交场景的 订单 ID

  1. 服务端提供了发送 token 的接口。执行业务前先去获取 token,同时服务端会把 token 保存到 redis 中;
  2. 然后业务端发起业务请求时,把 token 一起携带过去;
  3. 服务器判断 token 是否存在 redis 中,如不存在即第一次请求,可继续执行业务,执行业务完成后将 token 从 redis 中删除;
  4. 如果判断 token 存在 redis 中,就表示是重复操作,直接返回重复标记给 client,这样就保证了业务代码不被重复执行。 分布式锁:这是一个比较有意思的话题,业内有丰富的讨论,在本文中暂不展开描述

唯一索引,防止新增脏数据

适用于创建场景,利用数据库唯一键约束

该方式主要利用数据库本身的唯一键约束

根据业务场景设计唯一键

  1. idGenerator: 全局唯一键
  2. UUID: 结合机器的网卡、当地时间、一个随记数来生成 UUID
  3. 数据库自增 ID: 不推荐,数据自增仅可保证单表唯一, 在分布式场景下可能出现分库分表, 需要配合处理, 复杂度较高

悲观锁

适用于更新场景

  1. 简单理解就是:假设每一次拿数据,都有认为会被修改,所以给数据库的行或表上锁。
  2. 悲观锁适用于在事务中执行

当数据库执行 select for update 时会获取被 select 中的数据行的行锁,因此其他并发执行的 select for update 如果试图选中同一行则会发生排斥(需要等待行锁被释放),因此达到锁的效果。

乐观锁

适用于更新场景

简单理解就是在数据更新时需要去比较持有数据的版本号,版本号不满足条件的操作无法成功

  1. 为更新增加 version 的概念
  2. 条件更新,例如增加时间戳作为比较条件

状态机

本质也是乐观锁的一种,适用于业务有多种状态,且状态间流转是有向图的场景

主要思路就是通过状态标识的变更,保证业务中每个流程只会在对应的状态下执行,如果标识已经进入下一个状态,这时候来了上一个状态的操作就不允许变更状态,保证了业务的幂等性

状态机是一种强业务相关的设计, 需要根据具体场景处理

从 ROI 方面考虑, 乐观锁 > 唯一键约束 > 悲观锁

异步方案

Notification 方式

Notifications - a sender sends a message a recipient but does not expect a reply. Nor is one sent.

设计要点:

  1. 在代码中开启协程进行异步通知, 无需关心协程的执行成功与否
  2. 此类方法适仅适用于可以随时降级的非核心功能, 功能的执行成功与否不影响主进程的逻辑

流程:

  1. 在函数进程中开启 子协程 Notification
  2. 主进程继续执行, 不关心协程 Notification 的执行状态以及结果

优点:

  1. 提升主进程执行性能
  2. 可以实现故障隔离, 将非核心功能与核心功能剥离, 可做到服务降级

缺点:

  • Notification 协程的状态不可知

应用场景:

  • Metrics 打点
  • 日志打印
// example:  metrics 埋点
func CallExampleRpc(ctx context.Context, req *example.ExampleRequest) (*example.ExampleResponse, error) {
        const method = "CallExampleRpc"
        callRes := RPCCallResultSuccess
        defer func() {
                // 将 RPC Call 的执行结果上报 metrics
go MetricsEmitRPCCallerResult(cli.psm, cli.psm, callRes, method )
        }()
        
        // some logic here
        ... ...
}

接口响应式设计

获取被调用方响应常见有两种方式:

  • 轮询(pull):任务的发起方通过轮询的方式调用事件接收方执行状态或请求任务状态的共享存储(redis 或 mysql)
  • 回调 (push):发起方提供 callback 接口方法, 由异步事件接收方在消费后调用

轮询模式

设计要点:

  1. 相比于 4.1 Notification 的方式, 在接口相应式设计中,异步发起方需要获取被调用方的响应
  2. 相比与同步, 主要的提升是将部分逻辑拆分到另一个异步流程中进行
  3. 该模式适用于异步流程中的逻辑可以拆分成多个可并行独立执行的子流程, 从而提升执行效率

流程:

  1. 事件发起方触发异步任务,然后进入轮询等待状态
  2. 事件接受方在 Goroutine 中执行任务逻辑,并更新任务状态到 redis 中
  3. 事件发起方轮询 check redis 中的任务状态,查看任务执行成功或失败

优点:

  1. 实现简单,任务比较轻量级
  2. 通过 redis 可以监控任务状态,判断任务状态和运行异常

缺点:

  1. 如果程序退出,任务会存在丢失的情况,并且无法重试恢复
  2. 对于任务本身并没有持久化的记录,例如入参出参,出现数据不一致的情况无法对账对数据进行修复

场景分析: 适用于本身异步任务并不产生数据,对任务是否失败不是很敏感的场景,例如 用户进入画布时的冲突校验。这个场景下,冲突校验本身并不生产新的数据,不会由于任务丢失或失败产生脏数据影响用户的使用。另外这个校验场景本身对任务的成功或失败并不敏感,如果任务失败了,不需要自动重试校验,用户点击刷新页面,重新访问接口即可重新触发另一次的校验

// 通过基于 promise 模式实现的 goroutine 协程
        f := future.WhenAll(
            func() (interface{}, error) {
                return CheckDepartmentModifyProcess(realCtx, modifyDepChangeList, params)
            },
            // 用于示例, 截取部分片段
            ...
            func() (interface{}, error) {
                return CheckDepartmentDeactiveProcess(realCtx, deactiveDepChangeList, modifyParentOrCreateChangeList, params)
            },
        )
         // 等待异步流程执行完成,然后处理执行结果
    if maxExecuteTime != nil {
    res, err, timeout = f.GetOrTimeout( * maxExecuteTime)
    } else {
    res, err = f.Get()
    }

回调模式

设计要点:

  1. 相比于 4.1 Notification 的方式, 在接口相应式设计中,异步发起方需要获取被调用方的响应
  2. 相比与同步, 主要的提升是将部分逻辑拆分到另一个异步流程中进行
  3. 该模式适用于异步流程中的逻辑可以拆分成多个可并行执行的子流程, 从而提升执行效

流程:

  1. 事件发起方触发异步任务,然后继续执行后续其他逻辑
  2. 事件接受方在 Goroutine 中执行任务逻辑,当执行结束后,会通过事件发起方提供的 callback 方法传递执行结果
  3. 事件发起方在 callback 方法中接收事件接收方的结果, 并执行后续的回调逻辑

优点:

  • 事件发起方在触发事件后无需等待,资源利用率会得到提升

缺点:

  1. 如果程序退出,任务会存在丢失的情况,并且无法重试恢复
  2. 对于任务本身并没有持久化的记录,例如入参出参,出现数据不一致的情况无法对账对数据进行修复
  3. 回调嵌套的难度。通常被用作回调的函数,经常最终需要自己的回调。这导致了一系列回调嵌套并导致出现难以理解的代码。该模式通常被称为标题圣诞树(大括号代表树的分支)。
  4. 错误处理很复杂。嵌套模型使错误处理和传播变得更加复杂。
// 回调示例
func asyncHandler(ctx context.Context, param *AsyncParam) (error) {
    // do something
   defer handleAsyncErrorCallback(error)
    
}

func handleAsyncErrorCallback(error) {
   // do something
}

func doSomething(ctx context.Context, param *Param)() {
    // do something
    go asyncHandler(param)
    // do something else 
}

发布订阅式设计

设计要点:

通过 Rocketmq 发送 task 到队列,开启 consumer 消费任务,并使用 redis / DB 对任务进行状态的监控

流程:

1. 用户触发流程操作,提交 task 任务到 rocketmq 的 producer 中
2. Rocketmq 的 consumer 接收到 msg,并在 handler 中执行回调逻辑,并更新 redis/DB 状态
3. 用户 check redis/DB 中的状态,确定任务执行成功还是失败。同时也可以通过 check rocketmq 的消费 ack 情况判断任务是否真的执行完成

优点:

  1. 实现相对简单,任务执行比较轻量
  2. 任务执行和任务发起的机器可以不是同一台,对于复杂逻辑可以在一定程度上进行负载均衡
  3. Rocketmq 本身自带消息的 ack 功能,如果 task 消费失败,可以自动重试 (也可以通过参数设置不重试)
  4. Rocketmq 对消息会进行存储,可以通过 msg 存储的 task 进行对账,在必要时候修复不一致的数据

缺点:

  1. Rocketmq ACK 本身有时长限制 (Rocketmq 默认是 600s, 不过可以调),如果任务本身比较复杂,执行时间超长后,rocketmq 自动任务任务失败
  2. Rocketmq 消息在特殊情况下也有可能存在重复投递的情况,在一些情况下同一个 task 可能会被多次执行,如果任务本身不支持幂等则可能导致数据不一致的出现

场景分析:
由于 rocketmq 自动重试的特性,这种类型更适合,task 逻辑相对简单,且本身并不生产新的数据,但是任务本身是否成功比较敏感,在任务执行失败后,可以进行自动重试,确保服务的稳定

基于任务管理(托管)

设计要点:

本方案会引入三方分布式任务管理框架 通过 Scheduler Job 去触发任务,使用 Scheduler 本身的能力记录 task,通过 redis 记录 task 运行中间过程

流程:

  1. 用户提交 task,首先在 redis/DB 中创建一条 task record 用于记录任务执行状态
  2. 调用 Scheduler Job,通过 Processor 执行任务逻辑
  3. 在 Process 执行任务同时,在 redis / DB 中更新任务状态,如果出现 error 信息,也更新到 redis 中
  4. 通过 Check redis/DB 中的任务状态判断执行是否成功,通过 Scheduler 平台的任务记录完成任务的对账

优点:

  1. 借助 Scheduler 平台能力,可以对 task 记录进行持久化记录,方便后续 check 任务状态以及数据对账
  2. Scheduler 支持分片能力,相比 rocketmq 在投递时候通过 queue 进行 balance,scheduler 在更高并发的场景下,可以让任务分拆到不同的机器上执行,提高并发能力
  3. Scheduler 支持分布式调度,可以将多个并发 task 的执行结果聚合,并统一处理返回

缺点:

  1. 接入整体比较重型,接入成本较高,本身 Scheduler 只是一个分布式任务调度平台,业务需要自己设计实现来适配自己的业务场景
  2. 任务的失败重试需要业务自己进行监控,判断是否重试,scheduler task 本身也不配有断点重试功能,重试情况下仍需要业务自己保证代码逻辑幂等

场景分析:

由于 Scheduler 本身很重,因此本身更适合去执行逻辑复杂的 task,Scheduler 通过长链接监控任务执行状态,确保不会因为超时导致任务状态异常
另外由于 Scheduler 的 task 信息持久化的能力,如果任务本身会生产处理新的数据,这样的任务也更适合使用 Scheduler 进行任务管理,方便后续确保数据一致性问题时,进行对账

分享小结

本文从异步定义开始,通过对异步设计要点(数据一致性)的分析,推出异步设计的几种常见方案。

在异步设计过程中需要重点关注的指标: 数据一致性

  • 数据防丢失
  • 幂等

异步设计常见解决方案:(根据数据一致性要求从低到高排序)

  • Notification 消息通知模式
  • 接口响应式设计
  • 发布订阅式设计
  • 基于任务管理(托管)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VSCode自定义闪烁光标

打开VSCode 组合键ctrlshiftp搜索"settings.json",打开User Settings 加上这一句 "editor.cursorStyle": "block","workbench.colorCustomizations": {"editorCursor.foreground": "#5c8fb1","terminalCurs…

IDEA中Git面板操作介绍 变基、合并、提取、拉取、签出

IDEA中Git面板操作介绍 变基、合并、提取、拉取、签出 面板介绍 变基、合并 提取、拉取 签出、Checkout 面板介绍 如图,在IDEA的Git面板中,仓库会分为本地仓库和远程仓库,代码仓库里面放的是各个分支。 分支前面的书签🔖标志…

JavaScript高级——ES6基础入门

目录 前言let 和 const块级作用域模板字符串一.模板字符串是什么二.模板字符串的注意事项三. 模板字符串的应用 箭头函数一.箭头函数是什么二.普通函数与箭头函数的转换三.this指向1. 全局作用域中的 this 指向2. 一般函数(非箭头函数)中的this指向3.箭头…

MongoDB文档--基本安装-linux安装(mongodb环境搭建)-docker安装(挂载数据卷)-以及详细版本对比

阿丹: 前面了解了mongodb的一些基本概念。本节文章对安装mongodb进行讲解以及汇总。 官网教程如下: 安装 MongoDB - MongoDB-CN-Manual 版本特性 下面是各个版本的选择请在安装以及选择版本的时候参考一下: MongoDB 2.x 版本&#xff1a…

ELK日志分析系统概述及部署

ELK 平台是一套完整的日志集中处理解决方案,将 ElasticSearch、Logstash 和 Kibana 三个开源工具配合使用,完成更强大的用户对日志的查询、排序、统计需求。 一、ELK概述 1、组件说明 ①ElasticSearch ElasticSearch是基于Lucene(一个全文…

XML约束和解析

文章目录 概述使用场景语法dtd约束Schema约束解析DOM4j(重点) 概述 可扩展的标记性语言 使用场景 以前: 传输数据的媒介。 例如:微服务架构中,可以用xml文件进行多语言之间的的联系。 现在: 做配置文件 现在作为传输数据的媒介…

使用Gunicorn+Nginx部署Flask项目

部署-开发机上的准备工作 确认项目没有bug。用pip freeze > requirements.txt将当前环境的包导出到requirements.txt文件中,方便部署的时候安装。将项目上传到服务器上的/srv目录下。这里以git为例。使用git比其他上传方式(比如使用pycharm&#xff…

Matlab Optimization Toolbox中的遗传算法工具包(GA)

matlab optimization 中使用了GA求解器 默认的是小于等于 找到GA 工具包 找到 APP选择 Optimization Tool 选择Solver ga - Genetic Algorithm 应用GA solver 定义适应度函数(Fitness function)与问题约束(Constraints) example one 优化函数 sin(x) 2 * cos(x)极其重要的…

浅谈3D隐式表示(SDF,Occupancy field,NeRF)

本篇文章介绍了符号距离函数Signed Distance Funciton(SDF),占用场Occupancy Field,神经辐射场Neural Radiance Field(NeRF)的概念、联系与区别。 显式表示与隐式表示 三维空间的表示形式可以分为显式和隐式。 比较常用的显式表…

高并发与性能优化的神奇之旅

作为公司的架构师或者程序员,你是否曾经为公司的系统在面对高并发和性能瓶颈时感到手足无措或者焦头烂额呢?笔者在出道那会为此是吃尽了苦头的,不过也得感谢这段苦,让笔者从头到尾去探索,找寻解决之法。 目录 第一站…

让数据管理由繁至简的低代码开发平台

随着社会数字化能力的快速升级,各行各业正逐渐迈向数字化转型的新时代。尤其是AI的爆发,数据智能技术正在彻底改变着这个行业的面貌,随着越来越多的企业开始将人工智能、机器学习和大数据分析技术应用到其业务中,数据的价值正在得…

VirtualBox Ubuntu无法安装增强功能以及无法复制粘贴踩坑记录

在VirtualBox安装增强功能想要和主机双向复制粘贴,中间查了很多资料,终于是弄好了。记录一下过程,可能对后来人也有帮助,我把我参考的几篇主要的博客都贴上来了,如果觉得我哪里讲得不清楚的,可以去对应的博…

Vue.js2+Cesium 四、模型对比

Vue.js2Cesium 四、模型对比 Cesium 版本 1.103.0&#xff0c;低版本 Cesium 不支持 Compare 对比功能。 Demo 同一区域的两套模型&#xff0c;实现对比功能 <template><div style"width: 100%; height: 100%;"><divid"cesium-container"…

论文笔记:Adjusting for Autocorrelated Errors in Neural Networks for Time Series

2021 NIPS 原来的时间序列预测任务是根据预测论文提出用一阶自回归误差预测 一阶差分&#xff0c;类似于ResNet的残差思路&#xff1f;记为pred&#xff0c;最终的预测结果

Java:Map的getOrDefault()方法结果仍为null

1、问题 今天在工作中遇到一个问题&#xff0c;在一个通用的数据处理方法中&#xff0c;方法会从一个Map类型参数通过key里获取对象value&#xff0c;但方法的调用者并不都会传递value实例&#xff0c;若没有获取到value则需要初始化一个&#xff0c;处理方式是调用了Map的getO…

大牛练成记:用JavaScript徒手写出一个日期选择插件

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;100个JavaScript的小应用。 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收…

【C语言】初识指针

【C语言】初识指针 一、指针是什么&#xff1f;二、指针和指针类型1. 指针-整数2. 指针的解引用三、野指针1.野指针成因2 .如何规避野指针四、指针运算五、二级指针七、指针数组 &#x1f388;个人主页&#xff1a;库库的里昂&#x1f390;CSDN新晋作者&#x1f389;欢迎 &…

2023年中职组“网络安全”赛项吉安市竞赛任务书

2023年中职组“网络安全”赛项 吉安市竞赛任务书 一、竞赛时间 总计&#xff1a;360分钟 竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 A模块 A-1 登录安全加固 180分钟 200分 A-2 本地安全策略配置 A-3 流量完整性保护 A-4 事件监控 A-5 服务加固…

自定义el-slider 滑块的样式

最近用到了element组件中的滑块&#xff0c;翻看了官网和网上一些案例&#xff0c;感觉和我要的样式都不太一样&#xff0c;下面记录一下我用到的两种自定义滑块。 效果图 第一种自定义画过的间断点样式 起始样式 滑动的样式 第二种自定义拖动滑块的样式 起始样式 滑动的样…

linux环境安装mysql数据库

一&#xff1a;查看是否自带mariadb数据库 命令&#xff1a;rpm -qa | grep mariadb 如果自带数据库则卸载掉重新安装 命令&#xff1a;yum remove mariadb-connector-c-3.1.11-2.el8_3.x86_64 二&#xff1a;下载mysql 命令&#xff1a;wget -i -c http://dev.mysql.com/…