RoctetMQ使用(2):在项目中使用

一、导入相关依赖

        在项目中引入MQ客户端依赖,依赖版本最好和RocketMQ版本一致。

<!--            rocket客户端--><dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-client</artifactId><version>${rocketmq.version}</version></dependency>

二、消息发送

        首先介绍一下消息发送的大致流程,当我们调用消息发送方法时该方法会先对待发送消息进行前置验证,如果消息主题和消息内容均没有问题的话,就会根据消息主题(Topic)去获取路由信息,即消息主题对应的队列,broker,broker的ip和端口信息,然后选择一条队列发送消息,成功的话返回发送成功,失败的话会根据我们设置的重试次数进行重新发送,单向消息发送不会进行失败重试。

1、RocketMQ初始化

        RocketMQ配置类案例:

package com.example.framework.mq.config;import cn.hutool.core.thread.ThreadUtil;
import com.example.framework.mq.handler.MQHandler;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.TransactionMQProducer;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.Map;
import java.util.concurrent.ExecutorService;/*** MQ配置*/
@Configuration
public class MQConfig {@Value("${spring.application.name:application}")private String groupName;//集群名称,这边以应用名称作为集群名称/***************************消息消费者***************************/@Autowiredprivate Map<String, MQHandler> mqHandlerMap;// 消费者nameservice地址@Value("${rocketmq.consumer.namesrvAddr:127.0.0.1:9876}")private String cNamesrvAddr;// 最小线程数@Value("${rocketmq.consumer.consumeThreadMin:20}")private int consumeThreadMin;// 最大线程数@Value("${rocketmq.consumer.consumeThreadMax:64}")private int consumeThreadMax;// 消费者监听主题,多个主题以分号隔开(topic~tag;topic~tag)@Value("${rocketmq.consumer.topics:test~*}")private String topics;// 一次消费消息的条数,默认为1条@Value("${rocketmq.consumer.consumeMessageBatchMaxSize:1}")private int consumeMessageBatchMaxSize;@Beanpublic DefaultMQPushConsumer getRocketMQConsumer() throws Exception {DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(groupName);consumer.setNamesrvAddr(cNamesrvAddr);consumer.setConsumeThreadMin(consumeThreadMin);consumer.setConsumeThreadMax(consumeThreadMax);consumer.registerMessageListener(getMessageListenerConcurrently());// 设置Consumer第一次启动是从队列头部开始消费还是队列尾部开始消费,如果非第一次启动,那么按照上次消费的位置继续消费consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);// 设置消费模型,集群还是广播,默认为集群//consumer.setMessageModel(MessageModel.CLUSTERING);// 设置一次消费消息的条数,默认为1条consumer.setConsumeMessageBatchMaxSize(consumeMessageBatchMaxSize);try {// 设置该消费者订阅的主题和tag,如果是订阅该主题下的所有tag,则tag使用*;如果需要指定订阅该主题下的某些tag,则使用||分割,例如tag1||tag2||tag3String[] topicTagsArr = topics.split(";");for (String topicTags : topicTagsArr) {String[] topicTag = topicTags.split("~");consumer.subscribe(topicTag[0],topicTag[1]);}consumer.start();}catch (Exception e){throw new Exception(e);}return consumer;}// 并发消息侦听器(如果对顺序消费有需求则使用MessageListenerOrderly 有序消息侦听器)@Beanpublic MessageListenerConcurrently getMessageListenerConcurrently() {return new MQListenerConcurrently(mqHandlerMap);}/***************************消息生产者***************************/// 事务消息监听器@Autowiredprivate MQTransactionListener mqTransactionListener;// 生产者nameservice地址@Value("${rocketmq.producer.namesrvAddr:127.0.0.1:9876}")private String pNamesrvAddr;// 消息最大大小,默认4M@Value("${rocketmq.producer.maxMessageSize:4096}")private Integer maxMessageSize ;// 消息发送超时时间,默认3秒@Value("${rocketmq.producer.sendMsgTimeout:30000}")private Integer sendMsgTimeout;// 消息发送失败重试次数,默认2次@Value("${rocketmq.producer.retryTimesWhenSendFailed:2}")private Integer retryTimesWhenSendFailed;// 执行任务的线程池private static ExecutorService executor = ThreadUtil.newExecutor(32);//普通消息生产者@Bean("default")public DefaultMQProducer getDefaultMQProducer() {DefaultMQProducer producer = new DefaultMQProducer(this.groupName);producer.setNamesrvAddr(this.pNamesrvAddr);producer.setMaxMessageSize(this.maxMessageSize);producer.setSendMsgTimeout(this.sendMsgTimeout);producer.setRetryTimesWhenSendFailed(this.retryTimesWhenSendFailed);try {producer.start();} catch (MQClientException e) {System.out.println(e.getErrorMessage());}return producer;}//事务消息生产者(rocketmq支持柔性事务)@Bean("transaction")public TransactionMQProducer getTransactionMQProducer() {//初始化事务消息基本与普通消息生产者一致TransactionMQProducer producer = new TransactionMQProducer("transaction_" + this.groupName);producer.setNamesrvAddr(this.pNamesrvAddr);producer.setMaxMessageSize(this.maxMessageSize);producer.setSendMsgTimeout(this.sendMsgTimeout);producer.setRetryTimesWhenSendFailed(this.retryTimesWhenSendFailed);//添加事务消息处理线程池producer.setExecutorService(executor);//添加事务消息监听producer.setTransactionListener(mqTransactionListener);try {producer.start();} catch (MQClientException e) {System.out.println(e.getErrorMessage());}return producer;}
}

