RabbitMQ 在 Spring Boot中使用方式

文章目录

    • 作用
    • MQ docker 安装
    • MQ使用
      • RabbitMQ的整体架构及核心概念:
      • RabbitMQ的整体架构及核心概念:
      • 消费者消息推送限制
      • 交换机与队列
      • ## 项目使用MQ
      • Direct: 直连模式
      • Fanout: 广播模式
      • Topic: 主题模式
      • Headers: 头信息模式
    • 使用DEMO地址
    • 异常问题记录

在这里插入图片描述

作用

RabbitMQ 作为一款开源消息队列中间件(基于 AMQP 协议),在项目中主要解决系统间的异步通信、解耦、流量削峰等问题,提升系统的可扩展性和可靠性。以下是其核心作用及典型场景:

  1. 异步处理

    • 场景:耗时操作(如发送邮件、短信、文件处理)异步执行,避免阻塞主流程。
    • 示例:用户注册后,主线程快速返回,通过 RabbitMQ 异步触发邮件发送、数据清洗等任务。
    • 优势:提升响应速度,优化用户体验,提高系统吞吐量。
  2. 应用解耦

    • 场景:系统间通过消息通信,降低直接依赖。
    • 示例:订单系统生成订单后,发送消息到队列,库存系统、物流系统各自订阅消息处理,任一系统故障不影响主流程。
    • 优势:增强系统容错性,模块独立升级维护更灵活。
  3. 流量削峰

    • 场景:应对突发高并发,避免服务过载。
    • 示例:秒杀活动中,请求先写入消息队列,后端服务按处理能力消费,避免数据库被击穿。
    • 优势:平滑流量波动,保护后端资源,结合限流策略提升系统稳定性。
  4. 日志收集与数据处理

    • 场景:分布式系统中收集多节点日志或数据。
    • 示例:多个服务将日志发送到 RabbitMQ,由统一消费者写入 Elasticsearch 或 Hadoop 分析。
    • 优势:集中处理数据,降低对业务系统性能影响。
  5. 分布式系统协调

    • 场景:实现跨服务事务最终一致性。
    • 示例:电商下单后,通过消息队列通知积分系统增加积分,若失败则重试或补偿。
    • 优势:替代同步 RPC 调用,降低分布式事务复杂度。
  6. 跨语言协作

    • 场景:异构系统(不同语言/框架)间通信。
    • 示例:Java 服务与 Python 数据分析服务通过 RabbitMQ 交换数据。
    • 优势:协议标准化,支持多种客户端(Java、Python、Go 等)。
  7. 关键技术机制

    • 可靠性:通过生产者确认(Publisher Confirm)、持久化(Persistent Messages)、消费者手动确认(ACK)确保消息不丢失。
    • 灵活性:支持多种交换机类型(Direct、Topic、Fanout、Headers)实现消息路由。
    • 扩展性:集群与镜像队列提供高可用,横向扩展消费者提升处理能力。
    • 容错:死信队列(DLX)处理失败消息,避免消息无限重试。

注意事项

  • 消息顺序:默认不保证严格顺序,需通过单队列单消费者或业务逻辑处理。
  • 重复消费:网络重试可能导致消息重复,需业务层幂等设计。
  • 监控:使用管理界面或 Prometheus + Grafana 监控队列积压、消费者状态。

典型应用案例

  • 电商系统:订单状态更新、库存扣减、通知推送。
  • 微服务架构:服务间事件驱动通信(如用户注销触发多系统清理)。
  • IoT 数据流:设备上报数据缓冲与分发处理。

例如:
支付服务 ,将核心业务剥离 ,以下 交易服务、通知服务、 积分服务可以通过mq 去处理,无需实时服务调用
在这里插入图片描述

MQ docker 安装

