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

无忧微服务:如何实现大流量下新版本的发布自由

作者:项良、十眠

微服务上云门槛降低,用好微服务才是关键

据调研数据显示,约 70% 的生产故障是由变更引起的。在阿里云上的企业应用如茶百道、极氪汽车和来电等,他们是如何解决变更引起的稳定性风险,实现了在白天高流量情况下应用发布平滑无损。今天我们将揭开阿里云微服务全链路无损发布解决方案的面纱, 快来一起参与探讨,并动手实践吧。

随着微服务开源以及生态的成熟,大大地推进了微服务技术的标准化。下图是展示单体和微服务架构选型的图,其中横坐标代表的是系统复杂度,纵坐标代表着效率,绿色曲线代表着单体架构,蓝色曲线代表微服务架构。我们可以看到随着系统复杂度的升高,单体架构跟微服务之间选型存在着一个拐点,拐点向右更适合选型微服务架构。随着微服务技术的标准化,意味着微服务上云的门槛也大幅度降低,他们之间选型的拐点也在持续地左移,采用微服务架构的企业日益增多。当企业开始大规模推广使用微服务时,如何用好、用稳微服务成为了大家关注的点,用好微服务的关键就是稳定性跟效率。

图片

微服务变更风险大,发布被迫选择半夜

我们来一起看下一些客户真实的诉求,某茶饮企业变更引起事故占比一度超过 60%,每次发版都要避开高峰,在凌晨发版;某新能源企业业务连续性要求非常高,核心服务需要 7*24 小时持续在线,但是业务快速发展又需要保证迭代的效率,为了保证业务的稳定性,只能在业务低峰期即凌晨进行发布;同样,某科技公司因为缺少线上灰度能力,每次发布全量发布会导致影响面不可控,存在一定风险。

据调研数据称,70% 的生产故障都是由于变更导致的,除应用本身问题外,运行时风险概括为:不确定流量、不稳定调用、不稳定基础设施。今天,我们聊的解决方案就是为了全面消除变更态风险,解决用户生产变更态的稳定性风险。

图片

我们先来分析一下,为什么微服务应用不能白天发布?因为相比于单体应用来说,微服务架构本身就是复杂的拓扑网络状的调用跟依赖结构。那么就意味着如果我们的微服务应用没有处理好上下线有损的问题,那么我们的任一微服务在发布的过程中都会存在短暂的服务不可用的情况,短时间内会出现大量的异常,导致业务受损。

最直观的感受就是我们一不小心点击了某个应用的重新部署,然后报警群里就会收到大量订单成功率下跌的告警,想想就很吓人。其次,发布时整个功能上线到线上的最后一个环节,一些在设计、研发、测试过程中累计下来的问题,在最后发布的环节才会触发。如果发布涉及到多个应用,如何进行合理发布,并且不会因为版本的问题而导致流量损失?我们总不希望看到,同一个用户在下单页面时候访问到新的版本,到支付阶段又碰到了老版本。

图片

左下图是我拿开源 Spring Cloud 应用进行压测,并且在压测过程中重启应用后,整体压测请求的表现,我们可以看到有大量的异常。再进一步想一下,如果此时是生产系统,并且在白天大流量的场景下,极小的问题都会由于大流量的因素被快速放大,影响面难以控制。所以很多企业跟业务团队,宁愿选择在凌晨进行发布。

MSE 微服务全链路无损发布方案,消除变更风险

如何彻底解决变更稳定性问题?那就介绍今天的主角,MSE 微服务全链路无损发布方案。

图片

我们分别从两个角度来分析问题:

  1. 如果我们新版本的代码没任何问题,服务上下线过程还是会有损,应该怎么解决?

  2. 如果我们新版本的代码有问题,那么我们如何控制问题的影响面?

首先,代码没问题,为什么上下线的环节中流量会有损失。当我们的微服务提供者下线后,服务的消费者无法实时感知节点下线,持续调用已经下线的节点地址,那么流量就会出现持续的报错;如果服务提供者在下线的过程中,请求执行到一半时,应用节点就直接停止了,那么就会导致在途请求以及处理中的请求失败,严重时还会导致线上的数据不一致;同样,在应用上线环节中也会有许多问题,比如新启动的节点如果没有完成预热,就直接承受打流量,那么就会导致新启动的节点直接被打垮,更有严重情况下,大流量场景下服务扩容都扩不出来。同样,微服务体系的生命周期没有跟 K8s 维度的 Readiness/Liveness 生命周期没有对齐,那么在发布的过程中会出现没对齐而导致的请求异常。

