从零开始读RocketMq源码(五)Consumer消费Message流程解析

目录

前言

准备

拉取服务和重平衡服务启动

初识PullRequest

重平衡服务

对重平衡资源进行排序

MessageQueue消息队列集合来源

Consumer消费者集合数据来源

确实分配资源策略

执行分配策略

初始化ProcessQueue

初始化PullRequest

内存队列填充PullRequest

消息拉取服务

调用消息核心处理方法

调用远程Broker服务拉取消息

提交消费消息请求

初始化ConsumeRequest

消息监听器消费消息

总结


前言

上一篇我们对Consumer的启动流程就进行了解析,有了消费者那么消费的Message从何而来呢,这就是本篇学习的重点。本篇会讲到MessageQueue的分配、Message的拉取以及消费等,让我们一起来学习吧!

准备

源码地址:https://github.com/apache/rocketmq

目前最新版本为:5.2.0

那么我们在idea上切换分支为 release-5.2.0

拉取服务和重平衡服务启动

//源码位置
//包名:org.apache.rocketmq.client.impl.factory
//文件名:MQClientInstance
//行数:315
// Start pull service
this.pullMessageService.start();
// Start rebalance service
this.rebalanceService.start();
  • 拉取服务重平衡服务的启动其实就是在上一篇《从零开始读RocketMq源码(四)Consumer启动流程解析》中就已经执行了
  • 这里就是触发ConsumerBroker获取Message的源头,进入启动源码会发现其实也就是分别开启了两个独立的线程来运行的。消息的获取也是需要这两个线程相互合作才能完成。

为什么我们消息选择的是Push推模式但是这里服务启动的却是PullMessageService呢?

因为实际上,推模式下的实现还是基于消费者主动拉取的方式,推模式是通过一个长轮询的机制来实现的 , 消费者向 Broker 注册一个消息监听器,消费者内部维护一个线程,不断向 Broker 拉取消息,如果没有消息,则保持连接并等待消息到来。(下面会详细讲到)

初识PullRequest

我们按照启动顺序先对pullMessageService源码进行了解,进入线程的run()方法中

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:PullMessageService
//行数:131
MessageRequest messageRequest = this.messageRequestQueue.take();
if (messageRequest.getMessageRequestMode() == MessageRequestMode.POP) {this.popMessage((PopRequest) messageRequest);
} else {this.pullMessage((PullRequest) messageRequest);
}
  • 这里调用了内存队列take()方法,这个方法看着是否眼熟,因为我们在《Broker存储Message流程解析》中也使用过
  • take()方法的作用就是取出元素并从队列中删除,如果队列为空则会阻塞, 直到队列中有可用的消息请求为止。由此可见pullMessageService相当于内存队列中的消费者角色
  • 但是这次的内存队列使用的是LinkedBlockingQueue类型,Broker存储Message中却使用的是PriorityBlockingQueue类型
  • 内存队列获取出来的元素最终被转化为 PullRequest ,该对象在 RocketMQ 中用于封装消费者向 Broker 拉取消息时所需的所有信息。顾名思义这个对象就是向Broker发起拉取Message的请求对象。

扩展:LinkedBlockingQueue与PriorityBlockingQueue内存队列有什么区别吗?

LinkedBlockingQueue是一个 基于链表实现的阻塞队列按 FIFO(先进先出)顺序存储元素。插入元素时,会添加到队列的尾部,移除元素时,会从队列的头部取出 。 如果队列满了,插入操作会阻塞;如果队列空了,移除操作会阻塞。

PriorityBlockingQueue 基于优先级堆实现的阻塞队列元素按优先级顺序排列。默认情况下,使用元素的自然顺序(即元素需要实现 Comparable 接口) , 插入元素时,会根据优先级排序,移除元素时,总是移除优先级最高的元素 ,由于是无界队列,所以插入操作不会阻塞;如果队列空了,移除操作会阻塞。

因为项目刚初始化,所以messageRequestQueue队列中一定是空的,那么调用task()方法后,pullMessageService线程一直会是阻塞状态不能向下执行,那么什么地方会新增内存队列元素呢,那就只有先把另一个主角rebalanceService服务请出场了。

重平衡服务

