分布式环境中,接口超时重试带来的的幂等问题如何解决?

news2024/11/17 3:35:58

目录标题

  • 幂等不能解决接口超时吗?
  • 幂等的重要性
    • 什么是幂等?
    • 为什么需要幂等?
    • 接口超时了,到底如何处理?
  • 如何设计幂等?
  • 幂等设计的基本流程
  • 实现幂等的8种方案
    • 1.select+insert+主键/唯一索引冲突(常用)
    • 2.直接insert + 主键/唯一索引冲突
    • 3.状态机幂等(常用)
    • 4.抽取防重表
    • 5.token令牌(前后端交互常用)
    • 6.悲观锁(如select for update)(不用)
    • 7.乐观锁
    • 8.分布式锁(分布式环境下常用)
  • HTTP的幂等
    • GET方法
    • HEAD 方法
    • OPTIONS方法
    • DELETE方法
    • POST 方法
    • PUT 方法

幂等不能解决接口超时吗?

处理接口超时问题需要综合考虑多个方面,包括设置合理的超时时间、实现重试机制、引入熔断器、加强监控和报警、记录详细的日志、实施限流和降级策略、采用异步处理方式、优化代码和逻辑、合理管理资源以及使用缓存和负载均衡等。通过这些措施,可以有效提升系统的稳定性和可靠性。同时,确保操作的幂等性是处理重试问题的关键,可以避免因重试导致的数据不一致。

本篇就主要讲解超时重试带来的幂等性问题。幂等性是处理分布式系统中接口超时和重试问题的一个重要概念,但它本身并不直接解决超时问题幂等性是指一个操作可以多次执行而不会改变结果的状态。在处理超时和重试时,确保操作的幂等性可以避免重复操作带来的副作用。

幂等的重要性

当前互联网的系统几乎都是解耦隔离后,会存在各个不同系统的相互远程调用。调用远程服务会有三个状态:成功,失败,或者超时。前两者都是明确的状态,而超时则是未知状态。我们转账超时的时候,如果下游转账系统做好幂等控制,我们发起重试,那即可以保证转账正常进行,又可以保证不会多转一笔。所以掌握幂的用法非常重要!

什么是幂等?

幂等是一个数学与计算机科学概念。

在数学中,幂等用函数表达式就是:f(x) = f(f(x))。比如求绝对值的函数,就是幂等的,abs(x) = abs(abs(x))。
计算机科学中,幂等表示一次和多次请求某一个资源应该具有同样的副作用,或者说,多次请求所产生的影响与一次请求执行的影响效果相同。

为什么需要幂等?

举个例子:
我们开发一个转账功能,假设我们调用下游接口超时了。一般情况下,超时可能是网络传输丢包的问题,也可能是请求时没送到,还有可能是请求到了,返回结果却丢了。这时候我们是否可以重试呢?如果重试的话,是否会多转了一笔钱呢?

在这里插入图片描述

转账超时

当前互联网的系统几乎都是解耦隔离后,会存在各个不同系统的相互远程调用。调用远程服务会有三个状态:成功,失败,或者超时。前两者都是明确的状态,而超时则是未知状态。我们转账超时的时候,如果下游转账系统做好幂等控制,我们发起重试,那即可以保证转账正常进行,又可以保证不会多转一笔。

其实除了转账这个例子,日常开发中,还有很多很多例子需要考虑幂等。比如:
MQ(消息中间件)消费者读取消息时,有可能会读取到重复消息。(重复消费)
比如提交form表单时,如果快速点击提交按钮,可能产生了两条一样的数据(前端重复提交)

在这里插入图片描述

接口超时了,到底如何处理?

如果我们调用下游接口超时了,我们应该怎么处理呢?
有两种方案处理:

  • 方案一:就是下游系统提供一个对应的查询接口。如果接口超时了,先查下对应的记录,如果查到是成功,就走成功流程,如果是失败,就按失败处理。

拿我们的转账例子来说,转账系统提供一个查询转账记录的接口,如果渠道系统调用转账系统超时时,渠道系统先去查询一下这笔记录,看下这笔转账记录成功还是失败,如果成功就走成功流程,失败再重试发起转账。

在这里插入图片描述

  • 方案二:下游接口支持幂等,上游系统如果调用超时,发起重试即可。

