学习 RocketMQ 单机部署、消息发送、消息接收

文章目录

    • RocketMQ 介绍
    • 为什么要使用 MQ ?
    • RocketMQ 与其他产品对比
      • vs Kafka
      • vs RabbitMQ
      • vs ActiveMQ
    • RocketMQ 重要概念
    • 部署 Namesrver、Broker、Dashboard
    • 快速入门
      • 消息生产者
      • 消息消费者
    • 消费模式
    • 简单消息
      • 1)同步发送
      • 2)异步发送
      • 3)单向发送
    • 顺序消息
      • 顺序消息生产者
      • 顺序消息消费者
    • 广播消息
      • 广播消息消费模式
    • 延迟消息
      • 预定日程生产者
      • 预定日程消费者
      • 指定时间生产者
      • 指定时间消费者
    • RocketMQ 如何保证消息可靠性?
    • RocketMQ 如何解决消息积压问题?

RocketMQ 介绍

RocketMQ是一款由阿里巴巴开源的分布式消息中间件。它具有低延迟、高吞吐量、高可用性和高可靠性等特点,适用于构建具有海量消息堆积和异步解耦功能的应用系统。

为什么要使用 MQ ?

作用描述
异步系统耦合度降低,没有强依赖关系
削峰不需要同步执行的远程调用可以有效提高响应时间
解耦请求达到峰值后,后端或者数据库还可以保持固定消费速率消费,不会被压垮

RocketMQ 与其他产品对比

vs Kafka

  • 数据可靠性:RocketMQ 提供多种可靠性保证,Kafka 在极端情况下可能丢失数据。
  • 实时性:RocketMQ 在消息实时性上表现更佳。
  • 队列数与性能:RocketMQ 支持更多队列,Kafka 在高分区下性能下降。
  • 消息顺序性:RocketMQ 支持严格顺序,Kafka 可能产生乱序。
  • 生态:Kafka 生态更丰富,RocketMQ 与阿里技术栈集成好。

vs RabbitMQ

  • 性能:RocketMQ 在高并发和海量消息处理上表现更优。
  • 消息模型:RabbitMQ 模型灵活,RocketMQ 注重顺序和事务。
  • 适用场景:RabbitMQ 适用于可靠消息传递,RocketMQ 适用于高性能场景。

vs ActiveMQ

  • 跨平台与持久化:ActiveMQ 支持多种协议和数据库持久化,RocketMQ 持久化机制高效。
  • 灵活性:ActiveMQ 协议广泛,RocketMQ 多语言SDK集成友好。
  • 社区与文档:ActiveMQ 社区活跃度较低,RocketMQ 开发活跃但社区成熟度不及 RabbitMQ。

总结:RocketMQ 在数据可靠性、实时性、队列数与性能上具有优势,适合高性能和顺序消息场景。

RocketMQ 重要概念

Producer:消息的发送者,生产者 (发件人)

Consumer:消息接收者,消费者 (取件人)

Broker:暂时和传输消息的通道 (快递)

NameServer:管理Broker的;负责消息的存储和转发,接收生产者产生的消息并持久化消息;当用户发送的消息被发送到Broker时,Broker会将消息转发到与之关联的Topic中,以便让更多的接收者进行处理;各个快递公司的管理机构,相当于Broker的注册中心,保留了broker的信息 (监测快递是否健康)

Queue:队列,消息存放的位置,一个Broker中可以有多个队列 (驿站)

Topic:主题,消息的分类,用于标识同一类业务逻辑的消息 (取件码)

ConsumerGroup:消费者组,RocketMQ 中承载多个消费行为一致的消费者负载均衡分组。和消费者不同,消费者组是一个逻辑概念。

部署 Namesrver、Broker、Dashboard

Docker部署RocketMQ5.x (单机部署+配置参数详解+不使用docker-compose直接部署)_rocketmq不推荐用docker部署-CSDN博客

快速入门

1)创建一个基于 Maven 的 SpringBoot 项目,并添加以下依赖

<dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-client</artifactId><version>5.1.0</version>
</dependency>

消息生产者

  1. 创建消息生产者producer,并指定生产者组名
  2. 指定Nameserver地址
  3. 启动producer
  4. 创建消息对象,指定主题Topic、Tag和消息体
  5. 发送消息
  6. 关闭生产者
public class SyncProducer {public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {DefaultMQProducer producer = new DefaultMQProducer("SyncProducer");producer.setNamesrvAddr("10.226.8.14:9876");producer.start();for (int i = 0; i < 2; i++) {Message msg = new Message("Simple", //主题"TagA",  //设置消息Tag,用于消费端根据指定Tag过滤消息。"Simple-Sync".getBytes(StandardCharsets.UTF_8) //消息体。);SendResult send = producer.send(msg);System.out.printf(i + ".发送消息成功:%s%n", send);}producer.shutdown();}
}

消息消费者

  1. 创建消费者comsumer、指定消费者组名
  2. 指定Nameserver地址
  3. 创建监听订阅主题Topic和Tag等
  4. 处理消息
  5. 启动消费者comsumer
public class Consumer {public static void main(String[] args) throws Exception {DefaultMQPushConsumer pushConsumer = new DefaultMQPushConsumer("SimplePushConsumer");pushConsumer.setNamesrvAddr("10.226.8.14:9876");pushConsumer.subscribe("Simple","*");pushConsumer.setMessageListener(new MessageListenerConcurrently() {@Overridepublic ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {list.forEach( n->{System.out.printf("收到消息: %s%n" , n);});return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}});pushConsumer.start();System.out.printf("Consumer Started.%n");}
}

消费模式

MQ 的消费模式可以大致分为两种,一种是推 Push,一种是拉 Pull。

Push 是服务端主动推送消息给客户端,优点是及时性较好,但如果客户端没有做好流控,旦服务端推送大量消息到客户端时,就会导致客户端消息堆积甚至崩溃。

Pull 是客户端需要主动到服务端取数据,优点是客户端可以依据自己的消费能力进行消费但拉取的频率也需要用户自己控制,拉取频繁容易造成服务端和客户端的压力,拉取间隔长又容易造成消费不及时。

Push 模式也是基于 pull 模式的,只能客户端内部封装了 api,一般场景下,上游消息生产量小或者均速的时候,选择 push 模式。在特殊场景下,例如电商大促,抢优惠券等场景可以选择 pull 模式

简单消息

1)同步发送

可靠性要求高、数据量级少、实时响应,具体实现参考上面的入门代码

2)异步发送

不等待消息返回直接进入后续流程。

public class AsyncProducer {public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {DefaultMQProducer producer = new DefaultMQProducer("AsyncProducer");producer.setNamesrvAddr("10.226.8.14:9876");producer.start();CountDownLatch countDownLatch = new CountDownLatch(100);//计数for (int i = 0; i < 100; i++) {Message message = new Message("Simple", "TagA", "Simple-Async".getBytes(StandardCharsets.UTF_8));final int index = i;producer.send(message, new SendCallback() {@Overridepublic void onSuccess(SendResult sendResult) {countDownLatch.countDown();System.out.printf("%d 消息发送成功%s%n", index, sendResult);}@Overridepublic void onException(Throwable throwable) {countDownLatch.countDown();System.out.printf("%d 消息失败%s%n", index, throwable);throwable.printStackTrace();}});}countDownLatch.await(5, TimeUnit.SECONDS);producer.shutdown();}
}

3)单向发送

只负责发送,不管消息是否发送成功。

public class OnewayProducer {public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException {DefaultMQProducer producer = new DefaultMQProducer("AsyncProducer");producer.setNamesrvAddr("10.226.8.14:9876");producer.start();for (int i = 0; i < 10; i++) {Message message = new Message("Simple","TagA", "Simple-Oneway".getBytes(StandardCharsets.UTF_8));producer.sendOneway(message);System.out.printf("%d 消息发送完成 %n" , i);}Thread.sleep(5000);producer.shutdown();}
}

顺序消息

顺序消息指生产者局部有序发送到一个queue,但多个queue之间是全局无序的。