rebalanceService的核心作用就是 负责定期执行消费者的负载均衡操作当消费者实例数量或者消息队列数量发送变更,确保消息队列均匀分布在多个消费者实例上,然后将 PullRequest 塞到messageRequestQueue内存队列中。

Producer生产者也使用到了该服务,作用则是将Message负载均衡到不同的MessageQueue中

进入run()方法

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:RebalanceService
//行数:51
boolean balanced = this.mqClientFactory.doRebalance();
realWaitInterval = balanced ? waitInterval : minInterval;
  • 结合源码上下文会发现这是包裹在一个while()循环中,相当于一个定时任务
  • 当调用doRebalance()方法重平衡成功后,设置waitInterval= 20s后再次执行
  • 如果调用doRebalance()方法重平衡执行失败,设置minInterval= 1s后再次重试
  • 只要当前线程启动成功就会按照上面的逻辑周而复始

对重平衡资源进行排序

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:RebalanceImpl
//行数:342
Collections.sort(mqAll);
Collections.sort(cidAll);
  1. mqAll为当前Topic中所有消息队列集合,然后进行排序
  2. cidAll为所有订阅了当前Topic的消费者实例id集合,然后进行排序

看到这里一头雾水,为什么要进行排序呢?

  • 因为重平衡操作都是单独在每一个客户端进行的,而不是统一在Broker服务上进行分配的,那么为了保证消费者实例与消息队列能够合理的负载均衡,并且让每个消费者拿到互不相同的MessageQueue,那么就需要进行排序
  • 又因为订阅相同的topic,那么他们获取的总的消息队列和消费者实例都是完全相同的,然后每个客户端进行相同的排序,那么排序结果都是一样的,后续会使用到这些排序后的结果来对每个客户端进行分配MessageQueue,因为每个客户端id都不一样,从而经过后面的分配算法保证买个消费者分配的MessageQueue都不一样。后续分配算法会具体讲到。

MessageQueue消息队列集合来源

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:RebalanceImpl
//行数:325
Set<MessageQueue> mqSet = this.topicSubscribeInfoTable.get(topic);
//...
List<MessageQueue> mqAll = new ArrayList<>();
mqAll.addAll(mqSet);

mqAll集合最终是从Map集合中通过Topic获取:ConcurrentMap<String/* topic */, Set<MessageQueue>> topicSubscribeInfoTable;

秉承着所以数据都有迹可循的原则,不禁要问topicSubscribeInfoTable中的数据从哪里来的呢?

其实就在上一篇讲到的Consumer启动流程中的填入的数据,就在下面方法中

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:DefaultMQPushConsumerImpl
//行数:1001
this.updateTopicSubscribeInfoWhenSubscriptionChanged();

查看上方方法中的逻辑,会发现数据源又来自topic的订阅信息map:ConcurrentMap<String /* topic */, SubscriptionData> subscriptionInner通过subscriptionInner的循环处理来分别填充topic对应的MessageQueue集合的

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:DefaultMQPushConsumerImpl
//行数:1235
Map<String, SubscriptionData> subTable = this.getSubscriptionInner();

那么subscriptionInner中的数据又是什么时候填充的呢?

其实也是在最开始的Consumer启动的main方法设置订阅topic时中填充的。

//源码位置
//包名:org.apache.rocketmq.example.simple
//文件名:PushConsumer
//行数:39
consumer.subscribe(TOPIC, "*");

深入subscribe()方法,你就会发现填充数据的源码

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:DefaultMQPushConsumerImpl
//行数:1250
SubscriptionData subscriptionData = FilterAPI.buildSubscriptionData(topic, subExpression);
this.rebalanceImpl.getSubscriptionInner().put(topic, subscriptionData);

这样我们就理清楚了整个MessageQueue集合的来源和去向,有头也有尾,也不会再一知半解了

Consumer消费者集合数据来源

//源码位置
//包名:org.apache.rocketmq.client.impl
//文件名:MQClientAPIImpl
//行数:1349
RemotingCommand response = this.remotingClient.invokeSync(MixAll.brokerVIPChannel(this.clientConfig.isVipChannelEnabled(), addr),request, timeoutMillis);

由源码可知,消费者客户端集合cidAll是直接调用Broker服务来获取的