在这里插入图片描述
两种方案都是挺不错的,但是如果是MQ重复消费的场景,方案一处理并不是很妥,所以,我们还是要求下游系统对外接口支持幂等。

如何设计幂等?

既然这么多场景需要考虑幂等,那我们如何设计幂等呢?

幂等意味着一条请求的唯一性。不管是你哪个方案去设计幂等,都需要一个全局唯一的ID,去标记这个请求是独一无二的。

  • 如果你是利用唯一索引控制幂等,那唯一索引是唯一的
  • 如果你是利用数据库主键控制幂等,那主键是唯一的
  • 如果你是悲观锁的方式,底层标记还是全局唯一的ID

全局的唯一性ID

全局唯一性ID,我们怎么去生成呢?你可以回想下,数据库主键Id怎么生成的呢?

是的,我们可以使用UUID,但是UUID的缺点比较明显,它字符串占用的空间比较大,生成的ID过于随机,可读性差,而且没有递增。

我们还可以使用雪花算法(Snowflake) 生成唯一性ID。

雪花算法是一种生成分布式全局唯一ID的算法,生成的ID称为Snowflake IDs。这种算法由Twitter创建,并用于推文的ID。

一个Snowflake ID有64位。

  • 第1位:Java中long的最高位是符号位代表正负,正数是0,负数是1,一般生成ID都为正数,所以默认为0。
  • 接下来前41位是时间戳,表示了自选定的时期以来的毫秒数。
  • 接下来的10位代表计算机ID,防止冲突。
  • 其余12位代表每台机器上生成ID的序列号,这允许在同一毫秒内创建多个Snowflake ID。

在这里插入图片描述

当然,全局唯一性的ID,还可以使用百度的Uidgenerator,或者美团的Leaf。

幂等设计的基本流程

幂等处理的过程,说到底其实就是过滤一下已经收到的请求,当然,请求一定要有一个全局唯一的ID标记哈。然后,怎么判断请求是否之前收到过呢?把请求储存起来,收到请求时,先查下存储记录,记录存在就返回上次的结果,不存在就处理请求。

一般的幂等处理就是这样啦,如下:

在这里插入图片描述

实现幂等的8种方案

幂等设计的基本流程都是类似的,我们简简单单来过一下幂等实现的8中方案哈

1.select+insert+主键/唯一索引冲突(常用)

在这里插入图片描述

为什么前面已经select查询了,还需要try…catch…捕获重复异常呢?

是因为高并发场景下,两个请求去select的时候,可能都没查到,然后都走到insert的地方啦。
当然,用唯一索引代替数据库主键也是可以的哈,都是全局唯一的ID即可。

2.直接insert + 主键/唯一索引冲突

1方案中都会先查一下流水表的交易请求,判断是否存在,然后不存在再插入请求记录。如果重复请求的概率比较低的话,我们可以直接插入请求,利用主键/唯一索引冲突,去判断是重复请求。

在这里插入图片描述

大家别搞混哈,防重和幂等设计其实是有区别的。防重主要为了避免产生重复数据,把重复请求拦截下来即可。而幂等设计除了拦截已经处理的请求,还要求每次相同的请求都返回一样的效果。不过呢,很多时候,它们的处理流程可以是类似的。

3.状态机幂等(常用)

很多业务表,都是有状态的,比如转账流水表,就会有0-待处理,1-处理中、2-成功、3-失败状态。转账流水更新的时候,都会涉及流水状态更新,即涉及状态机 (即状态变更图)。我们可以利用状态机实现幂等,一起来看下它是怎么实现的。

比如转账成功后,把处理中的转账流水更新为成功状态,SQL这么写:

update transfr_flow set status=2 where biz_seq=666and status=1;

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

状态机是怎么实现幂等的呢?

  • 第1次请求来时,bizSeq流水号是 666,该流水的状态是处理中,值是 1,要更新为2-成功的状态,所以该update语句可以正常更新数据,sql执行结果的影响行数是1,流水状态最后变成了2。
  • 第2请求也过来了,如果它的流水号还是 666,因为该流水状态已经2-成功的状态了,所以更新结果是0,不会再处理业务逻辑,接口直接返回。

4.抽取防重表