如果新版本的代码存在问题,那我们希望在发布新版本的过程中尽可能地控制影响面,阿里巴巴安全生产最佳实践告诉我们,在发布变更的过程中需要做到安全生产三板斧,即可灰度、可观测、可回滚。可灰度就是通过灰度的方式控制问题的影响面,如果我们业务代码新版本存在 bug,大多数情况下我们只能选择在业务低峰期(凌晨)进行发布;在微服务架构下,灰度发布需要做到全链路灰度的能力,目前开源自建很难实现全链路灰度的能力,如果出现流量丢标的情况,会导致灰度流量打到生产环境,引入了额外不可控的风险;可回滚,要求我们在出问题后,需要快速恢复止血,但如果没有一套完整的回滚方案与策略,回滚速度慢或者在回滚过程中引入更大的问题都是不可接受的。

如何解决微服务上下线过程流量有损问题

如果新版本代码没问题,如何解决服务上下线过程中流量有损的问题?

减少不必要的 API 报错,是最好的用户体验,也是最好的微服务开发体验。如何解决这个在微服务领域内让人头疼的问题呢。在这之前我们先来了解一下为什么我们的微服务在下线的过程中会有可能出现流量损失的问题。

原理分析:无损下线

图片

如上图右侧所示,是一个微服务节点下线的正常流程。

  1. 下线前,消费者根据负载均衡规则调用服务提供者,业务正常。

  2. 服务提供者节点 A 准备下线,先对其中的一个节点进行操作,首先是触发停止 Java 进程信号。

  3. 节点停止过程中,服务提供者节点会向注册中心发送服务节点注销的动作。

  4. 服务注册中心接收到服务提供者节点列表变更的信号后会,通知消费者服务提供者列表中的节点已下线。

  5. 服务消费者收到新的服务提供者节点列表后,会刷新客户端的地址列表缓存,然后基于新的地址列表重新计算路由与负载均衡。

  6. 最终,服务消费者不再调用已经下线的节点。

微服务下线的流程虽然比较复杂,但整个流程还是非常符合逻辑的,微服务架构是通过服务注册与发现实现的节点感知,自然也是通过这条路子实现节点下线变化的感知。

参考我们这边给出的一些简单的实践数据,我想你的看法可能就会变得不同。从第 2 步到第 6 步的过程中,Eureka 在最差的情况下需要耗时 2 分钟,即使是 Nacos 在最差的情况下需要耗时 50 秒;在第 3 步中,Dubbo 3.0 之前的所有版本都是使用的是服务级别的注册与发现模型,意味着当业务量过大时,会引起注册中心压力大,假设每次注册/注销动作需要花费 20~30ms,五六百个服务则需要注册/注销花费掉近 15s 的时间;在第 5 步中, Spring Cloud 使用的 Ribbon 负载均衡默认的地址缓存刷新时间是 30 秒一次,那么意味着及时客户端实时地从注册中心获取到下线节点的信号,依旧会有一段时间客户端会将请求负载均衡至老的节点中。

如上图左侧所示,只有到客户端感知到服务端下线并且使用最新的地址列表进行路由与负载均衡时,请求才不会被负载均衡至下线的节点上。那么在节点开始下线的开始到请求不再被打到下线的节点上的这段时间内,业务请求都有可能出现问题,这段时间我们可以称之为服务调用报错期。

通过对微服务下线的流程分析,我们理解了解决微服务下线问题的关键就是:保证每一次微服务下线过程中,尽可能缩短服务调用报错期,同时确保待下线节点处理完任何发往该节点的请求之后再下线。

那么如何缩短服务调用报错期呢?我们想到了一些策略:

  1. 将步骤 3 即节点向注册中心执行服务下线的过程提前到步骤 2 之前,即让服务注销的通知行为在应用下线前执行,考虑到 K8s 提供了 Prestop 接口,那么我们就可以将该流程抽象出来,放到 K8s 的 Prestop 中进行触发。

  2. 如果注册中心能力不行,那么我们是否有可能服务端在下线之前绕过注册中心直接告知客户端当前服务端节点下线的信号,该动作也可以放在 K8s 的 Prestop 接口中触发。

  3. 客户端在收到服务端通知后,是否可以主动刷新客户端的地址列表缓存。