确实分配资源策略

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:RebalanceImpl
//行数:345
AllocateMessageQueueStrategy strategy = this.allocateMessageQueueStrategy;
List<MessageQueue> allocateResult = null;
try {allocateResult = strategy.allocate(this.consumerGroup,this.mQClientFactory.getClientId(),mqAll,cidAll);
} catch (Throwable e) {log.error("allocate message queue exception. strategy name: {}, ex: {}", strategy.getName(), e);return false;
}
  • 调用 strategy.allocate()方法对当前消费者实例分配消息队列,并返回一个集合allocateResult

进入分配方法中,会发现官方实现了多种分配策略

那么我们使用的是哪一种呢?其实在我们启动消费者服务实例化消费者对象时就已经设置了默认策略了

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:DefaultMQPushConsumer
//行数:308
public DefaultMQPushConsumer(final String consumerGroup) {this(consumerGroup, null, new AllocateMessageQueueAveragely());
}

new AllocateMessageQueueAveragely() 就是为我们默认指定的分配策略,即平均哈希队列算法

执行分配策略

//源码位置
//包名:org.apache.rocketmq.client.consumer.rebalance
//文件名:AllocateMessageQueueAveragely
//行数:32
List<MessageQueue> result = new ArrayList<>();
if (!check(consumerGroup, currentCID, mqAll, cidAll)) {return result;
}
int index = cidAll.indexOf(currentCID);
int mod = mqAll.size() % cidAll.size();
int averageSize =
mqAll.size() <= cidAll.size() ? 1 : (mod > 0 && index < mod ? mqAll.size() / cidAll.size()+ 1 : mqAll.size() / cidAll.size());
int startIndex = (mod > 0 && index < mod) ? index * averageSize : index * averageSize + mod;
int range = Math.min(averageSize, mqAll.size() - startIndex);
for (int i = 0; i < range; i++) {result.add(mqAll.get((startIndex + i) % mqAll.size()));
}

这就是给当前消费者分配消息队列的算法逻辑

  • index表示当前消费者实例所有排序后的消费者实例集合cidAll中的下标位置,每个currentCID都是唯一的。
  • mod:表示mqAll集合中的MessageQueue能否被cidAll集合中的消费者实例均匀分配。
  • averageSize:表示当前消费者平均能被分配到的MessageQueue数量。
  • startIndex:表了当前这个 Consumer 从 MessageQueue 数组的哪个位置开始取。
  • range: 代表当前这个 Consumer 获取到了多少个 MessageQueue

总结最终分配逻辑可以理解为两步所有消费者客户端都会分配到相同数量的MessageQueue,对剩余无法平均分配的MessageQueue按照cidAll集合的顺序进行分配。但其实源码中是一次性算出分配结果的。

初始化ProcessQueue

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:RebalanceImpl
//行数:528
ProcessQueue pq = createProcessQueue(topic);
pq.setLocked(true);
long nextOffset = this.computePullFromWhere(mq);
if (nextOffset >= 0) {ProcessQueue pre = this.processQueueTable.putIfAbsent(mq, pq);//...
}
  • 第一步初始化创建ProcessQueue处理队列
  • 最后将队列设置到processQueueTable的Map中,ConcurrentMap<MessageQueue, ProcessQueue> processQueueTable主要用于消费者负载均衡和消息消费管理,确保消息队列能够被正确地分配和处理
  • 该Map在上一篇消费者启动中讲到过,数据用于处理定时任务清除过期的消息
  • putIfAbsent()方法为如果存在相同的Key,就将原来的Value返回,不存在则返回null,同时put数据

初始化PullRequest

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:RebalanceImpl
//行数:532
if (pre != null) {log.info("doRebalance, {}, mq already exists, {}", consumerGroup, mq);
} else {log.info("doRebalance, {}, add a new mq, {}", consumerGroup, mq);PullRequest pullRequest = new PullRequest();pullRequest.setConsumerGroup(consumerGroup);pullRequest.setNextOffset(nextOffset);pullRequest.setMessageQueue(mq);pullRequest.setProcessQueue(pq);pullRequestList.add(pullRequest);changed = true;
}
  • 到这里就正式封装PullRequest拉取请求对象了,紧接着上面初始化ProcessQueue代码可知,只有当该MessageQueue是一个全新并且之前不存在的消息队列时才会进行拉取请求
  • 初始化PullRequest从而和本篇前面讲到的pullMessageService有了关联

内存队列填充PullRequest

