消息队列选型之 Kafka vs RabbitMQ

在面对众多的消息队列时,我们往往会陷入选择的困境:“消息队列那么多,该怎么选啊?Kafka 和 RabbitMQ 比较好用,用哪个更好呢?”想必大家也曾有过类似的疑问。对此本文将在接下来的内容中以 Kafka 和 RabbitMQ 为例分享消息队列选型的一些经验。

一、什么是消息队列

消息队列即 Message+Queue,消息可以说是一个数据传输单位,它包含了创建时间、通道/主题信息、输入参数等全部数据;队列(Queue)是一种 FIFO(先进先出)的数据结构,编程语言一般都内置(内存中的)队列实现,可以作为进程间通讯(IPC)的方法。使用队列最常见的场景就是生产者/消费者模式:生产者生产消息放到队列中,消费者从队列里面获取消息消费。典型架构如下图所示:

准确的说,消息队列是一种能实现生产者到消费者单向通信的通信模型,而一般大家说 MQ 是指实现了这个模型的中间件,比如 RabbitMQ、RocketMQ、Kafka 等。我们所要讨论的选型主要是针对消息中间件。

二、消息队列的应用场景

既然要选,那他们有什么应用场景呢?可以总结为 6 个字:异步、解耦、削峰。

2.1 异步

首先消息队列支持异步通信,发送方可以快速将消息放入队列中并立即返回,而不需要等待接收方的响应。这种异步通信模式可以减少请求等待,能让服务异步并行处理,提高系统的吞吐量和响应时间。

上图以支付会员红包系统交互过程为例,红包 Platform 通过 MQ 通知红包 Consumer 实现异步转账,同时有兜底 Task 查询转账所有未到终态领取单并通过 MQ 异步发送转账消息。

2.2 解耦

其次通过使用消息队列,发送方和接收方可以解耦,彼此之间不直接通信。发送方只需将消息发送到队列中,而不需要关心消息的具体处理方式和接收方的可用性。

上图通过举例账户和红包的消息队列说明,通过解耦不同服务,可以使整个系统更加灵活和可扩展。

2.3 削峰

最重要的优势就是能用来平滑处理系统中的高峰流量。当系统面临瞬时高流量时,消息队列可以作为一个缓冲层,将大量的请求消息存储在队列中,然后按照系统处理能力逐渐消费这些消息,平稳地处理高峰流量。

上图通过举例在秒杀活动中的利用消息队列实现流量削峰。通过在后台启动若干个队列处理程序,消费消息队列中的消息,再执行校验库存、下单等逻辑。因为只有有限个队列处理线程在执行,所以落入后端数据库上的并发请求是有限的 。而请求是可以在消息队列中被短暂地堆积, 当库存被消耗完之后,消息队列中堆积的请求就可以被丢弃了。

三、消息队列发展历程

言归正传,先看看有哪些主流消息队列可选。

  • ActiveMQ 是 Apache 出品的、采用 Java 语言编写的完全基于 JMS1.1 规范的面向消息的中间件,为应用程序提供高效的、可扩展的、稳定的和安全的企业级消息通信。不过由于历史原因包袱太重,目前市场份额没有后面三种消息中间件多,其最新架构被命名为 Apollo,号称下一代 ActiveMQ,有兴趣的同学可自行了解。
  • RabbitMQ 是采用 Erlang 语言实现的 AMQP 协议的消息中间件,最初起源于金融系统,用于在分布式系统中存储转发消息。RabbitMQ 发展到今天,被越来越多的人认可,这和它在可靠性、可用性、扩展性、功能丰富等方面的卓越表现是分不开的。
  • Kafka 起初是由 LinkedIn 公司采用 Scala 语言开发的一个分布式、多分区、多副本且基于 Zookeeper 协调的分布式消息系统,现已捐献给 Apache 基金会。它是一种高吞吐量的分布式发布订阅消息系统,以可水平扩展和高吞吐率而被广泛使用。目前越来越多的开源分布式处理系统如 Cloudera、Apache Storm、Spark、Flink 等都支持与 Kafka 集成。
  • RocketMQ 是阿里开源的消息中间件,目前已经捐献个 Apache 基金会,它是由 Java 语言开发的,具备高吞吐量、高可用性、适合大规模分布式系统应用等特点,经历过双十一的洗礼,实力不容小觑。
  • Pulsar 是 Apache 软件基金会的顶级项目,是下一代云原生分布式消息流平台,集消息、存储、轻量化函数式计算为一体,采用计算与存储分离架构设计,支持多租户、持久化存储、多机房跨区域数据复制,具有强一致性、高吞吐、低延时及高可扩展性等流数据存储特性。

