云原生消息流系统 Apache Pulsar 在腾讯云的大规模生产实践
导语
由 InfoQ 主办的 Qcon 全球软件开发者大会北京站上周已精彩落幕,腾讯云中间件团队的冉小龙参与了《云原生机构设计与音视频技术应用》专题,带来了以《云原生消息流系统 Apache Pulsar 在腾讯云的大规模生产实践》为主题的精彩演讲,在本篇内容中,将重点围绕腾讯云近期在 Apache Pulsar 稳定性和性能方面优化的工作展开介绍,为开发者提供参考。
作者简介
冉小龙
腾讯云高级研发工程师
Apache Pulsar committer
RoP maintainer
Apache Pulsar Go Client、Pulsarctl 与 Go Functions 作者与主要维护者
Apache Pulsar 作为云原生时代消息流系统,采用存储计算分离架构,支持大集群、多租户、百万级 Topic、跨地域数据复制、持久化存储、分层存储、高可扩展性等企业级和金融级功能。Apache Pulsar 提供了统一的消费模型,支持消息队列和流两种场景,既能为队列场景提供企业级读写服务质量和强一致性保障,又能为流场景提供高吞吐、低延迟。
Apache Pulsar 在腾讯云中已经得到大规模的生产实践,在过去一年中承接了诸多行业生态中不同的使用场景。在实际的生产实践中,腾讯云针对 Apache Pulsar 做了一系列的性能优化和稳定性功能方面的工作,来保障用户在不同的场景下系统的稳定高效的运行。本文围绕腾讯云近一年在 Pulsar 稳定性和性能方面优化最佳实践。
Pulsar 在腾讯云百万级 Topic 上的应用
为什么选择在生产环境中使用 Pulsar?
此前该用户使用 Kafka 集群来承载业务,由于业务的特定场景,集群的整体流量相对不大,但是需要使用的 Topic 较多。此前使用 Kafka 集群时,由于 Kafka 自身架构的限定,用户不能在一套集群中创建较多的 Topic,所以为了满足业务多 Topic 的使用场景,需要部署多套 Kafka 集群来满足业务的使用,导致业务使用的成本较大。
Pulsar 本身除了具备 Pub-Sub 的传统 MQ 功能外,其底层架构计算存储分离,在存储层分层分片,可以很容易地把 BookKeeper 中的数据 offload 到廉价存储上。Pulsar Functions 是 Serverless 的轻量化计算框架,为用户提供了 Topic 之间中转的能力。在开源之前,Pulsar 已在 Yahoo! 的生产环境中经历 5 年的打磨,并且可以轻松扩缩容,支撑多 Topic 场景。为了降低使用的成本,同时满足多 Topic 的业务场景,该用户切换到了 Pulsar 的集群上。
当前该用户的一套 Pulsar 集群可以承载 60W 左右的 Topic,在很好地满足了业务使用的场景的同时降低了使用成本。
Apache Pulsar 稳定性优化实践
实践 1:消息空洞的影响及规避措施
使用 Shared 订阅模式或单条 Ack 消息模型时,用户经常会遇到 Ack 空洞的情况。Pulsar 中单独抽象出了 individuallyDeletedMessages 集合来记录空洞消息的情况。该集合是开闭区间集合,开区间表明消息是空洞消息,闭区间表明消息已被处理。早期 Pulsar 支持单条 Ack 和批量 Ack 两种模型,后者对标 Kafka 的 Ack Offset。引入单条 Ack 模型主要针对在线业务场景,但也因此带来了 Ack 空洞问题。Ack 空洞即下图中 `individuallyDeletedMessage` 所展示的集合。
如何理解 individuallyDeletedMessage?以下图为例:
该记录中第一个 Ledger id 是 5:1280,该集合是闭区间,说明消息已经被 Ack;之后的 5:1281 是开区间,说明消息没有被 Ack。这里就用开闭区间的形式来区分消息是否被 Ack。
Ack 空洞的出现原因可能因为 Broker 处理失败,源于早期版本的设计缺陷,Ack 处理没有返回值。在 2.8.0 及以上版本中,对事务消息支持上引入了 AckResponse 概念,支持返回值。因此在早期版本中,调用 Ack 后无法确保 Broker 可以正确处理 Ack 请求。第二个原因可能因为客户端出于各种原因没有调用 Ack,在生产实践中出现较多。
为了规避 Ack 空洞,一种方法是精确计算 Backlog Size。因为在 Broker 上解析 Batch 消息会浪费性能,在 Pulsar 中对 Batch 消息的解析在消费者侧,因此一个 Entry 可能是单条消息也可能是 Batch 消息的。后者情况下 Batch 内的消息数量或形态是未知的。为此要精确计算 Backlog Size,但经过调研发现这种方法的复杂性和难度较大。
另一种方法是 Broker 的主动补偿策略。因为 individuallyDeletedMessage 存储在每一个 ManagedCursor,也就是每一个订阅对象到 Broker 实际类中的映射。每一个订阅都可以拿到对应的 individuallyDeletedMessage 集合,Broker 就可以主动把集合推送到客户端,也就是主动补偿。
接下来我们了解一下 Broker 主动补偿机制,即 Backlog 策略。在了解补偿机制之前,先要了解 Topic 可能的分布与构成。
正常来说,生产者向 Topic 发布消息,消费者从 Topic 接收消息。如上图,红、灰、蓝色代表消息在 Topic 中的三种形态。Pulsar 中引入了 Backlog 策略,用来描述生产者和消费者之间的 Gap。该策略提供了三种选项,包括 Producer Exception、Producer Request Hold 和 Consumer Backlog Eviction。
其中,Producer Exception 相对用户友好,在生产环境中更加常用。当消息堆积到一定程度,消费者处理消息的能力不足时,Producer Exception 会通知生产者出现了问题。Producer Request Hold 原理相同,但是 Producer Request Hold 只是会让生产者停止发送,而不会告知其原因(即不会向业务侧返回标识),用户感知为 Producer 停止发送消息但是无异常抛出。而 Consumer Backlog Eviction 则会自动丢弃最早的消息来保证消息持续处理,可能导致丢消息的情况出现。
此外,还需要注意的是 Pulsar 计算 Backlog Size 的方式。上图可以理解为一个事件流,生产者源源不断地 append message。Pulsar 计算 Backlog Size 时,是计算从当前 MarkedDeletedPosition 的位置,到 ReadPosition 的位置之前的 Backlog Size,而后结合 Producer Exception 策略暴露出来。如果 Ack 空洞,比如 Broker 侧请求失败,或者客户代码产生异常导致 Ack 永远不会被调用,Backlog Size 会到达一定速率,就相当于限制生产者。上图中,M4 和 M2 是两条空洞消息,出现这样的空洞消息时,生产者的发送流就迟早会被打断。
Broker 主动补偿机制的实现方式如上图。由于 individuallyDeletedMessage 记录了所有消息的 Ack 成功与否的状态,就可以从中获取 MarkedDeletedPosition 位置的消息,开启一个 Executor Service 定时任务,设置监听频率,间隔一段时间将消息重新推送到客户端侧,实现 Broker 的主动补偿,避免 Ack 空洞导致 Producer Exception 被频繁触发。
实践 2:再谈 TTL、Backlog 及 Retention 策略
我们先看下这三个概念:
-
TTL:表示消息在指定时间内没有被用户 Ack 时会在 Broker 主动 Ack。
-
Backlog :表示生产者发送的消息与消费者接收消息之间的差距。
-
Retention:表示当消息被 Ack 之后,继续在 Bookie 侧保留多久的时间,以 Ledger 为最小操作单元。
如果 TTL 和 Retention 同时设置,那么一条消息的生命周期该如何计算?来看以下代码:
void updateCursor (ManagedCursor Impl cursor, PositionImpl newPosition) tPair<PositionImpl, PositionImpl> pair = cursors.cursorUpdated (cursor, newPosition);if (pair == nulL) {Cursor has been removed in the meantimetrimConsumedLedgersInBackground();return;}PositionImplpreviousSlowestReader = pair.getLeftO);PositionImpl currentSlowestReader = pair.getRightO);if (previousSlowestReader.compareTo(currentSlowestReader)==0){// The slowest consumer has not changed position. Nothing to do right nowreturn;}//Only trigger a trimming when switching to the next Ledgerif (previousSlowestReader.getLedgerId() != newPosition.getLedgerId0)) ftrimConsumedLedgersInBackground();}
-
TTL:根据设置的时间(默认五分钟)定期检查,根据触发的策略不断更新 cursor 位置,处理消息过期。
-
Retention:检查 Ledger 的创建时间(通过元数据时间戳可以了解 Ledger 的生命周期)以及 Entry 的大小两个阈值来决定是否删除某一个 Ledger。
在以上代码中的最后三行中,将之前最慢的 LedgerId 与 newPosition 的 LedgerId 对比,检查 ManagedLedger 是否发生过切换,一旦切换就调用 trimConsumedLedgersInBackground()。该函数方法的核心代码策略就是 Retention 的逻辑。
由此可知:
-
当 TTL 时间小于 Retention 时间时,消息的完整生命周期就是 TTL 时间 + Retention 时间;
-
当 TTL 时间大于等于 Retention 时间,消息的生命周期就是 TTL 时间。
这里又引出了一个新问题:TTL 策略为什么要选择在 Ledger 切换的时机来触发 Ledger 的删除操作呢?因为 Retention 删除 Ledger 时是以 Ledger 为最小操作单元。如果 Ledger 不切换,Retention 也不会触发删除。所以上述代码逻辑会选择切换时机来交给 Retention 执行删除动作。
实践 3:延迟消息与 TTL 的关系
在团队曾经遇到的场景中,某用户发送了数十万延迟消息,延迟设置为十天,但 TTL 过期时间设置为五天,五天后所有延迟消息都已被过期。我们可以从源码层面看一下 TTL 策略。
public boolean expireMessages(int messageTTLInSeconds) {if (expirationCheckInProgressUpdater.compareAndSet( obj: this, FALSE, TRUE)) {log.info("[{}][{}] Starting message expiry check, ttl= {} seconds", topicName, subName,messageTTLInSeconds);cursor.asyncFindNewestMatching(ManagedCursor.FindPositionConstraint.SearchActiveEntries, entry -> {try {long entryTimestamp = Commands.getEntryTimestamp(entry.getDataBuffer());return Messaqelmpl.isEntryExpired(messageTTLInSeconds. entryTimestamp);} catch (Exception e) {log.error("[{}][{}] Error deserializing message for expiry check", topicName, subName, e);} finally {entry<release();}return false;}, callback: this, ctx: null);return true;
public static boolean isEntryExpired(int messageTTLInSeconds, long entryTimestamp) {return messageTTLInSeconds != 0&& (System.currentTimeMillis() >entryTimestamp + TimeUnit.SECONDS.toMillis(messageTTLInSeconds));
}
TTL 的核心逻辑是通过 cursor 传入的值决定消息是否过期,即是否能找到 Entry。TTL 只获取了消息的发布时间,却没有理会消息的延迟设置。结合上面两段代码,isEntryExpired 只关心 PublishedTime 时间戳元数据属性,FindNewestMatchingEntry 对象时可以从元数据中获取 PublishedTime。所以当延迟设置小于 TTL 时间就会导致延迟消息被过期,在用户侧就会发现消息丢失。
针对这一问题,腾讯团队向社区提供了 PR,主要逻辑是分别检查消息的发布时间和延迟时间,到达发布时间后如果延迟时间大于 TTL 时间,则 TTL 时间到达后依然不能过期消息。IsEntryExpired 会判断并检查 TTL 时间与延迟时间。这里发布时间和延迟时间要一次性从 Entry 中获取,否则每次获取的 Entry 对象是不一样的。此外,延迟时间需要发送时间点的时间戳,根据具体计算出延迟的时间长度来做判断。
实践 4:Admin API Block 的优化处理
在 Pulsar 之前的代码逻辑中:
-
如果在异步代码中频繁调用同步逻辑,那么其中的牵连关系很可能导致 Pulsar 外部的线程卡住,这时只能重启对应的 Broker 节点来恢复任务。
-
Pulsar 的 Http Lookup 服务调用的是外部端口,一旦异步调用同步导致阻塞,那么该服务外部端口的数据流也会出现阻塞。
-
Pulsar Web 服务的性能较差,主要是因为 CompletableFuture 的误用。当我们定义一个 CompletableFuture 对象后,经常调用 thenapply 或者 thencompose 来返回对象。这其实是 CompletableFuture 内对象的同步返回,是由当前线程栈执行的。如果异步任务没有返回,则由回调线程执行任务。
-
Pulsar 高版本加入了 Metadata Store 线程池的抽象。这个抽象会增大 ZooKeeper 的压力。当同一时间内的外部服务调用量增大,ZooKeeper 负载增大会导致消息延迟等指标出现退化。
腾讯团队针对上述问题,一方面剥离了 Metadata Store 线程池,另一方面通过服务监听来定位和发现 Web 服务的性能较弱的位置,去做进一步的优化处理。此外,团队还加入了超时处理逻辑,所有 Pulsar 外部线程如果在最后限定时间(30 秒)内无法处理完成就会抛出超时。虽然单个外部线程超时、重启影响不大,但这样避免了整个数据流阻塞的情况。
实践 5:zk-node 泄露
有时用户正在使用的 Topic 不多,但 zk-node 数量却很大,Pulsar 对 zk-node 的放大倍数较高。上图拐点是 zk-node 脏数据清理的时点,可以看到 zk-node 数据泄漏的情况非常严重,达到 5 倍之多。
在创建一个 Topic 时,首先要在 zk-path 的六级目录下涵盖所有 Topic 信息,在 ZooKeeper 上创建的资源量很大。此目录下涵盖了所有的 Topic,问题即出现在六个层级中。为此团队做了以下操作来处理 zk-node 脏数据:
-
首先通过 ZooKeeper client 读取 zk-path,按照指定的格式拼接所有 Topic 名字,获取 Topic 列表;
-
通过 pulsar-admin 检查集群中是否存在该 Topic;如果集群中不存在该 Topic,则相关数据一定是脏数据;(修复 zk-node 泄露问题的相关代码已 merge 进 2.8 + 的社区版本。)
-
切记在清理 ZookKeeper 脏数据之前备份 ZookKeeper 数据。
实践 6:Bookie Ledger 泄漏
团队在实践中发现,虽然 Retention 策略设置的消息生命周期最长应不超过 30 天,但检测扫描到的一些消息已经有数百天历史,且难以从 BookKeeper 中删除。针对这一问题,团队分析如下:
-
触发 Ledger 删除的唯一路径是 Retention 策略。这些消息产生的原因只能定位到一些 Bookie CLI 命令,这些命令生成了一些 Retention 策略管控不到的 Ledger。
-
每一个 Ledger 都有对应的 LedgerInfo,记录了它的元数据信息,包括创建时间等。获取元数据后,就可以确定 Ledger 是多久前创建的,还可以确定 Ledger 具体是在哪些 Bookie 节点上。
-
一个 Ledger 唯一归属于一个 Topic,所以可以获取 Topic 中存在 Ledger 的信息,进而确定某个 Ledger 是否存在于 Topic 的 Ledger 列表中,如果不在就是脏数据,可以清理。
-
如果 Ledger 对应的元数据已经丢失,那么 Ledger 本身也可以直接删除。
-
注意 Schema,如果忽略 Schema 可能会删除用户 Schema。恢复用户 Schema 时,Schema 的 Ledger 信息是存在 Bookie 中,Schema 自身的信息存在 Broker 归属的 ZK 中。恢复时需要先把 Broker 中存在的 Schema 信息删除,再让用户尝试使用生产端重建 Schema。
注意:执行以上操作前,切记提前备份数据。
实践 7:Apache Pulsar 多级缓存优化
如上图,Pulsar 现有缓存策略会导致明显的毛刺现象,出现服务周期性的剧烈性能波动和用户端的明显感知。
try {//We need to check all the segments, starting from the current//backward to minimize the//checks for recently inserted entriesint size = cacheSegments.size();for (int i = 0; i < size; i++)int segmentIdx = (currentSegmentIdx + (size - i)) % size;
try {int offset = currentSegmentOffset.getAndAdd(entrySize);if (offset + entrySize > segmentSize) {// Rollover to next segmentcurrentSegmentIdx = (currentSegmentIdx + 1) % cacheSegments.size(); currentSegment0ffset. set(alignedSize);cacheIndexes.get(currentSegmentIdx).clear();offset = 0;
}
这里腾讯团队主要做了读取缓存的优化。在读取缓存层面,可以看到 Pulsar 在读取缓存时迭代了缓存中的所有消息,如第一段代码倒数第二行所示。同时,一旦 offset + entrySize 大于 segmentSize,就会清除全部缓存,如第二段代码所示。这也就是为什么之前会出现明显的性能波动点的原因所在。
为此团队使用了 OHC + LRU 的策略,避免了缓存情况导致的剧烈波动,效果如下图:
总结与展望
本文分享了腾讯云团队在 Apache Pulsar 稳定性上的实践经验,重点介绍了消息空洞的影响及规避措施等最佳实践,为更多开发者提供参考。同时,腾讯云团队也在参与社区贡献中,和社区讨论以下重要问题并探索相关解决方案,如客户端超时时间内的重试策略,借鉴其他 MQ 的思路进行改进,尝试在客户端加入超时重试策略,通过多次重试机制来避免发送失败的情况发生;优化 Broker 和 Bookie OOM,针对 Ack 空洞对应集合无法缩容的问题进行改进;以及优化 Bookie Auto Recover,加入超时重试逻辑,避免 BookKeeper 和 ZooKeeper 之间发生 Session 超时的情况下服务重启。
相关文章:
云原生消息流系统 Apache Pulsar 在腾讯云的大规模生产实践
导语 由 InfoQ 主办的 Qcon 全球软件开发者大会北京站上周已精彩落幕,腾讯云中间件团队的冉小龙参与了《云原生机构设计与音视频技术应用》专题,带来了以《云原生消息流系统 Apache Pulsar 在腾讯云的大规模生产实践》为主题的精彩演讲,在本…...
【LeetCode刷题】--245.最短单词距离III
245.最短单词距离III class Solution {public int shortestWordDistance(String[] wordsDict, String word1, String word2) {int len wordsDict.length;int ans len;if(word1.equals(word2)){int prev -1;for(int i 0;i<len;i){String word wordsDict[i];if(word.equa…...
数字化时代的智能支持:亚马逊云科技轻量应用服务器技术领先
轻量应用服务器是一种简化运维、门槛低的弹性服务器,它的"轻"主要体现在几个方面:开箱即用、应用优质、上手简洁、投入划算、运维简便以及稳定可靠。相较于普通的云服务器,轻量应用服务器简化了云服务的操作难度、使用和管理流程&a…...
【智慧之窗】AI驱动产品探索
一.初识 ChatGPT ChatGPT 是由 OpenAI 开发的自然语言处理(NLP)模型,基于 GPT(Generative Pre-trained Transformer)架构。GPT 系列的模型旨在理解和生成自然语言文本。ChatGPT 专注于支持对话性任务,即与…...
BBS项目--登录
BBS阶段性测试总要求 django登录报错 Error: [WinError 10013] 以一种访问权限不允许的方式做了一个访问套接字的尝试。 原因分析:出现这种情况在Windows中很常见,就是端口被占用 解决措施:这时我们只需改一下端口便可以了 登录前端页面(HTML…...
Python---TCP服务端程序开发
1. 开发 TCP 服务端程序开发步骤回顾 创建服务端端套接字对象绑定端口号设置监听等待接受客户端的连接请求接收数据发送数据关闭套接字 2. socket 类的介绍 导入 socket 模块import socket 创建服务端 socket 对象socket.socket(AddressFamily, Type) 参数说明: AddressF…...
回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 (多指标,多图)
回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 (多指标,多图) 目录 回归预测 | MATLAB实现GWO-DHKELM基于灰狼算法优化深度混合核极限学习机的数据回归预测 (多指标,多图&#…...
听GPT 讲Rust源代码--src/tools(15)
File: rust/src/tools/rust-analyzer/crates/mbe/src/token_map.rs 在Rust源代码中,rust/src/tools/rust-analyzer/crates/mbe/src/token_map.rs文件的作用是实现了一个能够将输入的文本映射为标记的结构。具体来说,它定义和实现了几个结构体(…...
python可以做小程序研发嘛,python能做微信小程序吗
大家好,给大家分享一下python可以做微信小程序开发吗,很多人还不知道这一点。下面详细解释一下。现在让我们来看看! 大家好,给大家分享一下用python编写一个小程序,很多人还不知道这一点。下面详细解释一下用python代码…...
创建型模式 | 单例模式
一、单例模式 单例模式(Singleton Pattern),使用最广泛的设计模式之一。其意图是保证一个类仅有一个实例被构造,并提供一个访问它的全局访问接口,该实例被程序的所有模块共享。 1、饿汉式 1.1、基础版本 在程序启动后立刻构造单例࿰…...
【无标题】欢迎使用Markdown编辑器
这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…...
Postgresql中PL/pgSQL的游标、自定义函数、存储过程的使用
场景 Postgresql中PL/pgSQL代码块的语法与使用-声明与赋值、IF语句、CASE语句、循环语句: Postgresql中PL/pgSQL代码块的语法与使用-声明与赋值、IF语句、CASE语句、循环语句-CSDN博客 上面讲了基本语法,下面记录游标、自定义函数、存储过程的使用。 …...
【IDEA】Intellij IDEA相关配置
IDEA 全称 IntelliJ IDEA,是java编程语言的集成开发环境。IntelliJ在业界被公认为最好的Java开发工具,尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(git、svn等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以说是超…...
GD32移植STM32工程(因为懒,所以移植)
文章目录 一、前言二、差异性三、软件移植部分1.前期准备1.1 安装GD32固件库1.2 选择所用芯片 2.修改程序2.1 启动时间(内部时钟可不改)2.2 主频2.2.1 系统时钟配置2.2.2 108MHz宏定义第一处第二处第三处第四处第五处 2.2.3 串口2.2.4 FLASH 四、总结 一…...
mt5和mt4交易软件有什么区别?
MetaTrader 4(MT4)和MetaTrader 5(MT5)是两种广泛使用的外汇和金融市场交易平台,由MetaQuotes公司开发。尽管它们都是外汇交易的常见选择,但在功能和特性上存在一些区别。以下是MT4和MT5之间的主要区别&…...
零刻EQ12 N100 双2.5G网口 All In One新手教程
零刻EQ12 N100 双2.5G网口 All In One新手教程 前言1.硬件配置2.准备工作2.1. ESXI8.0U2镜像2.2. Rufus磁盘工具下载2.3. ikuai镜像下载2.4. StarWindConverter虚拟磁盘格式转换工具下载2.5. OpenWrt镜像下载2.6. 黑群晖RR引导镜像下载(DSM7.2)2.7. 需要准备的硬件2.8. 格式化需…...
竞赛保研 基于Django与深度学习的股票预测系统
文章目录 0 前言1 课题背景2 实现效果3 Django框架4 数据整理5 模型准备和训练6 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 **基于Django与深度学习的股票预测系统 ** 该项目较为新颖,适合作为竞赛课题方向ÿ…...
听GPT 讲Rust源代码--src/tools(16)
File: rust/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs rust-analyzer是一个基于Rust语言的IntelliSense引擎,用于提供IDE自动补全、代码导航和其他代码编辑功能。在rust-analyzer的源代码中,rust/src/tools/rust-analyzer…...
Leetcoed 双指针
三数之和 给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k ,同时还满足 nums[i] nums[j] nums[k] 0 。 请你返回所有和为 0 且不重复的三元组。 注意:答案中不可以包含重复的三元组…...
关于“Python”的核心知识点整理大全31
目录 12.4.2 在屏幕上绘制飞船 alien_invasion.py 编辑12.5 重构:模块 game_functions 12.5.1 函数 check_events() game_functions.py alien_invasion.py 12.5.2 函数 update_screen() game_functions.py alien_invasion.py 12.6 驾驶飞船 12.6.1 响应…...
第1章 SpringBoot开发入门
学习目标 了解SpringBoot的优点 掌握SpringBoot项目的构建 掌握SpringBoot的单元测试和热部署 熟悉SpringBoot的自动化配置原理 熟悉SpringBoot的执行流程 随着互联网的兴起,Spring势如破竹地占据了Java领域轻量级开发的王者之位。随着Java语言的发展以及市场…...
利用prometheus+grafana进行Linux主机监控
文章目录 一.架构说明与资源准备二.部署prometheus1.上传软件包2.解压软件包并移动到指定位置3.修改配置文件4.编写启动脚本5.启动prometheus服务 三.部署node-exporter1.上传和解压软件包2.设置systemctl启动3.启动服务 四.部署grafana1.安装和启动grafana2.设置prometheus数据…...
单词反转(字符串)
题目名字 单词反转 题目链接 题意 输入倒序的字符串,要求输出正序的字符串 思路 用while输入,这样当出现输入是空格时自动划分上一个为一个单词然后再次反输出 while循环的条件是当不再输入的时候,因为是字符串,不用getline输入…...
【Java 集合】LinkedBlockingDeque
在开始介绍 LinkedBlockingDeque 之前, 我们先看一下 LinkedBlockingDeque 的类图: 从其中可以看出他直接实现了 BlockingDeque 接口, 而 BlockingDeque 又实现了 BlockingQueue 的接口, 所以它本身具备了队列的特性。 而实现 BlockingDeque 使其在 BlockingQueue 的基础上多了…...
【hacker送书第3期】OpenCV轻松入门:面向Python(第2版)
第3期图书推荐 内容简介作者简介图书目录专家推荐参与方式 内容简介 本书基于面向 Python 的 OpenCV(OpenCV for Python),介绍了图像处理的方方面面。本书以 OpenCV 官方文档的知识脉络为主线,并对细节进行补充和说明。书中不仅介绍了 OpenCV 函数的使用…...
手把手教你isPalindrome 方法在密码验证中的应用
在信息安全领域中,密码验证是一个极为重要的组成部分。一个强密码应具备足够的复杂性,以免遭到破解。而回文密码是一种具备特殊性质的密码,其正序和倒序相同,因此具有极高的安全性,并能发挥重要作用。在实际密码策略中…...
drf入门规范(二)
四 RESTful API规范 REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征性状态转移)。 它首次出现在2000年Roy Fielding的博士论文中。 RESTful是一种定义Web API接口的设计风格,尤其适用…...
使用Redis和Nginx分别实现限制接口请求频率
前言 为啥需要限制接口请求频率?这个是因为防止接口一直被刷,比如发送手机验证码的接口,一直被刷的话,费钱费资源的,至少做点基本的防护工作。以下分别使用Redis和Nginx实现限制接口请求频率方案。 一、基于Redis实现…...
ansible模块 (7-13)
模块 7、hostname模块: 远程主机名管理模块 ansible 192.168.10.202 -m hostname -a nameliu 8、copy模块: 用于复制指定的主机文件到远程主机的模块 常用参数: dest: 指出要复制的文件在哪,必须使用绝对路径。如果源目标是…...
MySQL概括与SQL分类
文章目录 一、计算机语言二、SQL语言三、数据库系统四、MySQL简介 一、计算机语言 二、SQL语言 三、数据库系统 四、MySQL简介...
建筑模板工/百度优化点击软件
开发时 向后台发送请求,返回成功后,重新刷新页面并且弹窗提示成功。弹窗经常只显示一条线,就结束了。一开始initPage(); //重绘页面showInfo(操作成功); //提示框后来尝试用Promise同步new Promise(function(resolve){init.initPage();window…...
北辰手机网站建设/给你一个网站怎么优化
一转眼,吾家有女初长成。露露不再是出生时那个手指细如火柴、脑袋只有盈盈一捧的拇指姑娘,不再是那个因吃惯了奶瓶、使足了吃奶的劲也吸不出母乳的小可怜;也不再是那个抱起来就笑,一放下就哭的洋娃娃。虽然只有两岁多一点…...
企业搭建网站的必要性/广州seo优化推广
安静的早餐Scala学习手册(Learning Scala)第二章1、值与变量定义2、命名3、数据类型及其转换(toType)4、字符串和内插5、元组1、值与变量定义值的定义值,即为常量,不可变,基本语法定义:val : 创建一个名为a,类型为I…...
进行优化/深度优化
目录 题目描述 解决方案 代码 代码走读 传送门 题目描述 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。 输入/输出示例: 输入输出解释abcabcbb3因为无重复字符的最长子串是“abc”,所以其长度为3解决方案 这个问…...
可以玩游戏的网站/百度西安
摘要:二是进入炉渣,计算机在炉内有出路三条,气逸出一是随煤,进入三是生铁。电弧电压,热点嘴直径等气体流量和喷,焊接速度,直径艺参要有钨极钨极焊工手工数主。主要图的投影规律高平齐与俯三视是…...
软件销售网站模板/一站式营销推广
小编典典//The simple version for 10 Characters from the beginning of the string$string substr($string,0,10)....;更新:基于检查长度的建议(并确保修剪后的和未修剪的琴弦的长度相似):$string (strlen($string) > 13) ? substr($string,0,10…...