RocketMQ源码 事务消息 TransactionalMessage 源码分析

前言

原理:使用普通消息和订单事务无法保证一致的原因,本质上是由于普通消息无法像单机数据库事务一样,具备提交、回滚和统一协调的能力。 而基于 RocketMQ 的分布式事务消息功能,在普通消息基础上,支持二阶段的提交能力。将二阶段提交和本地事务绑定,实现全局提交结果的一致性。

解决问题:事务消息,主要解决生产方和消费方的数据最终一致性问题。

实现方式:二阶段消息 + 反查机制

源码版本:4.9.3

源码架构图

源码解析

接下来,将会按照上面提到的事务消息原理和源码架构图中的顺序,进行源码实现分析。

1. 发送半事务消息

源码入口 org.apache.rocketmq.client.producer.TransactionMQProducer#sendMessageInTransaction(org.apache.rocketmq.common.message.Message, java.lang.Object)

    public TransactionSendResult sendMessageInTransaction(final Message msg,final Object arg) throws MQClientException {if (null == this.transactionListener) {throw new MQClientException("TransactionListener is null", null);}msg.setTopic(NamespaceUtil.wrapNamespace(this.getNamespace(), msg.getTopic()));return this.defaultMQProducerImpl.sendMessageInTransaction(msg, null, arg);}

接下来对消息添加PROPERTY_TRANSACTION_PREPARED半事务标记,发送事务消息,发送成功后执行本地事务。

   // 发送事务消息public TransactionSendResult sendMessageInTransaction(final Message msg,final LocalTransactionExecuter localTransactionExecuter, final Object arg)throws MQClientException {// 检查,事务监听器不能为空TransactionListener transactionListener = getCheckListener();if (null == localTransactionExecuter && null == transactionListener) {throw new MQClientException("tranExecutor is null", null);}// ignore DelayTimeLevel parameterif (msg.getDelayTimeLevel() != 0) {MessageAccessor.clearProperty(msg, MessageConst.PROPERTY_DELAY_TIME_LEVEL);}Validators.checkMessage(msg, this.defaultMQProducer);SendResult sendResult = null;// 添加事务消息标记,预处理事务消息,半half消息MessageAccessor.putProperty(msg, MessageConst.PROPERTY_TRANSACTION_PREPARED, "true");MessageAccessor.putProperty(msg, MessageConst.PROPERTY_PRODUCER_GROUP, this.defaultMQProducer.getProducerGroup());try {// 发送消息sendResult = this.send(msg);} catch (Exception e) {throw new MQClientException("send message Exception", e);}LocalTransactionState localTransactionState = LocalTransactionState.UNKNOW;Throwable localException = null;switch (sendResult.getSendStatus()) {case SEND_OK: {try {if (sendResult.getTransactionId() != null) {msg.putUserProperty("__transactionId__", sendResult.getTransactionId());}String transactionId = msg.getProperty(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX);if (null != transactionId && !"".equals(transactionId)) {msg.setTransactionId(transactionId);}if (null != localTransactionExecuter) {localTransactionState = localTransactionExecuter.executeLocalTransactionBranch(msg, arg);} else if (transactionListener != null) {log.debug("Used new transaction API");// 重要,发送消息到broker后,mq客户端内部执行本地事务localTransactionState = transactionListener.executeLocalTransaction(msg, arg);}if (null == localTransactionState) {localTransactionState = LocalTransactionState.UNKNOW;}if (localTransactionState != LocalTransactionState.COMMIT_MESSAGE) {log.info("executeLocalTransactionBranch return {}", localTransactionState);log.info(msg.toString());}} catch (Throwable e) {log.info("executeLocalTransactionBranch exception", e);log.info(msg.toString());localException = e;}}break;case FLUSH_DISK_TIMEOUT:case FLUSH_SLAVE_TIMEOUT:case SLAVE_NOT_AVAILABLE:localTransactionState = LocalTransactionState.ROLLBACK_MESSAGE;break;default:break;}try {this.endTransaction(msg, sendResult, localTransactionState, localException);} catch (Exception e) {log.warn("local transaction execute " + localTransactionState + ", but end broker transaction failed", e);}TransactionSendResult transactionSendResult = new TransactionSendResult();transactionSendResult.setSendStatus(sendResult.getSendStatus());transactionSendResult.setMessageQueue(sendResult.getMessageQueue());transactionSendResult.setMsgId(sendResult.getMsgId());transactionSendResult.setQueueOffset(sendResult.getQueueOffset());transactionSendResult.setTransactionId(sendResult.getTransactionId());transactionSendResult.setLocalTransactionState(localTransactionState);return transactionSendResult;}

