RabbitMQ 交换机类型

常用交换机

发布订阅(Publish/Subscribe)交换机

 一个生产者给多个队列发送消息,X 代表交换机。

交换机的作用:类似网络路由器,主要提供转发功能,解决怎么把消息转发到不同的队列中,让消费者从不同队列取然后消费。

绑定:交换机和队列关联起来

发布订阅交换机,队列进行持久化,生产者发布消息,所有消费者都能接收到消息。

生产者代码

public class FanoutProducer{private static final String EXCHANGE_NAME = "fanout_exchange";public static void main(String[] argv) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {//创建交换机,参数:交换机名称,交换机类型channel.exchangeDeclare(EXCHANGE_NAME, "fanout");Scanner scanner = new Scanner(System.in);while(scanner.hasNext()){String message = scanner.nextLine();//第二个参数是路由规则channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));System.out.println(" [x] Sent '" + message + "'");}}}
}

消费者代码

public class FanoutConsumer {private static final String EXCHANGE_NAME = "fanout_exchange";public static void main(String[] argv) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = connection.createChannel();//绑定交换机,以及设置绑定规则channel.exchangeDeclare(EXCHANGE_NAME, "fanout");String queueName1 = "xiaowang";String queueName2 = "xiaoli";channel.queueDeclare(queueName1, true, false, false, null);channel.queueDeclare(queueName2, true, false, false, null);//创建队列,不指定队列,随机分配//String queueName = channel.queueDeclare().getQueue();channel.queueBind(queueName1, EXCHANGE_NAME, "");System.out.println(" [xiaowang] Waiting for messages. To exit press CTRL+C");//交换机绑定队列channel.queueBind(queueName2, EXCHANGE_NAME, "");System.out.println(" [xiaoli] Waiting for messages. To exit press CTRL+C");DeliverCallback deliverCallback1 = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(  " [xiaowang] Received '" + message + "'");};DeliverCallback deliverCallback2 = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(  " [xiaoli] Received '" + message + "'");};channel.basicConsume(queueName1, true, deliverCallback1, consumerTag -> {});channel.basicConsume(queueName2, true, deliverCallback2, consumerTag -> {});}
}

channel 频道:理解为操作消息队列的 Client,通过 channel 收发消息,提供了和消息对了 server 建立通信的传输方法

channel.queueDeclare 方法参数:

queue:这是一个字符串参数,代表要声明的队列的名称。如果队列不存在,则会自动创建一个新的队列。

durable:这是一个布尔值参数,表示队列是否持久化。如果设置为true,则队列会在服务器重启后仍然存在;如果设置为false,则队列在服务器重启后会被删除。默认值为false。

exclusive:这也是一个布尔值参数,表示队列是否为独占模式。如果设置为true,则只有当前连接可以访问该队列;如果设置为false,则其他连接也可以访问该队列。默认值为false。

autoDelete:这是另一个布尔值参数,表示队列是否自动删除。如果设置为true,则当最后一个消费者取消订阅时,队列将被删除;如果设置为false,则队列将一直存在,直到手动删除或服务器重启。默认值为false。

arguments:这是一个可选参数,用于设置队列的其他属性,比如消息的最大长度、最大优先级等。

channel.basicPublish 参数:

exchange:这是一个字符串参数,代表交换机的名称。如果不需要使用特定的交换机,可以传递一个空字符串("")。交换机是RabbitMQ中用于接收生产者发送的消息并根据绑定规则路由到队列的组件。

routingKey:这也是一个字符串参数,它指定了发布消息的队列。无论通道绑定到哪个队列,最终发布的消息都会包含这个指定的路由键。路由键是用来确定消息应该发送到哪个队列的重要信息。

message:这是要发布的消息本身,通常是字节数组的形式。

properties:这是一个可选参数,用于设置消息的属性,比如消息的优先级、过期时间等。

在使用channel.basicPublish时,需要注意以下几点:

exchange和routingKey不能为空:在AMQImpl类中的实现要求这两个参数都不能为null,否则会抛出异常。

交换机类型:根据不同的需求,可以选择不同类型的交换机,如fanout、direct或topic。每种类型的交换机都有其特定的路由规则。

非命名队列:在某些情况下,比如日志系统,可以使用非命名队列,这样消费者可以接收到所有相关的日志消息,而不是特定的部分。

