四、RocketMQ应用

一、普通消息

1、同步发送消息

在这里插入图片描述

public class SyncProducter {

    public static void main(String[] args) throws Exception {
        // 创建一个producer,参数为Producer Group名称
        DefaultMQProducer producer = new DefaultMQProducer("pg");
        // 指定nameServer地址
        producer.setNamesrvAddr("127.0.0.1:9876");
        // 设置当发送失败时重试发送的次数,默认为2次
        producer.setRetryTimesWhenSendFailed(3);
        // 设置发送超时时限为5s,默认3s
        producer.setSendMsgTimeout(5000);
        // 开启生产者
        producer.start();
        // 生产并发送100条消息
        for (int i = 0; i < 100; i++) {
            byte[] body = ("Hi," + i).getBytes();
            Message msg = new Message("someTopic", "someTag", body);
            // 为消息指定key
            msg.setKeys("key-" + i);
            // 发送消息
            SendResult sendResult = producer.send(msg);
            System.out.println(sendResult);
        }
        // 关闭producer
        producer.shutdown();
    }
}



// 消息发送的状态
public enum SendStatus {
    SEND_OK, // 发送成功
    FLUSH_DISK_TIMEOUT, // 刷盘超时。当Broker设置的刷盘策略为同步刷盘时才可能出现这种异常状态。异步刷盘不会出现
    FLUSH_SLAVE_TIMEOUT, // Slave同步超时。当Broker集群设置的Master-Slave的复制方式为同步复制时才可能出现这种异常状态。异步复制不会出现
    SLAVE_NOT_AVAILABLE, // 没有可用的Slave。当Broker集群设置为Master-Slave的复制方式为同步复制时才可能出现这种异常状态。异步复制不会出现
}

2、异步发送消息

在这里插入图片描述

3、单向发送消息

在这里插入图片描述

4、消息消费者

二、顺序消息

1、什么是顺序消息?

顺序消息指的是,严格按照消息的发送顺序进行消费的消息(FIFO)。

2、为什么需要顺序消息

3、有序性的分类

根据有序范围的不同,RocketMQ可以严格地保证两种消息的有序性:分区有序与全局有序。

1)全局有序

当发送和消费参与的Queue只有一个时,所保证的有序是整个Topic中消息的顺序, 称为全局有序。
(在创建 topic 时,)
在这里插入图片描述

2)分区有序

如果有多个Queue参与,其仅可保证在该Queue分区队列上的消息顺序,则称为分区有序。
在这里插入图片描述

如何实现Queue的选择?
在定义Producer时我们可以指定消息队列选择器,而这个选择器是我们自己实现了MessageQueueSelector接口定义的

在定义选择器的选择算法时,一般需要使用选择key。这个选择key可以是消息key也可以是其它数据。但无论谁做选择key,都不能重复,都是唯一的。

4、代码demo

public class OrderedProducer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("pg");
        producer.setNamesrvAddr("127.0.0.1:9876");
        producer.start();
        for (int i = 0; i < 100; i++) {
            Integer orderId = i;
            byte[] body = ("Hi," + i).getBytes();
            Message msg = new Message("TopicA", "TagA", body);
            // send()方法的第三个参数会传递给 MessageQueueSelector中select方法的第三个参数
            SendResult sendResult = producer.send(msg, new
                    MessageQueueSelector() {
                        @Override
                        public MessageQueue select(List<MessageQueue> mqs,
                                                   Message msg, Object arg) {
                            Integer id = (Integer) arg;
                            // 前面50条消息发送给第一个队列,后面50条消息发送给第二个队列
                            if (id <= 50) {
                                return mqs.get(0);
                            } else {
                                return mqs.get(1);
                            }
                        }
                    }, orderId);
            System.out.println(sendResult);
        }
        producer.shutdown();
    }
}



三、延时消息

1、什么是延时消息

2、延时等级

延时消息的延迟时长不支持随意时长的延迟,是通过特定的延迟等级来指定的。

