系列七、RocketMQ如何保证顺序消费消息

一、概述

        所谓顺序消费指的是可以按照消息的发送顺序来进行消费。例如一笔订单产生了3条消息,即下订单==》减库存==》增加订单,消费时要按照顺序消费才有意义,要不然就乱套了(PS:你总不能订单还没下,就开始减库存吧),与此同时多笔订单之间又是可以并行消费的。

二、RocketMQ实现顺序消费的方式

(1)保证同一个订单的消息,一定要发送到同一个队列;

(2)并且该队列只有一个消费者,也就是说同一个队列不能出现多个消费者并行消费的情况。到这里可能会有人产生疑问,一个队列只有一个消费者,那性能岂不是很低?关于这种情况,RocketMQ的解决方法是,虽然同一个队列不能并行消费,但是可以并行消费不同的队列。通俗点讲队列和消费者是一对一的关系,即一个队列只属于某一个消费者,消费者和队列是1对多的关系,即一个消费者可以对应多个队列。进而提升RocketMQ的效率。

三、队列消费的两种模式

3.1、并发消费模式

        当同一类消息被送入不同队列,且这些消息在处理上并不需要按照时序消费时,可以考虑使用并发消费模式。并发消费模式生产者会将消息轮询发送到不同的队列中,这些队列会和消费者建立多个连接(线程)将消息并发的送给不同的消费者。因为消费者处理速度有快有慢,所以并不能保证物流数据会按照1~9的顺序依次消费。并发消费模式处理效率很高,但是无法保证有序性。

 

3.2、有序消费模式

        有序消费是指生产者在生产数据的时候,根据hash规则指定让消息放入哪个队列,在消费者消费时会保证不同消费者针对每一个队列只有唯一一个连接(线程)用于消费指定队列。有序消费模式可以保证消息按队列FIFO顺序依次被消费,但因此失去并发性能,有序消费模式只有在业务要求必须按照顺序消费的场景下才允许使用。

3.2.1、RocketMQ如何实现有序消息

要想实现RocketMQ实现有序消息,只需要做如下两点调整:

(1)生产者端要求按照ID等唯一标识分配消息队列;

(2)消费者端采用专用的监听器保证对队列的单线程应用;

3.2.2、发送顺序消息