如何尽可能得保证服务端节点在处理完任何发往该节点的请求之后再下线?站在服务端视角考虑,在告知了客户端下线的信号后,可以提供一种等待机制,保证所有的在途请求以及服务端正在处理的请求被处理完成之后再进行下线流程。

实战演练:缩容过程中无损下线的表现

下面我们就通过一个实践来看看无损下线的效果与表现:

图片

该实践分为四个步骤,首先我们在 ACK 控制台配置定时伸缩任务,模拟应用扩缩容的场景,我们可以配置 5 分钟内第 2 分钟扩容,第 4 分钟缩容;第二步,应用接入 MSE 服务治理后默认会具备无损下线能力,我们需要将基线环境增加环境变量关闭无损下线能力,作为对照组;灰度环境接入 MSE 服务治理后,不做任何操作,即默认开启无损下线能力,作为实验组;第三步,我们同时发起基线跟灰度的流量,观察其表现;第四步,我们在 MSE 应用详情的 QPS 数据模块中可以看出,未打标环境(关闭无损下线的应用)在扩缩容过程中出现了 99 个异常,然而灰度环境则无任何错误数。

📝 更多实验细节详见 MSE 文档,结果验证:无损下线功能 [ 1]

原理分析:无损上线

为什么有了无损下线,还需要无损上线?应用启动的过程中也存在许多问题。我们将一个微服务应用的启动过程抽象总结成如下流程,在如下各个阶段我们都会遇到一些问题。

图片

我们先看一下一个标准的微服务启动过程中分别要经过哪几个阶段:

  1. 首先是应用初始化阶段,在这段时间内会进行 Spring Bean 的加载与初始化逻辑;

  2. 第二阶段就是连接池连接建立阶段,比如我们有用到 Redis 的话,就有涉及到 JedisPool 的连接池创建,默认情况下连接池创建后不会立即创建连接,只有等待请求进入后才会去建立连接,之前有个头部客户,在应用启动的过程中就因为连接还没建立完成,但是有大流量涌入,导致大量线程阻塞在连接创建阶段,导致大量的请求错误,因此在这个阶段我们需要提前将连接池的核心连接建立完成;

  3. 第三阶段就是服务注册,我们知道一旦服务完成注册,那么就意味着 Consumer 应用可以发现当前服务,就会有微服务的流量进入当前应用,因此我们需要确保在服务注册之前应用初始化就绪;在某些场景下,比如大数据计算服务,服务在启动后需要异步加载一些准备资源,大数据服务需要提前从 OSS 拉取几百兆的数据,等就绪后才能提供服务,因此如果应用启动后直接注册服务,会导致在异步资源就绪前的流量都因为前置资源没就绪而导致报错;

  4. 第四个阶段就是需要联动 K8s 生命周期,一旦 K8s 的就绪检查通过就认为当前 Pod 已经启动完成。在 K8s 滚动发布的过程中,Readiness 通过后说明 Pod 启动就绪,K8s 就会自动进行下一批 Pod 的滚动发布。如果 Readiness 仅仅关联端口启动,会出现这样的情况,老的 Pod 都已经停机了,但是最早新起的 Pod 依旧没进行完服务注册,从而在短时间内会存在注册中心中该服务没有地址的情况,客户端就会在发布过程中出现 service no provider 异常。

  5. 第五个阶段就是应用跟 Pod 都就绪了,微服务应用在刚启动后由于 JIT 预热、框架资源懒加载等情况,会导致应用刚启动后容量会相比正常情况下小很多,Java 应用在这个场景下问题会显得更加明显。如果这时候不进行任何预处理,应用直接接收线上均分下来的大流量极易出现大量请求响应慢,资源阻塞,应用实例宕机的现象。因此在这阶段我们需要通过小流量预热的方式,合理分配少量的流量启到充分预热我们系统的作用。

只有妥善处理完上面的所有流程,我们的微服务应用在其启动后才可以从容地面对生产上的大流量。

实战演练:无损上线效果演示

下面我们来快速看一下无损上线的 Demo:

图片

首先,我们需要在 MSE 无损上线页面开启无损上线开关,并配置预热时长;然后我们重启应用后,我们就可以看到上线 Pod 的流量曲线如上图所示,缓慢增长,符合小流量预热曲线。