1,2方案都是建立在业务流水表上bizSeq的唯一性上。很多时候,我们业务表唯一流水号希望后端系统生成,又或者我们希望防重功能与业务表分隔开来,这时候我们可以单独搞个防重表。当然防重表也是利用主键/索引的唯一性,如果插入防重表冲突即直接返回成功,如果插入成功,即去处理请求。

5.token令牌(前后端交互常用)

token 令牌方案一般包括两个请求阶段:

  • 客户端请求申请获取token,服务端生成token返回
  • 客户端带着token请求,服务端校验token

流程图如下:

在这里插入图片描述

1、客户端发起请求,申请获取token。
2、服务端生成全局唯一的token,保存到redis中(一般会设置一个过期时间),然后返回给客户端。
3、客户端带着token,发起请求。
4、服务端去redis确认token是否存在,一般用 redis.del(token)的方式,如果存在会删除成功,即处理业务逻辑,5、如果删除失败不处理业务逻辑,直接返回结果。

6.悲观锁(如select for update)(不用)

什么是悲观锁?

通俗点讲就是很悲观,每次去操作数据时,都觉得别人中途会修改,所以每次在拿数据的时候都会上锁。官方点讲就是,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。

悲观锁如何控制幂等的呢?就是加锁呀,一般配合事务来实现。

举个更新订单的业务场景:
假设先查出订单,如果查到的是处理中状态,就处理完业务,再然后更新订单状态为完成。如果查到订单,并且是不是处理中的状态,则直接返回
整体的伪代码如下:

begin;  # 1.开始事务
select * from order where order_id='666' # 查询订单,判断状态
if(status !=处理中){
   //非处理中状态,直接返回;
   return ;
}
## 处理业务逻辑
update order set status='完成' where order_id='666' # 更新完成
commit; # 5.提交事务

这种场景是非原子操作的,在高并发环境下,可能会造成一个业务被执行两次的问题:
当一个请求A在执行中时,而另一个请求B也开始状态判断的操作。因为请求A还未来得及更改状态,所以请求B也能执行成功,这就导致一个业务被执行了两次。

可以使用数据库悲观锁(select …for update)解决这个问题.

begin;  # 1.开始事务
select * from order where order_id='666' for update # 查询订单,判断状态,锁住这条记录
if(status !=处理中){
   //非处理中状态,直接返回;
   return ;
}
## 处理业务逻辑
update order set status='完成' where order_id='666' # 更新完成
commit; # 5.提交事务

这里面order_id需要是索引或主键哈,要锁住这条记录就好,如果不是索引或者主键,会锁表的!
悲观锁在同一事务操作过程中,锁住了一行数据。别的请求过来只能等待,如果当前事务耗时比较长,就很影响接口性能。所以一般不建议用悲观锁做这个事情。

7.乐观锁

悲观锁有性能问题,可以试下乐观锁。

什么是乐观锁?

乐观锁在操作数据时,则非常乐观,认为别人不会同时在修改数据,因此乐观锁不会上锁。只是在执行更新的时候判断一下,在此期间别人是否修改了数据。

怎样实现乐观锁呢?

就是给表的加多一列version版本号,每次更新记录version都升级一下(version=version+1)。具体流程就是先查出当前的版本号version,然后去更新修改数据时,确认下是不是刚刚查出的版本号,如果是才执行更新
比如,我们更新前,先查下数据,查出的版本号是version =1

select order_id,version from order where order_id='666'

然后使用version =1和订单Id一起作为条件,再去更新

update order set version = version +1,status='P' where  order_id='666' and version =1

最后更新成功,才可以处理业务逻辑,如果更新失败,默认为重复请求,直接返回。
流程图如下:
在这里插入图片描述
为什么版本号建议自增的呢?

因为乐观锁存在ABA的问题,如果version版本一直是自增的就不会出现ABA的情况啦。

8.分布式锁(分布式环境下常用)

分布式锁实现幂等性的逻辑就是,请求过来时,先去尝试获得分布式锁,如果获得成功,就执行业务逻辑,反之获取失败的话,就舍弃请求直接返回成功。执行流程如下图所示:

在这里插入图片描述
分布式锁可以使用Redis,也可以使用ZooKeeper,不过还是Redis相对好点,因为较轻量级。

Redis分布式锁,可以使用命令SET EX PX NX + 唯一流水号实现,分布式锁的key必须为业务的唯一标识哈
Redis执行设置key的动作时,要设置过期时间哈,这个过期时间不能太短,太短拦截不了重复请求,也不能设置太长,会占存储空间。