docker pull rabbitmq:latestdocker run \-e RABBITMQ_DEFAULT_USER=root \-e RABBITMQ_DEFAULT_PASS=123456 \-v F:/docker/data/rabbitmq/plugins:/plugins \--name mq \--hostname mq1 \-p 15672:15672 \-p 5672:5672 \-d \rabbitmq:latest
# 运行mq
docker run -e RABBITMQ_DEFAULT_USER=root -e RABBITMQ_DEFAULT_PASS=123456 --name mq --hostname mq1 -p 15672:15672 -p 5672:5672 -d rabbitmq:3.8-management
# 进入容器
docker exec -it rabbitmq bash
# 开启 外部web页面
rabbitmq-plugins enable rabbitmq_management

MQ使用

RabbitMQ的整体架构及核心概念:

  • virtual-host:虚拟主机,起到数据隔离的作用
  • publisher:消息发送者
  • consumer:消息的消费者
  • queue:队列,存储消息
  • exchange:交换机,负责路由消息

RabbitMQ的整体架构及核心概念:

virtual-host:虚拟主机,起到数据隔离的作用
publisher:消息发送者
consumer:消息的消费者
queue:队列,存储消息
exchange:交换机,负责路由消息

消费者消息推送限制

默认情况下,RabbitMQ的会将消息依次轮询投递给绑定在队列上的每一个消费者。但这并没有考虑到消费者是否已经处理完消息,可能出现消息堆积。
因此我们需要修改application.yml,设置preFetch值为1,确保同一时刻最多投递给消费者1条消息:

spring:rabbitmq:listener:simple:prefetch: 1 # 每次只能获取一条消息,处理完成才能获取下一个消息

交换机与队列

交换机(Exchange):
交换机是用来接收生产者发送的消息并将这些消息路由到一个或多个队列中的实体。有不同类型

  • Direct: 直连模式,根据路由键(Routing Key)精确匹配队列。
  • Fanout: 广播模式,将消息广播给所有绑定到该交换机的队列,忽略路由键。
  • Topic: 主题模式,使用通配符对路由键进行模式匹配来决定消息被发送到哪些队列。
  • Headers: 头信息模式,基于消息头属性而非路由键来路由消息。

队列:
队列用于存储消息直到消费者可以处理它们。队列是实际存放消息的地方,消费者从这里获取消息。

绑定(Binding):
绑定是指将队列连接到交换机的过程。绑定时可以指定一个绑定键(Binding Key),这个键在不同的交换机类型中有不同的意义。

  • 作用:它定义了交换机与队列之间的关系,即通过何种规则将消息从交换机路由到特定的队列。例如,在direct类型的交换机中,绑定键必须与消息的路由键完全匹配才能使消息进入对应的队列;而在topic类型的交换机中,则支持模式匹配。

## 项目使用MQ

maven 引入

<!-- amap -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

yaml 配置

spring:rabbitmq:host: 127.0.0.1port: 5672#虚拟主机virtual-host: /hmallusername: rootpassword: 123456

Direct: 直连模式

Direct: 直连模式
配置类定义队列、Direct类型的交换机以及它们之间的绑定关系。

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RabbitConfig {// 定义队列@Beanpublic Queue myQueue() {return new Queue("myQueue", true); // true表示持久化队列}// 定义Direct交换机@Beanpublic DirectExchange directExchange() {return new DirectExchange("directExchange");}// 绑定队列到交换机上,并指定绑定键@Beanpublic Binding binding(Queue myQueue, DirectExchange directExchange) {return BindingBuilder.bind(myQueue).to(directExchange).with("routingKey");}
}

接收消息

@RabbitListener(queues = "myQueue")
public void receiveDirectMessage(String message) {System.out.println(" [x] Received '" + message + "'");
}

创建一个服务类用于向Direct交换机发送消息。

@Test
public void sendDirect(){String message = "Direct消息";rabbitTemplate.convertAndSend("test.direct", "test1", message);System.out.println(" [x] Sent '" + message + "'");
}

Fanout: 广播模式

广播模式通常指的是使用fanout类型的交换机来实现消息的广播。fanout交换机会将接收到的消息广播给所有绑定到该交换机的队列,而不考虑路由键(routing key)
配置Fanout Exchange和Queue
创建一个配置类来定义fanout类型的交换机以及需要绑定到此交换机的队列。