📝 更多实验细节详见 MSE 文档,结果验证:无损上线功能 [ 2]

实战演练:压测态下的无损上下线表现

本次实践的 Demo 以开源 Spring Cloud 框架为例,我们准备了如下 demo。流量是阿里云性能测试服务 PTS 发起,通过开源的 Zuul 网关流入我们的系统。其中其服务调用链路如下图所示:

图片

图中流量从 Netflix Zuul 对应的 Ingress 进来,会调用 SC-A 应用对应的服务,SC-A 应用内部调用 SC-B 应用的服务,SC-B 应用内部调用 SC-C 应用的服务。

我们看下真实压测(500qps 持续压测)场景下无损上下线的效果:

图片

左图是未接入 MSE 的开源 Spring Cloud 应用表现,我们观察到从 17:53:42 开始出错,17:54:24 停止报错,其中报错时长持续 42 秒,总共出现 1 万多个异常。相比实验组,接入 MSE 无损上下线的应用在这个过程中,应用发布对业务流量来说没任何损失,整个过程非常平滑。

📝 更多实验细节详见 MSE 文档,揭秘大流量场景下发布如「丝般顺滑」背后的原因 [ 3]

一个简单的 Demo 应用表现都如此令人吃惊,那么在微服务架构下,生产系统面对每秒上万次请求的流量洪峰,在这个过程中即使服务调用报错期只有短短几秒,对于企业来说都是非常痛的影响。在一些更极端的情况下,服务调用报错期可能会恶化到数分钟,这导致许多企业不敢发布,最后不得不每次发版都安排在凌晨两三点。对于研发来说每次发版都是心惊胆颤,苦不堪言。无损上下线的能力就是为了解决这个问题,保证发布变更过程中流量无损。

如何控制微服务变更过程中的风险

如果我们新版本的代码有问题,那么我们如何可以有效地控制问题影响面?

图片

为了控制变更过程中的风险,我们都知道通过灰度发布的方式来控制问题的影响面;但是在微服务架构的场景下,传统的灰度发布模式往往不能满足微服务交付的复杂、多样化的需求。这是因为:

  • 微服务调用链路比较长,比较复杂。在微服务架构中,服务之间的调用链路比较复杂,一个服务的改动可能会影响到整个调用链路,从而影响整个应用的稳定性。
  • 一次灰度可能涉及多个模块,整个链路都要调用新版本。由于微服务架构中服务之间相互依赖,一个服务的修改可能需要其他服务的相应调整。这就导致了在进行灰度发布时,需要同时调用多个服务的新版本,增加了发布的复杂度和不确定性。
  • 多个项目并行,需要部署多套环境,环境构建不灵活、成本高。在微服务架构中,往往会有多个项目并行开发,需要部署多套环境来支持不同的项目。这就增加了环境构建的难度和成本,从而导致发布效率低下。

为了解决这些问题,我们需要采用更加灵活、可控并且适用于微服务场景的发布方式, 全链路灰度发布的场景也就应运而生。通常每个微服务都会有灰度环境或分组来接受灰度流量。我们希望进入上游灰度环境的流量也能进入下游灰度的环境中,确保 1 个请求始终在灰度环境中传递,从而形成流量“泳道”。在“泳道”内的流量链路中,即使这个调用链路上有一些微服务应用不存在灰度环境,那么这些微服务应用在请求下游应用的时候依然能够回到下游应用的灰度环境中。

图片

生产的流量是端到端的,实现全链路灰度“泳道”那么意味着我们需要控制流量在前端、网关、后端各个微服务等组件中闭环。不仅仅是 RPC/Http 的流量,对于异步调用比如 MQ 流量我们也需要符合全链路“泳道”调用的规则,这整个过程中涉及到的流量控制的复杂度也是非常高的。

MSE 全链路灰度方案

图片