目前市面上的消息中间件还有很多,比如腾讯系的 PhxQueue、CMQ、CKafka,又比如基于 Go 语言的 NSQ,有时人们也把类似 Redis 的产品也看做消息中间件的一种,当然它们都很优秀,但是本文篇幅限制无法穷尽所有。

四、选型考虑

衡量一款消息中间件是否符合需求需要从多个维度进行考察:

  1. 功能: 能否开箱即用;优先级队列;延迟队列;死信队列;消息重试;消息回溯;消息堆积 + 持久化;消息跟踪;消息过滤;消息顺序性;安全机制;消息幂等性;事务性消息等。
  2. 性能: 时延;吞吐率等。
  3. 运维: 高可用;异地容灾;集群扩容;使用成本等。
  4. 业务需求: 要明确你的业务需要什么样的消息队列功能。例如,是否需要支持延时消息、死信队列、事务消息等高级功能,还是只需要基本的生产和消费功能。
  5. 数据量: 考虑你的数据量是否大,是否需要高吞吐率和持久性。如果数据量较小,可以考虑使用非标准消息队列产品,如 Redis 或 MySQL,以减少复杂性和成本。
  6. 架构和性能需求: 如果你的业务涉及大消息和大流量,需要考虑选择具有高吞吐率、高并发、持久性和稳定性的消息队列产品,如 Kafka 或 Pulsar。
  7. 云原生  Serverless 需求: 随着云计算的发展,云原生和 Serverless 架构变得越来越重要。一些消息队列产品开始朝这个方向演进,因此你可能需要考虑是否需要与云原生或 Serverless 架构集成。
  8. 生态系统: 考虑消息队列产品的生态系统和社区活跃度。一个活跃的生态系统通常会提供更多的支持和工具。
  9. 成本: 最后,要考虑成本因素。不同的消息队列产品在许可费用、维护成本和学习成本方面有所不同,需要根据你的预算和资源来选择合适的产品。

总而言之,消息队列的选择应根据消息队列本身特性、具体的业务需求和场景等多种因素决定。下文将以 Kafka 和 RabbitMQ 的选型考虑为例进行介绍。

五、Kafka vs RabbitMQ

5.1 二者架构

5.1.1 Kafka 架构特点:

  • Producer: 生产者,负责将消息投递到 kafka 中。
  • Consumer: 消费者,通过拉的方式获取消息进行业务处理。
  • Broker: 一个独立的 Kafka 服务节点或实例,多个 Broker 组成 Kafka 集群。

Kafka 通过 ZooKeeper 来进行元数据管理,包括:集群、Broker、主题和分区等。

主题和分区

  • 主题(Topic) :是一类消息的集合。

  • 分区(Partition) :每个主题被分成多个分区,每个 Partition 在存储层面是 Append Log 文件。

  • 偏移量(Offset): 消息在分区中的位置称为偏移量,它唯一标记分区内的一条消息。

5.1.2 RabbitMQ 架构特点:

  • Producer:主要就是生成消息,通过信道(Channel),把消息发送给交换机(Exchange)。
  • Consumer:监听 RabbitMQ 中的(Queue)队列中的消息,然后去消费。
  • Queue:用于存储消息。
  • Exchange:生产者将消息发送到 Exchange,由交换器将消息路由到一个或者多个队列中。
  • Broker:可以将一个 RabbitMQ Broker 看作一台 RabbitMQ Server。
  • RoutingKey :生产者将消息发给交换器的时候,一般会指定一个 RoutingKey,用来指定这个消息的路由规则。
  • BindingKey: RabbitMQ 中通过绑定将交换器与队列关联起来,在绑定的时候一般会指定一个 BindingKey,这样 RabbitMQ 就知道如何正确地将消息路由到队列了。

可以看出两者架构具有一定差异,使得功能也有所不同,下面将从具体场景来分析两者该如何选择。

5.2 消息的顺序

以订单系统为例:当订单状态变化的时候,把订单状态变化的消息发送给所有关心订单变化的系统。订单会在创建成功、待付款、已支付、已发货的所有状态之间单向流动。

在我们把订单状态变化消息要发送给所有关心订单状态的系统上所实现方式就是消息队列。

