【RocketMQ】源码详解:Broker端消息储存流程、消息格式
消息存储流程
入口: org.apache.rocketmq.remoting.netty.NettyRemotingAbstract#processRequestCommand
org.apache.rocketmq.broker.processor.SendMessageProcessor#asyncProcessRequest
消息到达broker后会经过netty的解码、消息处理器等,最后根据不同的消息类型(普通消息,事务消息)有不同的处理,在对普通消息进行处理时,会对延时消息进行额外的处理(ack消息的延时级别大于0,所以会按照延时消息进行处理)
在将消息追加到commitlog中时, 会加锁进行。在写入消息前会按照固定的格式进行编码, 然后会判断文件剩余大小是否足够 消息长度 + 8字节(4字节文件剩余大小,4字节文件尾魔数), 若不够, 则在文件末尾写入8字节的文件结束的数据, 并新建一个文件, 将消息储存.
若空间足够, 则向末尾写入消息.
写入完消息后, 执行刷盘操作和主从复制操作
消息处理器
public void processRequestCommand(final ChannelHandlerContext ctx, final RemotingCommand cmd) {final Pair<NettyRequestProcessor, ExecutorService> matched = this.processorTable.get(cmd.getCode());final Pair<NettyRequestProcessor, ExecutorService> pair = null == matched ? this.defaultRequestProcessor : matched;final int opaque = cmd.getOpaque();if (pair != null) {// 创建处理请求的runnableRunnable run = new Runnable() {@Overridepublic void run() {try {// 前置钩子doBeforeRpcHooks(RemotingHelper.parseChannelRemoteAddr(ctx.channel()), cmd);// 创建响应回调函数final RemotingResponseCallback callback = new RemotingResponseCallback() {@Overridepublic void callback(RemotingCommand response) {// 执行后置钩子方法doAfterRpcHooks(RemotingHelper.parseChannelRemoteAddr(ctx.channel()), cmd, response);if (!cmd.isOnewayRPC()) {if (response != null) {response.setOpaque(opaque);response.markResponseType();try {ctx.writeAndFlush(response);} catch (Throwable e) {// 省略。。。 }} else {// resp为空,不做任何处理,也不返回}}}};/*** 1. 接收消息的处理器为 SendMessageProcessor* 2. 处理消息拉取请求的 PullMessageProcessor*//*** 如果是异步处理器,即AsyncNettyRequestProcessor* 则执行asyncProcessRequest方法,实际上,目前为止rocketmq的处理器都是AsyncNettyRequestProcessor类型的* 但是asyncProcessRequest只有SendMessageProcessor进行了重写,而默认的方法实际上还是同步的方法processRequest*/if (pair.getObject1() instanceof AsyncNettyRequestProcessor) {// 异步处理AsyncNettyRequestProcessor processor = (AsyncNettyRequestProcessor)pair.getObject1();processor.asyncProcessRequest(ctx, cmd, callback);} else {// 同步处理NettyRequestProcessor processor = pair.getObject1();RemotingCommand response = processor.processRequest(ctx, cmd);callback.callback(response);}} catch (Throwable e) {// 省略。。。 }}};// 省略。。。 try {// 提交处理请求的runnablefinal RequestTask requestTask = new RequestTask(run, ctx.channel(), cmd);pair.getObject2().submit(requestTask);} catch (RejectedExecutionException e) {// 省略。。。 }}// 省略。。。
}
/*** 消息处理器入口,接收消息经过此处理器* @param ctx* @param request* @return* @throws RemotingCommandException*/
public CompletableFuture<RemotingCommand> asyncProcessRequest(ChannelHandlerContext ctx, RemotingCommand request)throws RemotingCommandException {final SendMessageContext mqtraceContext;switch (request.getCode()) {// 消费者发送的ACK消息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);}}
}
private CompletableFuture<RemotingCommand> asyncSendMessage(ChannelHandlerContext ctx, RemotingCommand request,SendMessageContext mqtraceContext,SendMessageRequestHeader requestHeader) {// 创建响应对象,包括自动创建topic的逻辑final RemotingCommand response = preSend(ctx, request, requestHeader);final SendMessageResponseHeader responseHeader = (SendMessageResponseHeader)response.readCustomHeader();if (response.getCode() != -1) {return CompletableFuture.completedFuture(response);}final byte[] body = request.getBody();int queueIdInt = requestHeader.getQueueId();TopicConfig topicConfig = this.brokerController.getTopicConfigManager().selectTopicConfig(requestHeader.getTopic());if (queueIdInt < 0) {queueIdInt = randomQueueId(topicConfig.getWriteQueueNums());}MessageExtBrokerInner msgInner = new MessageExtBrokerInner();msgInner.setTopic(requestHeader.getTopic());msgInner.setQueueId(queueIdInt);// 处理重试和死信队列消息,将会对死信消息替换为死信topicif (!handleRetryAndDLQ(requestHeader, response, request, msgInner, topicConfig)) {return CompletableFuture.completedFuture(response);}msgInner.setBody(body);msgInner.setFlag(requestHeader.getFlag());// ....省略CompletableFuture<PutMessageResult> putMessageResult = null;String transFlag = origProps.get(MessageConst.PROPERTY_TRANSACTION_PREPARED);// 如果是事务消息if (transFlag != null && Boolean.parseBoolean(transFlag)) {if (this.brokerController.getBrokerConfig().isRejectTransactionMessage()) {response.setCode(ResponseCode.NO_PERMISSION);response.setRemark("the broker[" + this.brokerController.getBrokerConfig().getBrokerIP1()+ "] sending transaction message is forbidden");return CompletableFuture.completedFuture(response);}putMessageResult = this.brokerController.getTransactionalMessageService().asyncPrepareMessage(msgInner);} else {// 普通消息putMessageResult = this.brokerController.getMessageStore().asyncPutMessage(msgInner);}return handlePutMessageResultFuture(putMessageResult, response, request, msgInner, responseHeader, mqtraceContext, ctx, queueIdInt);
}
消息存储
org.apache.rocketmq.store.DefaultMessageStore#asyncPutMessage
org.apache.rocketmq.store.CommitLog#asyncPutMessage
@Override
public CompletableFuture<PutMessageResult> asyncPutMessage(MessageExtBrokerInner msg) {PutMessageStatus checkStoreStatus = this.checkStoreStatus();if (checkStoreStatus != PutMessageStatus.PUT_OK) {return CompletableFuture.completedFuture(new PutMessageResult(checkStoreStatus, null));}PutMessageStatus msgCheckStatus = this.checkMessage(msg);if (msgCheckStatus == PutMessageStatus.MESSAGE_ILLEGAL) {return CompletableFuture.completedFuture(new PutMessageResult(msgCheckStatus, null));}long beginTime = this.getSystemClock().now();// 调用commitLog储存消息CompletableFuture<PutMessageResult> putResultFuture = this.commitLog.asyncPutMessage(msg);putResultFuture.thenAccept((result) -> {long elapsedTime = this.getSystemClock().now() - beginTime;if (elapsedTime > 500) {log.warn("putMessage not in lock elapsed time(ms)={}, bodyLength={}", elapsedTime, msg.getBody().length);}this.storeStatsService.setPutMessageEntireTimeMax(elapsedTime);if (null == result || !result.isOk()) {this.storeStatsService.getPutMessageFailedTimes().incrementAndGet();}});return putResultFuture;
}
org.apache.rocketmq.store.CommitLog#asyncPutMessage
public CompletableFuture<PutMessageResult> asyncPutMessage(final MessageExtBrokerInner msg) {// Set the storage timemsg.setStoreTimestamp(System.currentTimeMillis());// Set the message body BODY CRC (consider the most appropriate setting// on the client)msg.setBodyCRC(UtilAll.crc32(msg.getBody()));// Back to ResultsAppendMessageResult result = null;StoreStatsService storeStatsService = this.defaultMessageStore.getStoreStatsService();String topic = msg.getTopic();int queueId = msg.getQueueId();final int tranType = MessageSysFlag.getTransactionValue(msg.getSysFlag());if (tranType == MessageSysFlag.TRANSACTION_NOT_TYPE|| tranType == MessageSysFlag.TRANSACTION_COMMIT_TYPE) {// Delay Delivery// 如果是延时级别大于0,则为延时消息if (msg.getDelayTimeLevel() > 0) {if (msg.getDelayTimeLevel() > this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel()) {msg.setDelayTimeLevel(this.defaultMessageStore.getScheduleMessageService().getMaxDelayLevel());}// 设置topic为SCHEDULE_TOPIC_XXXX,表示延时队列的消息topic = TopicValidator.RMQ_SYS_SCHEDULE_TOPIC;queueId = ScheduleMessageService.delayLevel2QueueId(msg.getDelayTimeLevel());// Backup real topic, queueId// 备份topic、queueID,如果是重试消息,则是把%RETRY%的topic放入,因为ack消息在这里之前已经把topic换成了新的MessageAccessor.putProperty(msg, MessageConst.PROPERTY_REAL_TOPIC, msg.getTopic());MessageAccessor.putProperty(msg, MessageConst.PROPERTY_REAL_QUEUE_ID, String.valueOf(msg.getQueueId()));msg.setPropertiesString(MessageDecoder.messageProperties2String(msg.getProperties()));msg.setTopic(topic);msg.setQueueId(queueId);}}InetSocketAddress bornSocketAddress = (InetSocketAddress) msg.getBornHost();if (bornSocketAddress.getAddress() instanceof Inet6Address) {msg.setBornHostV6Flag();}InetSocketAddress storeSocketAddress = (InetSocketAddress) msg.getStoreHost();if (storeSocketAddress.getAddress() instanceof Inet6Address) {msg.setStoreHostAddressV6Flag();}PutMessageThreadLocal putMessageThreadLocal = this.putMessageThreadLocal.get();// 将消息编码, 放入encoderBufferPutMessageResult encodeResult = putMessageThreadLocal.getEncoder().encode(msg);if (encodeResult != null) {return CompletableFuture.completedFuture(encodeResult);}msg.setEncodedBuff(putMessageThreadLocal.getEncoder().encoderBuffer);PutMessageContext putMessageContext = new PutMessageContext(generateKey(putMessageThreadLocal.getKeyBuilder(), msg));long elapsedTimeInLock = 0;MappedFile unlockMappedFile = null;// 加锁putMessageLock.lock(); //spin or ReentrantLock ,depending on store configtry {// 获取到最后一个可写的MappedFileMappedFile mappedFile = this.mappedFileQueue.getLastMappedFile();long beginLockTimestamp = this.defaultMessageStore.getSystemClock().now();this.beginTimeInLock = beginLockTimestamp;// Here settings are stored timestamp, in order to ensure an orderly// globalmsg.setStoreTimestamp(beginLockTimestamp);// 文件为空或者满了,则创建一个if (null == mappedFile || mappedFile.isFull()) {mappedFile = this.mappedFileQueue.getLastMappedFile(0); // Mark: NewFile may be cause noise}if (null == mappedFile) {log.error("create mapped file1 error, topic: " + msg.getTopic() + " clientAddr: " + msg.getBornHostString());beginTimeInLock = 0;return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.CREATE_MAPEDFILE_FAILED, null));}// 向文件最后追加消息result = mappedFile.appendMessage(msg, 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: " + msg.getTopic() + " clientAddr: " + msg.getBornHostString());beginTimeInLock = 0;return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.CREATE_MAPEDFILE_FAILED, result));}result = mappedFile.appendMessage(msg, 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:beginTimeInLock = 0;return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, result));default:beginTimeInLock = 0;return CompletableFuture.completedFuture(new PutMessageResult(PutMessageStatus.UNKNOWN_ERROR, result));}elapsedTimeInLock = this.defaultMessageStore.getSystemClock().now() - beginLockTimestamp;beginTimeInLock = 0;} finally {putMessageLock.unlock();}if (elapsedTimeInLock > 500) {log.warn("[NOTIFYME]putMessage in lock cost time(ms)={}, bodyLength={} AppendMessageResult={}", elapsedTimeInLock, msg.getBody().length, result);}if (null != unlockMappedFile && this.defaultMessageStore.getMessageStoreConfig().isWarmMapedFileEnable()) {this.defaultMessageStore.unlockMappedFile(unlockMappedFile);}PutMessageResult putMessageResult = new PutMessageResult(PutMessageStatus.PUT_OK, result);// StatisticsstoreStatsService.getSinglePutMessageTopicTimesTotal(msg.getTopic()).incrementAndGet();storeStatsService.getSinglePutMessageTopicSizeTotal(topic).addAndGet(result.getWroteBytes());// 刷盘CompletableFuture<PutMessageStatus> flushResultFuture = submitFlushRequest(result, msg);// 主从复制CompletableFuture<PutMessageStatus> replicaResultFuture = submitReplicaRequest(result, msg);return flushResultFuture.thenCombine(replicaResultFuture, (flushStatus, replicaStatus) -> {if (flushStatus != PutMessageStatus.PUT_OK) {putMessageResult.setPutMessageStatus(flushStatus);}if (replicaStatus != PutMessageStatus.PUT_OK) {putMessageResult.setPutMessageStatus(replicaStatus);if (replicaStatus == PutMessageStatus.FLUSH_SLAVE_TIMEOUT) {log.error("do sync transfer other node, wait return, but failed, topic: {} tags: {} client address: {}",msg.getTopic(), msg.getTags(), msg.getBornHostNameString());}}return putMessageResult;});
}
追加消息:org.apache.rocketmq.store.MappedFile#appendMessagesInner
org.apache.rocketmq.store.CommitLog.DefaultAppendMessageCallback#doAppend
public AppendMessageResult appendMessagesInner(final MessageExt messageExt, final AppendMessageCallback cb,PutMessageContext putMessageContext) {assert messageExt != null;assert cb != null;// 获取当前的写入指针位置int currentPos = this.wrotePosition.get();if (currentPos < this.fileSize) {// 文件没被写满// 获取mappedFile 在内存中的映射ByteBuffer byteBuffer = writeBuffer != null ? writeBuffer.slice() : this.mappedByteBuffer.slice();byteBuffer.position(currentPos);AppendMessageResult result;if (messageExt instanceof MessageExtBrokerInner) {// 单条消息result = cb.doAppend(this.getFileFromOffset(), byteBuffer, this.fileSize - currentPos,(MessageExtBrokerInner) messageExt, putMessageContext);} else if (messageExt instanceof MessageExtBatch) {result = cb.doAppend(this.getFileFromOffset(), byteBuffer, this.fileSize - currentPos,(MessageExtBatch) messageExt, putMessageContext);} else {return new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR);}this.wrotePosition.addAndGet(result.getWroteBytes());this.storeTimestamp = result.getStoreTimestamp();return result;}log.error("MappedFile.appendMessage return null, wrotePosition: {} fileSize: {}", currentPos, this.fileSize);return new AppendMessageResult(AppendMessageStatus.UNKNOWN_ERROR);
}
public AppendMessageResult doAppend(final long fileFromOffset, final ByteBuffer byteBuffer, final int maxBlank,final MessageExtBrokerInner msgInner, PutMessageContext putMessageContext) {// 省略事务消息。。。ByteBuffer preEncodeBuffer = msgInner.getEncodedBuff();// 获取消息整体长度(前四个字节为长度)final int msgLen = preEncodeBuffer.getInt(0);// Determines whether there is sufficient free space// 确定是否有足够的可用空间, maxBlank 是 传进来的“this.fileSize - currentPos”, 为剩余大小// 确定消息长度 + 8字节是否大于剩余大小。因为要有8字节存储:4字节的剩余大小,4字节的魔数if ((msgLen + END_FILE_MIN_BLANK_LENGTH) > maxBlank) {// 如果没有足够的空间this.msgStoreItemMemory.clear();// 1 TOTALSIZEthis.msgStoreItemMemory.putInt(maxBlank);// 2 MAGICCODEthis.msgStoreItemMemory.putInt(CommitLog.BLANK_MAGIC_CODE);// 3 The remaining space may be any value// Here the length of the specially set maxBlankfinal long beginTimeMills = CommitLog.this.defaultMessageStore.now();// 向最后写入4字节剩余大小,4字节的文件尾魔数byteBuffer.put(this.msgStoreItemMemory.array(), 0, 8);return new AppendMessageResult(AppendMessageStatus.END_OF_FILE,wroteOffset,maxBlank, /* only wrote 8 bytes, but declare wrote maxBlank for compute write position */msgIdSupplier,msgInner.getStoreTimestamp(),queueOffset,CommitLog.this.defaultMessageStore.now() - beginTimeMills);}int pos = 4 + 4 + 4 + 4 + 4;// 6 QUEUEOFFSETpreEncodeBuffer.putLong(pos, queueOffset);pos += 8;// 7 PHYSICALOFFSETpreEncodeBuffer.putLong(pos, fileFromOffset + byteBuffer.position());int ipLen = (msgInner.getSysFlag() & MessageSysFlag.BORNHOST_V6_FLAG) == 0 ? 4 + 4 : 16 + 4;// 8 SYSFLAG, 9 BORNTIMESTAMP, 10 BORNHOST, 11 STORETIMESTAMPpos += 8 + 4 + 8 + ipLen;// refresh store time stamp in lockpreEncodeBuffer.putLong(pos, msgInner.getStoreTimestamp());final long beginTimeMills = CommitLog.this.defaultMessageStore.now();// Write messages to the queue buffer// 向内存中写入消息byteBuffer.put(preEncodeBuffer);msgInner.setEncodedBuff(null);AppendMessageResult result = new AppendMessageResult(AppendMessageStatus.PUT_OK, wroteOffset, msgLen, msgIdSupplier,msgInner.getStoreTimestamp(), queueOffset, CommitLog.this.defaultMessageStore.now() - beginTimeMills);// 省略。。。return result;
}
消息编码
org.apache.rocketmq.store.CommitLog.MessageExtEncoder#encode(org.apache.rocketmq.store.MessageExtBrokerInner)
// Initialization of storage space 重置buffer
this.resetByteBuffer(encoderBuffer, msgLen);
// 1 TOTALSIZE 消息长度 4字节
this.encoderBuffer.putInt(msgLen);
// 2 MAGICCODE 魔数 4字节
this.encoderBuffer.putInt(CommitLog.MESSAGE_MAGIC_CODE);
// 3 BODYCRC 消息体CRC 4字节
this.encoderBuffer.putInt(msgInner.getBodyCRC());
// 4 QUEUEID queueId 4字节
this.encoderBuffer.putInt(msgInner.getQueueId());
// 5 FLAG 消息flag 4字节
this.encoderBuffer.putInt(msgInner.getFlag());
// 6 QUEUEOFFSET, need update later,消息在consumeQueue的偏移量,需要后续填充 8字节
this.encoderBuffer.putLong(0);
// 7 PHYSICALOFFSET, need update later,消息在commitlog的偏移量,需要后续填充 8字节
this.encoderBuffer.putLong(0);
// 8 SYSFLAG 消息系统flag,例如是否压缩、是否是事务消息 4字节
this.encoderBuffer.putInt(msgInner.getSysFlag());
// 9 BORNTIMESTAMP producer调用消息发送API的时间戳 8字节
this.encoderBuffer.putLong(msgInner.getBornTimestamp());
// 10 BORNHOST producer的IP和端口号 8字节
socketAddress2ByteBuffer(msgInner.getBornHost() ,this.encoderBuffer);
// 11 STORETIMESTAMP 消息存储时间戳 8字节
this.encoderBuffer.putLong(msgInner.getStoreTimestamp());
// 12 STOREHOSTADDRESS broker的IP和端口号 8字节
socketAddress2ByteBuffer(msgInner.getStoreHost() ,this.encoderBuffer);
// 13 RECONSUMETIMES 消息重试次数 4字节
this.encoderBuffer.putInt(msgInner.getReconsumeTimes());
// 14 Prepared Transaction Offset 事务消息物理偏移量 8字节
this.encoderBuffer.putLong(msgInner.getPreparedTransactionOffset());
// 15 BODY 消息体长度 4字节
this.encoderBuffer.putInt(bodyLength);
// 消息体
if (bodyLength > 0)this.encoderBuffer.put(msgInner.getBody());
// 16 TOPIC topic名称长度 1字节
this.encoderBuffer.put((byte) topicLength);
// topic名
this.encoderBuffer.put(topicData);
// 17 PROPERTIES 消息属性长度 2字节
this.encoderBuffer.putShort((short) propertiesLength);
// 消息属性
if (propertiesLength > 0)this.encoderBuffer.put(propertiesData);
encoderBuffer.flip();
相关文章:
【RocketMQ】源码详解:Broker端消息储存流程、消息格式
消息存储流程 入口: org.apache.rocketmq.remoting.netty.NettyRemotingAbstract#processRequestCommand org.apache.rocketmq.broker.processor.SendMessageProcessor#asyncProcessRequest 消息到达broker后会经过netty的解码、消息处理器等,最后根据…...
IoT项目系统架构案例2
项目背景 1.这个项目是对之前的案例的升级改造参考:IoT项目系统架构案例_iot案例_wxgnolux的博客-CSDN博客2.基于方案1的项目实施过程中碰到的问题,对硬件设备标准化的理念及新的功能需求(如根据天气预报温度调水温,APP界面可操作性优化等)•采用目前IoT主流厂商的架…...
Vue echarts封装
做大屏的时候经常会遇到 echarts 展示,下面展示在 Vue2.7 / Vue3 中对 echarts (^5.4.0) 的简单封装。 文章首发于https://blog.fxss.work/vue/echarts封装.html,样例查看 echarts 封装使用 props 说明 参数说明类型可选值默认…...
蓝桥杯入门即劝退(二十二)反转字符(不走寻常路)
欢迎关注点赞评论,共同学习,共同进步! ------持续更新蓝桥杯入门系列算法实例-------- 如果你也喜欢Java和算法,欢迎订阅专栏共同学习交流! 你的点赞、关注、评论、是我创作的动力! -------希望我的文章…...
数据仓库Hive
HIve介绍 Hive是建立在Hadoop上的数据仓库基础构架。它提供了一系列的工具,可以用来进行数据提取转化加载,可以简称为ETL。 Hive 定义了简单的类SQL查询语言,称为HQL,它允许熟悉SQL的用户直接查询Hadoop中的数据…...
嵌入式 STM32 步进电机驱动,干货满满,建议收藏
目录 步进电机 1、步进电机驱动原理 2、步进电机驱动 3、步进电机应用 1、第一步:初始化IO口 2、设置行进方式 四、源码 步进电机 步进电机被广泛应用于ATM机、喷绘机、刻字机、写真机、喷涂设备、医疗仪器及设备、计算机外设及海量存储设备、精密仪器、工业…...
详讲函数.2.
目录 5. 函数的嵌套调用和链式访问 5.1 嵌套调用 5.2 链式访问 小结: 6. 函数的声明和定义 6.1 函数的声明: 6.2 函数的定义: 5. 函数的嵌套调用和链式访问 函数和函数之间可以根据实际的需求进行组合的,也就是互相调用的…...
行测-判断推理-图形推理-位置规律-旋转、翻转
短指针每次逆时针旋转60(排除法选C走人)长指针每次顺时针旋转120选C左上菱形每次顺时针旋转90(排除C D)右上每次旋转180(选B走人)左下每次保持不变右下每次逆时针旋转90选B左上和右上为左右翻转,…...
linux shell 入门学习笔记15 shell 条件测试
概念 shell的条件测试目的是得出真和假。 shell 提供的条件测试语法 test 命令 [] 中括号命令 语法*: test条件测试 test命令用来评估一个表达式,他的结果是真,还是假,如果条件为真,那么命令执行状态结果就为0&…...
Apollo(阿波罗)分布式配置安装详解
Apollo(阿波罗) Apollo(阿波罗)是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性&#…...
Vue3之组件
何为组件 组件化的概念已经提出了很多年了,但是何为组件呢?组件有啥优势?本文将会做出解答,首先我们需要弄清楚何为组件。在VUE的官网中的解释是: 组件允许我们将 UI 划分为独立的、可重用的部分,并且可以对…...
【网络】套接字 -- UDP
🥁作者: 华丞臧. 📕专栏:【网络】 各位读者老爷如果觉得博主写的不错,请诸位多多支持(点赞收藏关注)。如果有错误的地方,欢迎在评论区指出。 推荐一款刷题网站 👉 LeetCode刷题网站 文章…...
Lambda原理及应用
Lambda原理及应用 Lambda介绍 Lambda 是 JDK8 以后版本推出的一个新特性,也是一个重要的版本更新,利用 Lambda 可以简化内部类,可以更方便的进行集合的运算,让你的代码看起来更加简洁,也能提升代码的运行效率。 Lambda语法 非…...
运动耳机推荐、最值得入手的运动耳机清单共享
现在市面上各式各样的运动蓝牙耳机着实让人挑花了眼,怎样才能从纷繁复杂的市场中挑选出专业性、安全性、舒适性等各个方面都做地可圈可点的运动蓝牙耳机可真不是一件易事啊,甚至连不少老朋友都会踩坑,为了能让大家挑到真正的运动蓝牙耳机,为此…...
c盘爆满--如何清理电脑C盘
问题 c盘饱满很多天了,今天终于忍无可忍,开始展开对c盘的处理 c盘的基本处理有两步, 第一步,电脑系统清理 1,c盘右键属性,有个磁盘清理,好像是系统更新的一些缓存资源,可以直接清理 当然这只…...
Nginx配置web服务器及部署反向代理
Nginx配置web服务器及部署反向代理配置web服务器location语法部署反向代理代理转发配置web服务器 项目部署到linux上的静态文件代理给Nginx处理。当访问服务器IP时,可以自动返回静态文件主页。 主配置文件中server块对应的次配置include /etc/nginx/conf.d/*.conf…...
mvvm和mvc
mvvm是model-view-viewmodel的缩写,前端开发的架构模式 m: model:模型,指的是数据和交互业务逻辑 v: view:视图,用户看到的ui界面 vm: viewmodel:视图模型࿰…...
JavaScript while 循环
JavaScript while 循环的目的是为了反复执行语句或代码块。只要指定条件为 true,循环就可以一直执行代码块。while 循环while 循环会在指定条件为真时循环执行代码块。语法while (条件){需要执行的代码 }实例本例中的循环将继续运行,只要变量 i 小于 5&a…...
CMU15-445 Project.0总结
在线测试 本地测试 Project #0 - C Primer 以下是Project #0的网址,2022FALL的Project #0本质上是实现一棵字典树,关于字典树的相关内容可以参考C实现字典树。 在本题中,为了存储对应着字符串的任意类型值,题目设计了一个Tri…...
计算机网络题库---错题本
(一)老生常谈 第一章: 1.什么是计算机网络?其主要功能是什么? 解答: 利用通信设备和线路,将分布在地理位置不同的、功能独立的多个计算机系统连接起来,以功能完善的网络软件实现网…...
【react】react创建项目与引入AntD组件库:
文章目录一、初始化项目:【1】创建项目【2】暴露项目配置文件【3】安装依赖【4】配置less二、快捷键:【1】rcctab三、安装AntD组件库:【1】安装【2】index.js【3】问题:【4】效果:一、初始化项目: 【1】创…...
hook与mixin
看完vue3就开始看vue3的源码,表示很懵~ 刚把rollup打包搞完,这不响应式就接着来了!,还是写篇直接使用vue3的博客清清脑吧! 什么是hook、mixin? mixin: Vue2中多个组件内存在重复JS业务逻辑,使…...
【C语言】自定义类型
一、什么是自定义类型C语言提供了丰富的内置类型,常见的有int, char, float, double, 以及各种指针。除此之外,我们还能自己创建一些类型,这些类型称为自定义类型,如数组,结构体,枚举类型和联合体类型。二、…...
没有上司的舞会(C++,树形DP)
题目描述 某大学有 nnn 个职员,编号为 1…n1\ldots n1…n。 他们之间有从属关系,也就是说他们的关系就像一棵以校长为根的树,父结点就是子结点的直接上司。 现在有个周年庆宴会,宴会每邀请来一个职员都会增加一定的快乐指数 ri…...
【java基础】static和final关键字的作用及其用法详解
文章目录static关键字静态字段静态方法静态代码块静态内部类final关键字final字段final方法final类static关键字 这个关键字表示静态的,用于不同地方意思不一样 静态字段 如果我们将其作用到字段上,那么该字段为类所拥有,我们使用new关键字…...
#集成学习#:bagging、boosting、stacking和blending
集成学习是一种机器学习方法,旨在提高单个模型的性能和鲁棒性。它基于这样一个假设:通过结合多个模型的预测结果,可以获得更好的预测性能,因为每个模型都可能从数据中提取不同的信息,因此他们的错误也可能是不同的&…...
NCRE计算机等级考试Python真题(一)
第一套试题1、关于数据的存储结构,以下选项描述正确的是A.数据所占的存储空间量B.数据在计算机中的顺序存储方式C.数据的逻辑结构在计算机中的表示D.存储在外存中的数据正确答案: C2、关于线性链表的描述,以下选项中正确的是A.存储空间不一定…...
C#协变逆变
文章目录协变协变接口的实现逆变里氏替换原则协变 协变概念令人费解,多半是取名或者翻译的锅,其实是很容易理解的。 比如大街上有一只狗,我说大家快看,这有一只动物!这个非常自然,虽然动物并不严格等于狗…...
算法设计与分析期末考试复习(四)
贪心算法(Greedy Algorithm) 找零钱问题 假设有4种硬币,面值分别为:二角五分、一角、五分和一分,现在要找给顾客六角三分钱,如何找使得给出的硬币个数最少? 首先选出1个面值不超过六角三分的最…...
qsort函数排序数据 and 模拟实现qosrt函数(详解)
前言:内容包括使用库函数qsort排序任意类型的数据,模拟实现qsort函数(冒泡排序的逻辑) 我们先了解qsort函数的语法:qsort函数默认按照升序排序数据 void qsort (void* base, size_t num, size_t size,int (*compar)(…...
为什么说做网站赚钱/营销方式有哪几种
深入Collection之ConCurrentHashMap(JDK7) 前言 有关Collection中Map的重要性不用多说,这种K-V的存储结构在Java中使用十分广泛。单线程中,HashMap已经足够使用。而多线程中,HasMap已经满足不了正常的并发使用。而…...
网站后端架构如何做/广州软文推广公司
MYSQL冷备份恢复1. SHOW VARIABLES LIKE %data% 查看datadir目录,这就是所有数据的备份目录,好像只有innodb的可以直接覆盖备份2. 停掉MYSQL服务,然后整个目录打包3. 到另一台win电脑上,同样的,找到那台电脑的data目录…...
做卖车的网站有哪些/百度一下百度搜索官网
基本数据类型转换 基本介绍 Golang 和 java / c 不同,Go 在不同类型的变量之间赋值时需要显式转换。也就是说 Golang 中数据类型不能自动转换。基本语法表达式 T(v) 将值 v 转换为类型 T T: 就是数据类型,比如 int32,int64&…...
常州企业网站建设/seo中国是什么
设计意图:初始化一次spawn对象,执行ssh后下发,一系列cmds。网上大部分时通过ssh rootip cmd的方法,这样的话,我就之需要通过for调用:def SSH_COMMANDS(ip,user,passwd,command):try:ssh_pc pexpect.spawn(…...
杭州网站建设哪家公司好/营销网站类型
在Linux系统下安装docker容器环境 1.容器介绍 1.1 镜像(Image) 镜像可以用来创建Docker 容器,Docker 提供了一个很简单的机制来创建镜像或者更新现有的镜像, 用户甚至可以直接从其他人那里下载一个已经做好的镜像来直接使用。1.…...
南川网站制作/搜狗站长推送工具
VS查看内存布局的方法:Properties - Configuration Properties - C/C - Command Line - Addtion Options添加/d1 reportAllClassLayout。这样会在Debug的输出中显示所有类的内存布局。如果要指定只查看某个类的,比如有个class A,则只要添加/d…...