MSE 通过简单的 UI 页面透出流量“泳道”的模型,我们可以通过创建泳道组与泳道,快速实现微服务架构下的全链路灰度发布能力。

  1. MSE 支持在控制台动态配置流量匹配规则引入精细化流量

    1. 支持数值比较、正则匹配、百分比条件等灵活的条件匹配规则,从而满足复杂的灰度发布诉求;
    2. 关于 Http 流量支持 Header、param、cookie 等参数进行匹配;关于 Dubbo 流量可按照服务、方法、参数进行匹配;
  2. 全链路隔离流量“泳道”

    1. 通过设置流量规则对所需流量进行’染色’,'染色’流量会路由到灰度机器。
    2. 灰度流量携带灰度标签自动传递,形成灰度专属环境流量泳道,对于无灰度环境应用灰度流量会默认选择未打标的基线环境;其中流量标签传递默认支持RPC、MQ 等流量。
  3. 端到端的稳定基线环境

    1. 未打标的应用属于基线稳定版本的应用,即稳定的线上环境。当我们将发布对应的灰度版本代码,然后可以配置规则定向引入特定的线上流量,控制灰度代码的风险。
    2. 在每一跳请求路由的过程中,灰度流量优先走流量标签对应的机器,如果没有匹配则 fallback 到基线环境;
  4. 流量一键动态切流,流量规则配置后,可根据需求进行一键停启,增删改查,实时生效。灰度引流更便捷。

  5. 低成本接入,基于 Java Agent 技术实现无需修改一行业务代码;无缝支持市面上近 5 年的所有 Spring Cloud 和 Dubbo 的版本,用户不用改一行代码就可以使用,不需要改变业务的现有架构,随时可上可下,没有绑定。

同时 MSE 全链路灰度能力还提供了配套的可观测能力,我们通过全链路灰度观测可以实时看到灰度情况的流量曲线,无论是测试验证阶段、还是生产灰度发布过程中,对于灰没灰到的流量情况我们可以做到心中有数。

图片

谁都在用 MSE 全链路无损发布方案?

在阿里云上的企业应用如茶百道、极氪汽车和来电等,他们是如何解决变更引起的稳定性风险,实现了在白天高流量情况下应用发布平滑无损。MSE 全链路无损发布方案助力云上微服务实现白天发布自由。

图片

极氪汽车

随着极氪汽车销售越发火爆,其注册用户和每日活跃用户快速增长,需要支持的业务场景和新功能也越来越多,平均两三天一个小版本、半个月一个大版本的升级频率。为了不影响白天业务高峰,每次发版只能选择在凌晨业务低峰期进行,想象一下如果开发/运维/测试人员每次都集中在晚上发布,那么这些参与发布的同学第二天的工作效率将会受到影响;如果晚上选择较少的人参与发布,那么当出问题的时候止血措施很可能会来不及实施,故障责任也不好划分。

极氪汽车针对核心业务链路上多个微服务同时需要发版的场景,基于 MSE 全链路无损发布解决方案实现了多业务的全链路灰度,通过这种方式让客户在不需要更改任何业务代码的情况下实现多业务白天发版,同时通过逐步流量放大进行验证,如出现问题可及时回切流量,降低了白天发布可能导致的稳定性风险。 同时通过改造云效流水线,帮助客户实现核心业务自动化发布,进一步提升了发布的效率。

来电科技

来电科技在全面微服务化、容器化改造完成之后就面临了如何用好微服务的难题,他们核心架构师也是 Dubbo 社区的参与者,对比了自研微服务治理的成本后,他们决心使用 MSE 治理解决微服务化深入后面临的稳定性与效率的问题。来电科技通过 MSE 全链路无损发布解决方案有效解决变更过程中灰度的诉求,以及发布过程中流量抖动的问题, 这个过程中无需代码改动与架构调整,借助 MSE 服务治理以更经济的方式、更高效的路径在云上快速构建起完整微服务治理体系。

总结

如何稳定地使用微服务已成为大家关注的话题。本文详细介绍了阿里云微服务全链路无损发布最佳实践方案,相信这个方案可以帮助云上的企业更好地利用微服务。在过去几年许多企业客户的实践中,我们逐步完善了这个方案。我相信这些最佳实践就像大海中的明珠一样,在不断的实践和时间的磨砺中变得更加耀眼夺目。我们期待着将这些实践与经验分享给更多企业,让他们能够更好地应用微服务,取得更好的效果。

相关链接:

[1] 结果验证:无损下线功能

https://help.aliyun.com/zh/mse/use-cases/implement-graceful-start-and-shutdown-of-microservice-applications-by-using-mse?spm=a2c4g.11186623.0.0.727b5fffObARTt#section-qvv-175-948

[2] 结果验证:无损上线功能