在这种业务下,我们最想要的是什么?

  1. 消息的顺序:对于同一笔订单来说,状态的变化都是有严格的先后顺序的。
  2. 吞吐量:如订单业务是希望订单越多越好。订单越多,吞吐量就越大。

        在这种情况下,我们先看看 RabbitMQ 是怎么做的。首先,对于发消息并广播给多个消费者这种情况,RabbitMQ 会为每个消费者建立一个对应的队列。也就是说,如果有 10 个消费者,RabbitMQ 会建立 10 个对应的队列。然后,当一条消息被发出后,RabbitMQ 会把这条消息复制 10 份放到这 10 个队列里。

        当 RabbitMQ 把消息放入到对应的队列后,我们紧接着面临的问题就是,我们应该在系统内部启动多少线程去从消息队列中获取消息。如果只是单线程去获取消息,那自然没有什么好说的。但是多线程情况,可能就会有问题。因为 RabbitMQ 在官方文档中声明了自己是不保证多线程消费同一个队列的消息,一定保证顺序的。而不保证的原因,是因为多线程时,当一个线程消费消息报错的时候,RabbitMQ 会把消费失败的消息再入队,此时就可能出现乱序的情况。        

T0 时刻,队列中有四条消息 A1、B1、B2、A2。其中 A1、A2 表示订单 A 的两个状态:待付款、已付款。B1、B2 也同理,是订单 B 的待付款、已付款。T1 时刻,消息 A1 被线程 1 收到,消息 B1 被线程 2 收到。此时,一切都还正常。T3 时刻,B1 消费出错了,同时呢,由于线程 1 处理速度快,又从消息队列中获取到了 B2。此时,问题开始出现。T4 时刻,由于 RabbitMQ 线程消费出错,可以把消息重新入队的特性,此时 B1 会被重新放到队列头部。所以,如果不凑巧,线程 1 获取到了 B1,就出现了乱序情况,B2 状态明明是 B1 的后续状态,却被提前处理了。

所以,可以看到了,这个场景用 RabbitMQ,出现了三个问题:

  1. 为了实现发布订阅功能,从而使用的消息复制会降低性能并耗费更多资源;
  2. 多个消费者无法严格保证消息顺序;
  3. 大量的订单集中在一个队列,吞吐量受到了限制。

而 Kafka 正好在这三个问题上表现的比 RabbitMQ 好得多。首先,Kafka 的发布订阅并不会复制消息,因为 Kafka 的发布订阅就是消费者直接去获取被 Kafka 保存在日志文件中的消息就好。无论是多少消费者,他们只需要主动去找到消息在文件中的位置即可。其次,Kafka 不会出现消费者出错后,把消息重新入队的现象。最后,Kafka 可以对订单进行分区,把不同订单分到多个分区中保存,这样,吞吐量能更好。

所以对于这个需求场景, Kafka 会更加合适。

5.3 消息的匹配

以营销系统为例,该系统中有个非常显著的特点,就是非常复杂非常灵活地匹配规则。比如要根据推广内容去匹配不同的方式做宣传、要根据不同的活动去匹配不同的渠道做分发。总之,数不清的匹配规则是这套系统中非常重要的特点。

在 RabbitMQ 中,因为 RabbitMQ 是允许在消息中添加 routing_key 或者自定义消息头,然后通过一些特殊的 Exchange,很简单的就能够实现消息匹配分发,所以开发几乎没有成本。

而在 Kafka 中如果要实现消息匹配,开发成本就比使用 RabbitMQ 高多了。首先,通过简单的配置去自动匹配和分发到合适的消费者端这件事是不可能的。其次,消费者端必须先把所有消息不管需要不需要,都取出来。然后,再根据业务需求,自己去实现各种精准和模糊匹配。可能因为过度的复杂性,还要引入规则引擎。

这个场景下 RabbitMQ 扳回一局。

5.4 消息的超时

以电商业务中下单后 15 分钟内未支付则自动取消订单为例。有些朋友可能会觉得奇怪,在单一的服务系统可以通过定时任务就能解决这个问题吧,为什么会用到消息队列呢?但是在 SOA 或者微服务架构下这样做是不行的。因为很多个服务都关心是否支付这件事,如果每种服务,都自己实现一套定时任务的逻辑,既重复又难以维护。在这种情况下,我们往往会做一层抽象:把要执行的任务封装成消息。当时间到了,直接扔到消息队列里,消息的订阅者们获取到消息后,直接执行即可。