延时等级定义在RocketMQ服务端的MessageStoreConfig类中的如下变量中:

3、延时消息实现原理

4、代码demo

// 生产者
public class DelayProducer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("pg");
        producer.setNamesrvAddr("127.0.0.1:9876");
        producer.start();
        for (int i = 0; i < 10; i++) {
            byte[] body = ("Hi," + i).getBytes();
            Message msg = new Message("TopicB", "someTag", body);
            // 指定消息延迟等级为3级,即延迟10s
             msg.setDelayTimeLevel(3);
            SendResult sendResult = producer.send(msg);
            // 输出消息被发送的时间
            System.out.print(new SimpleDateFormat("hh:mm:ss").format(new Date()));
            System.out.println(" ," + sendResult);
        }
        producer.shutdown();
    }
}

// 消费者
public class SomeConsumer1 {
    public static void main(String[] args) throws MQClientException {
        // 定义一个pull消费者
//         DefaultLitePullConsumer consumer = new DefaultLitePullConsumer("cg");
        // 定义一个push消费者
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("cg");
        // 指定nameServer
        consumer.setNamesrvAddr("127.0.0.1:9876");
        // 指定从第一条消息开始消费
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        // 指定消费topic与tag
        consumer.subscribe("TopicB", "*");
        // 指定采用“广播模式”进行消费,默认为“集群模式”
        // consumer.setMessageModel(MessageModel.BROADCASTING);
        // 注册消息监听器
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            // 一旦broker中有了其订阅的消息就会触发该方法的执行,
            // 其返回值为当前consumer消费的状态
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                // 逐条消费消息
                for (MessageExt msg : msgs) {
                    System.out.println(new SimpleDateFormat("hh:mm:ss").format(new Date()) + "--->" + msg.getMsgId());
                }
                // 返回消费状态:消费成功
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        // 开启消费者消费
        consumer.start();
        System.out.println("Consumer Started");
    }
}

在这里插入图片描述

四、事务消息

五、批量发送

1、批量发送消息

发送限制

批量发送大小
默认情况下,一批发送的消息总大小不能超过4MB字节。如果想超出该值,有两种解决方案:

  • 将批量消息进行拆分,拆分为若干不大于4M的消息集合分多次批量发送
  • 在Producer端与Broker端修改属性
    Producer端需要在发送之前设置Producer的maxMessageSize属性
    Broker端需要修改其加载的配置文件中的maxMessageSize属性

生产者发送的消息大小
在这里插入图片描述

2 批量消费消息

如何设置批量发送消息 : **
在这里插入图片描述
Consumer的MessageListenerConcurrently监听接口的consumeMessage()方法的第一个参数为消息列表,但默认情况下每次只能消费一条消息。若要使其一次可以消费多条消息,则可以通过修改Consumer的
consumeMessageBatchMaxSize**属性来指定。不过,该值不能超过32。因为默认情况下消费者每次可以拉取的消息最多是32条。若要修改一次拉取的最大值,则可通过修改Consumer的pullBatchSize属性来指定。

**存在的问题 : **
Consumer的pullBatchSize属性与consumeMessageBatchMaxSize属性是否设置的越大越好?当然不是

3、代码举例

定义 批量消息生产者

public class BatchProducer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("pg");
        producer.setNamesrvAddr("rocketmqOS:9876");
        // 指定要发送的消息的最大大小,默认是4M
        // 不过,仅修改该属性是不行的,还需要同时修改broker加载的配置文件中的
        // maxMessageSize属性
        // producer.setMaxMessageSize(8 * 1024 * 1024);
        producer.start();
        // 定义要发送的消息集合
        List<Message> messages = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            byte[] body = ("Hi," + i).getBytes();
            Message msg = new Message("someTopic", "someTag", body);
            messages.add(msg);
        }
        // 定义消息列表分割器,将消息列表分割为多个不超出4M大小的小列表
        MessageListSplitter splitter = new MessageListSplitter(messages);

        while (splitter.hasNext()) {
            try {
                List<Message> listItem = splitter.next();
                producer.send(listItem);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        producer.shutdown();
    }
}