https://help.aliyun.com/zh/mse/use-cases/implement-graceful-start-and-shutdown-of-microservice-applications-by-using-mse?spm=a2c4g.11186623.0.0.727b5fffObARTt#section-6gd-9lg-6zk

[3] 揭秘大流量场景下发布如「丝般顺滑」背后的原因

https://developer.aliyun.com/article/780231

相关文章:

无忧微服务:如何实现大流量下新版本的发布自由

作者:项良、十眠 微服务上云门槛降低,用好微服务才是关键 据调研数据显示,约 70% 的生产故障是由变更引起的。在阿里云上的企业应用如茶百道、极氪汽车和来电等,他们是如何解决变更引起的稳定性风险,实现了在白天高流…...

Halcon3D表面平面度检测-平面差值法

//倾斜平面矫正 https://blog.csdn.net/m0_51559565/article/details/137146179 //平面度和平面缺陷检测,平面矫正法 https://blog.csdn.net/m0_51559565/article/details/137163729前言 通常我们对表面平面度进行检测时,通常使用2种方式。1&#xff1a…...

golang 在多线程中避免 CPU 指令重排

发布日期:2024-03-26 16:29:39 起因 golang 的发明初衷便是多线程,是一门专门用于多线程高并发的编程语言。其独创的 GMP 模型在多线程的开发上提供了很大的便利。 现代计算机基本上都是多核 CPU 的结构。CPU 在进行指令运行的时候,为了提高…...

自动化更新包文件--shell脚本

自动化更新包文件--shell脚本 背景手动更包自动化更包 背景 作为一名实施工程师,当然也协助做些测试的工作,当产品功能开发后,研发会将本次迭代涉及的前后端包文件提供过来。有时会因为一些原因研发没法现场开发,那就需要我们配合…...

Vue element-plus 导航栏 [el-menu]

导航栏 [el-menu] Menu 菜单 | Element Plus el-menu有很多属性和子标签,为网站提供导航功能的菜单。 常用标签: 它里面有两个子标签。el-menu-item,它其实就是el-menu每一个里面的item,item就是真实匹配到路由的每个栏目&#…...

数据结构——数组

数组定义: 在计算机科学中,数组是由一组元素(值或变量)组成的数据结构,每个元素有至少一个索引或键来标识。 因为数组内的元素是连续存储的,所以数组中元素的地址,可以通过其索引计算出来。 性…...

python asyncio websockets server

python websocket server在收到接受消息处理完后会默认关闭连接。需要在msg_handler里面加个while true就能一直保持连接了。 start_server websockets.serve(msg_handler, "0.0.0.0", 29967) asyncio.get_event_loop().run_until_complete(start_server) asyncio.…...

视频素材免费网站有哪些?8个视频素材库网站下载推荐

在视频创作领域,选择正确的高质量无水印素材网站能够极大地丰富您的作品,让每一帧都鲜活起来。下面,我们继续为您介绍更多优质的视频素材网站,每一个都是您创作旅程中的宝贵资源。 1. 蛙学府(中国) 集合了…...

ChatGPT与传统搜索引擎的区别:智能对话与关键词匹配的差异

引言 随着互联网的快速发展,信息的获取变得比以往任何时候都更加便捷。在数字化时代,人们对于获取准确、及时信息的需求愈发迫切。传统搜索引擎通过关键词匹配的方式为用户提供了大量的信息,然而,这种机械式的检索方式有时候并不…...

xargs后调用bash自定义函数(写该函数文本到脚本, 并引导PATH)