  • 顺序消息生产者样例:通过MessageQueueSelector将消息有序发送到同一个queue中。
  • 顺序消息消费者样例:通过MessageListenerOrderly消费者每次读取消息都只从一个queue中获取(通过加锁的方式实现)。

顺序消息生产者

public class OrderProducer {public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {DefaultMQProducer producer = new DefaultMQProducer("OrderProducer");producer.setNamesrvAddr("10.226.8.14:9876");producer.start();for (int j = 0; j < 5; j++) {for (int i = 0; i < 10; i++) {Message message = new Message("OrderTopic","TagA",("order_" + j + "_step_" + i).getBytes(StandardCharsets.UTF_8));SendResult sendResult = producer.send(message, new MessageQueueSelector() {@Overridepublic MessageQueue select(List<MessageQueue> list, Message message, Object o) {Integer id = (Integer) o;int index = id % list.size();return list.get(index);}}, j);System.out.printf("%s%n", sendResult);}}producer.shutdown();}
}

顺序消息消费者

public class OrderConsumer {public static void main(String[] args) throws MQClientException {DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("OrderConsumer");consumer.setNamesrvAddr("10.226.8.14:9876");consumer.subscribe("OrderTopic","*");consumer.registerMessageListener(new MessageListenerOrderly() {@Overridepublic ConsumeOrderlyStatus consumeMessage(List<MessageExt> list, ConsumeOrderlyContext consumeOrderlyContext) {list.forEach(n->{System.out.println("QueueId:"+n.getQueueId() + "收到消息内容 "+new String(n.getBody()));});return ConsumeOrderlyStatus.SUCCESS;}});consumer.start();System.out.printf("Consumer Started.%n");}
}

广播消息

广播消息并没有特定的消息消费者样例,这是因为这涉及到消费者的集群消费模式。

  • MessageModel.BROADCASTING:广播消息。一条消息会发给所有订阅了对应主题的消费者,不管消费者是不是同一个消费者组。
  • MessageModel.CLUSTERING:集群消息。每一条消息只会被同一个消费者组中的一个实例消费。

广播消息消费模式

public class BroadcastConsumer {public static void main(String[] args) throws Exception {DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("BroadCastConsumer");consumer.setNamesrvAddr("10.226.8.14:9876");consumer.subscribe("simple","*");consumer.setMessageModel(MessageModel.BROADCASTING); //广播模式consumer.registerMessageListener(new MessageListenerConcurrently() {@Overridepublic ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {list.forEach(n->{System.out.println("QueueId:"+n.getQueueId() + "收到消息内容 "+new String(n.getBody()));});return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}});consumer.start();System.out.printf("Broadcast Consumer Started.%n");}
}

延迟消息

延迟消息实现的效果就是在调用producer.send方法后,消息并不会立即发送出去,而是会等一段时间再发送出去。这是RocketMQ特有的一个功能。