package com.itheima.consumer.config;import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RabbitFanoutConfig {// 定义第一个队列@Beanpublic Queue queue1() {return new Queue("queue1", true); // 'true' for durable queue}// 定义第二个队列@Beanpublic Queue queue2() {return new Queue("queue2", true);}// 定义fanout类型的交换机@Beanpublic FanoutExchange fanoutExchange() {return new FanoutExchange("myFanoutExchange");}// 将queue1绑定到fanout交换机上@Beanpublic Binding binding1(Queue queue1, FanoutExchange fanoutExchange) {return BindingBuilder.bind(queue1).to(fanoutExchange);}// 将queue2绑定到fanout交换机上@Beanpublic Binding binding2(Queue queue2, FanoutExchange fanoutExchange) {return BindingBuilder.bind(queue2).to(fanoutExchange);}
}

发送消息
发送消息到fanout类型的交换机时,不需要指定路由键,因为fanout交换机会忽略路由键并将消息广播给所有绑定的队列。

 @Test
public void sendFanout(){String message = "Fanout消息";rabbitTemplate.convertAndSend("myFanoutExchange", "", message);System.out.println(" [x] Sent '" + message + "'");
}

接收消息
为每个队列设置监听器来接收消息。

@RabbitListener(queues = "queue1")
public void receiveMessageFromQueue1(String message) {System.out.println("Queue1 [x] Received '" + message + "'");
}@RabbitListener(queues = "queue2") 
public void receiveMessageFromQueue2(String message) {System.out.println("Queue2 [x] Received '" + message + "'");
}

所有绑定到myFanoutExchange交换机的队列都将接收到相同的消息。

Topic: 主题模式

主题模式(Topic Exchange)是一种灵活的消息路由机制。与直接模式(Direct Exchange)不同的是,主题交换机允许使用通配符来匹配消息的路由键(routing key),从而实现更加复杂的消息路由逻辑。下面是如何在Spring Boot中配置和使用主题模式的详细步骤。

在主题模式下,如果你想根据路由键进行个性化的监听,可以使用@RabbitListener的bindings属性,而不是直接指定队列名。这样可以根据路由键动态地监听消息。

  • 在@RabbitListener中,使用了bindings属性来定义如何绑定队列到交换机上。
  • value指定了要使用的队列,如果该队列不存在,则会自动创建。
  • exchange指定了目标交换机及其类型(在这里是topic类型的交换机)。
  • key定义了用于匹配消息路由键的绑定键。在这个例子中,item.*表示任何以item.开头的消息都会被发送到queue1;而#.update则匹配任何以.update结尾的消息并将其发送到queue2。
@RabbitListener(bindings = @QueueBinding(value = @Queue(value = "queue1", durable = "true"),exchange = @Exchange(value = "topicExchange", type = "topic"),key = "item.*"))
public void receiveMessageFromQueue11(String message) {System.out.println("Queue1 [x] Received '" + message + "'");
}@RabbitListener(bindings = @QueueBinding(value = @Queue(value = "queue2", durable = "true"),exchange = @Exchange(value = "topicExchange", type = "topic"),key = "#.update"))
public void receiveMessageFromQueue21(String message) {System.out.println("Queue2 [x] Received '" + message + "'");
}

Topic Exchange发送消息。根据不同的路由键,消息会被分发到相应的队列中。

@Testpublic void sendTopic1(){String message = "Topic消息";rabbitTemplate.convertAndSend("topicExchange", "item.ded", message);System.out.println(" [x] Sent '" + message + "'");}@Testpublic void sendTopic(){String message = "Topic消息";rabbitTemplate.convertAndSend("topicExchange", "ded.update", message);System.out.println(" [x] Sent '" + message + "'");}

Headers: 头信息模式

Headers Exchange(头信息交换机)是一种基于消息头属性进行路由的消息传递机制。与直接模式(Direct Exchange)、扇出模式(Fanout Exchange)和主题模式(Topic Exchange)不同,Headers Exchange允许你根据消息的头部属性来决定路由逻辑,而不是简单的字符串匹配。
配置Headers Exchange、Queue及Bindings
创建一个配置类来声明Headers Exchange、队列以及它们之间的绑定关系。这里我们创建两个队列,并将它们通过不同的头部属性绑定到同一个Headers Exchange上。