xargs后调用bash自定义函数 需要3步骤,如下 function to_markdown_href_func() { fp$1 #echo $fpecho -e "\n[${fp}](${PREFIX}/${fp})" }BIN/tmp/bin/ F$BIN/to_markdown_href_func.sh mkdir -p $BIN 获得函数to_markdown_href_func的文本 ,写文本到 /tmp/bin/to_ma…...

学术论文写作新利器:ChatGPT技巧详解

ChatGPT无限次数:点击直达 学术论文写作新利器:ChatGPT技巧详解 在如今信息爆炸的时代,学术论文写作变得愈发重要且具有挑战性。随着人工智能技术的不断发展,ChatGPT作为一种强大的写作辅助工具,为学术论文创作者提供了全新的可能…...

Spring整合JDBC

1、引入依赖 <properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><depen…...

详解Qt中的布局管理器

Qt中的布局管理是用于组织用户界面中控件&#xff08;如按钮、文本框、标签等&#xff09;位置和尺寸调整的一种机制。说白了就是创建了一种规则&#xff0c;随着窗口变化其中的控件大小位置跟着变化。Qt提供了多种布局管理器&#xff0c;每种都有其特定用途和特点。以下是对Qt…...

MyBatis 参数重复打印的bug

现象 最近有个需求&#xff0c;需要在mybatis对数据库进行写入操作的时候&#xff0c;根据条件对对象中的某个值进行置空&#xff0c;然后再进行写入&#xff0c;这样数据库中的值就会为空了。 根据网上查看的资料&#xff0c;选择在 StatementHandler 类执行 update 的时候进…...

ES6学习之路:迭代器Iterator和生成器Generator

迭代器 一、知识背景 什么是迭代器 迭代器就是在一个数据集合中不断取出数据的过程迭代和遍历的区别 遍历是把所有数据都取出迭代器注重的是依次取出数据&#xff0c;它不会在意有多少数据&#xff0c;也不会保证能够取出多少或者能够把数据都取完。比如斐波那契额数列&#…...

如何使用 DynamiCrafter Interp Loop 无缝连接两张照片

DynamiCrafter Interp Loop 是一个基于 AI 的工具&#xff0c;可以用来无缝连接两张照片。它使用深度学习技术来生成中间帧&#xff0c;从而使两张照片之间的过渡更加自然流畅。 使用步骤 访问 DynamiCrafter Interp Loop 网站&#xff1a;https://huggingface.co/spaces/Dou…...

今天起,Windows可以一键召唤GPT-4了

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 发布在https://it.weoknow.com 更多资源欢迎关注 微软 AI 大计的最后一块拼图完成了&#xff1f; 把 Copilot 按钮放在 Window…...

使用Kaggle API快速下载Kaggle数据集

前言 在使用Kaggle网站下载数据集时&#xff0c;直接在网页上点击下载可能会很慢&#xff0c;甚至会出现下载失败的情况。本文将介绍如何使用Kaggle API快速下载数据集。 具体步骤 安装Kaggle API包 在终端中输入以下命令来安装Kaggle API相关的包&#xff1a; pip install…...

java 通过 microsoft graph 调用outlook(二)

这次提供一些基础调用方式API PS&#xff1a; getMailFolders 接口返回的属性中&#xff0c;包含了未读邮件数量unreadItemCount 一 POM文件 <!-- office 365 --><dependency><groupId>com.google.guava</groupId><artifactId>guava<…...

【机器学习】代价函数

&#x1f388;个人主页&#xff1a;豌豆射手^ &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 &#x1f917;收录专栏&#xff1a;机器学习 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共同学习、交流进…...

[leetcode] 100. 相同的树

给你两棵二叉树的根节点 p 和 q &#xff0c;编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1&#xff1a; 输入&#xff1a;p [1,2,3], q [1,2,3] 输出&#xff1a;true示例 2&a…...

08、Lua 函数

Lua 函数 Lua 函数Lua函数主要有两种用途函数定义解析&#xff1a;optional_function_scopefunction_nameargument1, argument2, argument3..., argumentnfunction_bodyresult_params_comma_separated 范例 : 定义一个函数 max()Lua 中函数可以作为参数传递给函数多返回值Lua函…...

【数据分析面试】1. 计算年度收入百分比(SQL)

题目 你需要为公司的营收来源生成一份年度报告。计算截止目前为止&#xff0c;在表格中记录的第一年和最后一年所创造的总收入百分比。将百分比四舍五入到两位小数。 示例&#xff1a; 输入&#xff1a; annual_payments 表 列名类型amountINTEGERcreated_atDATETIMEstatusV…...

数据库SQL语句速查手册

SQL 语句语法AND / ORSELECT column_name(s) FROM table_name WHERE condition AND|OR conditionALTER TABLEALTER TABLE table_name ADD column_name datatypeorALTER TABLE table_name DROP COLUMN column_nameAS (alias)SELECT column_name AS column_alias FROM table_name…...

智慧城市一屏统览,数字孪生综合治理

现代城市作为一个复杂系统&#xff0c;牵一发而动全身&#xff0c;城市化进程中产生新的矛盾和社会问题都会影响整个城市系统的正常运转。智慧城市是应对这些问题的策略之一。城市工作要树立系统思维&#xff0c;从构成城市诸多要素、结构、功能等方面入手&#xff0c;系统推进…...

Python读取PDF文字转txt,解决分栏识别问题,能读两栏

搜索了一下&#xff0c;大致有这些库能将PDF转txt 1. PyPDF/PyPDF2&#xff08;截止2024.03.28这两个已经合并成了一个&#xff09;pypdf PyPI 2. pdfplumber GitHub - jsvine/pdfplumber: Plumb a PDF for detailed information about each char, rectangle, line, et cete…...

微信支付平台与微信服务号关联配置要点

目录 JSAPI支付 前期资料及相关准备 申请微信服务号 服务号配置要点 微信认证 基本配置 功能设置 申请微信支付号 支付号配置要点 设置操作密码 API安全 开发设置 与服务号关联 小结 JSAPI支付 我们的开发应用场景以JSAPI支付为举例&#xff0c;这也是常用的一…...

C++类复习

C类 1. 类内成员函数隐式声明为inline class Str {int x;int y 3; public:inline void fun(){std::cout<<"pf,yes!"<<std::endl;} };这段代码不会报错&#xff0c;但是类内的成员函数隐式声明为inline函数&#xff0c;不需要单独写在前面。因此将成员…...

Spring使用(一)注解

Spring使用 资源 Spring 框架内部使用 Resource 接口作为所有资源的抽象和访问接口&#xff0c;在上一篇文章的示例代码中的配置文件是通过ClassPathResource 进行封装的&#xff0c;ClassPathResource 是 Resource 的一个特定类型的实现&#xff0c;代表的是位于 classpath …...

Linux基本指令篇

在前边&#xff0c;我们已经了解过了Linux操作系统的发展和应用&#xff0c;从该篇起&#xff0c;就正式进入对Linux的学习。 今天我们就来在Xshell上远程登录我们的云服务器。首先我们要知道自己云服务器的公网ip&#xff0c;然后修改一下密码。 点击跳转 修改完密码之后我们…...

有做翻译英文网站/长沙seo搜索

2019独角兽企业重金招聘Python工程师标准>>> java开发中&#xff0c;如果采用maven体系&#xff0c;那么需要nexus&#xff0c;私有库&#xff0c;这样方便一些jar包的共享。只需要deploy上去&#xff0c;别人就能更新到了。 第一步下载压缩包,解压缩 wget -c http…...

dante wordpress主题/如何在百度上投放广告

类的每一个属性都必须拥有一个唯一的名称,而类里定义的每一个索引器都必须拥有唯一的签名 或者参数列表,这样就可以实现索引器重载。属性可以是static,而索引器必须是实例成员。为索 引器定义的访问函数可以访问传递给索引器的参数,而属性访问函数则没有参数我们也可以为…...

旅游网站建设方案书/产品宣传推广策划

txt是一种使用广泛的文档、电子书格式&#xff0c;因为工作的原因&#xff0c;小编接触到很多不同的阅读器&#xff0c;今天小编将为大家推荐市面上最好的3个txt阅读器。 Top1&#xff1a;Neat Reader 这款在小编用过的阅读器中绝对排第一位&#xff0c;无论从阅读器的兼容度…...

拥有响应式网站/如何快速搭建一个网站

说明:本文主要是记录Rxjava 2.x 网上优秀博客 链接,便于学习所用. Rxjava2.x GitHub链接 https://github.com/ReactiveX/RxJava RxAndroid Github链接 https://github.com/ReactiveX/RxAndroid 一.基础到上手 这个非常推荐简书作者Season_zlc的“水管”系列&#xff0c;相信…...

如何给网站做排名/seo实战优化

研究了一下腾讯信鸽的demo&#xff0c;感觉里面一个自定义handler类有问题这是该handler的代码private static class HandlerExtension extends Handler {WeakReference mActivity;HandlerExtension(MainActivity activity) {mActivity new WeakReference(activity);}Override…...

wordpress接收表单/注册域名在哪里注册

一.凯撒密码加密代码思路基本思路&#xff1a;设置明文 和 位移步长(秘钥)将明文转成小写&#xff0c;准备 明文字节切片 与 密文切片循环将每个明文字符 按照 位移步长 做位移&#xff0c;存入密文切片返回密文导入包import ("fmt""strings" // 包含字符串…...