  • message.setDelayTimeLevel(3):预定日常定时发送。1到18分别对应messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h;可以在dashboard中broker配置查看。
  • msg.setDelayTimeMs(10L):指定时间定时发送。默认支持最大延迟时间为3天,可以根据broker配置:timerMaxDelaySec修改。

预定日程生产者

public class ScheduleProducer {public static void main(String[] args) throws Exception {DefaultMQProducer producer = new DefaultMQProducer("ScheduleProducer");producer.setNamesrvAddr("10.226.8.14:9876");producer.start();for (int i = 0; i < 2; i++) {Message msg = new Message("Schedule", //主题"TagA",  //设置消息Tag,用于消费端根据指定Tag过滤消息。"ScheduleProducer".getBytes(StandardCharsets.UTF_8) //消息体。);//1到18分别对应messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2hmsg.setDelayTimeLevel(3);producer.send(msg);System.out.printf(i + ".发送消息成功:%s%n", LocalTime.now());}producer.shutdown();}
}

预定日程消费者

public class ScheduleConsumer {public static void main(String[] args) throws Exception {DefaultMQPushConsumer pushConsumer = new DefaultMQPushConsumer("SimplePushConsumer");pushConsumer.setNamesrvAddr("10.226.8.14:9876");pushConsumer.subscribe("Schedule","*");pushConsumer.setMessageListener(new MessageListenerConcurrently() {@Overridepublic ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {list.forEach( n->{System.out.printf("接收时间:%s %n", LocalTime.now());});return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}});pushConsumer.start();System.out.printf("Simple Consumer Started.%n");}
}

指定时间生产者

public class TimeProducer {public static void main(String[] args) throws MQClientException, MQBrokerException, RemotingException, InterruptedException {DefaultMQProducer producer = new DefaultMQProducer("TimeProducer");producer.setNamesrvAddr("10.226.8.14:9876");producer.start();for (int i = 0; i < 2; i++) {Message msg = new Message("Schedule", //主题"TagA",  //设置消息Tag,用于消费端根据指定Tag过滤消息。"TimeProducer".getBytes(StandardCharsets.UTF_8) //消息体。);// 相对时间:延时消息。此消息将在 10 秒后传递给消费者。msg.setDelayTimeMs(10000L);// 绝对时间:定时消息。设置一个具体的时间,然后在这个时间之后多久在进行发送消息// msg.setDeliverTimeMs(System.currentTimeMillis() + 10000L);producer.send(msg);System.out.printf(i + ".发送消息成功:%s%n", LocalTime.now());}producer.shutdown();}
}

指定时间消费者

public class TimeConsumer {public static void main(String[] args) throws MQClientException {DefaultMQPushConsumer pushConsumer = new DefaultMQPushConsumer("TimeConsumer");pushConsumer.setNamesrvAddr("10.226.8.14:9876");pushConsumer.subscribe("Schedule","*");pushConsumer.setMessageListener(new MessageListenerConcurrently() {@Overridepublic ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {list.forEach( n->{System.out.printf("接收时间:%s %n", LocalTime.now());});return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}});pushConsumer.start();System.out.printf("Simple Consumer Started.%n");}
}

RocketMQ 如何保证消息可靠性?

我们将消息流程分为三大部分,每一部分都有可能会丢失数据。

  • 生产阶段:Producer 通过网络将消息发送给 Broker,这个发送可能会发生丢失。比如网络延迟不可达等。
  • 存储阶段:Broker 肯定是先把消息放到内存的,然后根据刷盘策略持久化到硬盘中。刚收到 Producer 的消息,放入内存,但是异常宕机了,导致消息丢失。
  • 消费阶段:消费失败。比如先提交ack再消费,处理过程中出现异常,该消息就出现了丢失。

解决方案:

  • 生产阶段:使用同步发送失败重试机制;异步发送重写回调方法检查发送结果;ACK 确认机制。
  • 存储阶段:同步刷盘机制(默认情况下是异步刷盘);集群模式采用同步复制
  • 消费阶段:正常消费处理完成才提交ACK(手动ACK);如果处理异常返回重试标识。

RocketMQ 如何解决消息积压问题?

  1. 增加消费者数量:
    增加消费者实例的数量,以提高消息的消费速度。
    确保消费者实例数量与消息队列数量匹配,以便每个队列都有专门的消费者处理。

  2. 优化消费者逻辑:
    优化消费者的处理逻辑,提高单个消费者的处理效率。
    使用批量消费的方式来减少每次消费的开销。

  3. 扩展消息队列容量:
    增加消息队列的数量,以分散消息负载。
    动态调整队列数量,增加处理能力,实现更高的并行处理。

  4. 设置消息消费失败处理机制:
    实施重试机制,确保消费失败的消息能够被重新处理。
    设置死信队列(DLQ)来处理多次消费失败的消息。

  5. 快速失败丢弃消息:
    如果某些消息可以丢弃,考虑在高峰期快速丢弃这些消息以减轻负担。