/*** 发送顺序消息* @throws Exception*/
@Test
public void sendOrderlyMessage() {// 1、创建生产者,指定组名DefaultMQProducer producer = new DefaultMQProducer("GROUP_ORDERLY");// 2、指定Namesrv地址producer.setNamesrvAddr(RocketMQConstant.NAME_SERVER_ADDR);try {// 3、启动生产者producer.start();// 模拟10笔订单for(Integer orderId = 1; orderId <= 10; orderId++) {// 每笔订单要发送3条消息,即:创建订单==》扣减库存==》增加积分for (int i = 0; i < 3; i++) {String data = "";switch (i % 3) {case 0:data = orderId + "号创建订单";break;case 1:data = orderId + "号扣减库存";break;case 2:data = orderId + "号增加积分";break;}// 创建消息对象 topic:TOPIC_ORDER、tags:TAG_ORDER、key:orderIdMessage message = new Message("TOPIC_ORDERLY","TAG_ORDERLY",orderId.toString(),data.getBytes(RemotingHelper.DEFAULT_CHARSET));// 发送消息,实现[MessageQueueSelector]接口SendResult sendResult = producer.send(message, new MessageQueueSelector() {@Overridepublic MessageQueue select(List<MessageQueue> messageQueues, Message message, Object o) {int orderId = Integer.parseInt(message.getKeys());int size = messageQueues.size();    // 队列的数量int index = orderId % size;         // 队列的索引MessageQueue messageQueue = messageQueues.get(index);   // 选择的队列log.info("订单id:{},队列数量:{},队列索引:{},发送内容:{},队列id:{}",orderId,size,index,new String(message.getBody()),messageQueue.getQueueId());return messageQueue;}}, null);}}} catch (Exception e) {log.error("error:{}",e.getMessage());}
}
// 控制台打印结果
"C:\Program Files\Java\jdk1.8.0_202\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:D:\Programs\ideaIU-2018.3.6\lib\idea_rt.jar=51556:D:\Programs\ideaIU-2018.3.6\bin -Dfile.encoding=UTF-8 -classpath "D:\Programs\ideaIU-2018.3.6\lib\idea_rt.jar;D:\Programs\ideaIU-2018.3.6\plugins\junit\lib\junit-rt.jar;D:\Programs\ideaIU-2018.3.6\plugins\junit\lib\junit5-rt.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_202\jre\lib\rt.jar;D:\augenstemn@gitee\rocketmq\rocketmq-example\target\test-classes;D:\augenstemn@gitee\rocketmq\rocketmq-example\target\classes;D:\mavenRepository\com\alibaba\fastjson\1.2.83\fastjson-1.2.83.jar;D:\mavenRepository\org\apache\rocketmq\rocketmq-client\4.9.2\rocketmq-client-4.9.2.jar;D:\mavenRepository\org\apache\rocketmq\rocketmq-common\4.9.2\rocketmq-common-4.9.2.jar;D:\mavenRepository\org\apache\rocketmq\rocketmq-remoting\4.9.2\rocketmq-remoting-4.9.2.jar;D:\mavenRepository\io\netty\netty-all\4.1.65.Final\netty-all-4.1.65.Final.jar;D:\mavenRepository\org\apache\rocketmq\rocketmq-logging\4.9.2\rocketmq-logging-4.9.2.jar;D:\mavenRepository\commons-validator\commons-validator\1.7\commons-validator-1.7.jar;D:\mavenRepository\commons-beanutils\commons-beanutils\1.9.4\commons-beanutils-1.9.4.jar;D:\mavenRepository\commons-digester\commons-digester\2.1\commons-digester-2.1.jar;D:\mavenRepository\commons-logging\commons-logging\1.2\commons-logging-1.2.jar;D:\mavenRepository\commons-collections\commons-collections\3.2.2\commons-collections-3.2.2.jar;D:\mavenRepository\org\projectlombok\lombok\1.18.22\lombok-1.18.22.jar;D:\mavenRepository\org\slf4j\slf4j-api\1.7.32\slf4j-api-1.7.32.jar;D:\mavenRepository\ch\qos\logback\logback-classic\1.2.10\logback-classic-1.2.10.jar;D:\mavenRepository\ch\qos\logback\logback-core\1.2.10\logback-core-1.2.10.jar;D:\mavenRepository\junit\junit\4.13.2\junit-4.13.2.jar;D:\mavenRepository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;D:\mavenRepository\org\apache\commons\commons-lang3\3.11\commons-lang3-3.11.jar;D:\mavenRepository\org\apache\commons\commons-collections4\4.4\commons-collections4-4.4.jar" com.intellij.rt.execution.junit.JUnitStarter -ideVersion5 -junit4 org.star.OrderMessageProducer,sendOrderlyMessage
RocketMQLog:WARN No appenders could be found for logger (io.netty.util.internal.InternalThreadLocalMap).
RocketMQLog:WARN Please initialize the logger system properly.
11:04:01.604 [main] INFO org.star.OrderMessageProducer - 订单id:1,队列数量:4,队列索引:1,发送内容:1号创建订单,队列id:1
11:04:01.803 [main] INFO org.star.OrderMessageProducer - 订单id:1,队列数量:4,队列索引:1,发送内容:1号扣减库存,队列id:1
11:04:01.804 [main] INFO org.star.OrderMessageProducer - 订单id:1,队列数量:4,队列索引:1,发送内容:1号增加积分,队列id:1
11:04:01.807 [main] INFO org.star.OrderMessageProducer - 订单id:2,队列数量:4,队列索引:2,发送内容:2号创建订单,队列id:2
11:04:01.808 [main] INFO org.star.OrderMessageProducer - 订单id:2,队列数量:4,队列索引:2,发送内容:2号扣减库存,队列id:2
11:04:01.810 [main] INFO org.star.OrderMessageProducer - 订单id:2,队列数量:4,队列索引:2,发送内容:2号增加积分,队列id:2
11:04:01.811 [main] INFO org.star.OrderMessageProducer - 订单id:3,队列数量:4,队列索引:3,发送内容:3号创建订单,队列id:3
11:04:01.813 [main] INFO org.star.OrderMessageProducer - 订单id:3,队列数量:4,队列索引:3,发送内容:3号扣减库存,队列id:3
11:04:01.816 [main] INFO org.star.OrderMessageProducer - 订单id:3,队列数量:4,队列索引:3,发送内容:3号增加积分,队列id:3
11:04:01.818 [main] INFO org.star.OrderMessageProducer - 订单id:4,队列数量:4,队列索引:0,发送内容:4号创建订单,队列id:0
11:04:01.820 [main] INFO org.star.OrderMessageProducer - 订单id:4,队列数量:4,队列索引:0,发送内容:4号扣减库存,队列id:0
11:04:01.822 [main] INFO org.star.OrderMessageProducer - 订单id:4,队列数量:4,队列索引:0,发送内容:4号增加积分,队列id:0
11:04:01.826 [main] INFO org.star.OrderMessageProducer - 订单id:5,队列数量:4,队列索引:1,发送内容:5号创建订单,队列id:1
11:04:01.831 [main] INFO org.star.OrderMessageProducer - 订单id:5,队列数量:4,队列索引:1,发送内容:5号扣减库存,队列id:1
11:04:01.834 [main] INFO org.star.OrderMessageProducer - 订单id:5,队列数量:4,队列索引:1,发送内容:5号增加积分,队列id:1
11:04:01.836 [main] INFO org.star.OrderMessageProducer - 订单id:6,队列数量:4,队列索引:2,发送内容:6号创建订单,队列id:2
11:04:01.838 [main] INFO org.star.OrderMessageProducer - 订单id:6,队列数量:4,队列索引:2,发送内容:6号扣减库存,队列id:2
11:04:01.840 [main] INFO org.star.OrderMessageProducer - 订单id:6,队列数量:4,队列索引:2,发送内容:6号增加积分,队列id:2
11:04:01.841 [main] INFO org.star.OrderMessageProducer - 订单id:7,队列数量:4,队列索引:3,发送内容:7号创建订单,队列id:3
11:04:01.844 [main] INFO org.star.OrderMessageProducer - 订单id:7,队列数量:4,队列索引:3,发送内容:7号扣减库存,队列id:3
11:04:01.846 [main] INFO org.star.OrderMessageProducer - 订单id:7,队列数量:4,队列索引:3,发送内容:7号增加积分,队列id:3
11:04:01.850 [main] INFO org.star.OrderMessageProducer - 订单id:8,队列数量:4,队列索引:0,发送内容:8号创建订单,队列id:0
11:04:01.852 [main] INFO org.star.OrderMessageProducer - 订单id:8,队列数量:4,队列索引:0,发送内容:8号扣减库存,队列id:0
11:04:01.853 [main] INFO org.star.OrderMessageProducer - 订单id:8,队列数量:4,队列索引:0,发送内容:8号增加积分,队列id:0
11:04:01.856 [main] INFO org.star.OrderMessageProducer - 订单id:9,队列数量:4,队列索引:1,发送内容:9号创建订单,队列id:1
11:04:01.858 [main] INFO org.star.OrderMessageProducer - 订单id:9,队列数量:4,队列索引:1,发送内容:9号扣减库存,队列id:1
11:04:01.860 [main] INFO org.star.OrderMessageProducer - 订单id:9,队列数量:4,队列索引:1,发送内容:9号增加积分,队列id:1
11:04:01.860 [main] INFO org.star.OrderMessageProducer - 订单id:10,队列数量:4,队列索引:2,发送内容:10号创建订单,队列id:2
11:04:01.864 [main] INFO org.star.OrderMessageProducer - 订单id:10,队列数量:4,队列索引:2,发送内容:10号扣减库存,队列id:2
11:04:01.866 [main] INFO org.star.OrderMessageProducer - 订单id:10,队列数量:4,队列索引:2,发送内容:10号增加积分,队列id:2Process finished with exit code 0