@RabbitListener(bindings = @QueueBinding(value = @Queue(value = "queue6", durable = "true"),exchange = @Exchange(value = "headersExchange", type = "headers"),arguments = {@Argument(name = "type", value = "info"),@Argument(name = "format", value = "json")}))
public void receiveMessageHeaderFromQueue1(String message) {System.out.println("Queue1 [x] Received '" + message + "'");
}@RabbitListener(bindings = @QueueBinding(value = @Queue(value = "queue7", durable = "true"),exchange = @Exchange(value = "headersExchange", type = "headers"),arguments = {@Argument(name = "type", value = "error")}))
public void receiveMessageHeaderFromQueue2(String message) {System.out.println("Queue2 [x] Received '" + message + "'");
}

发送消息

@Test
public void sendHeader(){Map<String, Object> headers = new HashMap<>();headers.put("type", "info");headers.put("format", "json");sendMessage(headers, "This is an info message in JSON format.");
}public void sendMessage(Map<String, Object> headers, String payload) {MessageProperties messageProperties = new MessageProperties();headers.forEach(messageProperties::setHeader);Message message = new Message(payload.getBytes(), messageProperties);rabbitTemplate.send("headersExchange", "", message);System.out.println(" [x] Sent with headers: " + headers + " and payload: " + payload);
}

使用DEMO地址

https://gitee.com/song_of_the_heart/mq-demo

异常问题记录

应该是 最新版的问题非集群, 退回到 3.8版本, docker 测试

org.springframework.amqp.AmqpIOException: java.io.IOExceptionat org.springframework.amqp.rabbit.support.RabbitExceptionTranslator.convertRabbitAccessException(RabbitExceptionTranslator.java:70) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:603) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.connection.CachingConnectionFactory.createConnection(CachingConnectionFactory.java:725) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.connection.ConnectionFactoryUtils.createConnection(ConnectionFactoryUtils.java:252) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.core.RabbitTemplate.doExecute(RabbitTemplate.java:2210) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.core.RabbitTemplate.execute(RabbitTemplate.java:2183) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.core.RabbitTemplate.execute(RabbitTemplate.java:2163) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.core.RabbitAdmin.getQueueInfo(RabbitAdmin.java:463) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.core.RabbitAdmin.getQueueProperties(RabbitAdmin.java:447) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.attemptDeclarations(AbstractMessageListenerContainer.java:1942) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.redeclareElementsIfNecessary(AbstractMessageListenerContainer.java:1915) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.initialize(SimpleMessageListenerContainer.java:1384) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:1230) ~[spring-rabbit-2.4.12.jar:2.4.12]at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Caused by: java.io.IOException: nullat com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:129) ~[amqp-client-5.14.2.jar:5.14.2]at com.rabbitmq.client.impl.AMQChannel.wrap(AMQChannel.java:125) ~[amqp-client-5.14.2.jar:5.14.2]at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:147) ~[amqp-client-5.14.2.jar:5.14.2]at com.rabbitmq.client.impl.AMQConnection.start(AMQConnection.java:439) ~[amqp-client-5.14.2.jar:5.14.2]at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:1225) ~[amqp-client-5.14.2.jar:5.14.2]at com.rabbitmq.client.ConnectionFactory.newConnection(ConnectionFactory.java:1173) ~[amqp-client-5.14.2.jar:5.14.2]at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.connectAddresses(AbstractConnectionFactory.java:641) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.connect(AbstractConnectionFactory.java:616) ~[spring-rabbit-2.4.12.jar:2.4.12]at org.springframework.amqp.rabbit.connection.AbstractConnectionFactory.createBareConnection(AbstractConnectionFactory.java:566) ~[spring-rabbit-2.4.12.jar:2.4.12]... 12 common frames omitted

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

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

相关文章