  6. 提升系统性能:
    优化服务器性能,增加硬件资源,如CPU、内存和网络带宽。
    确保网络连接的稳定性和速度,以减少延迟。

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

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

相关文章

Git客户端使用之命令行

一、git客户端命令行的使用 1、创建本地用户并绑定ssh-key到gitlab #在本地注册用户,这个用户随便创建它就是与git仓库连接的一个用户&#xff0c;不过最好喝git仓库用户一样有利于区分。 git config --global user.name "wenqiang1" git config --global user.ema…

SpringBoot+Vue+Uniapp智能社区服务小程序系统(源码+lw+部署文档+讲解等)

项目运行截图 技术框架 后端采用SpringBoot框架 Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的开源框架。它采用约定大于配置的理念&#xff0c;提供了一套默认的配置&#xff0c;让开发者可以更专注于业务逻辑而不是配置文件。Spring Boot 通过自动化配置和约…

web 0基础第一节 文本标签

学习web语言 首先推荐安装一个vs code 的软件 这个普及度更广一点 兼容性好 网上有很多下载的教程 这里就直接从 html5 的内容开始说了. 这是一个html文件的基本结构 在vs code 中使用英文的 ! 可快捷设置这样的结构 <!-- --> 是在html写注释的结构 以后的…

【Java数据结构】优先级队列(堆)

【本节目标】 1. 掌握堆的概念及实现 2. 掌握 PriorityQueue 的使用 一. 优先级队列 1 概念 前面学过队列&#xff0c;队列是一种先进先出 (FIFO) 的数据结构 &#xff0c;但有些情况下&#xff0c; 操作的数据可能带有优先级&#xff0c;一般出队 列时&#xff0c;可…

【前端】如何制作一个自己的网页(8)

以下内容接上文。 CSS的出现&#xff0c;使得网页的样式与内容分离开来。 HTML负责网页中有哪些内容&#xff0c;CSS负责以哪种样式来展现这些内容。因此&#xff0c;CSS必须和HTML协同工作&#xff0c;那么如何在HTML中引用CSS呢&#xff1f; CSS的引用方式有三种&#xff1…

【LeetCode算法笔记】Day1:动态规划基础

目录 动态规划简介动态规划的定义动态规划的核心思想动态规划的简单例子 动态规划特征最优子结构性质重复子问题性质无后效应 动态规划的基本思路 动态规划简介 动态规划的定义 简称DP,是一种求解多阶段决策过程最优化问题的方法。在动态规划中&#xff0c;通过把原问题分解为…

Golang | Leetcode Golang题解之第478题在圆内随机生成点

题目&#xff1a; 题解&#xff1a; type Solution struct {radius, xCenter, yCenter float64 }func Constructor(radius, xCenter, yCenter float64) Solution {return Solution{radius, xCenter, yCenter} }func (s *Solution) RandPoint() []float64 {r : math.Sqrt(rand.…

MySQL面试专题-索引

一、MySQL为什么要选择B树来存储索引&#xff1f; MySQL的索引选择B树作为数据结构来进行存储&#xff0c;其本质原因在于可以减少IO次数&#xff0c;提高查询效率&#xff0c;简单来说就是保证在树的高度不变的情况下可以存储更多的数据。 &#xff08;一&#xff09;IO角度 在…

约克VRF打造舒适绿色无污染的生活环境

在生活的各个方面&#xff0c;约克VRF都采取了多种措施助力碳中和。 采用国际领先的空气源热泵技术&#xff0c;只需少量电力就可将空气中的能量转化为室内热量&#xff0c;被称为“大自然的搬运工”&#xff01;COP能效值最高可达4.24&#xff08;每用一度电产生4.24度电热量&…

第 3 章:使用 Vue 脚手架

1. 初始化脚手架 1.1 说明 Vue 脚手架是 Vue 官方提供的标准化开发工具&#xff08;开发平台&#xff09;。最新的版本是 5.x。文档: https://cli.vuejs.org/zh/ 1.2 具体步骤 第一步&#xff08;仅第一次执行&#xff09;&#xff1a;全局安装vue/cli。 npm install -g vu…

衡石分析平台系统分析人员手册-仪表盘控件概述

控件​ 控件是仪表盘的基本组成单位。控件种类很多&#xff0c;有展示分析数据的图表类类控件&#xff0c;有展示图片、文字的展示类控件&#xff0c;还有可导出数据、刷新数据、过滤数据等功能类控件。一个完整的仪表盘由多种不同功能的控件构成。 控件类型​ 根据控件是否展…

海外动态代理IP的优缺点有哪些? 动态代理IP与静态代理IP的区别是什么?

海外动态代理IP的优缺点分析 在全球化的数字时代&#xff0c;网络安全和隐私保护的重要性日益凸显。海外动态代理IP作为一种灵活的网络工具&#xff0c;因其独特的特性在多个领域得到了广泛应用。然而&#xff0c;正如任何技术一样&#xff0c;它也有其优点和局限性。以下&…

Shell案例之一键部署mysql

1.问题 我认为啊学习就是一个思考的过程&#xff0c;思考问题的一个流程应该是&#xff1a;提出问题&#xff0c;分析问题&#xff0c;解决问题 在shell里部署mysql服务时&#xff0c;我出现一些问题&#xff1a; 1.安装mysql-server时&#xff0c;没有密钥&#xff0c;安装…

PE结构之导入表

流程图: 文件中\样式 加载到进程中时 加载到进程中时的过程,一张图不够放 续图 整个流程 补充导入表结构IMAGE_IMPORT_DESCRIPTOR 中的ForwarderChain字段, 该解释为 "某个导入模块涉及转发&#xff08;即该模块的某些函数从其他模块转发过来&#xff09;&#xff0c;那么…

windows安装deepspeed setup.py 207行找不到文件

一直报莫名奇妙的错误&#xff0c;查了半天也没查到 去看了一下源码&#xff0c;需要安装git&#xff0c;我没有安装 git命令获得信息也没啥用 直接注释掉 成功运行

YOLO11改进|注意力机制篇|引入轴向注意力Axial Attention

目录 一、【Axial Attention】注意力机制1.1【Axial Attention】注意力介绍1.2【Axial Attention】核心代码二、添加【Axial Attention】注意力机制2.1STEP12.2STEP22.3STEP32.4STEP4三、yaml文件与运行3.1yaml文件3.2运行成功截图一、【Axial Attention】注意力机制 1.1【Axi…

【JPCS独立出版,EI检索稳定】第三届能源互联网及电力系统国际学术会议(ICEIPS 2024)

第三届能源互联网及电力系统国际学术会议&#xff08;ICEIPS 2024&#xff09; 2024 3rd International Conference on Energy Internet and Power Systems ICEIPS 2024已成功申请JPCS - Journal of Physics: Conference Series (ISSN:1742-6596) ICEIPS 2024独立出版&…

TCP——Socket

应用进程只借助Socket API发和收但是不关心他是怎么进行传和收的 数据结构 图示Socket连接 捆绑属于隐式捆绑

200Kg大载重多旋无人机价格高昂技术分析

200Kg大载重多旋无人机作为一种高度专业化的航空工具&#xff0c;其价格相较于普通无人机显著较高&#xff0c;这主要是由于其在技术设计和生产过程中所需的高要求所致。以下是对其价格高昂的技术分析&#xff1a; 一、高性能材料与结构设计 1. 高强度轻量化材料&#xff1a;…

Python,Swift,Haskell三种语言在使用正则表达式上的方法对比

这里插入图片描述](https://i-blog.csdnimg.cn/direct/fea1494d0d0c4c9880881493929a8b91.png)在讨论 Python、Swift 和 Haskell 在正则表达式处理字符串方面的优缺点时&#xff0c;可以从它们对正则表达式的支持、灵活性和性能进行比较。以下通过具体的正则表达式字符串匹配例…