3.2.2、接收顺序消息(先启动监听)

/*** 接收顺序消息:消费者1* @throws Exception*/
@Test
public void receiveOrderlyMessageConsumer1() throws Exception {// 1、创建消费者DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("GROUP_ORDERLY");// 2、连接Namesrvconsumer.setNamesrvAddr(RocketMQConstant.NAME_SERVER_ADDR);/*** 3、订阅主题*      参数1:主题名称*      参数2:主题中消息类型,*表示订阅所有*/consumer.subscribe("TOPIC_ORDERLY","*");// 4、注册监听器,要想实现顺序消费,消费者端要增加[MessageListenerOrderly]监听器,用于实现有序队列consumer.registerMessageListener(new MessageListenerOrderly() {@Overridepublic ConsumeOrderlyStatus consumeMessage(List<MessageExt> messageExts, ConsumeOrderlyContext context) {// 遍历输出messageExts.forEach(e -> {log.info("我是消费者1,我正在消费消息,消息内容:{},队列id:{}",new String(e.getBody()),context.getMessageQueue().getQueueId());});return ConsumeOrderlyStatus.SUCCESS;}});// 5、启动消费者consumer.start();// 6、挂起当前JVMSystem.in.read();
}/*** 接收顺序消息:消费者2* @throws Exception*/
@Test
public void receiveOrderlyMessageConsumer2() throws Exception {// 1、创建消费者DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("GROUP_ORDERLY");// 2、连接Namesrvconsumer.setNamesrvAddr(RocketMQConstant.NAME_SERVER_ADDR);/*** 3、订阅主题*      参数1:主题名称*      参数2:主题中消息类型,*表示订阅所有*/consumer.subscribe("TOPIC_ORDERLY","*");// 4、注册监听器,要想实现顺序消费,消费者端要增加[MessageListenerOrderly]监听器,用于实现有序队列consumer.registerMessageListener(new MessageListenerOrderly() {@Overridepublic ConsumeOrderlyStatus consumeMessage(List<MessageExt> messageExts, ConsumeOrderlyContext context) {// 遍历输出messageExts.forEach(e -> {log.info("我是消费者2,我正在消费消息,消息内容:{},消息队列:{}",new String(e.getBody()),context.getMessageQueue().getQueueId());});return ConsumeOrderlyStatus.SUCCESS;}});// 5、启动消费者consumer.start();// 6、挂起当前JVMSystem.in.read();
}

