当前位置: 首页 > news >正文

RocketMq源码-broker(五)

一、RocketMq存储设计

RocketMQ 主要存储的文件包括Commitlog 文件、ConsumeQueue 文件、IndexFile。RocketMQ 将所有主题的消息存储在同一文件,确保消息发送时顺序写文件,尽最大的能力确保消息发送的高性能与高吞吐量。
但由于一般的消息中间件是基于消息主题的订阅机制,这样便给按照消息主题检索消息带来了极大的不便。为了提高消息消费的效率, RocketMQ 引入了ConsumeQueue 消息队列文件,每个消息主题包含多个消息消费队列,每个消息队列有一个消息文件IndexFile 索引文件,其主要设计理念就是为了加速消息的检索性能,可以根据消息的属性快速从Commitlog 文件中检索消息。整体如下:

1 ) CommitLog :消息存储文件,所有消息主题的消息都存储在CommitLog 文件中
2 ) ConsumeQueue :消息消费队列,消息到达CommitLog 文件后,将异步转发到消息消费队列,供消息消费者消费
3 ) IndexFile :消息索引文件,主要存储消息Key 与Offset 的对应关系

rockeqmq中存储目录store如下:

一、消息存储结构

一、CommitLog

CommitLog 以物理文件的方式存放,每台Broker 上的CommitLog 被本机器所有ConsumeQueue 共享,文件地址:$ {user.home}\store\$ { commitlog} \ $ { fileName}。在CommitLog 中,一个消息的存储长度是不固定的, RocketMQ 采取一些机制,尽量向CommitLog 中顺序写,但是随机读。commitlog 文件默认大小为lG ,可通过在broker 置文件中设置mapedFileSizeCommitLog 属性来改变默认大小。

Commitlog 文件存储的逻辑视图如下,每条消息的前面4 个字节存储该条消息的总长度。但是一个消息的存储长度是不固定的。

 二、ConsumeQueue

ConsumeQueue 是消息的逻辑队列,类似数据库的索引文件,存储的是指向物理存储的地址。每个Topic 下的每个Message Queue 都有一个对应的ConsumeQueue 文件, 文件地址在${$storeRoot} \consumequeue\$ {topicName} \$ { queueld} \$ {fileName}。

ConsumeQueue 中存储的是消息条目,为了加速ConsumeQueue 消息条目的检索速度与节省磁盘空间,每一个Consumequeue 条目不会存储消息的全量信息,消息条目如下: 

ConsumeQueue 即为Commitlog 文件的索引文件, 其构建机制是当消息到达Commitlog 文件后由专门的线程产生消息转发任务,从而构建消息消费队列文件(ConsumeQueue )与下文提到的索引文件。

存储机制这样设计有以下几个好处:
1 ) CommitLog 顺序写,可以大大提高写入效率。(实际上,磁盘有时候会比你想象的快很多,有时候也比你想象的慢很多,关键在如何使用,使用得当,磁盘的速度完全可以匹配上网络的数据传输速度。目前的高性能磁盘,顺序写速度可以达到600MB/s ,超过了一般网卡的传输速度,这是磁盘比想象的快的地方但是磁盘随机写的速度只有大概lOOKB/s,和顺序写的性能相差6000 倍!)
2 )虽然是随机读,但是利用操作系统的pagecache 机制,可以批量地从磁盘读取,作为cache 存到内存中,加速后续的读取速度。
3 )为了保证完全的顺序写,需要ConsumeQueue 这个中间结构,因为ConsumeQueue 里只存偏移量信息,所以尺寸是有限的,在实际情况中,大部分的ConsumeQueue 能够被全部读入内存,所以这个中间结构的操作速度很快,可以认为是内存读取的速度。此外为了保证CommitLog 和ConsumeQueue的一致性, CommitLog 里存储了Consume Queues 、Message Key、Tag 等所有信息,即使ConsumeQueue 丢失,也可以通过commitLog 完全恢复出来。

三、IndexFile

index 存的是索引文件,这个文件用来加快消息查询的速度。消息消费队列RocketMQ 专门为消息订阅构建的索引文件,提高根据主题与消息检索消息的速度,使用Hash 索引机制,具体是Hash 槽与Hash 冲突的链表结构。(这里不做过多解释)


四、Config

config 文件夹中存储着Topic 和Consumer 等相关信息。主题和消费者群组相关的信息就存在此。
topics.json : topic 配置属性
subscriptionGroup.json :消息消费组配置信息。
delayOffset.json :延时消息队列拉取进度。
consumerOffset.json :集群消费模式消息消进度。例如下面就是TopicTest主题中四个队列的消费偏移量,(第0个队列偏移量5,第一个队列偏移量3,第二个偏移量4,第三个偏移量5)

{"offsetTable":{"TopicTest@please_rename_unique_group_name_4":{0:5,1:3,2:4,3:5},"%RETRY%please_rename_unique_group_name_4@please_rename_unique_group_name_4":{0:20}}
}


consumerFilter.json :主题消息过滤信息。

五、内存映射

内存映射文件,是由一个文件到一块内存的映射。文件的数据就是这块区域内存中对应的数据,读写文件中的数据,直接对这块区域的地址操作就可以,减少了内存复制的环节。所以说,内存映射文件比起文件I/O 操作,效率要高,而且文件越大,体现出来的差距越大。
RocketMQ 通过使用内存映射文件来提高IO 访问性能,无论是CommitLog,ConsumeQueue 还是IndexFile ,单个文件都被设计为固定长度,如果一个文件写满以后再创建一个新文件,文件名就为该文件第一条消息对应的全局物理偏移量。