HTTP的幂等

我们的接口,一般都是基于http的,所以我们再来聊聊Http的幂等吧。HTTP 请求方法主要有以下这几种,我们看下各个接口是否都是幂等的。

GET方法

HTTP 的GET方法用于获取资源,可以类比于数据库的select查询,不应该有副作用,所以是幂等的。它不会改变资源的状态,不论你调用一次还是调用多次,效果一样的,都没有副作用。

如果你的GET方法是获取最近最新的新闻,不同时间点调用,返回的资源内容虽然不一样,但是最终对资源本质是没有影响的哈,所以还是幂等的。

HEAD 方法

HEAD 方法与 GET 方法类似,但只返回响应头,不返回响应体。同样不会改变服务器状态。主要区别是HEAD不含有呈现数据,而仅仅是HTTP的头信息,所以它也是幂等的。如果想判断某个资源是否存在,很多人会使用GET,实际上用HEAD则更加恰当。即HEAD方法通常用来做探活使用。

OPTIONS方法

HTTP OPTIONS 主要用于获取当前URL所支持的方法,也是有点像查询,因此也是幂等的。
OPTIONS 方法用于获取目标资源所支持的通信选项。它不会改变服务器状态。

DELETE方法

HTTP DELETE 方法用于删除资源,它是的幂等的。比如我们要删除id=666的帖子,一次执行和多次执行,影响的效果是一样的呢。

这个具体只能是指定条件删除!

POST 方法

HTTP POST 方法用于创建资源,可以类比于提交信息,显然一次和多次提交是有副作用,执行效果是不一样的,不满足幂等性。

比如:POST http://www.tianluo.com/articles的语义是在http://www.tianluo.com/articles下创建一篇帖子,HTTP 响应中应包含帖子的创建状态以及帖子的 URI。两次相同的POST请求会在服务器端创建两份资源,它们具有不同的 URI;所以,POST方法不具备幂等性。

PUT 方法

在大多数情况下,PUT 方法是幂等的,因为它用于更新或替换指定资源的全部内容。无论执行多少次相同的 PUT 请求,最终结果都应该是相同的。然而,在某些特定的情况下,PUT 方法可能会表现出非幂等的行为。以下是一些可能导致 PUT 方法不幂等的情况:

在大多数情况下,PUT 方法是幂等的,因为它用于更新或替换指定资源的全部内容。无论执行多少次相同的 PUT 请求,最终结果都应该是相同的。然而,在某些特定的情况下,PUT 方法可能会表现出非幂等的行为。以下是一些可能导致 PUT 方法不幂等的情况:

  1. 依赖外部状态
    如果 PUT 请求的结果依赖于外部状态或系统中的其他数据,那么它可能不是幂等的。

示例
假设有一个计数器服务,每次 PUT 请求都会增加一个计数器的值:

PUT /counter
Content-Type: application/json

{
 "value": 1
}

在这个例子中,每次执行 PUT 请求都会将计数器的值增加 1。因此,多次执行相同的请求会导致不同的结果,这使得 PUT 不再是幂等的。

  1. 包含时间戳或版本号
    如果 PUT 请求中包含时间戳或版本号,并且这些信息会影响服务器的状态,那么 PUT 可能不是幂等的。

示例
假设有一个资源,其内容包括一个时间戳字段:

PUT /resource/123
Content-Type: application/json

{
  "name": "John Doe",
  "timestamp": "2024-09-22T12:00:00Z"
}

每次 PUT 请求的时间戳不同,即使内容相同,服务器也可能将其视为不同的更新,从而导致非幂等行为。

  1. 包含自增字段
    如果 PUT 请求中包含自增字段(如 ID 或序列号),并且这些字段在服务器端生成,那么 PUT 可能不是幂等的。

示例
假设有一个资源,其中包含一个自增的 ID 字段:

PUT /resource/123
Content-Type: application/json

{
  "name": "John Doe",
  "id": 123
}

如果 id 是在服务器端生成的,并且每次 PUT 请求都会生成一个新的 ID,那么多次执行相同的 PUT 请求会导致不同的结果。

  1. 并发更新
    在并发环境中,多个客户端同时对同一个资源进行 PUT 操作时,可能会导致非幂等的行为。