消费者1控制台打印结果


11:04:01.802 [ConsumeMessageThread_5] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:1号创建订单,队列id:1
11:04:01.804 [ConsumeMessageThread_6] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:1号扣减库存,队列id:1
11:04:01.808 [ConsumeMessageThread_7] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:1号增加积分,队列id:1
11:04:01.829 [ConsumeMessageThread_8] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:4号创建订单,队列id:0
11:04:01.831 [ConsumeMessageThread_9] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:5号创建订单,队列id:1
11:04:01.831 [ConsumeMessageThread_10] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:4号扣减库存,队列id:0
11:04:01.833 [ConsumeMessageThread_10] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:4号增加积分,队列id:0
11:04:01.836 [ConsumeMessageThread_11] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:5号扣减库存,队列id:1
11:04:01.838 [ConsumeMessageThread_12] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:5号增加积分,队列id:1
11:04:01.853 [ConsumeMessageThread_13] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:8号创建订单,队列id:0
11:04:01.856 [ConsumeMessageThread_14] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:8号扣减库存,队列id:0
11:04:01.858 [ConsumeMessageThread_15] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:8号增加积分,队列id:0
11:04:01.858 [ConsumeMessageThread_16] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:9号创建订单,队列id:1
11:04:01.860 [ConsumeMessageThread_17] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:9号扣减库存,队列id:1
11:04:01.865 [ConsumeMessageThread_18] INFO org.star.OrderMessageConsumer - 我是消费者1,我正在消费消息,消息内容:9号增加积分,队列id:1

消费者2控制台打印结果

11:04:01.815 [ConsumeMessageThread_1] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:2号创建订单,消息队列:2
11:04:01.817 [ConsumeMessageThread_2] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:3号创建订单,消息队列:3
11:04:01.820 [ConsumeMessageThread_2] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:3号扣减库存,消息队列:3
11:04:01.821 [ConsumeMessageThread_2] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:3号增加积分,消息队列:3
11:04:01.820 [ConsumeMessageThread_1] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:2号扣减库存,消息队列:2
11:04:01.821 [ConsumeMessageThread_1] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:2号增加积分,消息队列:2
11:04:01.840 [ConsumeMessageThread_3] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:6号创建订单,消息队列:2
11:04:01.843 [ConsumeMessageThread_4] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:6号扣减库存,消息队列:2
11:04:01.844 [ConsumeMessageThread_5] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:6号增加积分,消息队列:2
11:04:01.845 [ConsumeMessageThread_6] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:7号创建订单,消息队列:3
11:04:01.848 [ConsumeMessageThread_7] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:7号扣减库存,消息队列:3
11:04:01.859 [ConsumeMessageThread_8] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:7号增加积分,消息队列:3
11:04:01.866 [ConsumeMessageThread_9] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:10号创建订单,消息队列:2
11:04:01.868 [ConsumeMessageThread_10] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:10号扣减库存,消息队列:2
11:04:01.868 [ConsumeMessageThread_10] INFO org.star.OrderMessageConsumer - 我是消费者2,我正在消费消息,消息内容:10号增加积分,消息队列:2