六、文件刷盘机制

RocketMQ 存储与读写是基于JDK NIO 的内存映射机制,具体使用MappedByteBuffer(基于MappedByteBuffer 操作大文件的方式,其读写性能极高)。

RocketMQ 的消息是存储到磁盘上的,这样既能保证断电后恢复,又可以让存储的消息超出内存的限制RocketMQ 为了提高性能,会尽可能地保证磁盘的顺序写消息在通过Producer 写人RocketMQ 的时候,有两种写磁盘方式:

一、异步刷盘方式

在返回写成功状态时,消息可能只是被写入了内存的PAGECACHE ,写操作的返回快,吞吐量大;当内存里的消息积累到一定程度时,统一触发写磁盘动作,快速写入。

二、同步刷盘方式 

在返回写成功状态时,消息已经被写人磁盘具体流程是,消息写入内存的PAGECACHE 后,立刻通知刷盘线程刷盘,然后等待刷盘完成,刷盘线程执行完成后唤醒等待的线程,返回消息写成功的状态消息存储时首先将消息追加到内存,再根据配值的刷盘略在不同时间进行刷写磁盘如果是同步刷盘,消息追加到内存后,将同步调用MappedByteBuffer force()方法;如果是异步刷盘,在消息追加到内存后立刻返回给消息发送端RocketMQ 使用个单独的线程按照某个设定的频执行刷盘操作。通过在broker 配置文件中配置flushDiskType 来设定刷盘方式,可选值为ASYNC_FLUSH (异步刷盘), SYNC_FLUSH 同步刷盘) 默认为异步。

实际应用中要结合业务场景,合理设置刷盘方式,尤其是同步刷盘的方式,由于频繁的触发磁盘写动作, 会明显降低性能。通常情况下,应该把Rocket置成异步刷盘方式。

七、过期文件删除

由于RocketMQ 操作CommitLog,ConsumeQueue 文件是基于内存映射机制并在启动的时候会加载commitlog,ConsumeQueue 目录下的所有文件,为了避免内存与磁盘的浪费,不可能将消息永久存储在消息服务器上,所以需要引入一种机制来删除己过期的文件。
删除过程分别执行清理消息存储文件( Commitlog )与消息消费队列文件( ConsumeQueue 文件), 消息消费队列文件与消息存储文件( Commitlog )共用一套过期文件机制。

RocketMQ 清除过期文件的方法是:如果非当前写文件在一定时间间隔内没有再次被更新,则认为是过期文件,可以被删除, RocketMQ 不会关注这个文件上的消息是否全部被消费。默认每个文件的过期时间为42 小时(不同版本的默认值不同,这里以4.4.0 为例) ,通过在Broker 配置文件中设置fileReservedTime 来改变过期时间,单位为小时。
触发文件清除操作的是一个定时任务,而且只有定时任务,文件过期删除定时任务的周期由该删除决定,默认每10s 执行一次。

一、过期判断

文件删除主要是由这个配置属性:fileReservedTime:文件保留时间。也就是从最后一次更新时间到现在,如果超过了该时间,则认为是过期文件,可以删除。
另外还有其他两个配置参数:
deletePhysicFilesInterval:删除物理文件的时间间隔(默认是100MS),在一次定时任务触发时,可能会有多个物理文件超过过期时间可被删除,因此删除一个文件后需要间隔deletePhysicFilesInterval 这个时间再删除另外一个文件,由于删除文件是一个非常耗费IO 的操作,会引起消息插入消费的延迟(相比于正常情况下),所以不建议直接删除所有过期文件。
destroyMapedFileIntervalForcibly:在删除文件时,如果该文件还被线程引用,此时会阻止此次删除操作,同时将该文件标记不可用并且纪录当前时间戳destroyMapedFileIntervalForcibly 这个表示文件在第一次删除拒绝后,文件保存的最大时间,在此时间内一直会被拒绝删除,当超过这个时间时,会将引用每次减少1000,直到引用小于等于0 为止,即可删除该文件.

二、删除条件

1)指定删除文件的时间点, RocketMQ 通过deleteWhen 设置一天的固定时间执行一次。删除过期文件操作, 默认为凌晨4 点。
2)磁盘空间是否充足,如果磁盘空间不充足(DiskSpaceCleanForciblyRatio。磁盘空间强制删除文件水位。默认是85),会触发过期文件删除操作。
另外还有RocketMQ 的磁盘配置参数:
1:物理使用率大于diskSpaceWarningLevelRatio(默认90%可通过参数设置),则会阻止新消息的插入。
2:物理磁盘使用率小于diskMaxUsedSpaceRatio(默认75%) 表示磁盘使用正常。

二、源码流程

一、启动流程BrokerController#start

Broker 最核心的功能就是接收到消息然后把消息进行存储,所以最核心的线程就是messageStore

if (this.messageStore != null) {this.messageStore.start();
}

 启动commitLog线程,负责将消息写入到commingLog文件中