// 消息列表分割器:其只会处理每条消息的大小不超4M的情况。
// 若存在某条消息,其本身大小大于4M,这个分割器无法处理,
// 其直接将这条消息构成一个子列表返回。并没有再进行分割
public class MessageListSplitter implements Iterator<List<Message>> {
    // 指定极限值为4M
    private final int SIZE_LIMIT = 4 * 1024 * 1024;
    // 存放所有要发送的消息
    private final List<Message> messages;
    // 要进行批量发送消息的小集合起始索引
    private int currIndex;

    public MessageListSplitter(List<Message> messages) {
        this.messages = messages;
    }

    @Override
    public boolean hasNext() {
// 判断当前开始遍历的消息索引要小于消息总数
        return currIndex < messages.size();
    }

    @Override
    public List<Message> next() {
        int nextIndex = currIndex;
        // 记录当前要发送的这一小批次消息列表的大小
        int totalSize = 0;
        for (; nextIndex < messages.size(); nextIndex++) {
            // 获取当前遍历的消息
            Message message = messages.get(nextIndex);
            // 统计当前遍历的message的大小
            int tmpSize = message.getTopic().length() + message.getBody().length;
            Map<String, String> properties = message.getProperties();
            for (Map.Entry<String, String> entry : properties.entrySet()) {
                tmpSize += entry.getKey().length() + entry.getValue().length();

            }
            tmpSize = tmpSize + 20;
            // 判断当前消息本身是否大于4M
            if (tmpSize > SIZE_LIMIT) {
                if (nextIndex - currIndex == 0) {
                    nextIndex++;
                }
                break;
            }
            if (tmpSize + totalSize > SIZE_LIMIT) {
                break;
            } else {
                totalSize += tmpSize;
            }
        }
        // 获取当前messages列表的子集合[currIndex, nextIndex)
        List<Message> subList = messages.subList(currIndex, nextIndex);
        // 下次遍历的开始索引
        currIndex = nextIndex;
        return subList;
    }
}

定义批量消息消费者

public class BatchConsumer {
    public static void main(String[] args) throws MQClientException {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("cg");

        consumer.setNamesrvAddr("127.0.0.1:9876");
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        consumer.subscribe("someTopicA", "*");
        // 指定每次可以消费10条消息,默认为1
        consumer.setConsumeMessageBatchMaxSize(10);
        // 指定每次可以从Broker拉取40条消息,默认为32
        consumer.setPullBatchSize(40);
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {

                for (MessageExt msg : msgs) {
                    System.out.println(msg);
                }
                // 消费成功的返回结果
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
                // 消费异常时的返回结果
                // return ConsumeConcurrentlyStatus.RECONSUME_LATER;
            }
        });
        consumer.start();
        System.out.println("Consumer Started");
    }
}



六、消息过滤

消费者在进行消息订阅时,除了可以指定要订阅消息的Topic外,还可以对指定Topic中的消息根据指定条件进行过滤,即可以订阅比Topic更加细粒度的消息类型。

对于指定Topic消息的过滤有两种过滤方式:Tag过滤与SQL过滤。

1、tag过滤

通过consumer的subscribe()方法指定要订阅消息的Tag。如果订阅多个Tag的消息,Tag间使用或运算符(双竖线||)连接。

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("CID_EXAMPLE");
consumer.subscribe("TOPIC", "TAGA || TAGB || TAGC");
public class FilterByTagProducer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("pg");
        producer.setNamesrvAddr("12.0.0.1:9876");
        producer.start();
        String[] tags = {"myTagA", "myTagB", "myTagC"};
        for (int i = 0; i < 10; i++) {
            byte[] body = ("Hi," + i).getBytes();
            String tag = tags[i % tags.length];
            Message msg = new Message("myTopic", tag, body);
            SendResult sendResult = producer.send(msg);
            System.out.println(sendResult);
        }
        producer.shutdown();
    }
}