3.2.3、结果分析

        通过对上述发送消息和接收消息控制台日志的分析,发现订单id相同的消息,都发送到了同一个队列中,也即保证了同一个订单的消息,发送到了同一个队列中。与此同时观察消费者的消费日志,发现队列id相同的消息队列中被分配给同一个消费者了,也即保证了一个队列唯一分配给了一个消费者,保证了顺序消费。另外观察日志发现,对于同一个消费者,同一个组的消息能够保证顺序,而不同组的消息无法保证顺序,也即局部有序。

3.2.4、如何实现全局顺序消费

只需要在生产者端固定将所有消息发送到0号队列即可保证全局有序,这也意味着全局采用单线程消费,这种方式执行效率极差。

@Override
public MessageQueue select(List<MessageQueue> messageQueues, Message message, Object o) {return messageQueues.get(0);
}

3.2.5、顺序消费的使用限制

        有序消费模式只支持集群模式(messageModel = MessageModel.CLUSTERING),不支持广播模式(messageModel = MessageModel.BROADCASTING),采用广播模式将无法接收到数据。

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

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

相关文章

vscode vue3+vite 配置eslint

vue2webpackeslint配置 目前主流项目都在使用vue3vite&#xff0c;因此针对eslint的配置做了一下总结。 引入ESlint、pritter 安装插件&#xff0c;执行以下命令 // eslint // prettier // eslint-plugin-vue // eslint-config-prettier // eslint-plugin-prettier yarn ad…

苹果电脑 Java切换版本

效果 1、安装 Java1.8和Java11 直接官网下载并安装 2、安装后的文件 /资源库/Java/JavaVirtualMachines/ 3、修改配置文件 vi ~/.bash_profile#java export JAVA_8_HOME"/Library/Java/JavaVirtualMachines/jdk1.8.0_202.jdk/Contents/Home" alias jdk8expor…

RFID工业识别技术:供应链智能化的科技颠覆

RFID工业识别技术&#xff0c;作为物联网的先锋&#xff0c;正在供应链管理领域展现着前所未有的科技颠覆。从物料追踪到库存管理&#xff0c;再到物流配送&#xff0c;RFID技术以其高效的数据采集和智能的自动化处理&#xff0c;彻底改变着传统供应链的运营方式。 RFID在物料追…

代驾小程序怎么做

代驾小程序是一款专门为用户提供代驾服务的手机应用程序。它具有以下功能&#xff1a; 1. 预约代驾&#xff1a;代驾小程序允许用户在需要代驾服务时提前进行预约。用户可以选择出发地点、目的地以及预计用车时间&#xff0c;系统会自动匹配最合适的代驾司机&#xff0c;并确保…

手把手教你,Selenium 遇见伪元素该如何处理?

问题发生 在很多前端页面中&#xff0c;大家会见到很多&#xff1a;:before、::after 元素&#xff0c;比如【百度流量研究院】&#xff1a; 比如【百度疫情大数据平台】&#xff1a; 如果你想学习自动化测试&#xff0c;我这边给你推荐一套视频&#xff0c;这个视频可以说是B…

vim学习笔记(致敬vim作者)

vim cheat sheet 30. vim 删除大法 vim 删除某个字符之后改行的其他的字符&#xff1f;删除某行之后的其他行&#xff1f;删除某个字符之后的其他字符&#xff1f;【1】删除单个字符&#xff1f; 跳到要删除的字符位置 按下d键然后按下shift 4键 【2】删除某行之后的其他行…

元宇宙时代超高清视音频技术白皮书关于流媒体协议和媒体传输解读

流媒体协议 元宇宙业务场景对流媒体传输的实时性和互动性提出了更高的要求&#xff0c;这就需要在传统的 RTMP、SRT、 HLS 等基础上增加实时互动的支持。实时互动&#xff0c;指在远程条件下沟通、协作&#xff0c;可随时随地接入、实时地传递虚实融合的多维信息&#xff0c;身…

python递归实现逆序输出数字