this.commitLog.start();
class FlushRealTimeService extends FlushCommitLogService {private long lastFlushTimestamp = 0;private long printTimes = 0;public void run() {CommitLog.log.info(this.getServiceName() + " service started");while (!this.isStopped()) {boolean flushCommitLogTimed = CommitLog.this.defaultMessageStore.getMessageStoreConfig().isFlushCommitLogTimed();int interval = CommitLog.this.defaultMessageStore.getMessageStoreConfig().getFlushIntervalCommitLog();int flushPhysicQueueLeastPages = CommitLog.this.defaultMessageStore.getMessageStoreConfig().getFlushCommitLogLeastPages();int flushPhysicQueueThoroughInterval =CommitLog.this.defaultMessageStore.getMessageStoreConfig().getFlushCommitLogThoroughInterval();boolean printFlushProgress = false;// Print flush progresslong currentTimeMillis = System.currentTimeMillis();if (currentTimeMillis >= (this.lastFlushTimestamp + flushPhysicQueueThoroughInterval)) {this.lastFlushTimestamp = currentTimeMillis;flushPhysicQueueLeastPages = 0;printFlushProgress = (printTimes++ % 10) == 0;}try {if (flushCommitLogTimed) {Thread.sleep(interval);} else {this.waitForRunning(interval);}if (printFlushProgress) {this.printFlushProgress();}long begin = System.currentTimeMillis();CommitLog.this.mappedFileQueue.flush(flushPhysicQueueLeastPages);long storeTimestamp = CommitLog.this.mappedFileQueue.getStoreTimestamp();if (storeTimestamp > 0) {CommitLog.this.defaultMessageStore.getStoreCheckpoint().setPhysicMsgTimestamp(storeTimestamp);}long past = System.currentTimeMillis() - begin;if (past > 500) {log.info("Flush data to disk costs {} ms", past);}} catch (Throwable e) {CommitLog.log.warn(this.getServiceName() + " service has exception. ", e);this.printFlushProgress();}}// Normal shutdown, to ensure that all the flush before exitboolean result = false;for (int i = 0; i < RETRY_TIMES_OVER && !result; i++) {result = CommitLog.this.mappedFileQueue.flush(0);CommitLog.log.info(this.getServiceName() + " service shutdown, retry " + (i + 1) + " times " + (result ? "OK" : "Not OK"));}this.printFlushProgress();CommitLog.log.info(this.getServiceName() + " service end");}
}

启动reputMessageService线程,RocketMQ 通过开启一个线程ReputMessageService 来监听CommitLog 文件更新事件,如果有新的消息,则及时更新ComsumeQueue、IndexFile 文件。

this.reputMessageService.start();

 启动netty服务,与nameserver,broker端进行通信

if (this.remotingServer != null) {this.remotingServer.start();
}if (this.brokerOuterAPI != null) {this.brokerOuterAPI.start();
}

 当broker端没有消息的时候,会将consumer端发起的拉取消息请求暂存在map中,就是这个线程做的处理。

if (this.pullRequestHoldService != null) {this.pullRequestHoldService.start();
}

启动定时线程注册broker信息到nameserver 

this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {@Overridepublic void run() {try {BrokerController.this.registerBrokerAll(true, false, brokerConfig.isForceRegister());} catch (Throwable e) {log.error("registerBrokerAll Exception", e);}}
}, 1000 * 200, Math.max(180000, Math.min(brokerConfig.getRegisterNameServerPeriod(), 60000)), TimeUnit.MILLISECONDS);

二、Broker中消息的生产

发送的消息到达broker,调用org.apache.rocketmq.broker.processor.SendMessageProcessor 类的processRequest()方法,processRequest()调用sendMessag()

public CompletableFuture<RemotingCommand> asyncProcessRequest(ChannelHandlerContext ctx,RemotingCommand request) throws RemotingCommandException {final SendMessageContext mqtraceContext;switch (request.getCode()) {case RequestCode.CONSUMER_SEND_MSG_BACK:return this.asyncConsumerSendMsgBack(ctx, request);default:SendMessageRequestHeader requestHeader = parseRequestHeader(request);if (requestHeader == null) {return CompletableFuture.completedFuture(null);}mqtraceContext = buildMsgContext(ctx, requestHeader);this.executeSendMessageHookBefore(ctx, request, mqtraceContext);if (requestHeader.isBatch()) {return this.asyncSendBatchMessage(ctx, request, mqtraceContext, requestHeader);} else {return this.asyncSendMessage(ctx, request, mqtraceContext, requestHeader);}}}

1.非批次发送消息sendMessag()
2.消息存储(注意,这里都只存储commitlog),调用DefaultMessageStore.putMessage()方法。
2.1 这里进行commitlog 的提交,调用CommitLog.putMessage()。

CompletableFuture<PutMessageResult> putMessageResult = this.brokerController.getMessageStore().asyncPutMessages(messageExtBatch);
CompletableFuture<PutMessageResult> resultFuture = this.commitLog.asyncPutMessages(messageExtBatch);

2.1.1 在MappedFile 类中,处理存储都是使用MappedFile 这个类进行处理的,最终调用appendMessage 方法。appendMessagesInner 方法中,这里进行文件的追加(AppendMessageCallback 接口的实现DefaultAppendMessageCallback 在CommitLog类中,是一个内部类)。
2.2 在commitlog 类中doAppend 方法中进行commitlog 的处理,还是基于byteBuffer 的操作。
2.3 在commitlog 类中doAppend 方法中进行返回,将消息写入的内存的位置信息、写入耗时封装为AppendMessageResult 对象返回。

result = mappedFile.appendMessages(messageExtBatch, this.appendMessageCallback, putMessageContext);
switch (result.getStatus()) {case PUT_OK:break;case END_OF_FILE:unlockMappedFile = mappedFile;// Create a new file, re-write the messagemappedFile = this.mappedFileQueue.getLastMappedFile(0);if (null == mappedFile) {// XXX: warn and notify melog.error("Create mapped file2 error, topic: {} clientAddr: {}", messageExtBatch.getTopic(), messageExtBatch.getBornHostString());beginTimeInLock = 0;return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.CREATE_MAPEDFILE_FAILED, result));}result = mappedFile.appendMessages(messageExtBatch, this.appendMessageCallback, putMessageContext);break;case MESSAGE_SIZE_EXCEEDED:case PROPERTIES_SIZE_EXCEEDED:beginTimeInLock = 0;return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.MESSAGE_ILLEGAL, result));case UNKNOWN_ERROR:default:beginTimeInLock = 0;return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, result));
}