循环处理PullRequest对象

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:RebalancePushImpl
//行数:266
for (PullRequest pullRequest : pullRequestList) {if (delay <= 0) {this.defaultMQPushConsumerImpl.executePullRequestImmediately(pullRequest);} else {this.defaultMQPushConsumerImpl.executePullRequestLater(pullRequest, delay);}
}

循环逻辑处理中,对每次PullRequest对象的处理延迟5s,可以看到这里已经进入了PullMessageService服务中

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:PullMessageService
//行数:45
this.scheduledExecutorService.schedule(new Runnable() {@Overridepublic void run() {PullMessageService.this.executePullRequestImmediately(pullRequest);}
}, timeDelay, TimeUnit.MILLISECONDS);

最后就是将PullRequest对象put进内存队列LinkedBlockingQueue<MessageRequest> messageRequestQueue中,从而激活PullMessageService服务,结束阻塞状态开始执行拉取逻辑。

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:PullMessageService
//行数:58
this.messageRequestQueue.put(pullRequest);

消息拉取服务

上面RebalanceService服务完成内存队列PullRequest入栈后,那么紧接着PullMessageService服务开始处理PullRequest。

调用消息核心处理方法

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:DefaultMQPushConsumerImpl
//行数:480
this.pullAPIWrapper.pullKernelImpl(pullRequest.getMessageQueue(),subExpression,subscriptionData.getExpressionType(),subscriptionData.getSubVersion(),pullRequest.getNextOffset(),this.defaultMQPushConsumer.getPullBatchSize(),this.defaultMQPushConsumer.getPullBatchSizeInBytes(),sysFlag,commitOffsetValue,BROKER_SUSPEND_MAX_TIME_MILLIS,CONSUMER_TIMEOUT_MILLIS_WHEN_SUSPEND,CommunicationMode.ASYNC,pullCallback
);

我们这里关注两个点:

  • CommunicationMode.ASYNC:表示向Broker服务拉取消息是一个异步的操作
  • pullCallback:异步回调后处理的逻辑就封装在该对象中

调用远程Broker服务拉取消息

//源码位置
//包名:org.apache.rocketmq.client.impl
//文件名:MQClientAPIImpl
//行数:1024this.remotingClient.invokeAsync(addr, request, timeoutMillis, new InvokeCallback() {//...public void operationSucceed(RemotingCommand response) {try {PullResult pullResult = MQClientAPIImpl.this.processPullResponse(response, addr);pullCallback.onSuccess(pullResult);} catch (Exception e) {pullCallback.onException(e);}}//...}

由源码我们可知,pullCallback回调对象提供了

  • pullCallback.onSuccess(pullResult):请求成功后处理逻辑方法
  • pullCallback.onException(e):请求异常处理逻辑方法

提交消费消息请求

在调用this.pullAPIWrapper.pullKernelImpl核心方法之前,就已经重写了pullCallback的回调方法

并在onSuccess()实现中进行提交消费请求操作

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:DefaultMQPushConsumerImpl
//行数:370
DefaultMQPushConsumerImpl.this.consumeMessageService.submitConsumeRequest(pullResult.getMsgFoundList(),processQueue,pullRequest.getMessageQueue(),dispatchToConsume);

初始化ConsumeRequest

ConsumeRequest消费请求是PullRequest拉取请求之后的又一请求对象

顾名思义这也是消息最终被消费的请求了

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:ConsumeMessageConcurrentlyService
//行数:211
ConsumeRequest consumeRequest = new ConsumeRequest(msgThis, processQueue, messageQueue);
try {this.consumeExecutor.submit(consumeRequest);
} catch (RejectedExecutionException e) {for (; total < msgs.size(); total++) {msgThis.add(msgs.get(total));}this.submitConsumeRequestLater(consumeRequest);
}
  • ConsumeRequest加入到线程池 ThreadPoolExecutor consumeExecutor;相当于就是为当前消费请求单独创建一个线程来异步处理
  • 如果线程池加入异常,则会延迟5s后再次重试一次

消息监听器消费消息

直接进入ConsumeRequest线程的run()方法中

