RabbitMQ发布确认和消息回退(6)

概念

发布确认原理

生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布的消息都将会被指派一个唯一的 ID(从 1 开始),一旦消息被投递到所有匹配的队列之后,broker就会发送一个确认给生产者(包含消息的唯一 ID),这就使得生产者知道消息已经正确到达目的队列了,如果消息和队列是可持久化的,那么确认消息会在将消息写入磁盘之后发出,broker 回传给生产者的确认消息中 delivery-tag 域包含了确认消息的序列号,此外 broker 也可以设置basic.ack 的 multiple 域,表示到这个序列号之前的所有消息都已经得到了处理。

confirm 模式最大的好处在于他是异步的,一旦发布一条消息,生产者应用程序就可以在等信道返回确认的同时继续发送下一条消息,当消息最终得到确认之后,生产者应用便可以通过回调方法来处理该确认消息,如果 RabbitMQ 因为自身内部错误导致消息丢失,就会发送一条 nack 消息,生产者应用程序同样可以在回调方法中处理该 nack 消息。

两种模式

1.简单确认模式(Simple Publisher Confirm)

在简单确认模式下,每次生产者发送一条消息到 RabbitMQ,都会立即等待 RabbitMQ 返回一个确认消息。如果消息成功发送到 RabbitMQ 服务器上的交换机,并且至少一个队列接收到了消息,RabbitMQ 就会返回一个确认消息给生产者。否则,如果消息发送失败,则会返回一个 Nack 消息。在这种模式下,生产者可以针对每一条消息都进行确认处理,确保消息是否被正确地发送到了 RabbitMQ。

3.批量确认模式(Publisher Confirm with Batch)

在批量确认模式下,生产者可以将一批消息发送到 RabbitMQ,然后等待一段时间后再收到确认消息。这样可以降低每条消息发送的确认成本,并提高性能。批量确认模式通过指定一个大小来定义批量确认的数量,当达到指定的数量后,RabbitMQ 会一次性发送确认消息给生产者。这种模式适用于需要发送大量消息的场景,可以减少确认消息的数量,提高消息发送的效率。

这两种发布确认模式在实现上有一些不同,可以根据实际的业务需求和性能要求来选择合适的模式。简单确认模式更适用于需要对每一条消息进行实时确认的场景,而批量确认模式适用于需要发送大量消息并且希望降低确认消息成本的场景。

开启方法

-- 确认模式配置

spring.rabbitmq.publisher-confirm-type: correlated

  • NONE

禁用发布确认模式,是默认值。

  • CORRELATED

发布消息成功到交换器后会触发回调方法。

  • SIMPLE

测试有两种效果,其一效果和 CORRELATED 值一样会触发回调方法,其二在发布消息成功后使用 rabbitTemplate 调用 waitForConfirms 或 waitForConfirmsOrDie 方法等待 broker 节点返回发送结果,根据返回结果来判定下一步的逻辑,要注意的点是 waitForConfirmsOrDie 方法如果返回 false 则会关闭 channel,则接下来无法发送消息到 broker。(相当于单一发布)

spring:# 配置RabbitMQrabbitmq:host: 192.168.0.70port: 5674username: guestpassword: guest# 虚拟主机virtual-host: my_vhost# 开启确认模式publisher-confirm-type: correlated

 测试

为了方便测试,此机制单独编写类去测试,使用的模式为路由模式(其他模式也可以)

1.创建回调函数

package com.model.callback;import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;/*** @Author: Haiven* @Time: 2024/4/22 17:26* @Description: TODO*/
@Component
@Slf4j
public class MyConfirmCallBack implements RabbitTemplate.ConfirmCallback {/*** 被调用的回调方法* @param correlationData 相关配置信息* @param ack 交换机是否成功收到消息 可以根据 ack 做相关的业务逻辑处理* @param cause 失败原因*/@Overridepublic void confirm(CorrelationData correlationData, boolean ack, String cause) {System.out.println("消息推送回调:配置信息="+correlationData+";结果="+ack+"失败原因="+cause);if(ack){//消息推送成功System.out.println("消息推送成功");}else{System.out.println("消息推送失败:" + cause);}}
}

回调函数会在消息推送到队列后调用