 提交刷盘请求,最终将内存中的commitLog数据刷到磁盘中

CompletableFuture<PutMessageStatus> flushOKFuture = submitFlushRequest(result, messageExtBatch);
CompletableFuture<PutMessageStatus> replicaOKFuture = submitReplicaRequest(result, messageExtBatch);

总结:
RocketMQ 接收到消息后,首先是写入Commitlog 文件,按照顺序进行写入,使用NIO 技术。在Commitlog 中putMessage 最后通过判断配置文件的主从同步类型和刷盘类型,进行刷盘。

借助java NIO 的力量,使得I/O 性能十分高。当消息来的时候,顺序写入CommitLog。
RocketMQ 下主要有三类大文件:commitlog 文件、Index 文件,consumequeue 文件,对于三类大文件,使用的就是NIO 的MappedByteBuffer 类来提高读写性能(主要是刷盘方法中)。这个类是文件内存映射的相关类,支持随机读和顺序写。在RocketMQ 中,被封装成了MappedFile 类

三、Broker中更新消息队列和索引文件

消息进入Commitlog 文件还不够,因为对于消费者来说,他们必须要看到ConsumeQueue 和IndexFile(ConsumeQueue 是因为消费要根据队列进行消费,另外没有索引文件IndexFile,消息的查找会出现很大的延迟)。
所以RocketMQ 通过开启一个线程ReputMessageService 来监听CommitLog 文件更新事件,如果有新的消息,则及时更新ComsumeQueue、IndexFile 文件。
源码跟踪
1. DefaultMessageStore 类中的内部类ReputMessageService 专门处理此项任务
2. ReputMessageService 类的run(),默认1 毫秒处理一次(文件从CommitLog 到ComsumeQueue 和Index)
3. ReputMessageService 类的doReput()方法。
4. ReputMessageService 类的doReput()方法中, doDispatch,最终会构建(构建消息消费队)和(构建索引文件)。

构建ConsumeQueue文件 

public void putMessagePositionInfo(DispatchRequest dispatchRequest) {ConsumeQueue cq = this.findConsumeQueue(dispatchRequest.getTopic(), dispatchRequest.getQueueId());cq.putMessagePositionInfoWrapper(dispatchRequest);
}
private boolean putMessagePositionInfo(final long offset, final int size, final long tagsCode,final long cqOffset) {if (offset + size <= this.maxPhysicOffset) {log.warn("Maybe try to build consume queue repeatedly maxPhysicOffset={} phyOffset={}", maxPhysicOffset, offset);return true;}this.byteBufferIndex.flip();this.byteBufferIndex.limit(CQ_STORE_UNIT_SIZE);this.byteBufferIndex.putLong(offset);this.byteBufferIndex.putInt(size);this.byteBufferIndex.putLong(tagsCode);final long expectLogicOffset = cqOffset * CQ_STORE_UNIT_SIZE;MappedFile mappedFile = this.mappedFileQueue.getLastMappedFile(expectLogicOffset);if (mappedFile != null) {if (mappedFile.isFirstCreateInQueue() && cqOffset != 0 && mappedFile.getWrotePosition() == 0) {this.minLogicOffset = expectLogicOffset;this.mappedFileQueue.setFlushedWhere(expectLogicOffset);this.mappedFileQueue.setCommittedWhere(expectLogicOffset);this.fillPreBlank(mappedFile, expectLogicOffset);log.info("fill pre blank space " + mappedFile.getFileName() + " " + expectLogicOffset + " "+ mappedFile.getWrotePosition());}if (cqOffset != 0) {long currentLogicOffset = mappedFile.getWrotePosition() + mappedFile.getFileFromOffset();if (expectLogicOffset < currentLogicOffset) {log.warn("Build  consume queue repeatedly, expectLogicOffset: {} currentLogicOffset: {} Topic: {} QID: {} Diff: {}",expectLogicOffset, currentLogicOffset, this.topic, this.queueId, expectLogicOffset - currentLogicOffset);return true;}if (expectLogicOffset != currentLogicOffset) {LOG_ERROR.warn("[BUG]logic queue order maybe wrong, expectLogicOffset: {} currentLogicOffset: {} Topic: {} QID: {} Diff: {}",expectLogicOffset,currentLogicOffset,this.topic,this.queueId,expectLogicOffset - currentLogicOffset);}}this.maxPhysicOffset = offset + size;return mappedFile.appendMessage(this.byteBufferIndex.array());}return false;
}

consumeQueue中刷盘线程启动,将创建好的consumeQueue刷新到磁盘中。

this.flushConsumeQueueService.start();
class FlushConsumeQueueService extends ServiceThread {private static final int RETRY_TIMES_OVER = 3;private long lastFlushTimestamp = 0;private void doFlush(int retryTimes) {int flushConsumeQueueLeastPages = DefaultMessageStore.this.getMessageStoreConfig().getFlushConsumeQueueLeastPages();if (retryTimes == RETRY_TIMES_OVER) {flushConsumeQueueLeastPages = 0;}long logicsMsgTimestamp = 0;int flushConsumeQueueThoroughInterval = DefaultMessageStore.this.getMessageStoreConfig().getFlushConsumeQueueThoroughInterval();long currentTimeMillis = System.currentTimeMillis();if (currentTimeMillis >= (this.lastFlushTimestamp + flushConsumeQueueThoroughInterval)) {this.lastFlushTimestamp = currentTimeMillis;flushConsumeQueueLeastPages = 0;logicsMsgTimestamp = DefaultMessageStore.this.getStoreCheckpoint().getLogicsMsgTimestamp();}ConcurrentMap<String, ConcurrentMap<Integer, ConsumeQueue>> tables = DefaultMessageStore.this.consumeQueueTable;for (ConcurrentMap<Integer, ConsumeQueue> maps : tables.values()) {for (ConsumeQueue cq : maps.values()) {boolean result = false;for (int i = 0; i < retryTimes && !result; i++) {result = cq.flush(flushConsumeQueueLeastPages);}}}if (0 == flushConsumeQueueLeastPages) {if (logicsMsgTimestamp > 0) {DefaultMessageStore.this.getStoreCheckpoint().setLogicsMsgTimestamp(logicsMsgTimestamp);}DefaultMessageStore.this.getStoreCheckpoint().flush();}}public void run() {DefaultMessageStore.log.info(this.getServiceName() + " service started");while (!this.isStopped()) {try {int interval = DefaultMessageStore.this.getMessageStoreConfig().getFlushIntervalConsumeQueue();this.waitForRunning(interval);this.doFlush(1);} catch (Exception e) {DefaultMessageStore.log.warn(this.getServiceName() + " service has exception. ", e);}}this.doFlush(RETRY_TIMES_OVER);DefaultMessageStore.log.info(this.getServiceName() + " service end");}
}

核心关键点:
定时任务来处理的消息存储转换。处理核心类是DefaultMessageStore 类中的内部类ReputMessageService

相关文章:

RocketMq源码-broker(五)

一、RocketMq存储设计 RocketMQ 主要存储的文件包括Commitlog 文件、ConsumeQueue 文件、IndexFile。RocketMQ 将所有主题的消息存储在同一文件&#xff0c;确保消息发送时顺序写文件&#xff0c;尽最大的能力确保消息发送的高性能与高吞吐量。 但由于一般的消息中间件是基于消…...

【Linux】文件描述符fd

1.前置预备 文件 内容 属性访问文件之前&#xff0c;都必须先打开他 #include<stdio.h> int main() { FILE* fpfopen("log.txt","w"); if(fpNULL) { perror("fopen"); return 1; } fclose(fp); return 0…...

mysql之事务

MySQL的事务隔离特性指的是多个并发事务之间相互隔离的程度&#xff0c;以保证数据的一致性和并发性。MySQL支持四个隔离级别&#xff0c;分别是读未提交&#xff08;Read Uncommitted&#xff09;、读已提交&#xff08;Read Committed&#xff09;、可重复读&#xff08;Repe…...

python插入mysql数据

# 插入与上一篇变化不大,只是需要进行确认操作. 增加确认操作的方法有两种&#xff08;假设类对象为a&#xff09;: 1.在连接时传入一个参数:autocommitTrue aConnection&#xff08; host"localhost", port3306, user"root", password"自己的密码…...

C语言面试题/笔试题/高频面试题

一、C&#xff1a; 1.static和const的作用优缺点 限制作用域&#xff1a; static声明中使用全局变量、函数 &#xff0c;仅当前文件内可用&#xff0c;其他文件不能引用 static修饰的局部变量只能在本函数中使用. 延长生命周期: static修饰的变量生命周期为整个程序 存放位置&a…...

rust websocket Echo server高性能服务器开发

最近在学习websocket时,一直没有发现好的websocket server工具来调试,于是就自己做了一个websocket server用来学习和调试。因为rust性能遥遥领先,所以就采用了rust来搭建服务器。废话不多说直接上代码main.rs: use tokio::net::TcpListener; use tokio_tungstenite::tung…...

Docker打包SpringBoot项目

一、项目打成jar包 在进行docker打包之前&#xff0c;先确定一下&#xff0c;项目能够正常的打成JAR包&#xff0c;并且启动之后能够正常的访问。这一步看似是可有可无&#xff0c;但是能避免后期的一些无厘头问题。 二、Dockerfile 项目打包成功之后&#xff0c;需要编写Doc…...

ViT学习笔记(二) Patch+Position Embedding阶段的详细推演与理解

我认为讲得最好的一个文章&#xff1a;Vision Transformer详解-CSDN博客 有很多文章&#xff0c;自己并没有完全正确理解。 我的笔记&#xff0c;以ViT的标准应用为例&#xff1a; • 输入图像&#xff1a;输入图像的尺寸是224x224&#xff0c;且是RGB图像&#xff0c;因此输…...

Elasticsearch 单节点安全配置与用户认证

Elasticsearch 单节点安全配置与用户认证 安全扫描时发现了一个高危漏洞&#xff1a;Elasticsearch 未授权访问 。在使用 Elasticsearch 构建搜索引擎或处理大规模数据时&#xff0c;需要启用基本的安全功能来防止未经授权的访问。本文将通过简单的配置步骤&#xff0c;为单节…...

【PHP项目实战】活动报名系统

目录 项目介绍 开发语言 后端 前端 项目截图&#xff08;部分&#xff09; 首页 列表 详情 个人中心 后台管理 项目演示 项目介绍 本项目是一款基于手机浏览器的活动报名系统。它提供了一个方便快捷的活动报名解决方案&#xff0c;无需下载和安装任何APP&#xff0c…...

ASP.NET Core8.0学习笔记(二十五)——EF Core Include导航数据加载之预加载与过滤

一、导航属性数据加载 1.在EF Core中可以使用导航属性来加载相关实体。 2.加载实体的三种方式&#xff1a; (1)预先加载&#xff1a;直接在查询主体时就把对应的依赖实体查出来&#xff08;作为初始查询的一部分&#xff09; (2)显式加载&#xff1a;使用代码指示稍后显式的从…...

【RK3562J开发笔记】MCP2518FD外部CAN-FD控制器的调试方法

“SPI转CAN-FD”是嵌入式开发领域的常用方法&#xff0c;它极大地促进了不同通信接口之间的无缝连接&#xff0c;并显著降低了系统设计的复杂性。飞凌嵌入式依托瑞芯微RK3562J处理器打造的OK3562J-C开发板因为内置了SPI转CAN-FD驱动&#xff0c;从而原生支持这一功能。该开发板…...

docker安装Emqx并使用自签名证书开启 SSL/TLS 连接

docker安装Emqx并使用自签名证书开启 SSL/TLS 连接 一、获取自签名证书1、创建openssl.cnf文件2、生成证书自签名证书 二、docker安装EMQX1、初始化目录2、加载镜像文件并挂载相应的文件目录3、启动docker容器4、EMQX加载自签名证书 三、客户端MQTTX连接测试四、Springboot整合…...

AI驱动的低代码平台:解密背后的算法与架构创新

引言 在如今的数字化浪潮中&#xff0c;企业对软件的需求正以前所未有的速度增长。传统的开发方式由于开发周期长、成本高&#xff0c;已逐渐无法满足市场的快速变化。而低代码平台的出现&#xff0c;使得开发者和业务人员能够以极简的方式快速构建应用。然而&#xff0c;随着企…...

ruoyi的excel批量导入

最简单方式 若依的官方文档提供了教程&#xff0c;可以按照起前后端的教学&#xff0c;进行代码编写 前段 组件 <!-- 导入对话框 --><el-dialogtitle"导入数据"v-model"openImport"width"500px"append-to-body><el-uploadref&quo…...

大数据-244 离线数仓 - 电商核心交易 ODS层 数据库结构 数据加载 DataX

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; Java篇开始了&#xff01; 目前开始更新 MyBatis&#xff0c;一起深入浅出&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff0…...

Spring Security

一.权限控制 1.1 认证和授权概念 问题1&#xff1a;在生产环境下我们如果不登录后台系统就可以完成这 些功能操作吗&#xff1f; 答案显然是否定的&#xff0c;要操作这些功能必须首先登录到系统才可 以。 问题2&#xff1a;是不是所有用户&#xff0c;只要登录成功就都可以操…...

OpenAI 正式发布 o1 完整版

OpenAI 在 o1 模型完整版&#xff0c;该模型相较于之前的 o1-preview 版本在智能能力上有所提升&#xff0c;特别是在编程能力方面&#xff0c;并且能够根据问题的难度智能调节响应速度。此外还新增了图像识别功能&#xff0c;但目前仍然不支持网页浏览、文件上传等功能 o1 模…...

Ubuntu22.04搭建LAMP环境(linux服务器学习笔记)

目录 引言&#xff1a; 一、系统更新 二、安装搭建Apache2 1.你可以通过以下命令安装它&#xff1a; 2.查看Apache2版本 3.查看Apache2运行状态 4.浏览器访问 三、安装搭建MySQL 1.安装MySQL 2.查看MySQL 版本 3.安全配置MySQL 3.1是否设置密码&#xff1f;(按y|Y表…...

C#与PLC通讯时,数据读取和写入浮点数,字节转换问题(ModbusTCP)

在与PLC进行通讯时&#xff0c;会发现一个问题&#xff0c;浮点数1.2接收过来后&#xff0c;居然变成了两个16位的整数。 经过一系列的分析&#xff0c;这是因为在PLC存储浮点数时32位&#xff0c;我们接收过来的数据会变成两个16位的高低字节&#xff0c;而且我们进行下发数据…...

synchronized的特性

1.互斥 对于synchronized修饰的方法及代码块不同线程想同时进行访问就会互斥。 就比如synchronized修饰代码块时&#xff0c;一个线程进入该代码块就会进行“加锁”。 退出代码块时会进行“解锁”。 当其他线程想要访问被加锁的代码块时&#xff0c;就会阻塞等待。 阻塞等待…...

NLP与LLM的工程化实践与学习思考 - 说说知识图谱

NLP与LLM的工程化实践与学习思考[24年半年工作总结] - 说说知识图谱 0 真的就是先说说1 为什么知识图谱什么是知识图谱&#xff1f;基于图的数据结构&#xff1f;基于数据结构的图&#xff1f;知识图谱的技术要点两个技术维度&#xff1a;知识、图七个技术要点&#xff1a;表示…...

php 系统函数 记录

PHP intval() 函数 PHP函数介绍—array_key_exists(): 检查数组中是否存在特定键名 如何使用PHP中的parse_url函数解析URL PHP is_array()函数详解&#xff0c;PHP判断是否为数组 PHP函数介绍&#xff1a;in_array()函数 strpos定义和用法 strpos() 函数查找字符串在另一字符串…...

游戏引擎学习第38天

仓库: https://gitee.com/mrxiao_com/2d_game 回顾上次的内容。 我们之前讨论了将精灵放在屏幕上&#xff0c;但颜色错误的问题。问题最终查明是因为使用了一个调整工具&#xff0c;导致文件的字节顺序发生了变化。重新运行“image magic”工具对一些大图像进行重新处理后&am…...

Android 15 行为变更:所有应用

Android 15 平台包含一些可能会影响您的应用的行为变更。以下行为变更将影响在 Android 15 上运行的所有应用&#xff0c;无论采用哪种 targetSdkVersion 都不例外。您应该测试您的应用&#xff0c;然后根据需要进行修改&#xff0c;以适当地支持这些变更。 此外&#xff0c;请…...

基于pytorch的深度学习基础4——损失函数和优化器

四&#xff0e;损失函数和优化器 4.1 均值初始化 为减轻梯度消失和梯度爆炸&#xff0c;选择合适的权重初值。 十种初始化方法 Initialization Methods 1. Xavie r均匀分布 2. Xavie r正态分布 4. Kaiming正态分布 5. 均匀分布 6. 正态分布 7. 常数分布 8. 正交矩阵初…...

《Clustering Propagation for Universal Medical Image Segmentation》CVPR2024

摘要 这篇论文介绍了S2VNet&#xff0c;这是一个用于医学图像分割的通用框架&#xff0c;它通过切片到体积的传播&#xff08;Slice-to-Volume propagation&#xff09;来统一自动&#xff08;AMIS&#xff09;和交互式&#xff08;IMIS&#xff09;医学图像分割任务。S2VNet利…...

Webpack Source Map 配置详解与优化策略

前言 Source Map 是前端开发和调试中的核心工具之一&#xff0c;它可以显著提高我们在代码调试和错误追踪方面的效率。随着 JavaScript 应用越来越复杂&#xff0c;代码打包和优化成为必然&#xff0c;而这一过程会使得调试变得异常困难。Source Map 的出现&#xff0c;为我们…...

el-tree组件刷新指定id的节点数据

示例 封装一个可以刷新多个指定id的节点数据。 <template><el-tree ref"treeRef"></el-tree> </template><script lang"ts" setup> const treeRef ref()function refreshTreeById(nodeIds: number[]) {nodeIds.forEach((…...

深入 Java 基础 XML:高级特性与最佳实践

在上一篇文章中&#xff0c;我们对 Java 基础 XML 有了一个初步的认识&#xff0c;了解了 XML 的基本结构以及在 Java 中常见的解析方式。今天&#xff0c;我们将进一步深入探讨 Java 与 XML 的结合&#xff0c;包括一些高级特性和最佳实践。 一、XML 命名空间 在复杂的 XML …...

aws(学习笔记第十六课) 使用负载均衡器(ELB)解耦webserver以及输出ELB的日志到S3

aws(学习笔记第十六课) 使用负载均衡器(ELB)以及输出ELB的日志到S3 学习内容&#xff1a; 使用负载均衡器(ELB)解耦web server输出ELB的日志到S3 1. 使用负载均衡器(ELB) 全体架构 使用ELB(Elastic Load Balancer)能够解耦外部internet访问和web server之间的耦合&#xff0c…...

Ubuntu与Centos系统有何区别?

Ubuntu和CentOS都是基于Linux内核的操作系统&#xff0c;但它们在设计理念、使用场景和技术实现上有显著的区别。以下是详细的对比&#xff1a; 1. 基础和发行版本 Ubuntu&#xff1a; 基于Debian&#xff0c;使用.deb包管理系统。包含两个主要版本&#xff1a; LTS&#xff…...

【OpenDRIVE_Python】使用python脚本读取txt指定内容,输出OpenDRIVE数据中对应的信息

示例代码说明&#xff1a; 读取txt指定内容如地物id&#xff0c;输出OpenDRIVE数据中的对应地物id和名称name信息为xml文件 import xml.dom.minidom from xml.dom.minidom import parse from xml.dom import Node import sys import os # 读取OpenDRIVE文件路径 xml_filepath…...

Qt入门8——Qt文件

1. Qt文件概述 文件操作是应用程序必不可少的部分。Qt作为⼀个通用开发库&#xff0c;提供了跨平台的文件操作能力。Qt 提供了很多关于文件的类&#xff0c;通过这些类能够对文件系统进行操作&#xff0c;如文件读写、文件信息获取、文件复制或重命名等。 2. 输入输出设备类 在…...

【每天一道面试题】JWT是什么?Java-jwt是什么?(2024/12/7)

【每天一道面试题】JWT是什么&#xff1f;Java-jwt是什么&#xff1f;&#xff08;2024/12/7) JWT&#xff1a;JSON Web Token 俗称令牌 当我们想实现&#xff1a; 1.用户不用输入用户名和密码就可以登录&#xff08;不用每次都输入用户名和密码&#xff09; 2.用户的信息在传…...

ORB-SLAM3源码学习:ImuTypes.cc:Eigen::Matrix3f RightJacobianSO3计算右雅可比矩阵

前言 计算右雅可比矩阵这个函数涉及到了函数重载&#xff0c;可以接受不同的参数来实现计算右雅可比矩阵。 代码分析 右雅可比矩阵&#xff1a; /** * brief 计算右雅可比* param xyz 李代数* return Jr*/ Eigen::Matrix3f RightJacobianSO3(const float &x, const fl…...

电子公文交换系统设计 ——基于商用密码标准的密码模块的应用

文章目录 《密码系统设计》实验实验项目实验四 密码模块的应用实践要求&#xff08;40 分&#xff09; 《密码系统设计》实验 实验项目 实验序号实验名称实验学时数实验目的实验内容实验类型学生学习预期成果实验四密码模块的应用6基于商用密码标准的密码模块的应用对电子公文…...

java抽象类

目录 一.抽象类 1.什么是抽象类 2.抽象类特点 (1)抽象类不能直接实例化对象 (2)可以包含抽象方法和具体方法 (3)可以有构造方法 (4)抽象类必须被继承&#xff0c;并且继承后子类要重写父类中的抽象方法&#xff0c;否则子类也是抽象类&#xff0c;必须要使用 abstract 修…...

渤海证券基于互联网环境的漏洞主动防护方案探索与实践

来源&#xff1a;中国金融电脑 作者&#xff1a;渤海证券股份有限公司信息技术总部 刘洋 伴随互联网业务的蓬勃发展&#xff0c;证券行业成为黑客进行网络攻击的重要目标之一&#xff0c;网络攻击的形式也变得愈发多样且复杂。网络攻击如同悬于行业之上的达摩克利斯之剑&…...

ClouderaManager 集群搭建

前提&#xff1a;服务器之前做过域名映射、免密登录 ClouderaManager 集群 1. 组件分布规划 服务器服务器h1zk、hdfs(dn)、yarn(nm)、spark、kafka、flumeh2hdfs(nn-standy)、yarn(rm-active)、sparkh3hdfs(nn-active)、yarn(rm-standy)、hive、sparkh4zk、hdfs(dn)、yarn(n…...

Nginx部署PHP服务端跨域以及跨域携带cookie

🤵 作者:coderYYY 🧑 个人简介:前端程序媛,目前主攻web前端,后端辅助,其他技术知识也会偶尔分享🍀欢迎和我一起交流!🚀(评论和私信一般会回!!) 👉 个人专栏推荐:《前端项目教程以及代码》 ✨一、前言 前端技术栈Vue+后端技术栈PHP+Mysql鉴权以及存储信息用…...

流量转发利器之Burpsuite概述(1)

目录 一、Burpsuite Burp Suite Spider 的主要特点&#xff1a; 在 Burp Suite 中使用 Spider&#xff1a; Spider 的用例&#xff1a; 限制&#xff1a; 声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 声明&#xff1a;本文主要用作技…...

【优选算法 二分查找】二分查找算法入门详解:二分查找小专题

x 的平方根 题目解析 算法原理 解法一&#xff1a; 暴力解法 如果要求一个数(x)的平方根&#xff0c;可以从 0 往后枚举&#xff0c;直到有一个数(a)&#xff0c;a^2<x&#xff0c;(a1)^2>x&#xff0c;a即为所求&#xff1b; 解法二&#xff1a;二分查找 …...

LeetCode—56. 合并区间(中等)

题目描述&#xff1a; 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例1&#xff1a; 输入&#x…...

SHELL----正则表达式

一、文本搜索工具——grep grep -参数 条件 文件名 其中参数有以下&#xff1a; -i 忽略大小写 -c 统计匹配的行数 -v 取反&#xff0c;不显示匹配的行 -w 匹配单词 -E 等价于 egrep &#xff0c;即启用扩展正则表达式 -n 显示行号 -rl 将指定目录内的文件打…...

web斗地主游戏实现指北

前后端通信 作为一个即时多人游戏&#xff0c;不论是即时聊天还是更新玩家状态&#xff0c;都需要服务端有主动推送功能&#xff0c;或者客户端轮询。轮询的时间间隔可能导致游玩体验差&#xff0c;因为不即时更新&#xff0c;而且请求数量太多可能会打崩服务器。 建议在cs间…...

ES(elasticsearch)整合Spring boot使用实例

1.1通过docker安装es详细教程参考 docker部署elasticsearch(内涵集群部署的compose文件)-CSDN博客 2.1创建MySQL数据库&#xff0c;通过sql命令进行表的创建与数据的写入&#xff08;sql命令如下&#xff09; /*Navicat Premium Data TransferSource Server : localSo…...

创建简单的 PL/pgSQL 存储过程

文章目录 创建简单的 PL/pgSQL 存储过程CREATE OR REPLACE FUNCTIONadd_two_numbers(a integer, b integer)RETURNS integerAS$$ ... $$函数体LANGUAGE plpgsql 创建带有 IN 和 OUT 参数的存储过程创建修改数据的存储过程创建带有异常处理的复杂存储过程 在 PostgreSQL 中&…...

前端路径“@/“的使用和配置

环境&#xff1a;vitets 需要安装types/node npm install types/node --save-dev在tsconfig.json中添加 如果有tsconfig.app.json和tsconfig.node.json文件&#xff0c;则在app.json中添加 "compilerOptions": {"baseUrl":".","paths&q…...

彻底理解ThreadLocal的应用场景和底层实现

一.概念 定义&#xff1a; ThreadLocal 是 Java 中所提供的线程本地存储机制&#xff0c;可以利用该机制将数据缓存在某个线程内部&#xff0c;该线程可以在任意时刻、任意方法中获取缓存的数据。 其实是可以通过调用 Set() 方法往里面存入值&#xff0c;存入的值是每个线程互…...