//源码位置
//包名:org.apache.rocketmq.client.impl.consumer
//文件名:ConsumeMessageConcurrentlyService
//行数:211
MessageListenerConcurrently listener = ConsumeMessageConcurrentlyService.this.messageListener;
ConsumeConcurrentlyContext context = new ConsumeConcurrentlyContext(messageQueue);
ConsumeConcurrentlyStatus status = null;
//...
status = listener.consumeMessage(Collections.unmodifiableList(msgs), context);
  • 这里就是消息消费的最后一步,将Message放入监听器MessageListenerConcurrentlyconsumeMessage()方法中。

Message最终在消费者启动中main()方法的注册监听器的地方打印出来,最后返回消费成功状态ConsumeConcurrentlyStatus.CONSUME_SUCCESS

//源码位置
//包名:org.apache.rocketmq.example.simple
//文件名:PushConsumer
//行数:37
consumer.registerMessageListener(new MessageListenerConcurrently() {@Overridepublic ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}
});

Consumer端消费Message简易流程图如下

总结

根据上一篇消费者的启动到本篇消息的拉取与消费,完成了一个Message在消费者端的闭环。本篇我们也学到了一个新的内存队列LinkedBlockingQueue,也讲到了与PriorityBlockingQueue的区别,至此消费者端的源码基本学习完了,希望从源码中大家都有所收获!

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

TikTok用户必看:代理IP的优缺点深度剖析

在咱们这庞大的网络世界里&#xff0c;TikTok就像是夜空中最亮的星星&#xff0c;吸引着全世界的人们。它不仅仅是个让大家开心的地方&#xff0c;更是个能让不同地方的人互相了解、分享生活的神奇平台。但你有没有想过&#xff0c;要是能让这个连接更顺畅&#xff0c;让TikTok…

h5点击电话号跳转手机拨号

需要使用到h5的 <a>标签 我们首先在<head>标签中添加代码 <meta name"format-detection" content"telephoneyes"/>然后再想要的位置添加代码 <a href"tel:10086"> 点击拨打&#xff1a;10086 </a> 这样功能就实现…

【CSS in Depth 2 精译_019】3.2 CSS 的盒模型

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09; 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位&#xff08;已完结&#xff09; 2.1 相对…

LLaMA 数据集

LLaMA的训练数据集来源多样&#xff0c;涵盖了多个不同的数据集和预处理步骤。以下是详细的描述&#xff1a; 公开数据来源和预处理 CommonCrawl [67%]&#xff1a; 使用CCNet管道&#xff08;Wenzek等人&#xff0c;2020年&#xff09;对2017年至2020年间的五个CommonCrawl转…

Vue3 + uni-app 微信小程序:仿知乎日报详情页设计及实现

引言 在移动互联网时代&#xff0c;信息的获取变得越来越便捷&#xff0c;而知乎日报作为一款高质量内容聚合平台&#xff0c;深受广大用户喜爱。本文将详细介绍如何利用Vue 3框架结合微信小程序的特性&#xff0c;设计并实现一个功能完备、界面美观的知乎日报详情页。我们将从…

生成式AI的未来:对话的艺术与代理的实践

生成式 AI 的发展方向&#xff0c;是 Chat 还是 Agent&#xff1f; 随着生成式AI技术的不断进步&#xff0c;关于其未来发展方向的讨论也愈发激烈。究竟生成式AI的未来是在对话系统&#xff08;Chat&#xff09;中展现智慧&#xff0c;还是在自主代理&#xff08;Agent&#x…

《驾驭AI浪潮:伦理挑战与应对策略》

AI发展下的伦理挑战&#xff0c;应当如何应对&#xff1f; 人工智能飞速发展的同时&#xff0c;也逐渐暴露出侵犯数据隐私、制造“信息茧房”等种种伦理风险。随着AI技术在社会各个领域的广泛应用&#xff0c;关于AI伦理和隐私保护问题日趋凸显。尽管国外已出台系列法规来规范…

npm安装依赖包报错,npm ERR! code ENOTFOUND

一、报错现象&#xff1a; npm WARN registry Unexpected warning for https://registry.npmjs.org/: Miscellaneous Warning ETIMEDOUT: request to https://registry.npmjs.org/vue failed, reason: connect ETIMEDOUT 104.16.23.35:443 npm WARN registry Using stale data…

【扁平化多级双向链表】python刷题记录

进入链表的遍历模块了 好复杂的题目描述。。。 DFS深度遍历扁平拼接 """ # Definition for a Node. class Node:def __init__(self, val, prev, next, child):self.val valself.prev prevself.next nextself.child child """class Soluti…

