RocketMQ 发送顺序消息

news2024/12/23 16:26:50

文章目录

  • 顺序消息应用场景
  • 消息组(MessageGroup)
  • 顺序性
    • 生产的顺序性
    • MQ 存储的顺序性
    • 消费的顺序性
  • rocketmq-client-java 示例(gRPC 协议)
    • 1. 创建 FIFO 主题
    • 生产者代码
    • 消费者代码
    • 解决办法
    • 解决后执行结果
  • rocketmq-client 示例(Remoting 协议)
    • 生产者
      • MessageQueueSelector 详解
    • 消费者

顺序消息应用场景

在有序事件处理、撮合交易、数据实时增量同步等场景下,异构系统间需要维持强一致的状态同步,上游的事件变更需要按照顺序传递到下游进行处理。在这类场景下使用 RocketMQ 的顺序消息可以有效保证数据传输的顺序性。比如:同一个用户的操作,一定是先生成订单、再进行支付、扣减库存、生成物流信息等。

消息组(MessageGroup)

RocketMQ 顺序消息的顺序关系通过消息组(MessageGroup)判定和识别,发送顺序消息时需要为每条消息设置归属的消息组,相同消息组的多条消息之间遵循先进先出的顺序关系,不同消息组、无消息组的消息之间不涉及顺序性。

基于消息组的顺序判定逻辑,支持按照业务逻辑做细粒度拆分,可以在满足业务局部顺序的前提下提高系统的并行度和吞吐能力。

顺序性

RocketMQ 的消息的顺序性分为两部分,生产顺序性和消费顺序性。

生产的顺序性

生产的顺序性就是必须保证每个消息在生成时是顺序的,且顺序的发送到 MQ 服务器。要保证生产的顺序,需要满足以下条件

  • 单一生产者:消息生产的顺序性仅支持单一生产者,不同生产者分布在不同的系统,即使设置相同的消息组,不同生产者之间产生的消息也无法判定其先后顺序。
  • 串行发送:Apache RocketMQ 生产者客户端支持多线程安全访问,但如果生产者使用多线程并行发送,则不同线程间产生的消息将无法判定其先后顺序。

MQ 存储的顺序性

MQ 按顺序收到消息后,会保证设置了同一消息组的消息,按照发送顺序存储在同一队列中。服务端顺序存储逻辑如下:

  • 相同消息组的消息按照先后顺序被存储在同一个队列。
  • 不同消息组的消息可以混合在同一个队列中,且不保证连续。
    在这里插入图片描述

消费的顺序性

消费的顺序性,是消费者在消费的时候要严格按照 MQ 中的存储顺序来执行。

  • 消费者保证执行的顺序
    • PushConsumer 类型消费者,RocketMQ 会保证消息按照存储顺序一条一条投递给消费者
    • SimpleConsumer 类型消费者,需要业务实现方自行保证消费的顺序。消费消息时需要严格按照接收—处理—应答的语义处理消息,避免因异步处理导致消息乱序。
  • 重试策略

Apache RocketMQ 顺序消息投递仅在重试次数限定范围内,即一条消息如果一直重试失败,超过最大重试次数后将不再重试,跳过这条消息消费,不会一直阻塞后续消息处理。

所以对于需要严格保证消费顺序的场景,请务设置合理的重试次数,避免参数不合理导致消息乱序。

rocketmq-client-java 示例(gRPC 协议)

1. 创建 FIFO 主题

本示例,我们模拟多个用户的一系列操作,并多个消息组区分不同的顺序消息。要求每个用户的消息按顺序执行,不同用户的消息之间不做必要关联。

$> ./mqadmin updatetopic -n localhost:9876 -c DefaultCluster -t MY_FIFO_TOPIC -o true -a +message.type=FIFO

注意:这里比普通消息和顺序消息多了一个 -o 参数,表示 order 的意思。

生产者代码