力扣动态规划-30【算法学习day.124】

前言 ###我做这类文章一个重要的目的还是记录自己的学习过程&#xff0c;我的解析也不会做的非常详细&#xff0c;只会提供思路和一些关键点&#xff0c;力扣上的大佬们的题解质量是非常非常高滴&#xff01;&#xff01;&#xff01; 习题 1.零钱兑换 题目链接:322. 零钱兑…

AI在电竞比分网中的主要应用场景

AI在电竞体育比分网的数据应用非常广泛&#xff0c;能够显著提升数据分析、预测、用户体验和商业价值。以下是AI在电竞比分网中的主要应用场景&#xff1a; 1. 实时数据采集与分析 比赛数据实时更新&#xff1a;AI通过自动化系统实时采集比赛数据&#xff08;如击杀数、经济差、…

【Spring Boot】Spring 魔法世界:Bean 作用域与生命周期的奇妙之旅

前言 ???本期讲解关于spring原理Bean的相关知识介绍~~~ ??感兴趣的小伙伴看一看小编主页&#xff1a;-CSDN博客 ?? 你的点赞就是小编不断更新的最大动力 ??那么废话不多说直接开整吧~~ 目录 ???1.Bean的作用域 ??1.1概念 ??1.2Bean的作用域 ??1.3代码演示…

用 Python 实现 DeepSeek R1 本地化部署

DeepSeek R1 以其出色的表现脱颖而出&#xff0c;不少朋友想将其本地化部署&#xff0c;网上基于 ollama 的部署方式有很多&#xff0c;但今天我要带你领略一种全新的方法 —— 使用 Python 实现 DeepSeek R1 本地化部署&#xff0c;让你轻松掌握&#xff0c;打造属于自己的 AI…

软考-系统架构设计师(月更版)

1.需求管理的主要活动包括变更控制&#xff0c;版本控制&#xff0c;需求跟踪&#xff0c;需求状态跟踪 需求跟踪是单个需求和其他系统元素之间的依赖关系和逻辑联系建跟踪&#xff0c; 这些元素包括各种类型的需求、业务规则、系统架构和构件、源代码、测试用例&#xff0c;以…

IOTDB安装部署

IOTDB一般用于工业互联网&#xff0c;至于具体的介绍请自行搜索 1.环境准备 安装前需要保证设备上配有 JDK>1.8 的运行环境&#xff0c;并配置好 JAVA_HOME 环境变量。 设置最大文件打开数为 65535。 关闭防火墙 systemctl stop firewalld.service systemctl disable …

分享 UniApp 实现列表长按删除功能

在移动应用开发中&#xff0c;列表是常见的展示形式&#xff0c;而长按删除列表项也是一个实用且常见的交互功能。今天就来和大家分享如何在 UniApp 中实现列表的长按删除功能&#xff0c;同时附上详细的代码。 效果预览 通过代码实现后&#xff0c;我们将得到一个带有红色边…

leetcode 2684. 矩阵中移动的最大次数

题目如下 数据范围 本题使用常规动态规划就行&#xff0c;不过要注意由于有三个转移的方向&#xff0c;所以我们对dp数组的遍历应该是从上到下 从左到右即按列优先遍历。通过代码 class Solution { public:int maxMoves(vector<vector<int>>& grid) {int …

深入剖析 Burp Suite:Web 应用安全测试利器

目录 前言 一、Burp Suite 简介 二、功能组件详解 三、使用场景 四、安装与使用步骤 安装步骤 使用步骤 五、总结 前言 在网络安全的复杂版图中&#xff0c;Burp Suite 宛如一颗璀璨的明珠&#xff0c;以其强大的功能和广泛的适用性&#xff0c;成为众多安全从业者不可…

linux内网部署deepseek大模型(ollama+anythingllm)

一、安装ollama 来源&#xff1a;ollama/docs/linux.md at main ollama/ollama GitHub 1.下载安装包 ollama下载链接&#xff1a;https://ollama.com/download/ollama-linux-amd64.tgz&#xff0c;下载后放在用户目录下 2.运行脚本 #!/bin/sh # This script installs Olla…