希望把消息延迟一定时间再处理的,被称为延迟队列。对于订单取消的这种业务,我们就会在创建订单的时候,同时扔一个包含了执行任务信息的消息到延迟队列,指定15分钟后,让订阅这个队列的各个消费者,可以收到这个消息。随后,各个消费者所在的系统就可以去执行相关的扫描订单的任务了。

此时 RabbitMQ 和 Kafka 消息队列该如何选?

先看 RabbitMQ,RabbitMQ 的消息自带手表,消息中有个 TTL 字段,可以设置消息在 RabbitMQ 中的存放的时间,超时了会被移送到一个叫死信队列的地方。所以,延迟队列 RabbitMQ 最简单的实现方式就是设置 TTL,然后一个消费者去监听死信队列。当消息超时了,监听死信队列的消费者就收到消息了。不过,这样做有个大问题:假设,我们先往队列放入一条过期时间是 10 秒的 A 消息,再放入一条过期时间是 5 秒的 B 消息。

那么问题来了,B 消息会先于 A 消息进入死信队列吗?答案是否定的。B 消息会优先遵守队列的先进先出规则,在 A 消息过期后,和其一起进入死信队列被消费者消费。

在 RabbitMQ 的 3.5.8 版本以后,官方推荐的 rabbitmq delayed message exchange 插件能解决这个问题。

  • 用了这个插件,我们在发送消息的时候,把消息发往一个特殊的 Exchange。
  • 同时,在消息头里指定要延迟的时间。
  • 收到消息的 Exchange 并不会立即把消息放到队列里,而是在消息延迟时间到达后,才会把消息放入。

再看下 Kafka :

而通过 Kafka 要实现延迟队列就很麻烦了。我们需要把消息先放入一个临时的 Topic,然后通过开发这个叫做中转的消费者,让这个中间的消费者先去把消息从这个临时的 Topic 中取出,这个时候因为没到时间并不能马上进行处理,也不能保存到内存中,所以就需要将其存入数据库中,等待到时间之后放入 Kafka 中,以便真正的消费者去执行业务逻辑。以上步骤已经接近调度平台了,如果需要再高级一点甚至还需要用时间轮算法才能更好更准确的完成任务执行。

所以在此种场景下, RabbitMQ 上那一条条戴手表的消息才是最好的选择。

5.5 消息的保持

在微服务里,事件溯源模式是经常用到的。如果想用消息队列实现,一般是把事件当成消息,依次发送到消息队列中。事件溯源有个最经典的场景,就是事件的重放。简单来讲就是把系统中某段时间发生的事件依次取出来再处理。而且,根据业务场景不同,这些事件重放很可能不是一次,更可能是重复 N 次。

假设,我们现在需要一批在线事件重放,去排查一些问题。RabbitMQ 此时就无法实现,因为消息被取出来后就被删除了。想再次被重复消费是没办法的。而在 Kafka 中消息会被持久化到一个专门的日志文件里,不会因为被消费了就被删除。所以在需要保持消息的场景中,对消息不离不弃的 Kafka 相对用过就抛的 RabbitMQ 会更合适。

5.6 消息的错误处理

大多时候,在做记录数据相关的业务时,Kafka 一般都是不二选择。不过有时在需要被记录的数据吞吐量不大时,我更推荐使用用 RabbitMQ。原因就是 Kafka 有一个设计原则:当单个分区中的消息一旦出现消费失败,就只能停止而不是跳过这条失败的消息继续消费后面的消息。即不允许消息空洞。只要消息出现失败,不管是 Kafka 自身消息格式的损坏,还是消费者处理出现异常,是不允许跳过消费失败的消息继续往后消费的。

所以,在数据统计不要求十分精确的场景下选择 Kafka 后,一旦出现了消息消费问题就会发生项目不可用的情况,这样也徒增了很多烦恼。而在开始的时候就选择 RabbitMQ 的话,它就会在消息出问题或者消费错误的时候,重新入队或者移动消息到死信队列,继续消费后面的消息,也会避免很多不必要的麻烦。所以在需要消息的错误处理场景下选择 RabbitMQ 更优。

5.7 消息的吞吐量

Kafka 是每秒几十万条消息吞吐,而 RabbitMQ 的吞吐量是每秒几万条消息。在一些吞吐量没那么大的项目中引入 Kafka 不如引入 RabbitMQ 的原因是因为 Kafka 为了更好的吞吐量,很大程度上增加了自己的复杂度。而这些复杂度对项目来说就会有很多不必要的麻烦,主要体现在两个方面:

  1. 配置复杂、维护复杂