 channel.basicConsume 参数:

queue:这是一个字符串参数,代表要消费的队列的名称。如果队列不存在,则会抛出异常。
onMessage:这是一个回调函数,当有新的消息到达时会被调用。该函数需要接收两个参数:一个表示消息内容的Delivery对象和一个表示通道的Channel对象。
consumerTag:这是一个可选参数,用于标识消费者。如果没有指定,则会自动生成一个唯一的标识符。
autoAck:这是一个布尔值参数,表示是否自动确认消息。如果设置为true,则在消息被处理后会自动发送确认信息;如果设置为false,则需要手动发送确认信息。默认值为false。
arguments:这是一个可选参数,用于设置消费者的其他属性,比如消息的最大长度、最大优先级等。
在使用channel.basicConsume时,需要注意以下几点:

队列名称:队列名称应该是唯一的,否则会抛出异常。
消息处理:在onMessage回调函数中,需要对消息进行处理,并根据需要发送确认信息。
消费者标识符:可以通过设置consumerTag来标识消费者,以便在后续操作中进行识别和管理。
消费者属性:可以通过设置消费者的其他属性来控制消费者的行为,比如设置消息的最大长度、最大优先级等。

路由交换机 (Direct exchange)

和订阅发布的区别是在交换机和队列之间有一个路由键,用来控制消息发送到哪个队列中供消费者消费。生产者给交换机一个标识,让交换机给指定的队列转发消息。

生产者代码

public class DirectProducer {private static final String EXCHANGE_NAME = "direct_exchange";public static void main(String[] argv) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {//创建交换机,交换机类型是 directchannel.exchangeDeclare(EXCHANGE_NAME, "direct");Scanner scanner = new Scanner(System.in);while (scanner.hasNext()){String userInput = scanner.nextLine();//输入的时候带着标识,标识就是路由键String[] strs = userInput.split(" ");if(strs.length<1){continue;}//消息String message = strs[0];//路由键String severity = strs[1];//发送消息时带着路由键channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8"));System.out.println(" [x] Sent '" + severity + "':'" + message + "'");}}}
}

消费者代码

public class DirectConsumer {private static final String EXCHANGE_NAME = "direct_exchange";public static void main(String[] argv) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = connection.createChannel();channel.exchangeDeclare(EXCHANGE_NAME, "direct");String queueName1 = "xiaohong";String queueName2 = "xiaobai";channel.queueDeclare(queueName1, true, false, false, null);channel.queueDeclare(queueName2, true, false, false, null);//交换机使用路由键绑定队列channel.queueBind(queueName1, EXCHANGE_NAME, "xiaohong");channel.queueBind(queueName2, EXCHANGE_NAME, "xiaobai");System.out.println(" [*] Waiting for messages. To exit press CTRL+C");DeliverCallback deliverCallback1 = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(" [xiaohong] Received '" +delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");};DeliverCallback deliverCallback2 = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(" [xiaobai] Received '" +delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");};channel.basicConsume(queueName1, true, deliverCallback1, consumerTag -> {});channel.basicConsume(queueName2, true, deliverCallback2, consumerTag -> {});}
}

主题交换机 (Topic exchange)

在路由交换机的基础上,消息会具有一个模糊的路由键转发给指定的对俄(一系列的路由键、一类的路由键)

1. (*)标识匹配一个单词,比如 *.orange 表示 a.orange b.orange 都能匹配

2. (#)表示 0 个或多个单词,比如 a,#, a.a, a.b 都可以

生产者代码

public class TopicProduce {private static final String EXCHANGE_NAME = "topic_exchange1";public static void main(String[] argv) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {channel.exchangeDeclare(EXCHANGE_NAME, "topic");Scanner scanner = new Scanner(System.in);while (scanner.hasNext()) {String userInput = scanner.nextLine();String[] strs = userInput.split(" ");if (strs.length < 1) {continue;}//消息String message = strs[0];//路由键String severity = strs[1];channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8"));System.out.println(" [x] Sent '" + severity + "':'" + message + "'");}}}
}

消费者代码

public class TopicConsumer {private static final String EXCHANGE_NAME = "topic_exchange1";public static void main(String[] argv) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = connection.createChannel();channel.exchangeDeclare(EXCHANGE_NAME, "topic");channel.exchangeDeclare(EXCHANGE_NAME, "topic");channel.exchangeDeclare(EXCHANGE_NAME, "topic");String queueName1 = "xiaohei";String queueName2 = "xiaolv";String queueName3 = "xiaohuang";channel.queueDeclare(queueName1, true, false, false, null);channel.queueDeclare(queueName2, true, false, false, null);channel.queueDeclare(queueName3, true, false, false, null);//交换机使用路由键绑定队列,路由键绑定在第三个参数channel.queueBind(queueName1, EXCHANGE_NAME, "#.前端.#");channel.queueBind(queueName2, EXCHANGE_NAME, "#.后端.#");channel.queueBind(queueName2, EXCHANGE_NAME, "#.产品.#");System.out.println(" [*] Waiting for messages. To exit press CTRL+C");//收到消息后如何处理DeliverCallback deliverCallback1 = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(" [xiaohei] Received '" + delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");};DeliverCallback deliverCallback2 = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(" [xiaolv] Received '" + delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");};DeliverCallback deliverCallback3 = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(" [xiaohuang] Received '" + delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");};channel.basicConsume(queueName1, true, deliverCallback1, consumerTag -> {});channel.basicConsume(queueName2, true, deliverCallback2, consumerTag -> {});channel.basicConsume(queueName3, true, deliverCallback3, consumerTag -> {});}
}

核心机制

消息过期机制

官方文档:Preventing Unbounded Buffers with RabbitMQ | RabbitMQ

每个消息指定一个有效期,一段时间内没有被消费者处理,就过期了。

比如消费者挂了,消息一直不被处理,订单就会失效。

可以清理过期的数据,模拟延迟队列的实现。

给每条消息都设置过期时间:

Map<String, Object> args = new HashMap<String, Object>();
args.put("x-message-ttl", 60000);
channel.queueDeclare(QUEUE_NAME, false, false, false, args);

给队列设置过期时间,设置在生产者中

AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder().expiration("1000").build();

生产者代码

public class TtlProducer {private final static String QUEUE_NAME = "Tll_queue";public static void main(String[] argv) throws Exception {//创建连接ConnectionFactory factory = new ConnectionFactory();//设置了本地连接,如果修改了用户名和密码,需要设置/*factory.setPassword();factory.setUsername();*/factory.setHost("localhost");//建立连接、创建频道//频道,类似客户端,用于调用serverConnection connection = factory.newConnection();Channel channel = connection.createChannel();String message = "Hello World!";//发消息设置过期时间AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder().expiration("1000").build();//发送消息channel.basicPublish("",QUEUE_NAME,properties,message.getBytes(StandardCharsets.UTF_8));System.out.println(" [*] Waiting for messages. To exit press CTRL+C");}
}

消费者代码

public class TtlConsumer {private final static String QUEUE_NAME = "Tll_queue";public static void main(String[] argv) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = connection.createChannel();//声明队列,同一个消息队列参数必须一致Map<String, Object> args = new HashMap<String, Object>();args.put("x-message-ttl", 60000);channel.queueDeclare(QUEUE_NAME, false, false, false, args);//定义了如何处理消息DeliverCallback deliverCallback = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(" [x] Received '" + message + "'");};//接收、消费消息 第二个参数 autoAckchannel.basicConsume(QUEUE_NAME, true, deliverCallback, consumerTag -> { });System.out.println(" [*] Waiting for messages. To exit press CTRL+C");}
}

死信队列

官方文档:Dead Letter Exchanges | RabbitMQ

为了保证消息的可靠性,比如每条消息都成功消费,需要提供一个容错机制,即:失效的消息怎么办?

死信:过期的消息、拒收的消息、消息队列满了、处理失败的消息的统称。

死信队列:处理死信的队列。

死信交换机:专门给死信队列转发消息的,存在路由绑定关系

实际就是设置一个普通的队列,专门将死信发送到这个队列中处理。

1. 创建死信交换机和死信队列

//声明死信交换机
String queueName = "laoban_dlx_queue";
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, EXCHANGE_NAME, "laoban");

2. 给失败之后需要容错处理的队列绑定死信交换机

args2.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);

3. 绑定交换机到死信队列

args2.put("x-dead-letter-routing-key", "waibao");

生产者代码

public class DLXDirectProducer {private static final String EXCHANGE_NAME = "direct2_exchange";private static final String DEAD_EXCHANGE_NAME = "dlx_direct2_exchange";public static void main(String[] argv) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");try (Connection connection = factory.newConnection();Channel channel = connection.createChannel()) {//声明死信交换机channel.exchangeDeclare(DEAD_EXCHANGE_NAME, "direct");String queueName = "laoban_dlx_queue";channel.queueDeclare(queueName, true, false, false, null);channel.queueBind(queueName, DEAD_EXCHANGE_NAME, "laoban");String queueName2 = "waibao_dlx_queue";channel.queueDeclare(queueName2, true, false, false, null);channel.queueBind(queueName2, DEAD_EXCHANGE_NAME, "waibao");channel.exchangeDeclare(EXCHANGE_NAME, "direct");DeliverCallback deliverCallback1 = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(" [laoban] Received '" +delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");};DeliverCallback deliverCallback2 = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(" [waibao] Received '" +delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");};channel.basicConsume(queueName, false, deliverCallback1, consumerTag -> {});channel.basicConsume(queueName2, false, deliverCallback2, consumerTag -> {});Scanner scanner = new Scanner(System.in);while (scanner.hasNext()){String userInput = scanner.nextLine();String[] strs = userInput.split(" ");if(strs.length<1){continue;}//消息String message = strs[0];//路由键String severity = strs[1];channel.basicPublish(EXCHANGE_NAME, severity, null, message.getBytes("UTF-8"));System.out.println(" [x] Sent '" + severity + "':'" + message + "'");}}}
}

消费者代码

public class DLXDirectConsumer {private static final String EXCHANGE_NAME = "direct2_exchange";private static final String DEAD_EXCHANGE_NAME = "dlx_direct2_exchange";public static void main(String[] argv) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = connection.createChannel();channel.exchangeDeclare(EXCHANGE_NAME, "direct");//死信交换机绑定工作队列,当信息错误就从工作队列发送到死信交换机Map<String, Object> args1 = new HashMap<String, Object>();//指定绑定哪个交换机args1.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);//死信要发送到哪个队列args1.put("x-dead-letter-routing-key", "laoban");String queueName1 = "doghuang";channel.queueDeclare(queueName1, true, false, false, args1);channel.queueBind(queueName1, EXCHANGE_NAME, "doghuang");//绑定cat 队列String queueName2 = "catbai";Map<String, Object> args2 = new HashMap<String, Object>();args2.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);args2.put("x-dead-letter-routing-key", "waibao");channel.queueDeclare(queueName2, true, false, false, args2);channel.queueBind(queueName2, EXCHANGE_NAME, "catbai");//交换机使用路由键绑定队列System.out.println(" [*] Waiting for messages. To exit press CTRL+C");DeliverCallback deliverCallback1 = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");channel.basicNack(delivery.getEnvelope().getDeliveryTag(),false,false);System.out.println(" [doghuang] Received '" +delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");};DeliverCallback deliverCallback2 = (consumerTag, delivery) -> {String message = new String(delivery.getBody(), "UTF-8");System.out.println(" [catbai] Received '" +delivery.getEnvelope().getRoutingKey() + "':'" + message + "'");};channel.basicConsume(queueName1, false, deliverCallback1, consumerTag -> {});channel.basicConsume(queueName2, false, deliverCallback2, consumerTag -> {});}
}

项目实战

项目中使用可以选择两种方法

1. 官方的客户端,兼容性好,灵活,需要自己维护管理

2. 使用封装好的客户端,比如 Spring Boot RabbitMQ Starter

优点:简单易用

缺点:不够灵活,被框架限制

小项目使用封装好的足够

1. 依赖引入

引入和自己 Spring Boot 版本相同的依赖,避免出现不能运行的错误

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId><version>2.6.13</version>
</dependency>

2. 引入配置

rabbitmq:host: localhostport: 5672password: guestusername: guset

3. 创建交换机和消息队列,这个只需要启动一次创建即可

/*** 只启动一次,创建交换机和消息队列*/
public class MqInitMain {private static final String EXCHANGE_NAME = "code_exchange";public static void main(String[] args) throws Exception {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = connection.createChannel();channel.exchangeDeclare(EXCHANGE_NAME, "direct");//绑定一个队列String queueName = "code_queue";channel.queueDeclare(queueName,true,false,false,null);channel.queueBind(queueName,EXCHANGE_NAME,"BI_routingKey");}
}

4. 生产者

/*** 生产者*/
@Component
public class MyMessageProducer {@Resourceprivate RabbitTemplate rabbitTemplate;//1.交换机名称2. 交换机路由键3.发送的消息public void sendMessage(String exchange, String routingKey,String message){rabbitTemplate.convertAndSend(exchange,routingKey,message);}}

5. 消费者

/*** 消费者*/
@Component
@Slf4j
public class MessageConsumer {@RabbitListener(queues = {"code_queue"},ackMode = "MANUAL")public void receiveMessage(String message, Channel channel,@Header(AmqpHeaders.DELIVERY_TAG) long delivery){log.info("receiveMessage message={}",message);try {channel.basicAck(delivery,false);} catch (IOException e) {throw new RuntimeException(e);}}
}

6. 测试

@SpringBootTest
class MyMessageProducerTest {@Resourceprivate MyMessageProducer myMessageProducer;@Testvoid sendMessage() {myMessageProducer.sendMessage("code_exchange","BI_routingKey","你好吗");}
}

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

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

相关文章

第十八篇:探索非关系型数据库:从入门到实践

探索非关系型数据库&#xff1a;从入门到实践 1. 引言 1.1 非关系型数据库的崛起&#xff1a;背景与重要性 在过去的几十年里&#xff0c;关系型数据库&#xff08;RDBMS&#xff09;一直在数据存储和管理领域占据主导地位。其严谨的结构化数据模型以及强大的事务处理能力&am…

Mysql触发器优化大数据表

背景 数据库的订单数量过多&#xff0c;需要分出热表用于快速查询&#xff0c;热表仅保存10天的订单数据。 解决思路 每次数据库订单表触发增删改时&#xff0c;同步操作到trigger_order_mul_info表&#xff0c;然后trigger_order_mul_info会定期删除超过10天的数据。 增删…

家政项目day1 配置说明前端

目录 1.配置1.1 开发环境1.2 配置虚拟机1.3 编写nacos配置中心1.4 配置OSS存储1.5 配置高德地图api 2 设计前端并且进行部署2.1 开发环境2.2 安装类库2.3 修改代码2.4 试运行前端2.4.1 OSS配置验证 1.配置 1.1 开发环境 由于个人资金问题&#xff0c;可能担负不起8h8g的服务器…

React-JSX基础

什么是JSX 概念&#xff1a;JSX是JavaScript和XML&#xff08;HTML&#xff09;的缩写&#xff0c;表示在JS代码中编写HTML模板结构&#xff0c;它是React中编写UI模板的方式 优势&#xff1a;1.HTML的声明式模板写法 2.JS的可编程能力 JSX的本质 JSX并不是标准的JS语法&…

学习现货黄金分析技术前 有3点注意

投资者要做现货黄金交易&#xff0c;就需要懂得分析技术&#xff0c;通过分析投资者能找到市场的交易机会。其实分析也是对现货黄金市场进行思考的过程&#xff0c;未经分析而得到的入场机会&#xff0c;失败的可能性是较大的。但是我们在学习现货黄金分析技术之前&#xff0c;…

在做题在学习(60):和可被K整除的子数组

974. 和可被 K 整除的子数组 - 力扣&#xff08;LeetCode&#xff09; 解法&#xff1a;前缀和 哈希表 同余定理 同余定理&#xff1a; 而此题要求返回能被k整除(%k 0)的子数组的个数&#xff0c;如下图&#xff1a; 把问题转化为——> 有多少个前缀和的余数 sum%k &a…

D60SB60-ASEMI整流桥D60SB60参数、封装、尺寸

编辑&#xff1a;ll D60SB60-ASEMI整流桥D60SB60参数、封装、尺寸 型号&#xff1a;D60SB60 品牌&#xff1a;ASEMI 封装&#xff1a;D-SB 批号&#xff1a;2024 特性&#xff1a;插件、整流桥 平均正向整流电流&#xff08;Id&#xff09;&#xff1a;60A 最大反向击穿…

C++---运算符重载

运算符重载介绍 在类中重新定义运算符&#xff0c;赋予运算符新的功能以适应类的运算&#xff0c;就称为运算符重载。 运算符重载是一种形式的C多态,它使得对象操作更直观,本质上也是属于函数重载。 实际上&#xff0c;我们已经在不知不觉之中使用了运算符重载。例如&#xff…

编一个自己的万年历

编一个自己的万年历 前阶段突然想查一下某一天是星期几&#xff0c;于是自己编了一个[小程序][https://blog.csdn.net/weixin_41905135/article/details/138972055?spm1001.2014.3001.5501]&#xff0c;但是功能很单一&#xff0c;就是单纯的查是星期几。&#xff08;虽然用网…

IRFB3207PBF TO-220 N沟道75V/180A 直插MOSFET场效应管

英飞凌&#xff08;Infineon&#xff09;的 IRFB3207PBF 是一款高性能的 N 沟道 MOSFET&#xff0c;适用于多种电子设备和系统中的高侧开关应用。以下是 IRFB3207PBF 的一些典型应用场景&#xff1a; 1. 电源管理&#xff1a;在电源管理系统中&#xff0c;IRFB3207PBF 可以作为…

【LeetCode】【4】寻找两个正序数组的中位数(2105字)

文章目录 [toc]题目描述样例输入输出与解释样例1样例2 提示Python实现二分查找划分数组 个人主页&#xff1a;丷从心 系列专栏&#xff1a;LeetCode 刷题指南&#xff1a;LeetCode刷题指南 题目描述 给定两个大小分别为m和n的正序&#xff08;从小到大&#xff09;数组nums1…

2024年是不是转行AI产品经理的机会?

首先从一个公司的微观角度来谈谈这一年来公司对AI看法的转变。一年前自己在某大厂做了一件小事&#xff1a;在商家后端嵌入一个小功能&#xff1a;智能生成商品卖点描述&#xff0c;商品评价描述。那时候是一个边缘项目&#xff0c;我们对接的AI 底层团队基本没什么活儿可以接&…

Java面试八股之Synchronized和ReentrantLock的区别

Synchronized和ReentrantLock的区别 实现级别&#xff1a; synchronized是Java的一个关键字&#xff0c;属于JVM层面的原生支持&#xff0c;它通过监视器锁&#xff08;Monitor&#xff09;来实现同步控制&#xff0c;无需手动获取和释放锁。 ReentrantLock是java.util.conc…

本地centos7+docker+ollama+gpu部署

1、一台有 NVIDIA GPU 驱动的机器 2、Docker CE安装 # 删除旧版本的 Docker&#xff08;如果存在&#xff09; sudo yum remove -y docker docker-common docker-selinux docker-engine # 安装必要的软件包&#xff1a; sudo yum install -y yum-utils device-mapper-persiste…

更新web文件40秒后生效

服务器web服务使用的是nginx。 经测试&#xff0c;上传文件后大约40秒后生效。 更新文件不立即生效。 网上资料说根nginx中sendfile选项有关。 在nginx配置文件中&#xff0c;http区域里将sedfile设置为off&#xff0c;重启nginx服务。 谷歌浏览器强制刷新一次&#xff0c;…

docker中安装jenkins,并在node和cloud上跑通基于源码控制SCM的pipeline

目录 一、摘要 二、部署和使用 1. docker部署jenkins 1.1 准备数据目录 1.2 拉取jenkins镜像并启动 1.3 初始化配置 1.3.1 登录容器查看初始化密码 1.3.2 访问jenkins并输入初始化密码 1.3.3 创建管理员账户 1.3.4 初始化完成 2. jenkins使用之多分支流水线 2.1 准…

EI会议的录用通知和后续步骤是什么?

收到EI会议的录用通知后&#xff0c;通常会有一系列后续步骤&#xff0c;以下是一般的流程&#xff1a; 1. 录用通知 确认录用通知&#xff1a;在收到录用通知后&#xff0c;仔细阅读通知内容&#xff0c;确认你的论文已经被会议录用。查看详细信息&#xff1a;录用通知中通常…

WPF中DataGrid实现多选框功能

1. 效果图 2. Model建立 public class RstModelCheck : ObservableObject {//为了显示Head1和Head2.而且View中绑定属性而非字段&#xff0c;否则不能显示。public string? Name { get; set; } public bool PlatenAll {get > _platenAll;set{SetProperty(ref _platenAl…

Python实现将LabelMe生成的JSON格式转换成YOLOv8支持的TXT格式

标注工具 LabelMe 生成的标注文件为JSON格式&#xff0c;而YOLOv8中支持的为TXT文件格式。以下Python代码实现3个功能&#xff1a; 1.将JSON格式转换成TXT格式&#xff1b; 2.将数据集进行随机拆分&#xff0c;生成YOLOv8支持的目录结构&#xff1b; 3.生成YOLOv8支持的YAML文件…

基于EBAZ4205矿板的图像处理:07sobel边缘检测算法

基于EBAZ4205矿板的图像处理&#xff1a;07sobel边缘检测算法 项目文件 随后会上传项目全部文件&#xff0c;和之前一样免费下载 先看效果 如上所见&#xff0c;能够提取图像的边缘&#xff0c;这个sobel边缘检测算法的阈值&#xff08;认定是否为边缘的阈值&#xff09;一样…