import com.yyoo.mq.rocket.MyMQProperties;
import org.apache.rocketmq.client.apis.ClientConfiguration;
import org.apache.rocketmq.client.apis.ClientException;
import org.apache.rocketmq.client.apis.ClientServiceProvider;
import org.apache.rocketmq.client.apis.message.Message;
import org.apache.rocketmq.client.apis.producer.Producer;
import org.apache.rocketmq.client.apis.producer.SendReceipt;

import java.io.IOException;

public class FifoProducerDemo {

    public static void main(String[] args) throws ClientException, IOException {

        // 用于提供:生产者、消费者、消息对应的构建类 Builder
        ClientServiceProvider provider = ClientServiceProvider.loadService();

        // 构建配置类(包含端点位置、认证以及连接超时等的配置)
        ClientConfiguration configuration = ClientConfiguration.newBuilder()
                // endpoints 即为 proxy 的地址,多个用分号隔开。如:xxx:8081;xxx:8081
                .setEndpoints(MyMQProperties.ENDPOINTS)
                .build();

        // 构建生产者
        Producer producer = provider.newProducerBuilder()
                // Topics 列表:生产者和主题是多对多的关系,同一个生产者可以向多个主题发送消息
                .setTopics("MY_FIFO_TOPIC")
                .setClientConfiguration(configuration)
                // 构建生产者,此方法会抛出 ClientException 异常
                .build();

        for(int i = 1; i <= 10;i++) {

            String msgGroup = "user" ; // 表示有两个用户
            String keys = "key_" + i;
            // 构建消息类
            Message message = provider.newMessageBuilder()
                    // 设置消息发送到的主题
                    .setTopic("MY_FIFO_TOPIC")
                    // 设置消息索引键,可根据关键字精确查找某条消息。其一般为业务上的唯一值。如:订单id
                    .setKeys(keys)
                    // 设置消息Tag,表示为创建订单
                    .setTag("ORDER_CREATE")
                    // 设置消息组
                    .setMessageGroup(msgGroup)
                    // 消息体,单条消息的传输负载不宜过大。所以此处的字节大小最好有个限制
                    .setBody(("{\"success\":true,\"msg\":\""+ msgGroup + ":" + keys +"\"}").getBytes())
                    .build();

            // 发送消息(此处最好进行异常处理,对消息的状态进行一个记录)
            try {
                SendReceipt sendReceipt = producer.send(message);
                System.out.println(keys);
                System.out.println("Send message successfully, messageId=" + sendReceipt.getMessageId());
            } catch (ClientException e) {
                System.out.println("Failed to send message");
            }
        }


        // 发送完,关闭生产者
        // producer.close();


    }

}

发送顺序消息时,消息一定要设置消息组,同一消息组的消息将会按服务器接收的顺序进行消费。

注:发送顺序消息前需要设置 NameServer 中的配置 orderMessageEnable 和 returnOrderTopicConfigToBroker 为 true。特别是 orderMessageEnable 默认为 false。建议在启动 namesrv 的时候使用自定义配置,在自定义配置中配置选项为true即可。

# namesrv.conf 为我们自定义的配置文件
nohup sh bin/mqnamesrv -c conf/namesrv.conf &

消费者代码

import com.yyoo.mq.rocket.MyMQProperties;
import org.apache.rocketmq.client.apis.ClientConfiguration;
import org.apache.rocketmq.client.apis.ClientException;
import org.apache.rocketmq.client.apis.ClientServiceProvider;
import org.apache.rocketmq.client.apis.consumer.ConsumeResult;
import org.apache.rocketmq.client.apis.consumer.FilterExpression;
import org.apache.rocketmq.client.apis.consumer.FilterExpressionType;
import org.apache.rocketmq.client.apis.consumer.PushConsumer;

import java.nio.ByteBuffer;
import java.util.Collections;

public class FifoConsumerDemo {