Windows双网卡上网原理以及配置方法

目录 1. 背景 2. IP路由原理 3. windows双网卡上网解决方案 3.1. 基础配置解决方案 3.2. 高阶配置解决方案 1. 背景 在windwos上使用多网卡在工作和生活中是一个常见的操作&#xff0c;比如为了获取内部消息将有线连接到内部局域网中&#xff0c;为而了访问外网又将电脑的…

申请https证书的具体流程

申请HTTPS证书的具体流程通常涉及以下步骤&#xff0c;不过请注意&#xff0c;具体细节可能因不同的证书颁发机构&#xff08;CA&#xff09;而有所差异&#xff1a; 1、确定证书类型&#xff1a; 证书类型&#xff1a;根据需求选择合适的SSL证书类型。常见的有DV&#xff08;…

Windows下使用Cygwin创建rsync服务端

1 下载Cygwin 访问官网Cygwin&#xff0c;点击setup-X86_64.exe即可开始下载 2 安装 前面全部默认。路径可以自己选择&#xff0c;站点选阿里云的&#xff0c;等待安装即可 3 配置 使用打开Cygwin安装后创建的快捷方式窗口&#xff0c;输入下面的指令将windows用户导入到cyg…

如何将几百兆的包优化到几十兆----记一次vue项目的打包优化过程

打包优化 现象 前段时间开发的时候遇到客户反馈的一个问题 界面无法打开&#xff0c;显示白屏&#xff0c;控制台无报错 经过我们在开发环境&#xff0c;测试环境反复测试都没复现出客户的问题&#xff0c;然后我们又不停的在生产环境上找问题&#xff0c;也没复现出来 最…

正点原子imx6uSD卡复制files文件到u盘rootfs的root内失败

进入rootfs的home目录 再进入root&#xff0c;一般是要输入密码的&#xff0c;更改权限&#xff0c;设置全部可以读写&#xff0c;删除原有的文件。再把files文件夹复制过来就行 后面找不带分区&#xff0c;哎。相当于内存卡就是启动u盘&#xff0c;进入了linux系统&#xff0c…

彻底解决idea的编解码问题

一、打开idea,找到Setting,点击File Encoding编解码设置,将以下标红的三个部分全部设置为UTF-8.同理如果你的项目使用的是GBK或者其他编码格式,那么也设置为统一。 二、点击Java Compiler设置补齐-encoding utf-8参数 三、如果你的项目使用到了tomcat,那么需要配置下tomca…

挖矿宝藏之硬盘分区

目录 一、硬盘分区的相关知识 二、主分区、活动分区、扩展分区、逻辑盘和盘符 三、硬盘分区原因 1.减少硬盘空间的浪费 2.便于文件的分类管理 3.有利于病毒的防治 四、硬盘分区的原则 1.方便性 2.实用性 3.安全性 五、利用Diskpart进行分区 1.命令行工具Diskpart …

谷歌浏览器自动填充密码时,el-input样式错乱

使用到谷歌浏览器的记忆功能&#xff0c;选择的内容为浏览器保存的内容时 会导致element-plus的el-input样式改变 只需要增加一个css样式&#xff0c;就可以解决问题 :deep .el-input__inner {box-shadow: 0 0 0 1000px #fff inset; }修改后

Chapter13 深度和法线纹理——Shader入门精要学习笔记

Chapter13 深度和法线纹理 一、深度和法线纹理的原理和获取1.背后的原理①深度纹理②法线纹理 2.如何获取3.查看深度和法线纹理 二、再谈运动模糊1.速度映射2.MotionBlurWithDepthTexture.cs3.MotionBlurShader 三、全局雾效 —— 屏幕后处理1.重建世界坐标interpolatedRay的求…

电脑压缩软件有哪些?整理了6个常用的,总有一款满足你的需求 !

对于长期需要借助电脑来办公的小伙伴来说&#xff0c;电脑压缩软件是不可获取的办公软件之一。电脑压缩软件具有多种重要作用&#xff0c;它们在日常的计算机使用、文件管理、网络传输和存储中扮演着不可或缺的角色。 下是电脑压缩软件的主要作用&#xff1a; 1、减少文件大小…

【C++】C++ 学生信息管理系统(源码+面向对象)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…