【学术投稿-第四届智能电网和绿色能源国际学术会议(ICSGGE 2025)】CSS基本选择器详解:掌握基础,轻松布局网页

可线上 官网&#xff1a;www.icsgge.org 时间&#xff1a;2025年2月28-3月2日 目录 前言 一、基本选择器简介 1. 元素选择器&#xff08;Type Selector&#xff09; 基本语法 示例 注意事项 2. 类选择器&#xff08;Class Selector&#xff09; 基本语法 示例 注意…

聊聊 IP 地址和端口号的区别

在计算机网络中&#xff0c;两个基本概念对于理解设备如何通过网络进行通信至关重要。IP 地址和端口号是 TCP/IP 的典型特征&#xff0c;其定义如下&#xff1a;IP 地址是分配给连接到网络的每台机器的唯一地址&#xff0c;用于定位机器并与其通信。相反&#xff0c;端口号用于…

【个人开发】cuda12.6安装vllm安装实践【内含踩坑经验】

1. 背景 vLLM是一个快速且易于使用的LLM推理和服务库。企业级应用比较普遍&#xff0c;尝试安装相关环境&#xff0c;尝试使用。 2. 环境 模块版本python3.10CUDA12.6torch2.5.1xformers0.0.28.post3flash_attn2.7.4vllm0.6.4.post1 2.1 安装flash_attn 具体选择什么版本&…

问界M8细节曝光,L3自动驾驶有了!

文 | AUTO芯球 作者 | 雷慢 太惊喜了&#xff0c; 问界M8近距离实拍曝光了&#xff0c; 我看了一圈&#xff0c; 给大家扒出几个炸裂的信息&#xff0c; 注意看侧身这一堆传感器&#xff0c; 这可不是什么普通摄像头&#xff0c; 这一片传感器和和尊界S800那套一模一样&a…

idea打开springboot项目打不开文件夹 一直loading

点击file - InvalidateCaches 全选 后点击 Invalidate and Restart

early bird inject

基本原理 本质是利用windows系统的apc机制&#xff0c;以及涉及到windows进程启动的流程. 因为线程初始化阶段LdrInitializeThunk函数会调用NtTestAlert函数,这个函数执行后,所有apc队列中的例程都会执行.因此我们在主线程初始化之前向主线程的apc队列中加入恶意代码即可实现…

Flutter 实现 iOS 小组件与主 App 的通信

前言 在 Flutter 项目中实现 iOS 小组件与主 App 的通信&#xff0c;主要依赖于 App Groups 技术&#xff0c;以下是具体实现步骤和注意事项。 配置 App Groups 在 Xcode中为 Flutter 主应用&#xff08;Runner&#xff09;和 Widget Extension 配置相同的App Group 标识符…

【Unity Shader编程】之顶点着色器

来一张AI提供的资料 在shader编程中&#xff0c;定义的结构体&#xff0c;有些是会被自动赋值&#xff0c;有些是必须要手动赋值的&#xff0c;这就涉及到了语义&#xff0c; 例如 struct appdata{float4 vertex : POSITION;float vertex2;float2 uv : TEXCOORD0;};结构体里面定…

数据结构与算法-栈

参考学习&#xff1a;B站-逊哥带你学编程 栈的定义与实现 补充&#xff1a; 栈是限制插入和删除操作只能在一个位置进行的表&#xff0c;该位置是表的末端&#xff0c;叫作栈顶(top)。 对栈的基本操作有进栈(push)和出栈(Pop)&#xff0c;前者相当于插入后者则是删除最后插入…

嵌入式硬件篇---OpenMV的硬件流和软件流

文章目录 前言一、硬件流控制&#xff08;Hardware Flow Control&#xff09;1. 基本原理RTSCTS 2. OpenMV中的实现• 硬件要求• 代码配置• 工作流程 二、软件流控制&#xff08;Software Flow Control&#xff09;1. 基本原理XONXOFF 2. OpenMV中的实现• 代码配置• 工作流…