    public static void main(String[] args) throws ClientException {



        // 用于提供:生产者、消费者、消息对应的构建类 Builder
        ClientServiceProvider provider = ClientServiceProvider.loadService();

        // 构建配置类(包含端点位置、认证以及连接超时等的配置)
        ClientConfiguration configuration = ClientConfiguration.newBuilder()
                // endpoints 即为 proxy 的地址,多个用分号隔开。如:xxx:8081;xxx:8081
                .setEndpoints(MyMQProperties.ENDPOINTS)
                .build();


        // 设置过滤条件(这里为使用 tag 进行过滤)
        String tag = "ORDER_CREATE";
        FilterExpression filterExpression = new FilterExpression(tag, FilterExpressionType.TAG);

        // 构建消费者
        PushConsumer pushConsumer = provider.newPushConsumerBuilder()
                .setClientConfiguration(configuration)
                // 设置消费者分组
                .setConsumerGroup("MY_FIFO_GROUP")
                // 设置主题与消费者之间的订阅关系
                .setSubscriptionExpressions(Collections.singletonMap("MY_FIFO_TOPIC", filterExpression))
                .setMessageListener(messageView -> {
                    System.out.println(messageView);
                    ByteBuffer rs = messageView.getBody();
                    byte[] rsByte = new byte[rs.limit()];
                    rs.get(rsByte);

                    System.out.println("Message body:" + new String(rsByte));
                    // 处理消息并返回消费结果。
                    System.out.println("Consume message successfully, messageId=" + messageView.getMessageId());

                    return ConsumeResult.SUCCESS;
                }).build();

        System.out.println(pushConsumer);

        // 如果不需要再使用 PushConsumer,可关闭该实例。
        // pushConsumer.close();

    }

}

注:多验证几次后会发现,消费执行并没有严格的按照顺序执行,查找源码后发现,PushConsumer 的 builder 在构建 PushConsumer 的时候有个 Settings 对象,该对象的主题配置信息是从服务器获取,获取后有一个 isFifo 参数,此参数对应是否顺序消费,但是目前此值一直为false。此问题为消费者分组的问题,Remoting 协议方式无此问题,因为两种 Client 的实现是不一样的。

解决办法

在 MQ bin目录执行如下命令即可,具体的相关说明,我们将在后续章节中(《RocketMQ 消费者分类与分组》)详细说明。

$> ./mqadmin updateSubGroup -n 127.0.0.1:9876 -g MY_FIFO_GROUP -o true -c DefaultCluster

解决后执行结果

MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000001, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543268, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_2], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000000, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543178, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_1], messageGroup=user2, deliveryTimestamp=null, properties={__SHARDINGKEY=user2}}
Message body:{"success":true,"msg":"user1:key_2"}
Message body:{"success":true,"msg":"user2:key_1"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000000
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000001
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000002, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543279, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_3], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
Message body:{"success":true,"msg":"user1:key_3"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000002
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000004, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543294, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_5], messageGroup=user2, deliveryTimestamp=null, properties={__SHARDINGKEY=user2}}
Message body:{"success":true,"msg":"user2:key_5"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000004
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000003, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543288, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_4], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000005, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543301, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_6], messageGroup=user2, deliveryTimestamp=null, properties={__SHARDINGKEY=user2}}
Message body:{"success":true,"msg":"user1:key_4"}
Message body:{"success":true,"msg":"user2:key_6"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000005
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000003
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000006, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543313, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_7], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
Message body:{"success":true,"msg":"user1:key_7"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000006
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000007, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543320, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_8], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
Message body:{"success":true,"msg":"user1:key_8"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000007
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000008, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543331, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_9], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
Message body:{"success":true,"msg":"user1:key_9"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000008
MessageViewImpl{messageId=010456E5ECA6F32F6C051313D700000009, topic=MY_FIFO_TOPIC, bornHost=DESKTOP-S1DMOAD, bornTimestamp=1694595543340, endpoints=ipv4:192.168.1.1:8081, deliveryAttempt=1, tag=ORDER_CREATE, keys=[key_10], messageGroup=user1, deliveryTimestamp=null, properties={__SHARDINGKEY=user1}}
Message body:{"success":true,"msg":"user1:key_10"}
Consume message successfully, messageId=010456E5ECA6F32F6C051313D700000009

注意:user1 和 user2 的操作顺序是一致的。因为我们不需要保证 user1 的操作必须在 user2 之前,只需要保证他们各自的操作为顺序的就可以。

rocketmq-client 示例(Remoting 协议)

生产者

import com.yyoo.mq.rocket.MyMQProperties;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.MessageQueueSelector;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.common.message.MessageQueue;
import org.apache.rocketmq.shaded.commons.lang3.RandomUtils;

import java.util.List;

public class FifoProducerDemo {

    /**
     * 生产者分组
     */
    private static final String PRODUCER_GROUP = "FIFO_PRODUCER_GROUP";

    /**
     * 主题
     */
    private static final String TOPIC = "MY_FIFO_TOPIC";

    public static void main(String[] args) throws MQClientException {


        /*
         * 创建生产者,并使用生产者分组初始化
         */
        DefaultMQProducer producer = new DefaultMQProducer(PRODUCER_GROUP);

        /*
         * NamesrvAddr 的地址,多个用分号隔开。如:xxx:9876;xxx:9876
         */
        producer.setNamesrvAddr(MyMQProperties.NAMESRV_ADDR);

        /*
         * 发送消息超时时间,默认即为 3000
         */
        producer.setSendMsgTimeout(3000);

        /*
         * 启动生产者,此方法抛出 MQClientException
         */
        producer.start();


        /*
         * 发送消息
         */
        for (int i = 1; i <= 10; i++) {

            try {
                Message msg = new Message();
                msg.setTopic(TOPIC);
                // 设置消息索引键,可根据关键字精确查找某条消息。
                msg.setKeys("messageKey");
                // 设置消息Tag,用于消费端根据指定Tag过滤消息。
                msg.setTags("ORDER_CREATE");
                // 设置消息体
                msg.setBody(("顺序消息" + i).getBytes());

                // 这里 userId 取值为 1,2,3(模拟有3个用户的顺序操作)
                int userId = RandomUtils.nextInt(1,4);

                SendResult sendResult = producer.send(msg, new MessageQueueSelector() {
                    @Override
                    public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
                        // 这个arg就是对应userId
                        Integer userId = (Integer)arg;
                        // 我们按队列的数量,对每个user进行分组
                        int index = userId % mqs.size();
                        // 同一个user的消息放入同一个队列
                        return mqs.get(index);
                    }
                },userId);

                System.out.printf("%s%n", sendResult);

            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("消息发送失败!i = " + i);
            }

        }


        // 如果生产者不再使用,则调用关闭
        // 异步发送消息注意:异步发送消息,建议此处不关闭或者在sleep一段时间后再关闭
        // 因为异步 SendCallback 执行的时候,shutdow可能已经执行了,生产者被关闭了
        // producer.shutdown();
    }

}

MessageQueueSelector 详解

public interface MessageQueueSelector {
    MessageQueue select(final List<MessageQueue> mqs, final Message msg, final Object arg);
}

mqs:队列列表,我们前面说了,默认 8 个队列
msg:当前消息
arg:为我们 send 方法传的第三个参数,示例中就是 userId

MessageQueueSelector 意为队列选择器,Remoting 协议客户端中没有 消息组的概念,所以需要我们手动的为消息进行分组(将需要严格顺序的消息放在同一个队列),这个接口就是完成此任务的,而且分组的逻辑需要我们自己实现。实际应用中我们可以使用 用户id、订单id等来为顺序消息分组。

消费者

import com.yyoo.mq.rocket.MyMQProperties;
import org.apache.rocketmq.client.apis.ClientException;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeOrderlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerOrderly;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.apache.rocketmq.common.message.MessageExt;

import java.util.List;


public class FifoConsumerDemo {

    public static void main(String[] args) throws MQClientException {

        // 初始化 consumer
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("REMOTING_FIFO_CONSUMER_GROUP");

        // 设置 namesrv 地址
        consumer.setNamesrvAddr(MyMQProperties.NAMESRV_ADDR);
        // 设置从开头开始读取消息
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);