... 接下来的发送流程和发送普通消息几乎一致,broker接受到事务消息后的处理流程也和普通消息几乎一致,不一致的地方在于,broker的ReputMessageService重分发消息服务线程,在后台读取 CommitLog 数据分发到 ConsumeQueue时,不会分发半Half事务消息和回滚事务消息对应的CommitLog。因为半事务消息,需要等待本地事务执行完,才能投递到消费者队列。

消费分发代码:org.apache.rocketmq.store.DefaultMessageStore.CommitLogDispatcherBuildConsumeQueue

    class CommitLogDispatcherBuildConsumeQueue implements CommitLogDispatcher {@Overridepublic void dispatch(DispatchRequest request) {// 事务类型final int tranType = MessageSysFlag.getTransactionValue(request.getSysFlag());switch (tranType) {// 非事务和提交事务,写入消息到消费队列case MessageSysFlag.TRANSACTION_NOT_TYPE:case MessageSysFlag.TRANSACTION_COMMIT_TYPE:DefaultMessageStore.this.putMessagePositionInfo(request);break;// 预处理事务和回滚事务,不做处理case MessageSysFlag.TRANSACTION_PREPARED_TYPE:case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE:break;}}}

2. 执行本地事务 与 提交本地事务状态

接着上面的发送消息往后看,发送事务消息成功后,就会执行本地事务。

org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl#sendMessageInTransaction

        switch (sendResult.getSendStatus()) {case SEND_OK: {try {if (sendResult.getTransactionId() != null) {msg.putUserProperty("__transactionId__", sendResult.getTransactionId());}String transactionId = msg.getProperty(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX);if (null != transactionId && !"".equals(transactionId)) {msg.setTransactionId(transactionId);}if (null != localTransactionExecuter) {localTransactionState = localTransactionExecuter.executeLocalTransactionBranch(msg, arg);} else if (transactionListener != null) {log.debug("Used new transaction API");// 重要,发送消息到broker后,mq客户端内部执行本地事务localTransactionState = transactionListener.executeLocalTransaction(msg, arg);}if (null == localTransactionState) {localTransactionState = LocalTransactionState.UNKNOW;}if (localTransactionState != LocalTransactionState.COMMIT_MESSAGE) {log.info("executeLocalTransactionBranch return {}", localTransactionState);log.info(msg.toString());}} catch (Throwable e) {log.info("executeLocalTransactionBranch exception", e);log.info(msg.toString());localException = e;}}break;case FLUSH_DISK_TIMEOUT:case FLUSH_SLAVE_TIMEOUT:case SLAVE_NOT_AVAILABLE:localTransactionState = LocalTransactionState.ROLLBACK_MESSAGE;break;default:break;}

这里使用的transactionListener 事务监听器,使我们在发起本地事务的代码里写的。如example工程中给的:

public class TransactionListenerImpl implements TransactionListener {private AtomicInteger transactionIndex = new AtomicInteger(0);private ConcurrentHashMap<String, Integer> localTrans = new ConcurrentHashMap<>();@Overridepublic LocalTransactionState executeLocalTransaction(Message msg, Object arg) {int value = transactionIndex.getAndIncrement();int status = value % 3;localTrans.put(msg.getTransactionId(), status);return LocalTransactionState.UNKNOW;}@Overridepublic LocalTransactionState checkLocalTransaction(MessageExt msg) {Integer status = localTrans.get(msg.getTransactionId());if (null != status) {switch (status) {case 0:return LocalTransactionState.UNKNOW;case 1:return LocalTransactionState.COMMIT_MESSAGE;case 2:return LocalTransactionState.ROLLBACK_MESSAGE;default:return LocalTransactionState.COMMIT_MESSAGE;}}return LocalTransactionState.COMMIT_MESSAGE;}
}

本地事务执行成功后会发送提交消息或回滚消息到broker master节点。

broker收到消息后,由EndTransactionProcessor 结束事务消息处理器进行处理。针对 CommitType类型消息,去除半事务消息标记,发送最终形态的消息,且删除原 CommitLog中的消息。针对 RollBackType类型的消息,删除原CommitLog中的消息。

org.apache.rocketmq.broker.processor.EndTransactionProcessor#processRequest

    @Overridepublic RemotingCommand processRequest(ChannelHandlerContext ctx, RemotingCommand request) throwsRemotingCommandException {// 封装响应final RemotingCommand response = RemotingCommand.createResponseCommand(null);// 提取请求头final EndTransactionRequestHeader requestHeader =(EndTransactionRequestHeader)request.decodeCommandCustomHeader(EndTransactionRequestHeader.class);LOGGER.debug("Transaction request:{}", requestHeader);// 处理结束事务消息的broker只能是master节点,否者直接返回if (BrokerRole.SLAVE == brokerController.getMessageStoreConfig().getBrokerRole()) {response.setCode(ResponseCode.SLAVE_NOT_AVAILABLE);LOGGER.warn("Message store is slave mode, so end transaction is forbidden. ");return response;}// 默认falseif (requestHeader.getFromTransactionCheck()) {switch (requestHeader.getCommitOrRollback()) {case MessageSysFlag.TRANSACTION_NOT_TYPE: {LOGGER.warn("Check producer[{}] transaction state, but it's pending status."+ "RequestHeader: {} Remark: {}",RemotingHelper.parseChannelRemoteAddr(ctx.channel()),requestHeader.toString(),request.getRemark());return null;}case MessageSysFlag.TRANSACTION_COMMIT_TYPE: {LOGGER.warn("Check producer[{}] transaction state, the producer commit the message."+ "RequestHeader: {} Remark: {}",RemotingHelper.parseChannelRemoteAddr(ctx.channel()),requestHeader.toString(),request.getRemark());break;}case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE: {LOGGER.warn("Check producer[{}] transaction state, the producer rollback the message."+ "RequestHeader: {} Remark: {}",RemotingHelper.parseChannelRemoteAddr(ctx.channel()),requestHeader.toString(),request.getRemark());break;}default:return null;}} else {// 分类处理switch (requestHeader.getCommitOrRollback()) {// 非事务类型,打印日志,返回nullcase MessageSysFlag.TRANSACTION_NOT_TYPE: {LOGGER.warn("The producer[{}] end transaction in sending message,  and it's pending status."+ "RequestHeader: {} Remark: {}",RemotingHelper.parseChannelRemoteAddr(ctx.channel()),requestHeader.toString(),request.getRemark());return null;}// 提交事物类型,break,继续处理case MessageSysFlag.TRANSACTION_COMMIT_TYPE: {break;}// 回滚事物类型,break,继续处理case MessageSysFlag.TRANSACTION_ROLLBACK_TYPE: {LOGGER.warn("The producer[{}] end transaction in sending message, rollback the message."+ "RequestHeader: {} Remark: {}",RemotingHelper.parseChannelRemoteAddr(ctx.channel()),requestHeader.toString(),request.getRemark());break;}default:return null;}}OperationResult result = new OperationResult();// 提交事物if (MessageSysFlag.TRANSACTION_COMMIT_TYPE == requestHeader.getCommitOrRollback()) {// 调用事物消息提交服务,实现是从消息存储组件中拿到预处理消息result = this.brokerController.getTransactionalMessageService().commitMessage(requestHeader);if (result.getResponseCode() == ResponseCode.SUCCESS) {// 检查预处理消息,封装命令协议RemotingCommand res = checkPrepareMessage(result.getPrepareMessage(), requestHeader);if (res.getCode() == ResponseCode.SUCCESS) {// 预处理消息转换为broker内部消息MessageExtBrokerInner msgInner = endMessageTransaction(result.getPrepareMessage());msgInner.setSysFlag(MessageSysFlag.resetTransactionValue(msgInner.getSysFlag(), requestHeader.getCommitOrRollback()));msgInner.setQueueOffset(requestHeader.getTranStateTableOffset());msgInner.setPreparedTransactionOffset(requestHeader.getCommitLogOffset());msgInner.setStoreTimestamp(result.getPrepareMessage().getStoreTimestamp());// 删除预处理事务消息标记MessageAccessor.clearProperty(msgInner, MessageConst.PROPERTY_TRANSACTION_PREPARED);// 发送最终消息RemotingCommand sendResult = sendFinalMessage(msgInner);if (sendResult.getCode() == ResponseCode.SUCCESS) {this.brokerController.getTransactionalMessageService().deletePrepareMessage(result.getPrepareMessage());}return sendResult;}return res;}}// 回滚事物else if (MessageSysFlag.TRANSACTION_ROLLBACK_TYPE == requestHeader.getCommitOrRollback()) {// 回滚消息,从消息存储组件中拿到预处理消息result = this.brokerController.getTransactionalMessageService().rollbackMessage(requestHeader);if (result.getResponseCode() == ResponseCode.SUCCESS) {// 检查消息,封装命令协议RemotingCommand res = checkPrepareMessage(result.getPrepareMessage(), requestHeader);if (res.getCode() == ResponseCode.SUCCESS) {// 删除预处理事务消息this.brokerController.getTransactionalMessageService().deletePrepareMessage(result.getPrepareMessage());}return res;}}response.setCode(result.getResponseCode());response.setRemark(result.getResponseRemark());return response;}

 

3. 未收到事务提交状态时,回查本地事务状态

可以看到源码架构图中,broker 内部有一个后台线程,TransactionalMessageCheckService 事务消息检查服务,它会定时扫描没有提交事物状态的事务,向 mqClient 发起检查本地事务状态请求。

org.apache.rocketmq.broker.processor.EndTransactionProcessor#processRequest

// 事务消息检查服务
public class TransactionalMessageCheckService extends ServiceThread {@Overrideprotected void onWaitEnd() {long timeout = brokerController.getBrokerConfig().getTransactionTimeOut(); // 6sint checkMax = brokerController.getBrokerConfig().getTransactionCheckMax(); // 15long begin = System.currentTimeMillis();log.info("Begin to check prepare message, begin time:{}", begin);this.brokerController.getTransactionalMessageService().check(timeout, checkMax, this.brokerController.getTransactionalMessageCheckListener());log.info("End to check prepare message, consumed time:{}", System.currentTimeMillis() - begin);}}

... 通过事务消息服务 TransactionalMessageService扫描到需要检查的事务后会调用 Broker2Client组件发送请求到mq客户端。

org.apache.rocketmq.broker.client.net.Broker2Client#checkProducerTransactionState

    // 检查生产者事务状态public void checkProducerTransactionState(final String group, // 生产者组final Channel channel, // 生产者长连接final CheckTransactionStateRequestHeader requestHeader, // 检查事务状态请求头final MessageExt messageExt // 消息扩展) throws Exception {// 构造检查事务状态请求RemotingCommand request =RemotingCommand.createRequestCommand(RequestCode.CHECK_TRANSACTION_STATE, requestHeader);request.setBody(MessageDecoder.encode(messageExt, false));try {// 通过网络通信服务器向生产者发送检查事务状态请求,单向发送this.brokerController.getRemotingServer().invokeOneway(channel, request, 10);} catch (Exception e) {log.error("Check transaction failed because invoke producer exception. group={}, msgId={}, error={}",group, messageExt.getMsgId(), e.toString());}}

mqClient接受请求

    @Overridepublic RemotingCommand processRequest(ChannelHandlerContext ctx,RemotingCommand request) throws RemotingCommandException {switch (request.getCode()) {// 检查事务状态case RequestCode.CHECK_TRANSACTION_STATE:return this.checkTransactionState(ctx, request);}

 接着调用生产者实例,执行检查 producer.checkTransactionState(addr, messageExt, requestHeader);

    public RemotingCommand checkTransactionState(ChannelHandlerContext ctx,RemotingCommand request) throws RemotingCommandException {// 解码检查事务状态请求头final CheckTransactionStateRequestHeader requestHeader =(CheckTransactionStateRequestHeader) request.decodeCommandCustomHeader(CheckTransactionStateRequestHeader.class);final ByteBuffer byteBuffer = ByteBuffer.wrap(request.getBody());// 解码消息体final MessageExt messageExt = MessageDecoder.decode(byteBuffer);if (messageExt != null) {if (StringUtils.isNotEmpty(this.mqClientFactory.getClientConfig().getNamespace())) {messageExt.setTopic(NamespaceUtil.withoutNamespace(messageExt.getTopic(), this.mqClientFactory.getClientConfig().getNamespace()));}String transactionId = messageExt.getProperty(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX);if (null != transactionId && !"".equals(transactionId)) {messageExt.setTransactionId(transactionId);}// 事务消息必须有生产分组名称final String group = messageExt.getProperty(MessageConst.PROPERTY_PRODUCER_GROUP);if (group != null) {// 获取生产者实例MQProducerInner producer = this.mqClientFactory.selectProducer(group);if (producer != null) {// 从netty网络通道中获取客户端地址final String addr = RemotingHelper.parseChannelRemoteAddr(ctx.channel());// 检查事务状态producer.checkTransactionState(addr, messageExt, requestHeader);} else {log.debug("checkTransactionState, pick producer by group[{}] failed", group);}} else {log.warn("checkTransactionState, pick producer group failed");}} else {log.warn("checkTransactionState, decode message failed");}return null;}

生产者实例在本地执行,我们发送事务消息时,设置的transactionCheckListener 事务检查监听器。然后将本地事务执行状态,重新提交到 broker(接下来的流程就和发送事务消息后,执行本地事务,提交本地事务状态的流程一样了)。

org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl#checkTransactionState

    // 检查事务状态,未收到half消息时,进行事务回查@Overridepublic void checkTransactionState(final String addr, final MessageExt msg,final CheckTransactionStateRequestHeader header) {// 封装一步任务,放进线程池异步执行Runnable request = new Runnable() {private final String brokerAddr = addr;private final MessageExt message = msg;private final CheckTransactionStateRequestHeader checkRequestHeader = header;private final String group = DefaultMQProducerImpl.this.defaultMQProducer.getProducerGroup();@Overridepublic void run() {TransactionCheckListener transactionCheckListener = DefaultMQProducerImpl.this.checkListener();TransactionListener transactionListener = getCheckListener();if (transactionCheckListener != null || transactionListener != null) {LocalTransactionState localTransactionState = LocalTransactionState.UNKNOW;Throwable exception = null;try {// 调用本地事务监听器,检查本地事务状态if (transactionCheckListener != null) {localTransactionState = transactionCheckListener.checkLocalTransactionState(message);} else if (transactionListener != null) {log.debug("Used new check API in transaction message");// 检查本地事务状态localTransactionState = transactionListener.checkLocalTransaction(message);} else {log.warn("CheckTransactionState, pick transactionListener by group[{}] failed", group);}} catch (Throwable e) {log.error("Broker call checkTransactionState, but checkLocalTransactionState exception", e);exception = e;}this.processTransactionState(localTransactionState,group,exception);} else {log.warn("CheckTransactionState, pick transactionCheckListener by group[{}] failed", group);}}// 处理事务状态private void processTransactionState(// 调用本地事务监听器,查询到的本地事务状态final LocalTransactionState localTransactionState,// 生产者分组final String producerGroup,// 异常final Throwable exception) {// 封装事务状态请求final EndTransactionRequestHeader thisHeader = new EndTransactionRequestHeader();thisHeader.setCommitLogOffset(checkRequestHeader.getCommitLogOffset());thisHeader.setProducerGroup(producerGroup);thisHeader.setTranStateTableOffset(checkRequestHeader.getTranStateTableOffset());thisHeader.setFromTransactionCheck(true);String uniqueKey = message.getProperties().get(MessageConst.PROPERTY_UNIQ_CLIENT_MESSAGE_ID_KEYIDX);if (uniqueKey == null) {uniqueKey = message.getMsgId();}thisHeader.setMsgId(uniqueKey);thisHeader.setTransactionId(checkRequestHeader.getTransactionId());switch (localTransactionState) {// 提交消息状态case COMMIT_MESSAGE:thisHeader.setCommitOrRollback(MessageSysFlag.TRANSACTION_COMMIT_TYPE);break;// 回滚消息状态case ROLLBACK_MESSAGE:thisHeader.setCommitOrRollback(MessageSysFlag.TRANSACTION_ROLLBACK_TYPE);log.warn("when broker check, client rollback this transaction, {}", thisHeader);break;// 未知消息状态case UNKNOW:thisHeader.setCommitOrRollback(MessageSysFlag.TRANSACTION_NOT_TYPE);log.warn("when broker check, client does not know this transaction state, {}", thisHeader);break;default:break;}String remark = null;if (exception != null) {remark = "checkLocalTransactionState Exception: " + RemotingHelper.exceptionSimpleDesc(exception);}// 执行钩子函数doExecuteEndTransactionHook(msg, uniqueKey, brokerAddr, localTransactionState, true);try {// 通过netty网络通信客户端组件,向broker发送事务状态请求,oneway模式,不需要感知发送结果DefaultMQProducerImpl.this.mQClientFactory.getMQClientAPIImpl().endTransactionOneway(brokerAddr, thisHeader, remark,3000);} catch (Exception e) {log.error("endTransactionOneway exception", e);}}};this.checkExecutor.submit(request);}

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

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

相关文章

像专家一样使用TypeScript映射类型

掌握TypeScript的映射类型&#xff0c;了解TypeScript内置的实用类型是如何工作的。 您是否使用过Partial、Required、Readonly和Pick实用程序类型? 你知道他们内部是怎么运作的吗? 如果您想彻底掌握它们并创建自己的实用程序类型&#xff0c;那么不要错过本文所涵盖的内容。…

LabVIEW开发自动读取指针式仪表测试系统

LabVIEW开发自动读取指针式仪表测试系统 在工业领域&#xff0c;尤其是煤矿、变电站和集气站等环境中&#xff0c;指针式仪表因其简单的结构、抗干扰能力强以及能适应高温高压等恶劣环境条件而被广泛应用于设备运行状态监视。然而&#xff0c;传统的人工读表方式不仅成本高昂&…

【AIGC工具】我找到了使用大模型问答的最短路径!

大家好&#xff0c;我是豆小匠~ 好久没介绍提高效率的工具啦&#xff0c;这次来介绍一个UTools的骚操作&#xff0c;可以极速打开LLM进行提问&#xff01; 完成后的效果是&#xff1a; 快捷键调出输入框&#xff1b;2. 输入问题&#xff1b;3. 选择模型&#xff1b;4. 回车提…

IDEA 中搭建 Spring Boot Maven 多模块项目 (父SpringBoot+子Maven)

第1步&#xff1a;新建一个SpringBoot 项目 作为 父工程 [Ref] 新建一个SpringBoot项目 删除无用的 .mvn 目录、 src 目录、 mvnw 及 mvnw.cmd 文件&#xff0c;最终只留 .gitignore 和 pom.xml 第2步&#xff1a;创建 子maven模块 第3步&#xff1a;整理 父 pom 文件 ① …

【VTKExamples::Visualization】第一期 Arbitrary3DCursor

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ&#xff1a;870202403 前言 本文分享Example中Visualization模块中的Arbitrary3DCursor样例&#xff0c;主要解析vtkProbefileter&#xff0c;希望对各位小伙伴有所帮助&#xff01; 感谢各位小伙伴的点赞关注&#xff0c;小易会…

【Docker基础二】Docker安装Mysql8

下载镜像 安装mysql&#xff08;版本&#xff1a;8.0.35&#xff09; # 拉取镜像 docker pull mysql:8.0.35 # 查看镜像是否已经下载 docker images 创建挂载目录 # 宿主机上创建挂载目录 (可以不创建&#xff0c;docker run -v配置了挂载目录&#xff0c;docker会自动…

Priors in Deep Image Restoration and Enhancement: A Survey

深度图像恢复和增强中的先验&#xff1a;综述 论文链接&#xff1a;https://arxiv.org/abs/2206.02070 项目链接&#xff1a;https://github.com/VLIS2022/Awesome-Image-Prior (Preprint. Under review) Abstract 图像恢复和增强是通过消除诸如噪声、模糊和分辨率退化等退化…

【python入门】day21:向文件输出“奋斗成就更好的你”、输出北京的天气预报

向文件输出“奋斗成就更好的你” #向文件输出‘奋斗成就更好的你’ 第一种方式&#xff1a;使用print方式进行输出&#xff08;输出目的地是文件&#xff09; fpopen(e:/text.txt,w)#w只写模式&#xff0c;也可以用a读写模式 print(奋斗成就更好的你,filefp) fp.close() 第二种…

python统计分析——箱线图(plt.boxplot)

参考资料&#xff1a;用python动手学统计学 使用matplotlib.pyplot.boxplot()函数绘制箱线图 import numpy as np import pandas as pd from matplotlib import pyplot as pltdata_set1np.array([2,3,3,4,4,4,4,5,5,6]) data_set2np.array([[2,3,3,4,4,4,4,5,5,6],[5,6,6,7,7…

Linux程序、进程和计划任务

目录 一.程序和进程 1.程序的概念 2.进程的概念 3.线程的概念 4.单线程与多线程 5.进程的状态 二.查看进程信息相关命令&#xff1a; 1.ps&#xff1a;查看静态进程信息状态 2.top&#xff1a;查看动态进程排名信息 3.pgrep&#xff1a;查看指定进程 4.pstree&#…

docker 安装elasticsearch、kibana、cerebro、logstash

安装步骤 第一步安装 docker 第二步 拉取elasticsearch、kibana、cerebro、logstash 镜像 docker pull docker.elastic.co/elasticsearch/elasticsearch:7.10.2 docker pull docker.elastic.co/kibana/kibana:7.10.2 docker pull lmenezes/cerebro:latest docker pull l…

Ps:混合颜色带 - 应用篇

混合颜色带 Blend If是基于亮度&#xff08;灰色&#xff09;或颜色通道的特定范围来显示或隐藏图层的特定区域。 当前图层 Current Layer&#xff0c;可根据当前图层的亮度值来隐藏该图层中的像素。 下一图层 Underlying Layer&#xff0c;可根据下方所有图层的复合图像的亮度…

单向可控硅充电电路图

单向可控硅工作原理 单向可控硅有阳极A、阴极K、控制极G三个电极&#xff0c;由四层半导体PNPN构成。单向可控硅有三个PN结&#xff0c;其内部结构与等效电路符号如图4-10所示。单相可控硅可等效看成一个PNP型三极管Vl和一个NPN型三极管V2组合而成&#xff0c;Vl基极和V2集电极…

电子学会C/C++编程等级考试2023年12月(三级)真题解析

C/C++编程(1~8级)全部真题・点这里 第1题:因子问题 任给两个正整数N、M,求一个最小的正整数a,使得a和(M-a)都是N的因子。 时间限制:10000 内存限制:65536 输入 包括两个整数N、M。N不超过1,000,000。 输出 输出一个整数a,表示结果。如果某个案例中满足条件的正整数不存…

MSCKF+OpenVins梳理

reference&#xff1a; openvins学习中的问题https://zhuanlan.zhihu.com/p/355319559 OpenVins代码梳理https://www.zhihu.com/people/anson2004110/posts OpenVINS能观一致性分析和FEJhttps://zhuanlan.zhihu.com/p/101478814 MSCKF那些事https://zhuanlan.zhihu.com/p/76894…

力扣labuladong一刷day54天前缀树

力扣labuladong一刷day54天前缀树 文章目录 力扣labuladong一刷day54天前缀树一、208. 实现 Trie (前缀树)二、648. 单词替换三、211. 添加与搜索单词 - 数据结构设计四、1804. 实现 Trie &#xff08;前缀树&#xff09; II五、677. 键值映射 一、208. 实现 Trie (前缀树) 题…

一键了解获取网页requests方式

目录 一、爬虫原理&#xff1a; 二、安装&#xff1a; 测试&#xff1a; 三、文件的操作 方式一 方式二: 方式三 四、认识User-Agent 4.1、为什么用User-Agent&#xff1a; 步骤&#xff1a; 五、请求方式 5.1、get 5.2、post 六、爬出有中国关键字页面案例 一、爬…

win10下vscode+cmake编译C代码操作详解

0 工具准备 1.Visual Studio Code 1.85.1 2.cmake 3.24.01 前言 当我们只有一个.c文件时直接使用vscodeCode Runner插件即可完成编译&#xff0c;如果我们的工程很复杂包含多个.c文件时建议使用cmake来生成对应的make&#xff0c;指导编译器完成编译&#xff0c;否则会提示各…

强化学习的数学原理学习笔记 - 基于模型(Model-based)

文章目录 概览&#xff1a;RL方法分类基于模型&#xff08;Model-Based&#xff09;值迭代&#xff08;Value Iteration&#xff09;&#x1f7e6;策略迭代&#xff08;Policy Iteration&#xff09;&#x1f7e1;截断策略迭代&#xff08;Truncated Policy Iteration&#xff…

YOLOv5改进 | 损失函数篇 | EIoU、SIoU、WIoU、DIoU、FocusIoU等二十余种损失函数

一、本文介绍 这篇文章介绍了YOLOv5的重大改进,特别是在损失函数方面的创新。它不仅包括了多种IoU损失函数的改进和变体,如SIoU、WIoU、GIoU、DIoU、EIOU、CIoU,还融合了“Focus”思想,创造了一系列新的损失函数。这些组合形式的损失函数超过了二十余种,每种都针对特定的…