Kafka 的参数配置相对 RabbitMQ 是很复杂的。比如:磁盘管理相关参数、集群管理相关参数、ZooKeeper 交互相关参数、Topic 级别相关参数等都需要一些思考和调优。另外 Kafka 本身集群和参与管理集群的 ZooKeeper 也带来了更多的维护成本。Kafka 要用好就需要考虑 JVM、消息持久化、集群本身交互,以及 ZooKeeper 本身和它与 Kafka 之间的可靠和效率。

  1. 用好,用对存在门槛

Kafka 的 Producer 和 Consumer 本身要用好用对也存在很高的门槛。比如,Producer 消息可靠性保障、幂等性、事务消息等都需要对 Kafka Producer 有深入的了解。而 Consumer 更不用说了,仅仅日志偏移管理的复杂度就很高。相对来说,RabbitMQ 就简单得多,可能都不用配置什么,直接启动起来就能很稳定可靠地使用了;就算配置,也是寥寥几个参数的设置即可。所以,大家在项目中引入消息 队列 的时候,真的要好好考虑下,不要因为大家都鼓吹 Kafka 好,就无脑引入。

六、总结

从 Kafka 与 RabbitMQ 选型对比中可以看出,如果我们要做消息队列选型,有两件事是必须要做好的:列出业务最重要的几个特点和深入到消息队列的细节中去比较。当我们对这些中间件的特点非常熟悉之后,甚至可以根据不同的子业务特征,引入不同的消息队列,即消息队列混用,这样就可能实现最大化获益,最小化成本。

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

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

相关文章

阿里AI-Spring Cloud Alibaba AI:快速搭建自己的通义千问

本文基于官方文档。 Spring AI 官方文档:Spring AI :: Spring AI Reference 中文文档:Spring AI 简介 - spring 中文网 (springdoc.cn) Spring AI 是 Spring 官方社区项目,旨在简化 Java AI 应用程序开发,让 Java 开发者像使用…

流光卡片,生成炫酷文字,开源API

https://fireflycard.shushiai.com/ 这是我的一个网站,流光卡片,主要功能是帮你制作酷炫的文字卡片,用精美的卡片让你的文字生动起来。 展示效果如下: 你可以用它制作卡片,来记录自己的表达。支持设定卡片背景、LOGO…

梗图生成器突然爆红;ElevenLabs发布IOS APP 高质量语音朗读手机各种文本内容;开源工作流架构ControlFlow

✨ 1: 梗图生成器 fabianstelzer 在Glif做的一个超强meme生成器 Glif 是一个工作流,能生成文字图片和视频,用工作流的形式可以完成很多的花样来。 最近爆红的梗图生成器,WOJAK MEME GENERATOR ,也是用工作流的形式来生成这些有…

宝塔面板之 wwwroot修改不了权限

宝塔使用Apache环境,搭建网站出现 You don’t have permission to access this resource.Server unable to read h出错时的解决办法 今天由于某些原因导致我宝塔 在Apache和Nginx运行环境下不断切换,结果我网站全部不能正常打不开了 结果我发现原本宝塔…

boss直聘招聘数据可视化分析

boss直聘招聘数据可视化分析 一、数据预处理二、数据可视化三、完整代码一、数据预处理 在 上一篇博客中,笔者已经详细介绍了使用selenium爬取南昌市web前端工程师的招聘岗位数据,数据格式如下: 这里主要对薪水列进行处理,为方便处理,将日薪和周薪的数据删除,将带有13薪…

自媒体内容创作者必备:ChatGPT助你提升文章质量

随着自媒体的迅猛发展,越来越多的人加入到内容创作的行列。然而,要在这个竞争激烈的领域脱颖而出,不仅需要创意和独特的观点,更需要高质量的文章内容。在这方面,ChatGPT作为一个智能写作助手,能够帮助自媒体…

靠!AI绘画月入过万!是否现实?

前言 AI人工智能已经出现在了越来越多领域中,比如最近一段时间,AI绘画就受到了许多人的关注,一来,其背后隐藏的版权问题、替代性问题引发了人们的广泛讨论,再者,AI绘画在短期时间内成为了流量密码&#xf…

暑假追高必备:ChildLife全新钙镁锌小绿钙