2、发送消息

        消息发送根据消息功能主要分为普通消息、事务消息、顺序消息、延时消息等!特别说明一下事务消息多用于保证多服务模块间的事务一致性,事务消息发送后并不会直接通知消费者消费消息,而是会先生成一个半消息,会先进入事务消息监听器中,确保该消息事务提交成功后才会向broker发送消息,从而被消费者获取并进行消费。

        根据发送方式可以分为同步消息,异步消息和单向消息等:

  • 同步消息常用于比较重要的消息发送,需要等待broker响应告知消息发送状态。
  • 异步消息的话常用于对响应时间敏感,需要快速返回的模块,我们会设置一个回调代码块去异步监听Borker的响应。
  • 单向消息的话主要用于对发送结果不敏感,不会影响业务的模块,无需监听broker响应,常用于日志发送等模块。

        下面代码演示四种消息的使用方式:

package com.example.order.service.impl;import com.alibaba.fastjson.JSON;
import com.example.framework.utils.SonwflakeUtils;
import com.example.order.entity.Order;
import com.example.order.mapper.OrderMapper;
import com.example.order.service.OrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.*;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;/*** 服务实现类*/
@Service
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {@Qualifier("default")@Autowiredprivate DefaultMQProducer producer;@Autowiredprivate TransactionMQProducer transactionMQProducer;/*** 添加订单(发送消息积分模块同步添加积分)* @param order 订单信息* @return org.apache.rocketmq.client.producer.TransactionSendResult**/@Overridepublic Order addOder(Order order) {order.setOrderId(SonwflakeUtils.get().id());if (order.getMessageType() == 1) {//普通消息this.save(order);Message message = new Message("points", "default", JSON.toJSONString(order).getBytes());try {//同步消息SendResult sendResult = producer.send(message);System.out.println("发送状态:" + sendResult.getSendStatus() +",消息ID:" + sendResult.getMsgId() +",队列:" + sendResult.getMessageQueue().getQueueId());
//                producer.sendOneway(message);//单向消息
//----------------------------异步消息-----------------------------------
//                producer.send(message, new SendCallback() {
//                    @Override
//                    public void onSuccess(SendResult sendResult) {
//
//                    }
//
//                    @Override
//                    public void onException(Throwable throwable) {
//
//                    }
//                });} catch (RemotingException | MQBrokerException | InterruptedException | MQClientException e) {e.printStackTrace();}} else {//事务消息Message message = new Message("points", "transaction", JSON.toJSONString(order).getBytes());try {transactionMQProducer.sendMessageInTransaction(message, null);} catch (MQClientException e) {e.printStackTrace();}}return order;}
}

3、消息消费

        边对MessageListenerConcurrently有进行一定封装,主要是为了在消息处理时通过注解定位消息Topic和tag而自动选择对应的消息处理类进行业务处理;封装代码如下:

package com.example.framework.mq.config;import cn.hutool.core.util.StrUtil;
import com.example.framework.mq.annotation.MQHandlerActualizer;
import com.example.framework.mq.handler.MQHandler;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;import java.util.Arrays;
import java.util.List;
import java.util.Map;/*** 并发消息监听器*/
public class MQListenerConcurrently implements MessageListenerConcurrently {@Autowiredprivate Map<String, MQHandler> mqHandlerMap;public MQListenerConcurrently(Map<String, MQHandler> mqHandlerMap) {this.mqHandlerMap = mqHandlerMap;}@Overridepublic ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {if(CollectionUtils.isEmpty(list)){System.out.println("接受到的消息为空,不处理,直接返回成功");return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}MessageExt messageExt = list.get(0);// 判断该消息是否重复消费(RocketMQ不保证消息不重复,如果你的业务需要保证严格的不重复消息,需要你自己在业务端去重)// 获取该消息重试次数int reconsume = messageExt.getReconsumeTimes();if(reconsume ==3){//消息已经重试了3次,需做告警处理,已经相关日志return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}// 处理对应的业务逻辑String topic = messageExt.getTopic();String tags  = messageExt.getTags();System.out.println("接受到的消息主题为:" + topic + "; tag为:" + tags);MQHandler mqMsgHandler = null;//获取消息处理类中的topic和tag注解,根据topic和tag进行策略分发出来具体业务for (Map.Entry<String, MQHandler> entry : mqHandlerMap.entrySet()) {MQHandlerActualizer msgHandlerActualizer = entry.getValue().getClass().getAnnotation(MQHandlerActualizer.class);if (msgHandlerActualizer == null) {//非消息处理类continue;}String annotationTopic = msgHandlerActualizer.topic();if (!StrUtil.equals(topic,annotationTopic)) {//非该主题处理类continue;}String[] annotationTags = msgHandlerActualizer.tags();if(StrUtil.equals(annotationTags[0],"*")){//获取该实例mqMsgHandler = entry.getValue();break;}boolean isContains = Arrays.asList(annotationTags).contains(tags);if(isContains){//注解类中包含tag则获取该实例mqMsgHandler = entry.getValue();break;}}if (mqMsgHandler == null) {return ConsumeConcurrentlyStatus.RECONSUME_LATER;}ConsumeConcurrentlyStatus status = mqMsgHandler.handle(tags,messageExt);// 如果没有return success,consumer会重新消费该消息,直到return successreturn status;}
}

        事务消息监听器封装:

package com.example.framework.mq.config;import cn.hutool.core.util.StrUtil;
import com.example.framework.mq.annotation.MQHandlerActualizer;
import com.example.framework.mq.handler.MQTransactionHandler;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.TransactionListener;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.factory.annotation.Autowired;import java.util.Arrays;
import java.util.Map;public class MQTransactionListener implements TransactionListener {@Autowiredprivate Map<String, MQTransactionHandler> mqTransactionHandlerMap;@Overridepublic LocalTransactionState executeLocalTransaction(Message message, Object o) {MQTransactionHandler mqTransactionHandler = getListenner(message.getTopic(),message.getTags());return mqTransactionHandler.executeLocalTransaction(message,o);}@Overridepublic LocalTransactionState checkLocalTransaction(MessageExt messageExt) {MQTransactionHandler mqTransactionHandler = getListenner(messageExt.getTopic(),messageExt.getTags());return mqTransactionHandler.checkLocalTransaction(messageExt);}private MQTransactionHandler getListenner(String topic,String tags) {MQTransactionHandler mqTransactionHandler = null;for (Map.Entry<String, MQTransactionHandler> entry : mqTransactionHandlerMap.entrySet()) {MQHandlerActualizer msgHandlerActualizer = entry.getValue().getClass().getAnnotation(MQHandlerActualizer.class);if (msgHandlerActualizer != null) {String annotationTopic  = msgHandlerActualizer.topic();String[] annotationTags = msgHandlerActualizer.tags();if (!StrUtil.equals(topic,annotationTopic)) {//非该主题处理类continue;}if(StrUtil.equals(annotationTags[0],"*")){//获取该实例mqTransactionHandler = entry.getValue();break;}boolean isContains = Arrays.asList(annotationTags).contains(tags);if(isContains){//注解类中包含tag则获取该实例mqTransactionHandler = entry.getValue();break;}}}return mqTransactionHandler;}
}

        使用注解@MQHandlerActualizer标明该消息处理类的主题,默认监听所有tag,如果需要对tag监听进行分类,后面加上tag即可。消息监听器在收到消息后会自动调用主题对应的处理类进行业务处理,示例如下:

package com.example.points.mqHandler;import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.framework.mq.annotation.MQHandlerActualizer;
import com.example.framework.mq.handler.MQHandler;
import com.example.framework.utils.SonwflakeUtils;
import com.example.points.entity.Points;
import com.example.points.service.PointsService;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.factory.annotation.Autowired;import java.util.Map;/***/
@MQHandlerActualizer(topic = "points")
public class PointsMQHandler implements MQHandler {@Autowiredprivate PointsService pointsService;@Overridepublic ConsumeConcurrentlyStatus handle(String tag, MessageExt messageExt) {//消息监听String messageStr = new String(messageExt.getBody());Map orderMap = (Map) JSON.parse(messageStr);Points points = new Points();Long orderId = (Long) orderMap.get("orderId");System.out.println("消息tag为:" + tag);System.out.println("消息监听:"  + "为订单" + orderId + "添加积分");//查询该订单是否已经生成对应积分(rocketMQ可能会重复发送消息,需实现幂等)QueryWrapper<Points> pointsQueryWrapper = new QueryWrapper<>();pointsQueryWrapper.lambda().eq(Points::getOrderId,orderId);Points tempPoints = pointsService.getOne(pointsQueryWrapper);if (tempPoints != null) {//该订单已经生成积分System.out.println(orderId + "已经生成积分");return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}points.setPointsId(SonwflakeUtils.get().id());points.setOrderId(orderId);Integer orderAmout = (Integer) orderMap.get("orderAmout");points.setPoints(orderAmout * 10);pointsService.save(points);return  ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}
}

使用消息队列注意事项

  1. RocketMQ确保所有消息至少传递一次。虽然大多数情况下,消息不会重复,但还是需要对重复消息做。
  2. 尽量减小消息的体积,例如选择轻量的协议,超过一定体积做压缩处理,就消息协议而言, 二进制协议 < 文本协议。而文本协议中 json < xml 等等。

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

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

相关文章

npm常用指令

基础 命令&#xff1a;run 解释&#xff1a;运行脚本 示例&#xff1a;npm run dev 命令&#xff1a;list || ls 解释&#xff1a;查看依赖列表 示例&#xff1a;npm list || npm ls 命令&#xff1a;install || i 解释&#xff1a;安装依赖 示例&#xff1a;npm install ||…

一文掌握python随机数random模块

目录 一、常用函数 1、 random.random() 2、random.uniform(a, b) 3、random.randint(a, b) 4、random.randrange(start, stop[, step]) 5、random.choice(sequence) 6、random.shuffle(x[, random]) 7、random.sample(population, k) 8、random.choices(population, w…

Qml:锚点

import QtQuick import QtQuick.WindowWindow {width: 800height: 600visible: truetitle: qsTr("Test Anchors")///锚点 上下左右Rectangle{id: anchor1width:200height: 150color:"#EEEEEE"Rectangle{id:rect1width:50height:50color: "red"Te…

Ubuntu 20/22 安装 Jenkins

1. 使用 apt 命令安装 Java Jenkins 作为一个 Java 应用程序&#xff0c;要求 Java 8 及更高版本&#xff0c;检查系统上是否安装了 Java。 sudo apt install -y openjdk-17-jre-headless安装完成后&#xff0c;再次验证 Java 是否已安装 java --version2. 通过官方存储库安…

动态地控制kafka的消费速度,从而满足业务要求

kafka是一个分布式流媒体平台&#xff0c;它可以处理大规模的数据流&#xff0c;并允许实时消费该数据流。在实际应用中&#xff0c;我们需要动态控制kafka消费速度&#xff0c;以便处理数据流的速率能够满足系统和业务的需求。本文将介绍如何在kafka中实现动态控制消费速度的方…

APH-Archives of Public Health

文章目录 一、期刊简介二、征稿信息三、期刊表现四、投稿须知五、投稿咨询 一、期刊简介 Archives of Public Health是一份范围广泛的公共卫生杂志&#xff0c;致力于出版公共卫生领域所有可靠的科学。该杂志旨在更好地了解人群的健康。该杂志有助于公共卫生知识&#xff0c;加…

【考研数学】李林《880》是什么难度水平强化够用吗

880是公认的质量高&#xff0c;但要是刷的方法不对&#xff0c;心态直接炸裂&#xff01;&#x1f649; 我24年二战就是用的 880660 的黄金搭档&#xff0c;143分逆袭上岸211&#xff01;&#xff08;为什么说逆袭呢&#xff0c;因为我23年一战数学83&#xff0c;妥妥的菜鸡&am…

2024.5.20 学习记录

1、react 原理&#xff08;jsx的本质、事件机制原理、setState和batch Update、组件渲染更新和diff算法、fiber&#xff09; 2、代码随想录贪心刷题

ArcGIS10.X入门实战视频教程(arcgis入门到精通)

点击学习&#xff1a; ArcGIS10.X入门实战视频教程&#xff08;GIS思维&#xff09;https://edu.csdn.net/course/detail/4046?utm_sourceblog2edu 点击学习&#xff1a; ArcGIS10.X入门实战视频教程&#xff08;GIS思维&#xff09;https://edu.csdn.net/course/detail/404…

银河麒麟操作系统下使用QT连接TiDB数据库开发步骤

目标:实现项目软件+硬件都运行在国产化操作系统平台上。 方法:在虚拟机中安装麒麟系统V10Sp1+Qt5.14.2+MySql8.0+TiDB软件,编译MySql驱动,测试连接TiDB数据库项目。 步骤: 1、使用虚拟机软件VMWare安装银河麒麟操作系统。 2、在银河麒麟系统上安装QT5.14.2软件。 3、…

Web Server项目实战3-Web服务器简介及HTTP协议

Web Server&#xff08;网页服务器&#xff09; 一个 Web Server 就是一个服务器软件&#xff08;程序&#xff09;&#xff0c;或者是运行这个服务器软件的硬件&#xff08;计算机&#xff09;。其主要功能是通过 HTTP 协议与客户端&#xff08;通常是浏览器&#xff08;Brow…

【精品】使用 v-md-editor 上传图片

简介 v-md-editor 是基于 Vue 开发的 markdown 编辑器组件&#xff0c;即支持vue2也支持vue3。 gitee&#xff1a;https://gitee.com/ckang1229/vue-markdown-editor文档&#xff1a;https://code-farmer-i.github.io/vue-markdown-editor/zh/ 服务器端代码 RestController…

[Cocos Creator 3.5赛车游戏]第5节 为汽车节点挂载自定义脚本

在前面的章节中您已经学会了如何创建一个汽车节点&#xff0c;这一章我们将会学习如何通过挂载自定义节点的方式让小车变得可控制&#xff0c;所以通过这一章的学习后&#xff0c;您将实现一个效果&#xff1a;开始运行后&#xff0c;小车每隔一帧就延y轴向上移动一段距离。在这…

cx_Oracle Python 库连接 Oracle 数据库时遇到报错

这个错误 DPI-1047: Cannot locate a 64-bit Oracle Client library: "The specified module could not be found" 是在尝试使用 cx_Oracle Python 库连接 Oracle 数据库时遇到的。这个错误表明 cx_Oracle 无法找到 Oracle 客户端库&#xff08;通常称为 Instant Cli…

顶顶通呼叫中心中间件-自动外呼输入分机号(比如隐私号)(mod_cti基于FreeSWITCH)

顶顶通呼叫中心中间件-自动外呼输入分机号(比如隐私号)(mod_cti基于FreeSWITCH) 比如有些人的号码是这样的就需要用上自动外呼输入分机号了 号码1&#xff1a;182XXXX8111-1234 号码2&#xff1a;182XXXX8222 如果号码是这样的就根据以下步骤配置 注意使用这个需要&#xff1a;…

Redis学习篇2:Redis在IEDA中的应用

本文继上文开始讲述了Redis在IDEA中如何应用以及集成进入spring开发环境&#xff0c;以及如何使用Redis客户端。上一个文章&#xff1a;Redis学习篇1&#xff1a;初识Redishttps://blog.csdn.net/jialuosi/article/details/139057088 一、Redis在java中的客户端 二、SpringDat…

MySQL存储过程_触发器_游标——Baidu Comate

# 问题1&#xff1a; 帮我创建2个表student与score表&#xff0c;要求student表有id,createDate,userName,phone,age,sex,introduce, 要求score表有id,scoreName,result,studentId(student表的id外键)。 要求student表中插入5条学生信息&#xff0c;都要是中文的。 要求score表…

onload和onunload有什么区别(代码举例说明)

onload 和 onunload 是两种常用于网页中的事件处理器&#xff08;event handlers&#xff09;&#xff0c;但它们处理的是完全不同的页面生命周期事件。 onload onload 事件会在页面或指定的元素&#xff08;如图片、框架等&#xff09;完成加载后触发。对于页面整体来说&…

树莓派 Raspberry Pi M.2 HAT+ 现已发售!原理图流出!

​Raspberry Pi M.2 HAT 使您能够将 M.2 M-key 外设&#xff08;如 NVMe 驱动器和人工智能加速器&#xff09;连接到 Raspberry Pi 5。它能够提供与这些外设之间的快数据传输&#xff08;高达 500 MB/s&#xff09;&#xff0c;现在就可以从树莓派的授权经销商网络购买&#xf…

c语言:strcmp

strcmp函数是用于比较两个字符串的库函数&#xff0c;其功能是根据ASCII值逐一对两个字符串进行比较。 语法&#xff1a;strcmp(str1, str2) 返回值&#xff1a; 如果str1等于str2&#xff0c;则返回0。 如果str1小于str2&#xff0c;则返回负数&#xff08;具体值取决于C…