public class FilterByTagConsumer {
    public static void main(String[] args) throws Exception {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("pg");
        consumer.setNamesrvAddr("12.0.0.1:9876");
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        consumer.subscribe("myTopic", "myTagA || myTagB");
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt me : msgs) {
                    System.out.println(me);
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
        System.out.println("Consumer Started");
    }
}

2、SQL过滤

SQL过滤是一种通过特定表达式对事先埋入到消息中的用户属性进行筛选过滤的方式。通过SQL过滤,可以实现对消息的复杂过滤。不过,只有使用PUSH模式的消费者才能使用SQL过滤

public class FilterBySQLProducer {
    public static void main(String[] args) throws Exception {
        DefaultMQProducer producer = new DefaultMQProducer("pg");
        producer.setNamesrvAddr("127.0.0.0:9876");
        producer.start();
        for (int i = 0; i < 10; i++) {
            try {
                byte[] body = ("Hi," + i).getBytes();
                Message msg = new Message("myTopic", "myTag", body);
                msg.putUserProperty("age", i + "");
                SendResult sendResult = producer.send(msg);
                System.out.println(sendResult);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        producer.shutdown();
    }
}


public class FilterBySQLConsumer {
    public static void main(String[] args) throws Exception {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("pg");
        consumer.setNamesrvAddr("12.0.0.1:9876");
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        consumer.subscribe("myTopic", MessageSelector.bySql("age between 0and 6"));
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                for (MessageExt me : msgs) {
                    System.out.println(me);
                }
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
        System.out.println("Consumer Started");
    }
}



七、消息发送重试机制

1、消息发送重试是什么?

Producer对发送失败的消息进行重新发送的机制,称为消息发送重试机制,

注意:

  • 生产者在发送消息时,若采用同步或异步发送方式,发送失败会重试,但oneway消息发送方式发送失败是没有重试机制的
  • 只有普通消息具有发送重试机制,顺序消息是没有的

2、同步发送失败策略

对于普通消息,消息发送默认采用round-robin策略来选择所发送到的队列。如果发送失败,默认重试2次。但在重试时是不会选择上次发送失败的Broker,而是选择其它Broker。当然,若只有一个Broker其也只能发送到该Broker,但其会尽量发送到该Broker上的其它Queue。

 DefaultMQProducer producer = new DefaultMQProducer("pg");
        // 指定nameServer地址
        producer.setNamesrvAddr("127.0.0.1:9876");
        // 设置当发送失败时重试发送的次数,默认为2次
        producer.setRetryTimesWhenSendFailed(3);
        // 设置发送超时时限为5s,默认3s
        producer.setSendMsgTimeout(5000);

Broker还具有失败隔离功能,使Producer尽量选择未发生过发送失败的Broker作为目标Broker。其可以保证其它消息尽量不发送到问题Broker,为了提升消息发送效率,降低消息发送耗时。

3 异步发送失败策略

异步发送失败重试时,异步重试不会选择其他broker,仅在同一个broker上做重试,所以该策略无法保证消息不丢。

4 消息刷盘失败策略



八、消息消费重试机制

1、顺序消息的消费重试

对于顺序消息,当Consumer消费消息失败后,为了保证消息的顺序性,其会自动不断地进行消息重试,直到消费成功。消费重试默认间隔时间为1000毫秒。重试期间应用会出现消息消费被阻塞的情况。

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("cg");
// 顺序消息消费失败的消费重试时间间隔,单位毫秒,默认为1000,其取值范围为[10,30000]
consumer.setSuspendCurrentQueueTimeMillis(100);

由于对顺序消息的重试是无休止的,不间断的,直至消费成功,所以,对于顺序消息的消费,务必要保证应用能够及时监控并处理消费失败的情况,避免消费被永久性阻塞。

注意,顺序消息没有发送失败重试机制,但具有消费失败重试机制

2、无序消息的消费重试

对于无序消息(普通消息、延时消息、事务消息),当Consumer消费消息失败时,可以通过设置返回状态达到消息重试的效果。不过需要注意,无序消息的重试只对集群消费方式生效,广播消费方式不提供失败重试特性。

对于广播消费,消费失败后,失败消息不再重试,继续消费后续消息。

3、消费重试次数与间隔

对于无序消息集群消费下的重试消费,每条消息默认最多重试16次,但每次重试的间隔时间是不同的,会逐渐变长。每次重试的间隔时间如下表。
在这里插入图片描述
若一条消息在一直消费失败的前提下,将会在正常消费后的第4小时46分后进行第16次重试。若仍然失败,则将消息投递到死信队列
修改消费重试次数

DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("cg");
// 修改消费重试次数
consumer.setMaxReconsumeTimes(10);

对于修改过的重试次数,将按照以下策略执行:

  • 若修改值小于16,则按照指定间隔进行重试
  • 若修改值大于16,则超过16次的重试时间间隔均为2小时

4、重试队列

对于需要重试消费的消息,并不是Consumer在等待了指定时长后再次去拉取原来的消息进行消费,而是将这些需要重试消费的消息放入到了一个特殊Topic的队列中,而后进行再次消费的。这个特殊的队列就是重试队列

当出现需要进行重试消费的消息时,Broker会为每个消费组都设置一个Topic名称为%RETRY%consumerGroup@consumerGroup 的重试队列

1)这个重试队列是针对消费者组的,而不是针对每个Topic设置的(一个Topic的消息可以让多个消费者组进行消费,所以会为这些消费者组各创建一个重试队列)
2)只有当出现需要进行重试消费的消息时,才会为该消费者组创建重试队列
在这里插入图片描述