示例
假设有两个客户端同时尝试更新同一个资源:

  • 客户端 A 发送 PUT 请求,更新资源为 { "name": "John Doe" }
  • 客户端 B 在客户端 A 的请求处理完成之前发送 PUT 请求,更新资源为 { "name": "Jane Doe" }

在这种情况下,最终资源的状态取决于哪个请求先被处理,这可能导致非幂等的行为。

  1. 副作用
    如果 PUT 请求有副作用,例如触发其他操作或事件,那么它可能不是幂等的。

示例
假设 PUT 请求不仅更新资源,还触发了一个通知事件:

PUT /resource/123
Content-Type: application/json

{
  "name": "John Doe"
}

每次执行 PUT 请求时,都会触发一个通知事件,即使资源内容没有变化。这种情况下,PUT 请求不再是幂等的。

虽然 PUT 方法在标准定义下是幂等的,但在实际应用中,由于上述情况的存在,PUT 请求可能会表现出非幂等的行为。为了确保 PUT 方法的幂等性,应该避免依赖外部状态、时间戳、自增字段和副作用,并且在并发环境下使用适当的锁机制来防止竞态条件。通过这些措施,可以确保 PUT 方法在分布式系统中的可靠性和一致性。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Oauth2整合gateway网关实现微服务单点登录】