 2.配置回调函数

将上述回调函数设置到mq的配置中,这里再RabbitmqConfig文件中配置

package com.model.config;import com.model.callback.MyConfirmCallBack;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.annotation.Resource;/*** @Author: Haiven* @Time: 2024/4/18 17:28* @Description: TODO*/
@Configuration
public class RabbitmqConfig {@Value("${rabbitmq.work.queue}")private String workQueue;@Resourceprivate MyConfirmCallBack myConfirmCallBack;/*** 工作模式的队列* @return 队列*/@Bean(name = "workQueue")public Queue getWorkQueue(){return QueueBuilder.durable(workQueue).build();}@Beanpublic RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory) {RabbitTemplate rabbitTemplate = new RabbitTemplate();rabbitTemplate.setConnectionFactory(connectionFactory);// 设置开启 Mandatory 强制执行调用回调函数rabbitTemplate.setMandatory(true);//设置回调rabbitTemplate.setConfirmCallback(myConfirmCallBack);//设置回退回调return rabbitTemplate;}}

 3.创建交换机和队列

这里创建ConfirmConfig配置文件与其他队列进行区分

package com.model.config;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @Author: Haiven* @Time: 2024/4/22 17:22* @Description: TODO*/
@Configuration
public class ConfirmConfig {/*** 测试发布确认的交换机* @return exchange*/@Bean(name = "confirmExchange")public Exchange getConfirmExchange(){return ExchangeBuilder.directExchange("exchange_confirm").build();}/*** 队列* @return queue*/@Bean(name = "confirmQueue")public Queue getConfirmQueue(){return QueueBuilder.durable("queue_confirm").build();}/*** 绑定队列* @return binding*/@Beanpublic Binding getConfirmBinding(){return BindingBuilder.bind(getConfirmQueue()).to(getConfirmExchange())//路由键 队列1接收debug级别的消息.with("confirm").noargs();}}

 4.创建消费者

package com.model.listener;import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;/*** @Author: Haiven* @Time: 2024/4/22 17:31* @Description: TODO*/
@Component
public class ConfirmConsumer {@RabbitListener(queues = {"queue_confirm"})public void routingConfirm(String msg){System.out.println("消费者 -confirm- 接收消息:" + msg);}
}