5、如何配置消费者进行失败重试

在这里插入图片描述
集群消费方式下,消息消费失败后若不希望消费重试,则在捕获到异常后同样也返回与消费成功后的相同的结果,即ConsumeConcurrentlyStatus.CONSUME_SUCCESS,则不进行消费重试。

九、死信队列

1、什么是死信队列

当一条消息初次消费失败,消息队列会自动进行消费重试;达到最大重试次数后,若消费依然失败,则表明消费者在正常情况下无法正确地消费该消息,此时,消息队列不会立刻将消息丢弃,而是将其发送到该消费者对应的特殊队列中。这个队列就是死信队列(Dead-Letter Queue,DLQ),而其中的消息则称为死信消息(Dead-Letter Message,DLM)

2、死信队列的特征

  • 死信队列中的消息不回再被消费者正常消费
  • 死信存储的有效期和正常消息相同,均为3天(commit log文件的过期时间),三天后会被自动删除
  • 死信队列是一个特殊的topic,名称为 %DLQ%consumerGroup@consumerGroup ,即每个消费者组都有一个死信队列。如果一个消费者组未产生死信消息,则不会为期创建相应的死信队列。

3、死信消息的处理

实际上,当⼀条消息进入死信队列,就意味着系统中某些地方出现了问题,从而导致消费者无法正常消费该消息,比如代码中原本就存在Bug。
因此,对于死信消息,通常需要开发人员进行特殊处理。最关键的步骤是要排查可疑因素,解决代码中可能存在的Bug,然后再将原来的死信消息再次进行投递消费。

  • 19
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
重磅,史上最全的阿里云分享的云原生技术学习资料合集,共120份。 一、阿里云开源书合集 2020微服务领域开源数字化报告 阿里巴巴云原生技术与实践13讲 阿里巴巴云原生实践15讲 不一样的双11技术:阿里巴巴经济体云原生实践 深入浅出Kubernetes 云原生架构白皮书 Knative云原生应用开发指南 二、思维图合集 分布式系统大图 分布式系统知识体系大图 架构图思考路径 Go指南大图 三、大咖演讲 阿里巴巴 DevOps 的三架马车:基础设施、运维编排、监控 阿里巴巴 K8s 超大规模实践经验 阿里巴巴 Kubernetes 应用管理实践中的经验与教训 阿里巴巴超大规模神龙裸金属 Kubernetes 集群运维实践 阿里巴巴核心应用落地 Service Mesh 的挑战与机遇 阿里巴巴云原生开源探索与实践司徒放(姬风) 阿里云文件存储:K8s 云原生场景下的共享高性能存储 安全容器的发展与思考 拐点已至, 云原生引领数字化转型升级 拐点已至,云原生引领数字化转型升级易立 函数计算在 双11 小程序场景中的应用 基于 K8s 扩展机制实现 PaaS 平台云原生演进 金融级云原生 PaaS 探索与实践 客如云容器化改造经验 蚂蚁金服双十一 Service Mesh 超大规模落地揭秘 全民双十一:基于容器服务的大促备战 容器混合云趋势与展望 容器上云的攻与守 三维家的 Paas 平台建设与微服务架构 深入 Kubernetes 的“无人区”-蚂蚁金服双十一的调度系统 使用 Kubernetes 运行 MXNet 和 AutoTVM 谐云科技阿里云联合新品解读 云原生应用分发协同实践 Distributed Database on Cloud Edge Kubernetes - 构建边缘云计算基础设施 Improving Resource Efficiency in Kubernetes Clusters Kata Containers 云原生服务的一块坚定基石 OpenKruise — 助力阿里巴巴集团落地 Kubernetes的核心开源技术 OpenKruise — 自动化部署 Kubernetes 应用的新方法 Serverless 弹性容器实例:设计、实现和性能优化 Serverless autoscaling in kubernetes Serverless Deep Learning Inference Service Mesh 在『路口』的产品思考与实践 TiDB Operator 实现原理解析 四、架构师成长系列直播 10分钟高质量完成应用容器化迁移 从 2019 到 2020,Apache Dubbo 年度总结与展望 从代码到部署,云原生应用 DevSecOps 实践 攻克痛点:如何保证复杂微服务架构下的数据一致性 构建安全可靠的微服务 Nacos 在颜铺 SaaS 平台的应用实践 函数计算最佳实践:搭建基于 Serverless 的在线转换工具 函数计算最佳实践:基于函数计算实现 Serverless 自动化运维 函数计算最佳实践:快速开发一个分布式 Puppeteer 网页截图服务 函数计算最佳实践:如何轻松构建弹性高可用的音视频处理系统? 基于 DLedger 构建高可用的 Apache RocketMQ 集群 基于 RocketMQ + Knative 驱动云原生 Serverless 应用 基于 RocketMQ Prometheus Exporter 打造定制化 DevOps 平台 快速交付云原生应用的 3 种发布策略详解 流量太大容易挂?接入 Sentinel 让 N 个 9 成为可能! 容器化应用痛点剖析:问题诊断、监控及运维 如何借助 Serverless 技术降低闲置计算资源成本 十分钟上线-使用函数计算构建支付宝小程序服务 为什么 RocketMQ 是业务消息的首选 消息队列 Kafka 版差异化特性 云原生时代的在线教育 DevOps 之道 中小企业如何实现在家研发软件 Apache RocketMQ 未来展望 Arthas 在线应用诊断实践 Dubbo 2.7.6 新特性 GitOps之应用安全发布模型实践 Nacos 全新权限控制系统介绍 OAM:云原生时代的应用模型与下一代 DevOps 技术 RocketMQ 新特性揭秘-Request-Reply 模式 RocketMQ Connect 平台的搭建与实践 RocketMQ Go 客户端实践 RocketMQ Operator-K8s 平台自动化部署工具 Serverless 工作流适用场景及最佳实践 Serverless 开发实战--十分钟上线一个 Web 应用 Service Mesh 实践及落地风险控制
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值