文章目录 一.什么是单点登录?二.Oauth2整合网关实现微服务单点登录三.时序图四.代码实现思路1.基于OAuth2独立一个认证中心服务出来2.网关微服务3产品微服务4.订单微服务5.开始测试单点登录 一.什么是单点登录? 单点登录(Single Sign On&…

sql语法学习:关键点和详细解释

学习SQL语法是掌握数据库操作的基础。以下是SQL语法的一些关键点和详细解释: 1. SQL基础 SQL(Structured Query Language)是一种用于管理和操作关系型数据库的标准语言。它主要包括以下几个部分: 数据定义语言(DDL&…

全栈开发(五):初始化前端项目(nuxt3+vue3+element-plus)+前端代理

1.初始化前端项目 Nuxt3:搭建项目_nuxt3 项目搭建-CSDN博客、 2.配置代理 nuxt.config.ts // https://nuxt.com/docs/api/configuration/nuxt-configexport default defineNuxtConfig({devtools: { enabled: true },modules: ["element-plus/nuxt", "pinia/n…

智能PPT行业赋能用户画像

智能PPT市场在巨大的需求前景下,已吸引一批不同类型的玩家投入参与竞争。从参与玩家类型来看,不乏各类与PPT创作有关的上下游企业逐步向智能PPT赛道转型进入,也包括顺应生成式AI技术热潮所推出的创业企业玩家。当前,智能PPT赛道发…

在虚幻引擎中创建毛发/头发

在虚幻引擎中创建毛发/头发 , 首先开启两个插件 Groom 和 Alembic Groom Importer 打开蒙皮缓存 导出人物模型 将人物导入Blender , 选择需要种植头发的点 指定并选择 点击毛发 这里变成爆炸头了 , 把数量和长度调一下 切换到梳子模式 调整发型 导出为abc , 文件路径不…

基于opencv的车牌检测和识别系统(代码+教程)

车牌检测与识别技术详解 车牌检测和识别(License Plate Recognition, LPR)是一项重要的计算机视觉任务,它在交通管理、安全监控以及智能门禁系统等多个领域都有着广泛的应用。随着深度学习技术的发展,LPR系统的准确性和鲁棒性得到…

【算法业务】基于Multi-Armed Bandits的个性化push文案自动优选算法实践

1. 背景介绍 该工作属于多年之前的用户增长算法业务项目。在个性化push中,文案扮演非常重要的角色,是用户与push的商品之间的桥梁,文案是用户最直接能感知的信息。应该说在push产品信息之外,最重要的就是文案,直接能…

机器学习 | Scikit Learn中的普通最小二乘法和岭回归

在统计建模中,普通最小二乘法(OLS)和岭回归是两种广泛使用的线性回归分析技术。OLS是一种传统的方法,它通过最小化预测值和实际值之间的平方误差之和来找到数据的最佳拟合线。然而,OLS可以遭受高方差和过拟合时&#x…

Unreal Engine 5 C++: 插件编写03 | MessageDialog

在虚幻引擎编辑器中编写Warning弹窗 准备工作 FMessageDialog These functions open a message dialog and display the specified informations there. EAppReturnType::Type 是 Unreal Engine 中用于表示应用程序对话框(如消息对话框)返回结果的枚举…

vue.js 展示树状结构数据,动态生成 HTML 内容

展示树状结构数据: 从 jsonData 读取树状结构的 JSON 数据,将其解析并生成 HTML 列表来展示。树状结构数据根据 id 和 label 属性组织,节点可以包含子节点 children。 展示评级信息: 从预定义的表单字段 form 中读取 arRateFlag 和…

GS-SLAM论文阅读笔记--GLC-SLAM

前言 最近GS-SLAM回环检测的工作已经逐步发展了,看一下这篇新文章。 文章目录 前言1.背景介绍2.关键内容2.1 tracking2.2 local mapping2.3 Loop Closing2.4总体流程 3.文章贡献 1.背景介绍 现有的基于3dgs的SLAM方法往往存在累积的跟踪误差和地图漂移&#xff0c…

三菱FX5U CPU模块的初始化“(格式化PLC)”

1、连接FX5U PLC 1、使用以太网电缆连接计算机与CPU模块。 2、从工程工具的菜单选择[在线]中[当前连接目标]。 3、在“简易连接目标设置 Connection”画面中,在与CPU模块的直接连接方法中选择[以太网]。点击[通信测试]按钮,确认能否与CPU模块连接。 FX5…

小柴冲刺软考中级嵌入式系统设计师系列二、嵌入式系统硬件基础知识(1)数字电路基础

目录 一、信号特征 二、组合逻辑电路和时序逻辑电路 1、组合逻辑电路 2、时序逻辑线路 三、信号转换 1、数字集成电路的分类 2、常用电平接口技术 四、可编程逻辑器件 flechazohttps://www.zhihu.com/people/jiu_sheng 小柴冲刺嵌入式系统设计师系列总目录https://blo…

[vulnhub] Prime 1

https://www.vulnhub.com/entry/prime-1,358/ 主机发现端口扫描 探测存活主机,137是靶机 nmap -sP 192.168.75.0/24 // Starting Nmap 7.93 ( https://nmap.org ) at 2024-09-22 16:25 CST Nmap scan report for 192.168.75.1 Host is up (…

Rust - 字符串:str 与 String

在其他语言中,字符串通常都会比较简单,例如 “hello, world” 就是字符串章节的几乎全部内容了。 但是Rust中的字符串与其他语言有所不同,若带着其他语言的习惯来学习Rust字符串,将会波折不断。 所以最好先忘记脑中已有的关于字…

MMD模型一键完美导入UE5-VRM4U插件方案(一)

1、下载pmx模型 1、去模之屋官网下载MMD模型,模之屋 2、下载完成得到pmx和Texture文件 2、下载并启用VRM4U插件 1、下载VRM4U插件, VRM4U,点击Latest下载对应引擎版本 2、将插件放到Plugins目录,然后

GB28181语音对讲协议详解

GB28181-2016语音对讲流程如下图1所示: 图1.语音对讲流程。 其中, 信令 1 、2 、 3 、 4 为语音广播通知、 语音广播应答消息流程; 信令 5 、 1 2 、 1 3 、 1 4 、 1 5 、 1 6 为 S I P 服务器接收到客户端的呼叫请求通过 B 2 B UA 代理方式建立语音流接收者与媒…

DevExpress WPF中文教程:如何解决行焦点、选择的常见问题?

DevExpress WPF拥有120个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…

【HarmonyOS】应用权限原理和封装

背景 在项目中,避免不了需要调用系统资源和系统能力,比如:日历读写、摄像头等。因此,需要了解对系统资源访问权限的申请方式方法。 授权方式 包括两种授权方式,分别是system_grant(系统授权) 和 user_grant(用户授权)…

ruoyi源码解析学习 - 微服务版 - ruoyi-gateway

com.ruoyi.gateway 今天简单看看若依的gateway的配置模块干了啥 最近面试很多外包公司,都对低代码平台有点要求,这些代码虽说用起来不费劲,但是其中还是有很多细节能让我学习学习的。(微服务版,上次搞jeecgboot的笔试…