一、问题描述 编程实现将输入的整数逆序输出 二、问题分析 逆序输出数字实际是一个数值问题的递归 三、算法设计 该问题要求输入任意一个整数&#xff0c;实现它的逆序输出。首先判断输入的整数是正整数还是负整数&#xff0c;如果是负整数&#xff0c; 则在逆序输出前应先…

使用埋点方式对应用监控

在指标监控的世界里&#xff0c;应用和业务层面的监控有两种典型手段&#xff0c;一种是在应用程序里埋点&#xff0c;另一种是分析日志&#xff0c;从日志中提取指标。埋点的方式性能更好&#xff0c;也更灵活&#xff0c;只是对应用程序有一定侵入性&#xff0c;而分析日志的…

【网络通信】socket编程——TCP套接字

TCP依旧使用代码来熟悉对应的套接字&#xff0c;很多接口都是在udp中使用过的 所以就不会单独把他们拿出来作为标题了&#xff0c;只会把第一次出现的接口作为标题 文章目录 服务端 tcp_servertcpserver.hpp(封装)初始化 initServer1. 创建socket2. 绑定 bindhtons —— 主机序…

C语言创建目录(文件夹)之mkdir

一、mkdir 说明&#xff1a;创建目录。 头文件库&#xff1a; #include <sys/stat.h> #include <sys/types.h>函数原型&#xff1a; int mkdir(const char *pathname, mode_t mode);mode方式&#xff1a;可多个权限相或&#xff0c;如0755表示S_IRWXU | S_IRGRP…

程序使用Microsoft.XMLHTTP对象请求https时出错解决

程序中使用Microsoft.XMLHTTP组件请求https时出现如下错误&#xff1a; 出错程序代码示例&#xff1a; strUrl "https://www.xxx.com/xxx.asp?id11" dim objXmlHttp set objXmlHttp Server.CreateObject("Microsoft.XMLHTTP") objXmlHttp.open "…

[保研/考研机试] KY180 堆栈的使用 吉林大学复试上机题 C++实现

题目链接&#xff1a; 堆栈的使用_牛客题霸_牛客网 描述 堆栈是一种基本的数据结构。堆栈具有两种基本操作方式&#xff0c;push 和 pop。其中 push一个值会将其压入栈顶&#xff0c;而 pop 则会将栈顶的值弹出。现在我们就来验证一下堆栈的使用。 输入描述&#xff1a; 对于…

mysql进阶篇(二)

前言 「作者主页」&#xff1a;雪碧有白泡泡 「个人网站」&#xff1a;雪碧的个人网站 「推荐专栏」&#xff1a; ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄&#xff0c;vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

四、Linux常用命令(一)

1、ls命令 (1)ls: list&#xff0c;列表。 (2)作用&#xff1a;使用列表把当前文件夹下所有文件显示出来。 (3)ls -a: 显示所有文件&#xff0c;包括隐藏文件。 (3)ls -l: 显示文件的详细信息。 (3)显示所有文件且显示详细信息&#xff0c;以下四种方式都可以。 ls -a -l…

初学vue3时应该注意的几个问题

初学vue3时应该注意的几个问题 声明响应式 响应式数据的声明在vue2的时候很简单&#xff0c;在data中声明就行了。但现在可以使用多个方式。 reactive用于声明Object, Array, Map, Set; ref用于声明String, Number, Boolean 使用reactive来声明基础数据类型&#xff08;Str…

QT网络编程之TCP

QT网络编程之TCP TCP 编程需要用到俩个类: QTcpServer 和 QTcpSocket。 #------------------------------------------------- # # Project created by QtCreator 2023-08-

leetcode169. 多数元素

题目 给定一个大小为 n 的数组 nums &#xff0c;返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。 你可以假设数组是非空的&#xff0c;并且给定的数组总是存在多数元素。 示例 示例 1&#xff1a; 输入&#xff1a;nums [3,2,3] 输出&#x…

小米基于 Flink 的实时计算资源治理实践

摘要&#xff1a;本文整理自小米高级软件工程师张蛟&#xff0c;在 Flink Forward Asia 2022 生产实践专场的分享。本篇内容主要分为四个部分&#xff1a; 发展现状与规模框架层治理实践平台层治理实践未来规划与展望 点击查看原文视频 & 演讲PPT 一、发展现状与规模 如上图…

jquery发送ajax练习

jquery发送ajax练习 工具代码运行结果 工具 HBuilder X 代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>通过ajax进行图片的提取和显示</title><style>div{background-color: beige;color: red;font-s…