2024年暑假将至,家长们对于孩子的健康关注再次提升,其中补钙成为许多家长关注的重点。暑假期间,孩子有更多时间进行户外活动,加上高温流汗多,身体的钙更容易流失,因此需要额外地补充。为此,美国…

工业AIoT竞赛流程

不要点到重置!!!要刷新虚拟机就点重启 xshell连接虚拟机:ssh rootPublic IP 环境构建 vim /etc/hosts 按 i 进入插入模式,加内网ip和主机名,按esc,按 : ,按wq 三个虚拟机都这样配 …

创新实训博客(十三)——admin前端工作效果

管理/教师端前端工作汇总education-admin: 首先是登录注册页面的展示 管理员 首页 管理员登录后的首页如下图所示 管理员拥有所有的权限 课程管理 1、可以查看、修改、增添、删除课程列表内容 2、可以对课程资源进行操作 3、可以对课程的类别信息进行管理&…

什么类型的网站需要配置OV证书

目录 什么网站更适合OV证书: 申请OV需要注意: 申请单位组织验证型OV SSL证书的详细步骤 OV SSL证书全称Organization Validation SSL(组织验证性SSL证书),是一种需要验证网站真实身份的数字证书。通过证书颁发机构审查网站企业身份和域名所…

3D模型优化10个最佳实践

对于许多在建模、渲染和动画方面经验丰富的 3D 建模者来说,3D 优化可能是一个令人畏惧的过程 - 特别是当你正在优化实时应用程序的 3D 模型时! 在 Google 上快速搜索“如何优化 3D 文件”将会出现一些建议,例如减少多边形数和消除多余的顶点。…

为什么叫云计算?云计算的优势有哪些

说起云计算大家并不会感到陌生,那么为什么叫云计算?云计算技术的引入通常会使企业的信息技术应用更高效、更可靠、更安全。云计算支持用户在任意位置、使用各种终端获取应用服务。使用了数据多副本容错、计算节点同构可互换等措施来保障服务的高可靠性&a…

等保测评中的问题与建议

随着信息技术的广泛使用和飞速发展,网络安全已逐渐演变为威胁经济社会发展的关键议题。信息安全的范围涵盖了政治、商务、军事、教育等多个方面。其中,信息的存储、分享以及管理,主要取决于政府的宏观规划和决策、商业运作的信息、银行的财务…

构建 Audio Unit 应用程序

构建 Audio Unit 应用程序 构建 Audio Unit 应用程序从选择设计模式开始I/O Pass ThroughI/O Without a Render Callback FunctionI/O with a Render Callback FunctionOutput-Only with a Render Callback Function其他设计模式 构建应用程序配置 audio session指定 audio uni…

金融行业自动化运维的研究与实践

金融行业自动化运维的研究与实践 在金融行业中,信息技术(IT)运维已经成为保障业务连续性和稳定性的重要环节。随着金融业务的复杂化和信息系统的多样化,传统的手工运维模式已无法满足高效、安全的需求。自动化运维技术的应用变得…

流计算状态算子灵活开发指南

随着实时数据流处理需求的不断增长,高效、可扩展的流计算框架变得愈发重要。DolphinDB 作为一款高性能分布式时间序列数据库,不仅在数据存储和查询上表现出色,还通过引入面向对象编程(OOP)编程范式,使得开发…

聚焦 HW 行动,构筑重保邮件安全防线

随着信息技术的飞速发展,网络安全已成为国家安全的重要组成部分。HW行动作为国家级网络安全演练,通过模拟实战攻防,检验和提升国家关键信息基础设施的防护能力。 CACTER凭借多年HW防护经验,提供全面的邮件安全防护体系&#xff0…

MySQL详细介绍:开源关系数据库管理系统的魅力

学习总结 1、掌握 JAVA入门到进阶知识(持续写作中……) 2、学会Oracle数据库入门到入土用法(创作中……) 3、手把手教你开发炫酷的vbs脚本制作(完善中……) 4、牛逼哄哄的 IDEA编程利器技巧(编写中……) 5、面经吐血整理的 面试技…

Flutter 小技巧之为什么推荐 Widget 使用 const

今天收到这个问题,本来想着简单回复下,但是感觉这个话题又可以稍微展开讲讲,干脆就整理成一篇简单的科普,这样也能更方便清晰地回答这个问题。 聊这个问题之前,我们需要把一个“老生常谈”的概念拿出来说,那…