        // 设置订阅的主题,以及过滤tag
        consumer.subscribe("MY_FIFO_TOPIC", "ORDER_CREATE || TagA || TagD || messageTag");

        consumer.registerMessageListener(new MessageListenerOrderly() {

            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
                System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);

                for(MessageExt msg : msgs){
                    System.out.println(new String(msg.getBody()));
                }

                return ConsumeOrderlyStatus.SUCCESS;
            }
        });

        consumer.start();
        System.out.printf("Consumer Started.%n");

    }

}

注意:顺序消息消费者的监听类型为 MessageListenerOrderly ,注意与我们前面的示例 MessageListenerConcurrently 进行区分。

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

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

相关文章

Nginx配置负载均衡时访问地址无法生效

场景还原 今天有小伙伴练习Nginx配置负载均衡时总是无法使用配置好的网址访问 配置文件信详情 http {# 负载均衡 后端IP地址和端口 webservers 策略 轮询upstream webservers{server 192.168.1.100:8080 weight90; server 127.0.0.1:8080 weight10; }server{listen 80;ser…

基于Java+SpringBoot+Vue的旧物置换网站设计和实现

基于JavaSpringBootVue的旧物置换网站设计和实现 源码传送入口前言主要技术系统设计功能截图数据库设计代码论文目录订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码传送入口 前言 摘 要 随着时代在一步一步在进步&#xff0c;旧物也成人们的烦恼&#xff0c;…

深度学习中安装了包但是依然导入(import)失败这一问题,例如pytorch环境下已经安装了scikit-learn但是import不了

在跑深度学习模型的时候我们要先搭建pytorch环境&#xff0c;这个环境跟windows环境是不同的&#xff0c;我们默认在windows中安装的包在当前的虚拟环境中读取不到&#xff0c;所以导致我们明明安装了包但是依然在实际的导入中(import)报错。解决办法就是我们去虚拟环境中安装包…

使用Scrapy构建高效的网络爬虫

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 Scrapy是一个强大的Pyth…

电脑怎么取消磁盘分区?

有时候&#xff0c;我们的电脑会出现一个磁盘爆满&#xff0c;但另一个却空着&#xff0c;这时我们可以通过取消磁盘分区来进行调整&#xff0c;那么&#xff0c;这该怎么操作呢&#xff1f;下面我们就来了解一下。 磁盘管理取消磁盘分区 磁盘管理是Windows自带的磁盘管理工具…

展会动态 | 迪捷软件邀您参加2023世界智能网联汽车大会

*9月18日之前注册的观众免收门票费* 由北京市人民政府、工业和信息化部、公安部、交通运输部和中国科学技术协会联合主办的2023世界智能网联汽车大会将于9月21日-24日在北京中国国际展览中心&#xff08;顺义馆&#xff09;举行。 论坛背景 本届展会以“聚智成势 协同向新——…

论文阅读 - Natural Language is All a Graph Needs

目录 摘要 Introduction Related Work 3 InstructGLM 3.1 Preliminary 3.2 Instruction Prompt Design 3.3 节点分类的生成指令调整 3.4 辅助自监督链路预测 4 Experiments 4.1 Experimental Setup 4.2 Main Results 4.2.1 ogbn-arxiv 4.2.2 Cora & PubMed 4.…

类似东郊到家上门按摩小程序/包括商家入驻、服务查询、订单管理、用户评价等

类似东郊到家上门按摩小程序&#xff0c;简单前端模版。覆盖产品&#xff0c;订单&#xff0c;技师&#xff0c;招聘&#xff0c;充值&#xff0c;优惠等功能。 东郊到家小程序同城预约上门小程序的功能非常齐全&#xff0c;包括商家入驻、服务查询、订单管理、用户评价等&…

全国职业技能大赛云计算--高职组赛题卷④(私有云)

全国职业技能大赛云计算--高职组赛题卷④&#xff08;私有云&#xff09; 第一场次题目&#xff1a;OpenStack平台部署与运维任务1 基础运维任务&#xff08;5分&#xff09;任务3 OpenStack云平台运维&#xff08;15分&#xff09;任务4 OpenStack云平台运维开发&#xff08;1…

数据结构——八叉树

八叉树&#xff08;Octree&#xff09;是一种用于表示和管理三维空间的树状数据结构。它将三维空间递归地分割成八个八分体&#xff08;octant&#xff09;&#xff0c;每个八分体可以继续分割&#xff0c;以实现对三维空间的更精细的划分。八叉树通常用于解决空间搜索和查询问…

高级深入--day27

Scrapy 框架 Scrapy是用纯Python实现一个为了爬取网站数据、提取结构性数据而编写的应用框架,用途非常广泛。 框架的力量,用户只需要定制开发几个模块就可以轻松的实现一个爬虫,用来抓取网页内容以及各种图片,非常之方便。 Scrapy 使用了 Twisted[twɪstɪd](其主要对手是T…

如何下载安装 WampServer 并结合 cpolar 内网穿透,轻松实现对本地服务的公网访问

文章目录 前言1.WampServer下载安装2.WampServer启动3.安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4.固定公网地址访问 前言 Wamp 是一个 Windows系统下的 Apache PHP Mysql 集成安装环境&#xff0c;是一组常用来…

jvm中对象创建、内存布局以及访问定位

对象创建 Java语言层面&#xff0c;创建对象通常&#xff08;例外&#xff1a;复制、反序列化&#xff09;仅仅是一个new关键字即可&#xff0c;而在虚拟机中&#xff0c;对象&#xff08;限于普通Java对象&#xff0c;不包括数组和Class对象等&#xff09;的创建又是怎样一个过…

基于SSM的电动车租赁网站设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

竞赛 基于深度学习的人脸专注度检测计算系统 - opencv python cnn

文章目录 1 前言2 相关技术2.1CNN简介2.2 人脸识别算法2.3专注检测原理2.4 OpenCV 3 功能介绍3.1人脸录入功能3.2 人脸识别3.3 人脸专注度检测3.4 识别记录 4 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的人脸专注度…

Kotlin Coroutines包下的select函数简介

在工作中&#xff0c;发现了kotlin Coroutines包下有大量功能非常强大的API&#xff0c;这篇文章中&#xff0c;我们主要来聊一聊select函数 1. 什么是select函数 想象一下这个场景&#xff0c;在程序应用中&#xff0c;为了实现一个业务逻辑&#xff0c;你可能有好几种方式来…

石河子大学《乡村振兴战略下传统村落文化旅游设计》许少辉博士新著——2023学生开学季辉少许

石河子大学《乡村振兴战略下传统村落文化旅游设计》许少辉博士新著——2023学生开学季辉少许

LLM预训练之RLHF(一):RLHF及其变种

在ChatGPT引领的大型语言模型时代&#xff0c;国内外的大模型呈现爆发式发展&#xff0c;尤其是以年初的LLaMA模型为首的开源大模型和最近百川智能的baichuan模型&#xff0c;但无一例外&#xff0c;都使用了「基于人类反馈的强化学习」&#xff08;RLHF&#xff09;来提升语言…

football 篮球数据集-目标检测548张

“篮球&#xff08;basketball&#xff09;&#xff0c;是以手为中心的身体对抗性体育运动&#xff0c;是奥运会核心比赛项目。1891年12月21日&#xff0c;由美国马萨诸塞州斯普林菲尔德基督教青年会训练学校体育教师詹姆士奈史密斯发明。1896年&#xff0c;篮球运动传入中国天…

扩散原理详解与实战

学习一下扩散模型的数学原理。 前向扩散 q ( x t ∣ x 0 ) N ( x t ; α ˉ t x 0 , ( 1 − α ˉ t ) I ) q\left(\mathbf{x}_t \mid \mathbf{x}_0\right)\mathcal{N}\left(\mathbf{x}_t ; \sqrt{\bar{\alpha}_t} \mathbf{x}_0,\left(1-\bar{\alpha}_t\right) \mathbf{I}\rig…