 5.发送并接收消息

package com.model.controller;import com.code.domain.Response;
import com.model.service.RabbitService;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** @Author: Haiven* @Time: 2024/4/19 9:46* @Description: TODO*/
@RestController
@RequestMapping("/producer")
public class ProducerController {@Resourceprivate RabbitService rabbitService;@GetMapping("/simple")public Response<Void> simple(String msg){boolean res = rabbitService.simple(msg);return res ? Response.success() : Response.fail();}@GetMapping("/work")public Response<Void> work(String msg){boolean res = rabbitService.work(msg);return res ? Response.success() : Response.fail();}@GetMapping("/sub")public Response<Void> sub(String msg){boolean res = rabbitService.sub(msg);return res ? Response.success() : Response.fail();}@GetMapping("/routing")public Response<Void> routing(String msg, String type){boolean res = rabbitService.routing(msg, type);return res ? Response.success() : Response.fail();}@GetMapping("/topic")public Response<Void> topic(String msg, String type){boolean res = rabbitService.topic(msg, type);return res ? Response.success() : Response.fail();}@GetMapping("/confirm")public Response<Void> confirm(String msg, String type){boolean res = rabbitService.confirm(msg, type);return res ? Response.success() : Response.fail();}
}
package com.model.service.impl;import com.model.service.RabbitService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** @Author: Haiven* @Time: 2024/4/19 10:51* @Description: TODO*/
@Service
@Slf4j
public class RabbitServiceImpl implements RabbitService {@Resourceprivate RabbitTemplate rabbitTemplate;@Value("${rabbitmq.simple.queue}")private String simpleQueue;@Value("${rabbitmq.work.queue}")private String workQueue;@Overridepublic boolean simple(String msg) {try {rabbitTemplate.convertAndSend(simpleQueue, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean work(String msg) {try {rabbitTemplate.convertAndSend(workQueue, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean sub(String msg) {try {//路由模式就不能直接发送消息到队列了, 而是发送到交换机,由交换机进行广播, routingKey为路由Key 订阅模式给""rabbitTemplate.convertAndSend("exchange_sub","", msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean routing(String msg, String type) {System.out.println("理由模式发送消息:msg="+msg+",type="+type+"");try {//路由模式就不能直接发送消息到队列了, 而是发送到交换机,由交换机进行广播, routingKey为路由Key 订阅模式给""rabbitTemplate.convertAndSend("exchange_routing",type, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean topic(String msg, String type) {System.out.println("主题模式发送消息:msg="+msg+",type="+type+"");try {//主题模式会根据 type的通配符进行分发rabbitTemplate.convertAndSend("exchange_topic",type, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean confirm(String msg, String type) {System.out.println("发布确认模式发送消息:msg="+msg+",type="+type+"");try {rabbitTemplate.convertAndSend("exchange_confirm",type, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}
}

 发送消息

 接收消息

 发送成功后回调函数会执行,即使消费者没有消费该消息,回调函数仍然会执行

 消息回退

当第二条消息推送后消费者是没有消费消息的,虽然推送成功,但是却被丢弃了,而此时生产者需要知道此消息是否被消费成功,所有就使用到了消息回退机制

spring.rabbitmq.publisher-returns=true

1.设置回调函数

MyReturnCallBack

package com.model.callback;import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;/*** @Author: Haiven* @Time: 2024/4/23 10:16* @Description: TODO*/
@Component
@Slf4j
public class MyReturnCallBack implements RabbitTemplate.ReturnCallback {/**** @param msg 消息对象* @param errCode 错误码* @param errMsg 错误信息* @param exchange 交换机* @param rout 路由键*/@Overridepublic void returnedMessage(Message msg, int errCode, String errMsg, String exchange, String rout) {log.debug("消息对象={},错误码={},错误消息={},交换机={},路由键={}", msg, errCode, errMsg, exchange, rout);}
}

 此函数会在消息被丢弃或者消费失败后回调

2.设置到配置中

在RabbitmqConfig配置文件设置

package com.model.config;import com.model.callback.MyConfirmCallBack;
import com.model.callback.MyReturnCallBack;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.annotation.Resource;/*** @Author: Haiven* @Time: 2024/4/18 17:28* @Description: TODO*/
@Configuration
public class RabbitmqConfig {@Value("${rabbitmq.work.queue}")private String workQueue;@Resourceprivate MyConfirmCallBack myConfirmCallBack;@Resourceprivate MyReturnCallBack myReturnCallBack;/*** 工作模式的队列* @return 队列*/@Bean(name = "workQueue")public Queue getWorkQueue(){return QueueBuilder.durable(workQueue).build();}@Beanpublic RabbitTemplate createRabbitTemplate(ConnectionFactory connectionFactory) {RabbitTemplate rabbitTemplate = new RabbitTemplate();rabbitTemplate.setConnectionFactory(connectionFactory);// 设置开启 Mandatory 强制执行调用回调函数rabbitTemplate.setMandatory(true);//设置发布确认rabbitTemplate.setConfirmCallback(myConfirmCallBack);//设置消息回退rabbitTemplate.setReturnCallback(myReturnCallBack);//设置回退回调return rabbitTemplate;}}

先注入,在设置到rabbitTemplate对象中,这样发送消息时就可以回调

3.发送并接收消息

此处使用confirm交换机测试

发送一条没有路由的消息

 

 此时消息推送成功,但是没有被消费者消费,而是被丢弃,所有消息回退的回调函数执行:

消息对象=(

        Body:'消息',

        MessageProperties : [

                headers={},

                contentType=text/plain,

                contentEncoding=UTF-8,                                           

                contentLength=0,

                receivedDeliveryMode=PERSISTENT,

                priority=0, deliveryTag=0])

错误码=312

错误消息=NO_ROUTE

交换机=exchange_confirm

路由键=unknown

 发送一条路由的消息

 消息推送到交换机成功,并被成功消费,回退消息的回调函数未执行

备份交换机

有了发布确认和回退消息,我们获得了对无法投递消息的感知能力,在生产者的消息无法被投递时发现并处理。但有时候,我们并不知道该如何处理这些无法路由的消息,最多打个日志,然后触发报警,再来手动处理。而通过日志来处理这些无法路由的消息是很不优雅的做法,特别是当生产者所在的服务有多台机器的时候,手动复制日志会更加麻烦而且容易出错。而且设置参数会增加生产者的复杂性,需要添加处理这些被退回的消息的逻辑。如果既不想丢失消息,又不想增加生产者的复杂性,就可以使用备份交换机

 备份交换机可以理解为 RabbitMQ 中交换机的“备份”,当我们为某一个交换机声明一个对应的备份交换机时,就是为它创建一个 备份,当交换机接收到一条不可路由消息时,将会把这条消息转发到备份交换机中,由备份交换机来进行转发和处理,通常备份交换机的类型为 Fanout ,这样就能把所有消息都投递到与其绑定的队列中,然后我们在备份交换机下绑定一个队列,这样所有那些原交换机无法被路由的消息,就会都进 入这个队列了。当然,我们还可以建立一个报警队列,用独立的消费者来进行监测和报警。

1.创建备份交换机

为了方便区分,这里新建一个配置类ConfirmBackupConfig,用于创建虚拟机和队列,备份交换机的类型一定要为fanout

package com.model.config;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.annotation.Order;/*** @Author: Haiven* @Time: 2024/4/23 15:07* @Description: TODO*/
@Configuration
public class ConfirmBackupConfig {/*** exchange_confirm 交换机的备份交换机* @return exchange*/@Bean(name = "confirmBackupExchange")public Exchange getConfirmBackupExchange(){return ExchangeBuilder.fanoutExchange("exchange_confirm_backup").build();}@Bean("confirmBackupQueue")public Queue getConfirmBackupQueue(){return QueueBuilder.durable("queue_confirm_backup").build();}@Bean("confirmBackupBinding")public Binding getConfirmBackupBinding(){return BindingBuilder.bind(getConfirmBackupQueue()).to(getConfirmBackupExchange()).with("").noargs();}
}

 2.绑定备份交换机

这里我们用上面的消息回退测试用的交换机进行测试,就不额外再创建了,在ConfirmConfig配置文件中直接绑定:

.withArgument("alternate-exchange", "exchange_confirm_backup")

在创建交换机的时候直接指定 alternate-exchange,exchange_confirm_backup为备份交换的名称

package com.model.config;import org.springframework.amqp.core.*;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;/*** @Author: Haiven* @Time: 2024/4/22 17:22* @Description: TODO*/
@Configuration
public class ConfirmConfig {/*** 测试发布确认的交换机* @return exchange*/@Bean(name = "confirmExchange")public Exchange getConfirmExchange(){return ExchangeBuilder.directExchange("exchange_confirm").withArgument("alternate-exchange", "exchange_confirm_backup").durable(true).build();}/*** 队列* @return queue*/@Bean(name = "confirmQueue")public Queue getConfirmQueue(){return QueueBuilder.durable("queue_confirm").build();}/*** 绑定队列* @return binding*/@Beanpublic Binding getConfirmBinding(){return BindingBuilder.bind(getConfirmQueue()).to(getConfirmExchange())//路由键 队列1接收debug级别的消息.with("confirm").noargs();}}

 由于之前创建exchange_confirm交换机的时候没有指定备份交换机,所以这里要先将该交换机删掉,然后重新创建,备份交换机一定要在创建的时候指定

 进入控制台删除,不想删可创建新的交换机用于测试

 3.备份交换机消费者

直接在ConfirmConsumer配置文件中声明confirmBackupConsumer

package com.model.listener;import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;/*** @Author: Haiven* @Time: 2024/4/22 17:31* @Description: TODO*/
@Component
@Slf4j
public class ConfirmConsumer {@RabbitListener(queues = {"queue_confirm"})public void confirmConsumer(String msg){System.out.println("消费者 -confirm- 接收消息:" + msg);}@RabbitListener(queues = {"queue_confirm_backup"})public void confirmBackupConsumer(String msg){log.debug("消费者 -- 备份队列 -- 接收消息:" + msg);}
}

4.发送消息测试

发送一条没有路由的消息

1.可以看到发布确认的消息回调执行,说明已经推送到交换机

2.但是消息回退的回调没有执行,但该消息没有被confirm的消费者消费

3.该消息被备份交换机的消费者消费

有了备份交换机,消息如果消费失败,消息不会回退,而会被备份队列的消费者接收

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

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

相关文章

qt实现方框调整

效果 在四周调整 代码 #ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QWidget>class MainWindow : public QWidget {Q_OBJECT public:explicit MainWindow(QWidget *parent 0);~MainWindow();void paintEvent(QPaintEvent *event);void updateRect();void re…

Restful API 具体设计规范(概述)

协议 https 域名 https://www.baidu.com/api 版本 https://www.baidu.com/v1 路径 https://www.baidu.com/v1/blogs 方法 数据过滤 状态码返回结果 返回的数据格式 尽量使用 JSON&#xff0c;避免使用 XML。 总结&#xff1a; 看 url 就知道要什么看 http method 就知道干…

Linux进阶篇:CentOS7搭建NFS文件共享服务

CentOS7搭建NFS文件共享服务 一、NFS介绍 NFS(Network File System)意为网络文件系统&#xff0c;它最大的功能就是可以通过网络&#xff0c;让不同的机器不同的操作系统可以共享彼此的文件。简单的讲就是可以挂载远程主机的共享目录到本地&#xff0c;就像操作本地磁盘一样&…

Docker——数据管理和网络通信

目录 一、Docker的数据管理 1.数据卷 2.数据卷容器 3.容器互联 二、Docker镜像的创建 1.基于现有镜像创建 2.基于本地模板创建 3.基于Dockerfile 创建 3.1联合文件系统&#xff08;UnionFS&#xff09; 3.2镜像加载原理 3.3为什么Docker里的Centos大小才200M 4.Dcok…

9【PS作图】像素画Tips

放大缩小 “窗口”-排列-为…画布新建窗口&#xff0c;就可以新建一个窗口&#xff0c;实时看作图效果 如果要保持放大或缩小的像素画仍然保持硬边缘&#xff0c;需要设置两个东西 将 编辑 > 首选项 > 常规 中的 插值方式 改为 “邻近&#xff08;靠近硬边缘&#xff09…

HTML网页自动播放背景音乐和全屏背景图代码

HTML网页自动播放背景音乐的代码 背景音乐代码及分析代码的应用背景图代码及分析下期更新预报 背景音乐代码及分析 能使网站上自动循环的背景音乐代码如下&#xff1a; <audio src"music.mid" autostart"true" loop"true" hidden"true…

【小梦C嘎嘎——启航篇】C++四大类型转换

&#x1f60e; 前言&#x1f64c;C四大类型转换什么是类型转换C语言中的类型转换为什么C要嫌弃C语言的类型转换&#xff1f;自行搞一套呢&#xff1f;C强制类型转换1、static_cast2、reinterpret_cast3、const_cast4、dynamic_cast为什么要支持向下转呢&#xff1f; RTTI 总结撒…

RabbitMQ工作模式(4) - 路由模式

概念 路由模式&#xff08;Routing&#xff09;是 RabbitMQ 中的一种消息传递模式&#xff0c;也称为直连模式。它允许生产者将消息发送到一个交换机&#xff0c;并指定一个或多个路由键&#xff08;routing key&#xff09;&#xff0c;交换机根据路由键将消息路由到与之匹配的…

实验7:路由冗余协议HSRP配置管理(课内实验以及解答)

实验目的及要求&#xff1a; 理解首跳冗余协议&#xff08;FHRP&#xff09;的工作原理&#xff0c;掌握热备份路由器协议 (HSRP)&#xff08;思科私有协议&#xff09;原理和配置。能够实现网络终端设备虚拟网关的配置和网络故障的灵活切换&#xff0c;完成相应网络的联通性测…

多模态大语言模型综述

去年以来&#xff0c;我们见证了以 GPT-4V 为代表的多模态大语言模型(Multimodal Large Language Model&#xff0c;MLLM)的飞速发展。为此我们对综述进行了重大升级&#xff0c;帮助大家全面了解该领域的发展现状以及潜在的发展方向。 MLLM 发展脉络图 MLLM 脱胎于近年来广受…

【Redis 开发】缓存雪崩和缓存击穿

缓存问题 缓存雪崩解决方案 缓存击穿互斥锁逻辑时间基于互斥锁解决缓存击穿问题基于逻辑过期方式解决缓存击穿问题 缓存雪崩 缓存雪崩是指在同一时间段&#xff0c;大量的缓存key同时失效或者Redis服务器宕机&#xff0c;导致大量请求到达数据库&#xff0c;带来巨大压力 解决…

Qt | QAbstractButton 抽象类

QAbstractButton 类中的属性 ①、autoExclusive:bool 访问函数:bool autoExclusive() const; void setAutoExclusive(bool); 描述了按钮的自动排他性,若启用了该属性,则属于同一父部件的可选中按钮的行为, 就好像是在同一排他性组中的按钮一样。除了单选按钮,默认为关…

【网络编程】TCP流套接字编程 | Socket类 | ServerSocket类 | 文件资源泄露 | TCP回显服务器 | 网络编程

文章目录 TCP流套接字编程1.ServerSocket类2.Socket类3.文件资源泄露4.**TCP回显服务器** TCP流套接字编程 ​ ServerSocket类和Socket类这两个类都是用来表示socket文件&#xff08;抽象了网卡这样的硬件设备&#xff09;。 TCP是面向字节流的&#xff0c;传输的基本单位是b…

Facebook的未知力量:数字世界的新引擎

在数字化的时代&#xff0c;社交媒体已经成为了我们日常生活中不可或缺的一部分&#xff0c;而Facebook作为其中的巨头&#xff0c;其影响力远远超出了我们的想象。但是&#xff0c;Facebook背后隐藏的力量和影响远不止于此&#xff0c;它正逐渐成为数字世界的新引擎&#xff0…

python 使用flask_httpauth和pyjwt实现登录权限控制

最近需要用到&#xff0c;学习了一下记录 首先安装依赖 pip install Flask-HTTPAuth pyjwt passlib Welcome to Flask-HTTPAuth’s documentation! — Flask-HTTPAuth documentation Welcome to PyJWT — PyJWT 2.8.0 documentation Passlib 1.7.4 documentation — Passl…

Unreal Engine子类化系统UButton

UE系统Button点击事件无法传递参数&#xff0c;通过子类化系统Button添加自定义参数扩展实现Button点击事件参数传递点击C类文件夹&#xff0c;在右边的区域点击鼠标右键&#xff0c;在弹出的菜单中选择“新建C类”在弹出的菜单中选中“显示所有类”&#xff0c;选择Button作为…

Docker从无到有

主要为windows下docker的安装与使用~ 初始Docker Docker理解 对于docker的加简介&#xff0c;我们可以官网获取它的概念&#xff0c;接下来就从什么是docker、为什么要使用docker以及它的作用来进行一个快速入门 前提&#xff1a;项目在发布时&#xff0c;不仅需要其jar包同…

FSMC读取FPGA的FIFO

一、硬件说明 FSMC配置 单片机的代码如下&#xff1a; #define VALUE_ADDRESS_AD1 (__IO uint16_t *)0x60400000while (1){if(!HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_8)) //数据非空{data *(__IO uint16_t *)VALUE_ADDRESS_AD1;data2 *(__IO uint16_t *)VALUE_ADDRESS_AD1…

英伟达助力日本量子技术创新战略!合作打造量子超级计算机 ABCI-Q

内容来源&#xff1a;量子前哨&#xff08;ID&#xff1a;Qforepost&#xff09; 文丨浪味仙 排版丨沛贤 深度好文&#xff1a;1000字丨5分钟阅读 摘要&#xff1a;日本将在英伟达的AI和HPC基础设施的帮助下&#xff0c;通过大规模开发&#xff0c;在量子计算和人工智能领域取…

xfce4 panel 不能显示QQ,钉钉的状态图标

有一段时间不能显示了&#xff0c;之前刚装完系统的时候很长时间内都是好的&#xff0c;所以刚开始肯定是支持显示这些状态图标的。就是因为不能显示的原因&#xff0c;所以还装了lxQt桌面&#xff0c;这个桌面确实不错。不过还是有时会怀念xfce4&#xff0c;想看看能不能解决这…