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

做网站百度百科/打开百度官网

做网站百度百科,打开百度官网,如何做企业网站优化,wordpress 建立后台默认用户1.绪论 在深度解析RocketMq源码-高可用存储组件(一) raft协议详解-CSDN博客 中讲过,raft协议中,日志同步主要有两个地方,一个是leader会跟follower同步数据,另一个是在新leader诞生的时候,会与…

1.绪论

在深度解析RocketMq源码-高可用存储组件(一) raft协议详解-CSDN博客 中讲过,raft协议中,日志同步主要有两个地方,一个是leader会跟follower同步数据,另一个是在新leader诞生的时候,会与以前的follower进行日志匹配,作一致性校验。而在Dledger的进行日志校验的组件就是DLedgerEntryPusher,接下来我们将探索该组件的源码。

2.DLedgerEntryPusher的组成

数据同步组件,主要是leader写入数据后,将消息同步给follower & 一致性检查过后,leader与消息保持索引一致

//数据同步组件,主要是leader写入数据后,将消息同步给follower & 一致性检查过后,leader与消息保持索引一致
public class DLedgerEntryPusher {private static Logger logger = LoggerFactory.getLogger(DLedgerEntryPusher.class);//Dledger的配置private DLedgerConfig dLedgerConfig;//所属的存储组件private DLedgerStore dLedgerStore;//当前节点的元信息private final MemberState memberState;//负责网络请求的组件private DLedgerRpcService dLedgerRpcService;//key:term <key:peerId value:每个阶段最后的一条日志Index>private Map<Long, ConcurrentMap<String, Long>> peerWaterMarksByTerm = new ConcurrentHashMap<>();//key:term <key:日志的index id value:写入结果,是一个future>private Map<Long, ConcurrentMap<Long, TimeoutFuture<AppendEntryResponse>>> pendingAppendResponsesByTerm = new ConcurrentHashMap<>();//follower使用 接收leader的日志同步,然后按照顺序写入到日志文件中private EntryHandler entryHandler;//raft的日志写入是通过二阶段提交实现,如果第一阶段,超过半数follower写入成功,就认为数据写入成功,该组件就是检查是否follower是否写入成功的private QuorumAckChecker quorumAckChecker;//leader使用 将不同的命令同步给followerprivate Map<String, EntryDispatcher> dispatcherMap = new HashMap<>();//将命令应用到状态机的组件private Optional<StateMachineCaller> fsmCaller;
}

3.DLedger每条消息的组成结构-DLedgerEntry

可以看出每条消息都包含term和index这两个关键信息

public class DLedgerEntry {public final static int POS_OFFSET = 4 + 4 + 8 + 8;public final static int HEADER_SIZE = POS_OFFSET + 8 + 4 + 4 + 4;public final static int BODY_OFFSET = HEADER_SIZE + 4;//魔数private int magic;//消息大小private int size;//日志索引private long index;//termprivate long term;//物理位置private long pos; //used to validate dataprivate int channel; //reservedprivate int chainCrc; //like the block chain, this crc indicates any modification before this entry.private int bodyCrc; //the crc of the body//真正的消息体private byte[] body;
}

4.同步消息的步骤

4.1 leader发送同步请求-EntryDispatcher

leader节点会给每个follower构建一个EntryDispatcher,专门用来进行数据同步。

4.1.1 组件组成

该组件是follower同步leader数据的组件,有4种可能:
1.APPEND: 将消息同步到follower的commitLog中;
2.COMPARE:  当leader发生变更,leader会与所有的follower进行一致性检查;
3.TRUNCATE: 如果leader检查完成过后,发现follower的最后一条索引更大,需要将他删除掉;
4.COMMIT: 当leader超过一半的节点都append日志成功过后,leader会进行commit,这个时候会发送

//该组件是follower同步leader数据的组件,有4种可能
//1.APPEND:将消息同步到follower的commitLog中//2.COMPARE: 当leader发生变更,leader会与所有的follower进行一致性检查//3.TRUNCATE:如果leader检查完成过后,发现follower的最后一条索引更大,需要将他删除掉//4.COMMIT:当leader超过一半的节点都append日志成功过后,leader会进行commit,这个时候会发送一个请求通知所有的follower进行commit
private class EntryDispatcher extends ShutdownAbleThread {//需要对日志进行处理的类型private AtomicReference<PushEntryRequest.Type> type = new AtomicReference<>(PushEntryRequest.Type.COMPARE);//上一次同步日志的时间private long lastPushCommitTimeMs = -1;//节点id 给哪个节点使用的private String peerId;//已经发送过比较请求的索引private long compareIndex = -1;//已经发送过同步请求的索引private long writeIndex = -1;//最大有多少条日志没有同步private int maxPendingSize = 1000;//周期private long term = -1;//leader的idprivate String leaderId = null;//上一次check的时间private long lastCheckLeakTimeMs = System.currentTimeMillis();//同步索引过后,会在次等待结果
}

4.1.2 主要功能

其实是一个死循环,一直调用doWork()方法

public void doWork() {try {//判断当前节点是否是leader节点if (!checkAndFreshState()) {waitForRunning(1);return;}if (type.get() == PushEntryRequest.Type.APPEND) {if (dLedgerConfig.isEnableBatchPush()) {doBatchAppend();} else {//如果是append进行append超过doAppend();}} else {//如果是一致性检测进行doComparedoCompare();}waitForRunning(1);} catch (Throwable t) {DLedgerEntryPusher.logger.error("[Push-{}]Error in {} writeIndex={} compareIndex={}", peerId, getName(), writeIndex, compareIndex, t);changeState(-1, PushEntryRequest.Type.COMPARE);DLedgerUtils.sleep(500);}}}

4.1.3 同步日志步骤

1.发送日志预提交请求
//同步那条索引的数据
private void doAppendInner(long index) throws Exception {//根据index获取到commitLog中的数据,其实首先从indexfile中获取到日志的具体的物理偏移量,再根据物理偏移量获取到对应的数据DLedgerEntry entry = getDLedgerEntryForAppend(index);if (null == entry) {return;}//流量控制checkQuotaAndWait(entry);//构建append请求PushEntryRequest request = buildPushRequest(entry, PushEntryRequest.Type.APPEND);//通过网络请求,发送请求CompletableFuture<PushEntryResponse> responseFuture = dLedgerRpcService.push(request);//加入peddingMap中,便是第index条索引正在同步pendingMap.put(index, System.currentTimeMillis());responseFuture.whenComplete((x, ex) -> {try {PreConditions.check(ex == null, DLedgerResponseCode.UNKNOWN);DLedgerResponseCode responseCode = DLedgerResponseCode.valueOf(x.getCode());switch (responseCode) {case SUCCESS:pendingMap.remove(x.getIndex());//如果发送请求成功 便更新当前节点已经发送请求的最新的一条索引updatePeerWaterMark(x.getTerm(), peerId, x.getIndex());quorumAckChecker.wakeup();break;case INCONSISTENT_STATE:logger.info("[Push-{}]Get INCONSISTENT_STATE when push index={} term={}", peerId, x.getIndex(), x.getTerm());changeState(-1, PushEntryRequest.Type.COMPARE);break;default:logger.warn("[Push-{}]Get error response code {} {}", peerId, responseCode, x.baseInfo());break;}} catch (Throwable t) {logger.error("", t);}});//更新最后的同步时间lastPushCommitTimeMs = System.currentTimeMillis();
}
//构建日志的请求
private PushEntryRequest buildPushRequest(DLedgerEntry entry, PushEntryRequest.Type target) {PushEntryRequest request = new PushEntryRequest();//所属组request.setGroup(memberState.getGroup());//需要发送的节点idrequest.setRemoteId(peerId);//当前leaderIdrequest.setLeaderId(leaderId);//自己的idrequest.setLocalId(memberState.getSelfId());//当前周期request.setTerm(term);//日志内容request.setEntry(entry);//消息类型request.setType(target);//主节点中最新一条commit过的日志索引 通过它可以将已经提交过的索引带给follower,follower收到过后可以将该索引之间的日志进行提交request.setCommitIndex(dLedgerStore.getCommittedIndex());return request;
}
2.进行commit操作
private void doCommit() throws Exception {//如果超过上次1000ms,才提交commit请求 实现批量commit的功能if (DLedgerUtils.elapsed(lastPushCommitTimeMs) > 1000) {//发送commit请求PushEntryRequest request = buildPushRequest(null, PushEntryRequest.Type.COMMIT);//Ignore the resultsdLedgerRpcService.push(request);lastPushCommitTimeMs = System.currentTimeMillis();}
}

4.1.4 leader的一致性检测步骤

其实就是比较follower的最后一条索引的合法性,如果存在脏数据,便构建truncate请求删掉。如果比较失败,便将自己的最新的index减1发送到follower再次比对,直到成功。

  private void doCompare() throws Exception {while (true) {if (!checkAndFreshState()) {break;}if (type.get() != PushEntryRequest.Type.COMPARE&& type.get() != PushEntryRequest.Type.TRUNCATE) {break;}if (compareIndex == -1 && dLedgerStore.getLedgerEndIndex() == -1) {break;}//revise the compareIndex//设置commpare的开始比较的索引为leader的写入额度最后一条索引if (compareIndex == -1) {compareIndex = dLedgerStore.getLedgerEndIndex();logger.info("[Push-{}][DoCompare] compareIndex=-1 means start to compare", peerId);} else if (compareIndex > dLedgerStore.getLedgerEndIndex() || compareIndex < dLedgerStore.getLedgerBeginIndex()) {logger.info("[Push-{}][DoCompare] compareIndex={} out of range {}-{}", peerId, compareIndex, dLedgerStore.getLedgerBeginIndex(), dLedgerStore.getLedgerEndIndex());compareIndex = dLedgerStore.getLedgerEndIndex();}//获取entryDLedgerEntry entry = dLedgerStore.get(compareIndex);PreConditions.check(entry != null, DLedgerResponseCode.INTERNAL_ERROR, "compareIndex=%d", compareIndex);//封装compare请求 & 发送请求PushEntryRequest request = buildPushRequest(entry, PushEntryRequest.Type.COMPARE);CompletableFuture<PushEntryResponse> responseFuture = dLedgerRpcService.push(request);PushEntryResponse response = responseFuture.get(3, TimeUnit.SECONDS);PreConditions.check(response != null, DLedgerResponseCode.INTERNAL_ERROR, "compareIndex=%d", compareIndex);PreConditions.check(response.getCode() == DLedgerResponseCode.INCONSISTENT_STATE.getCode() || response.getCode() == DLedgerResponseCode.SUCCESS.getCode(), DLedgerResponseCode.valueOf(response.getCode()), "compareIndex=%d", compareIndex);long truncateIndex = -1;//如果比较成功,证明此时leader和follower的数据是一致的,但是follower的最后一条索引的位置大于leader的最后一条的位置的话,证明follower有脏数据,需要删除if (response.getCode() == DLedgerResponseCode.SUCCESS.getCode()) {/** The comparison is successful:* 1.Just change to append state, if the follower's end index is equal the compared index.* 2.Truncate the follower, if the follower has some dirty entries.*/if (compareIndex == response.getEndIndex()) {changeState(compareIndex, PushEntryRequest.Type.APPEND);break;} else {truncateIndex = compareIndex;}//如果follower的位置不在leader的索引位置之间,有脏数据,需要删除} else if (response.getEndIndex() < dLedgerStore.getLedgerBeginIndex()|| response.getBeginIndex() > dLedgerStore.getLedgerEndIndex()) {/*The follower's entries does not intersect with the leader.This usually happened when the follower has crashed for a long time while the leader has deleted the expired entries.Just truncate the follower.*/truncateIndex = dLedgerStore.getLedgerBeginIndex();} else if (compareIndex < response.getBeginIndex()) {/*The compared index is smaller than the follower's begin index.This happened rarely, usually means some disk damage.Just truncate the follower.*/truncateIndex = dLedgerStore.getLedgerBeginIndex();} else if (compareIndex > response.getEndIndex()) {/*The compared index is bigger than the follower's end index.This happened frequently. For the compared index is usually starting from the end index of the leader.*/compareIndex = response.getEndIndex();//如果比较失败,即follower的index在leader的index之间,但是小于leader的index,便leader的index-- 再进行比较} else {/*Compare failed and the compared index is in the range of follower's entries.*/compareIndex--;}/*The compared index is smaller than the leader's begin index, truncate the follower.*/if (compareIndex < dLedgerStore.getLedgerBeginIndex()) {truncateIndex = dLedgerStore.getLedgerBeginIndex();}/*If get value for truncateIndex, do it right now.*/if (truncateIndex != -1) {changeState(truncateIndex, PushEntryRequest.Type.TRUNCATE);doTruncate(truncateIndex);break;}}}

4.2 leader检测是否有半数以上follower写入成功-QuorumAckChecker 

    private class QuorumAckChecker extends ShutdownAbleThread {private long lastPrintWatermarkTimeMs = System.currentTimeMillis();private long lastCheckLeakTimeMs = System.currentTimeMillis();private long lastQuorumIndex = -1;public QuorumAckChecker(Logger logger) {super("QuorumAckChecker-" + memberState.getSelfId(), logger);}@Overridepublic void doWork() {try {if (DLedgerUtils.elapsed(lastPrintWatermarkTimeMs) > 3000) {if (DLedgerEntryPusher.this.fsmCaller.isPresent()) {//获取上次已经被应用到状态机的indexfinal long lastAppliedIndex = DLedgerEntryPusher.this.fsmCaller.get().getLastAppliedIndex();logger.info("[{}][{}] term={} ledgerBegin={} ledgerEnd={} committed={} watermarks={} appliedIndex={}",memberState.getSelfId(), memberState.getRole(), memberState.currTerm(), dLedgerStore.getLedgerBeginIndex(), dLedgerStore.getLedgerEndIndex(), dLedgerStore.getCommittedIndex(), JSON.toJSONString(peerWaterMarksByTerm), lastAppliedIndex);} else {logger.info("[{}][{}] term={} ledgerBegin={} ledgerEnd={} committed={} watermarks={}",memberState.getSelfId(), memberState.getRole(), memberState.currTerm(), dLedgerStore.getLedgerBeginIndex(), dLedgerStore.getLedgerEndIndex(), dLedgerStore.getCommittedIndex(), JSON.toJSONString(peerWaterMarksByTerm));}lastPrintWatermarkTimeMs = System.currentTimeMillis();}if (!memberState.isLeader()) {waitForRunning(1);return;}long currTerm = memberState.currTerm();checkTermForPendingMap(currTerm, "QuorumAckChecker");checkTermForWaterMark(currTerm, "QuorumAckChecker");if (pendingAppendResponsesByTerm.size() > 1) {//raft协议规定,每个leader只能commit自己term的请求,如果还存在其他周期待确认的日志,直接返回term已经改变,并且移除掉for (Long term : pendingAppendResponsesByTerm.keySet()) {if (term == currTerm) {continue;}for (Map.Entry<Long, TimeoutFuture<AppendEntryResponse>> futureEntry : pendingAppendResponsesByTerm.get(term).entrySet()) {AppendEntryResponse response = new AppendEntryResponse();response.setGroup(memberState.getGroup());response.setIndex(futureEntry.getKey());response.setCode(DLedgerResponseCode.TERM_CHANGED.getCode());response.setLeaderId(memberState.getLeaderId());logger.info("[TermChange] Will clear the pending response index={} for term changed from {} to {}", futureEntry.getKey(), term, currTerm);futureEntry.getValue().complete(response);}pendingAppendResponsesByTerm.remove(term);}}if (peerWaterMarksByTerm.size() > 1) {for (Long term : peerWaterMarksByTerm.keySet()) {if (term == currTerm) {continue;}logger.info("[TermChange] Will clear the watermarks for term changed from {} to {}", term, currTerm);peerWaterMarksByTerm.remove(term);}}//获取当前周期每个节点的最新一条发送成功的索引Map<String, Long> peerWaterMarks = peerWaterMarksByTerm.get(currTerm);//根据索引进行排序List<Long> sortedWaterMarks = peerWaterMarks.values().stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());//获取中间的数,比如现在有4个followerA B C D,分别已经发送成功的索引为[5 7 4 9],根据排序取中间数为5,一定是超过半数的节点发送成功的索引是大于5的,所以leader可以commitlong quorumIndex = sortedWaterMarks.get(sortedWaterMarks.size() / 2);final Optional<StateMachineCaller> fsmCaller = DLedgerEntryPusher.this.fsmCaller;if (fsmCaller.isPresent()) {// If there exist statemachine//如果存在状态机,便leader自己提交索引,并且将其应用到状态机DLedgerEntryPusher.this.dLedgerStore.updateCommittedIndex(currTerm, quorumIndex);final StateMachineCaller caller = fsmCaller.get();caller.onCommitted(quorumIndex);// Check elapsedif (DLedgerUtils.elapsed(lastCheckLeakTimeMs) > 1000) {updatePeerWaterMark(currTerm, memberState.getSelfId(), dLedgerStore.getLedgerEndIndex());checkResponseFuturesElapsed(caller.getLastAppliedIndex());lastCheckLeakTimeMs = System.currentTimeMillis();}if (quorumIndex == this.lastQuorumIndex) {waitForRunning(1);}} else {dLedgerStore.updateCommittedIndex(currTerm, quorumIndex);ConcurrentMap<Long, TimeoutFuture<AppendEntryResponse>> responses = pendingAppendResponsesByTerm.get(currTerm);boolean needCheck = false;int ackNum = 0;for (Long i = quorumIndex; i > lastQuorumIndex; i--) {try {CompletableFuture<AppendEntryResponse> future = responses.remove(i);if (future == null) {needCheck = true;break;} else if (!future.isDone()) {AppendEntryResponse response = new AppendEntryResponse();response.setGroup(memberState.getGroup());response.setTerm(currTerm);response.setIndex(i);response.setLeaderId(memberState.getSelfId());response.setPos(((AppendFuture) future).getPos());future.complete(response);}ackNum++;} catch (Throwable t) {logger.error("Error in ack to index={} term={}", i, currTerm, t);}}if (ackNum == 0) {checkResponseFuturesTimeout(quorumIndex + 1);waitForRunning(1);}if (DLedgerUtils.elapsed(lastCheckLeakTimeMs) > 1000 || needCheck) {updatePeerWaterMark(currTerm, memberState.getSelfId(), dLedgerStore.getLedgerEndIndex());checkResponseFuturesElapsed(quorumIndex);lastCheckLeakTimeMs = System.currentTimeMillis();}}lastQuorumIndex = quorumIndex;} catch (Throwable t) {DLedgerEntryPusher.logger.error("Error in {}", getName(), t);DLedgerUtils.sleep(100);}}}

4.3 follower收到日志同步请求的处理

构建响应的步骤如下:


private PushEntryResponse buildResponse(PushEntryRequest request, int code) {//够级pushEntry的响应PushEntryResponse response = new PushEntryResponse();response.setGroup(request.getGroup());//设置响应码response.setCode(code);//当前周期response.setTerm(request.getTerm());if (request.getType() != PushEntryRequest.Type.COMMIT) {response.setIndex(request.getFirstEntryIndex());response.setCount(request.getCount());}//设置当前从节点的开始和结束索引response.setBeginIndex(dLedgerStore.getLedgerBeginIndex());response.setEndIndex(dLedgerStore.getLedgerEndIndex());return response;}

4.3.1 follower处理删除请求

    private void doTruncate(long truncateIndex) throws Exception {PreConditions.check(type.get() == PushEntryRequest.Type.TRUNCATE, DLedgerResponseCode.UNKNOWN);//构建请求DLedgerEntry truncateEntry = dLedgerStore.get(truncateIndex);PreConditions.check(truncateEntry != null, DLedgerResponseCode.UNKNOWN);logger.info("[Push-{}]Will push data to truncate truncateIndex={} pos={}", peerId, truncateIndex, truncateEntry.getPos());//发送请求PushEntryRequest truncateRequest = buildPushRequest(truncateEntry, PushEntryRequest.Type.TRUNCATE);PushEntryResponse truncateResponse = dLedgerRpcService.push(truncateRequest).get(3, TimeUnit.SECONDS);PreConditions.check(truncateResponse != null, DLedgerResponseCode.UNKNOWN, "truncateIndex=%d", truncateIndex);PreConditions.check(truncateResponse.getCode() == DLedgerResponseCode.SUCCESS.getCode(), DLedgerResponseCode.valueOf(truncateResponse.getCode()), "truncateIndex=%d", truncateIndex);lastPushCommitTimeMs = System.currentTimeMillis();//删除完成过后,再把动作调整为appendchangeState(truncateIndex, PushEntryRequest.Type.APPEND);}

4.3.2 follower处理追加请求

//处理append
private void handleDoAppend(long writeIndex, PushEntryRequest request,CompletableFuture<PushEntryResponse> future) {try {PreConditions.check(writeIndex == request.getEntry().getIndex(), DLedgerResponseCode.INCONSISTENT_STATE);//调用appendAsFollower方法直接建数据写入到commitLog中DLedgerEntry entry = dLedgerStore.appendAsFollower(request.getEntry(), request.getTerm(), request.getLeaderId());PreConditions.check(entry.getIndex() == writeIndex, DLedgerResponseCode.INCONSISTENT_STATE);//够建立返回请求future.complete(buildResponse(request, DLedgerResponseCode.SUCCESS.getCode()));//更新commitIndex,主节点会将自己在当前term的commitIndex发送过来updateCommittedIndex(request.getTerm(), request.getCommitIndex());} catch (Throwable t) {logger.error("[HandleDoAppend] writeIndex={}", writeIndex, t);future.complete(buildResponse(request, DLedgerResponseCode.INCONSISTENT_STATE.getCode()));}}​

4.3.3 follower处理一致性比较请求

       private CompletableFuture<PushEntryResponse> handleDoCompare(long compareIndex, PushEntryRequest request,CompletableFuture<PushEntryResponse> future) {try {PreConditions.check(compareIndex == request.getEntry().getIndex(), DLedgerResponseCode.UNKNOWN);PreConditions.check(request.getType() == PushEntryRequest.Type.COMPARE, DLedgerResponseCode.UNKNOWN);//获取需要比较的索引数据,构建成响应返回给leaderDLedgerEntry local = dLedgerStore.get(compareIndex);PreConditions.check(request.getEntry().equals(local), DLedgerResponseCode.INCONSISTENT_STATE);future.complete(buildResponse(request, DLedgerResponseCode.SUCCESS.getCode()));} catch (Throwable t) {logger.error("[HandleDoCompare] compareIndex={}", compareIndex, t);future.complete(buildResponse(request, DLedgerResponseCode.INCONSISTENT_STATE.getCode()));}return future;}

相关文章:

深度解析RocketMq源码-高可用存储组件(四)Dledger框架日志同步流程

1.绪论 在深度解析RocketMq源码-高可用存储组件&#xff08;一&#xff09; raft协议详解-CSDN博客 中讲过&#xff0c;raft协议中&#xff0c;日志同步主要有两个地方&#xff0c;一个是leader会跟follower同步数据&#xff0c;另一个是在新leader诞生的时候&#xff0c;会与…...

ONLYOFFICE 文档开发者版 8.1:API 更新

随着版本 8.1 新功能的发布&#xff0c;我们更新了编辑器、文档生成器和插件的 API&#xff0c;并添加了 Office API 板块。阅读下文了解详情。 ​ ONLYOFFICE 文档是什么 ONLYOFFICE 文档是一个功能强大的文档编辑器&#xff0c;支持处理文本文档、电子表格、演示文稿、可填写…...

Activemq单节点在Windows下的配置部署

1.环境信息 服务器信息jdk版本activemq版本备注Windows Server 2008R2 Enterprisejdk-17_windows-x64_bin.exeapache-activemq-5.18.42.jdk配置 1.下载jdk 地址: Java Downloads | Oracle 中国 2.上传至Windows服务器,点击安装,在选择安装目录页面,选择合适的安装目录即…...

SpringBoot-注解@ImportResource引入自定义spring的配置xml文件和配置类

1、注解ImportResource 我们知道Spring的配置文件是可以有很多个的&#xff0c;我们在web.xml中如下配置就可以引入它们&#xff1a; SprongBoot默认已经给我们配置好了Spring&#xff0c;它的内部相当于已经有一个配置文件&#xff0c;那么我们想要添加新的配置文件怎么办&am…...

GitLab配置免密登录之后仍然需要Git登录的解决办法

GitLab配置免密登录之后仍然需要Git登录的解决办法 因为实习工作需要&#xff0c;要在本地拉取gitlab上的代码&#xff0c;设置了密钥之后连接的时候还需要登录的token&#xff0c;摸索之后有了下面的解决办法。 方法一&#xff1a; 根据报错的提示&#xff0c;去网站上设置个人…...

探索小众爱好:打造个人韧性与特色之路

在这个信息爆炸的时代&#xff0c;我们很容易陷入“千篇一律”的漩涡中&#xff0c;无论是生活方式还是兴趣爱好&#xff0c;似乎都趋向于某种“流行”或“热门”。然而&#xff0c;真正的个性与魅力&#xff0c;往往来源于那些不为大众所知的小众爱好。今天&#xff0c;我想和…...

GitHub使用教程(小白版)

看一百篇文章不如自己写一篇 第一步&#xff1a;注册和安装 注册GitHub账号 访问 GitHub官网。点击右上角的 "Sign up" 按钮。按照提示输入你的邮箱、创建用户名和密码&#xff0c;完成注册。 安装Git 访问 Git官网。下载并安装适用于你操作系统的Git。安装…...

深度解析SD-WAN在企业组网中的应用场景

在现代企业快速发展的网络环境中&#xff0c;SD-WAN技术不仅是实现企业各站点间高效连接的关键&#xff0c;也是满足不同站点对互联网、SaaS云应用和公有云等多种业务需求的理想选择。本文将从企业的WAN业务需求出发&#xff0c;对SD-WAN的组网场景进行全面解析&#xff0c;涵盖…...

【INTEL(ALTERA)】Eclipse Nios II SBT 无法从模板创建新应用程序和 BSP

目录 说明 解决方法 说明 您应该能够创建新的应用程序和 BSP 模板包含以下步骤&#xff1a; 选择 Nios II应用程序和 BSP 来自模板。选择您的.sopcinfo 文件并选择模板。从您的工作区单击 选择现有的 BSP 项目。单击 创建。选择所需的 BSP 选项。单击 完成。 但是&#xf…...

Vue_cli搭建过程项目创建

概述 vue-cli 官方提供的一个脚手架&#xff0c;用于快速生成一个 vue 的项目模板&#xff1b;预先定义 好的目录结构及基础代码&#xff0c;就好比咱们在创建 Maven 项目时可以选择创建一个 骨架项目&#xff0c;这个骨架项目就是脚手架&#xff0c;我们的开发更加的快速&am…...

面试题4:POST 比 GET 安全?

不是。HTTP就没有加密功能。 我们知道 GET一般将参数放到URL的查询字符串中&#xff0c;如果是实现登录页面&#xff0c;我们的用户名和密码就直接显示到浏览器的地址栏中了&#xff0c;此时就会轻易的被他人获取账号密码&#xff0c;很不安全。而POST会把参数放到 body 里&am…...

Github生成Personal access tokens及在git中使用

目录 生成Token 使用Token-手工修改 使用Token-自动 生成Token 登录GitHub&#xff0c;在GitHub右上角点击个人资料头像&#xff0c;点击Settings → Developer Settings → Personal access tokens (classic)。 在界面上选择点击【Generate new token】&#xff0c;填写如…...

【BUG记录】条件查询没有查询结果 || MybatisPlus打印查询语句

结论 先说结论&#xff0c;查询没有结果&#xff0c;可能是数据库连接&#xff0c;数据问题之类&#xff0c;最有可能的根本原因是查询语句问题&#xff0c;需要想办法检查查询语句&#xff0c;使用mybatisPlus等自动生成查询语句的框架不能直接看语句&#xff0c;可以依靠日志…...

【C#】找不到属性集方法。get只读属性用了反射设置setValue肯定报错

欢迎来到《小5讲堂》 这是《C#》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 背景 找不到属性集方法。get只读属性用了反射设置setValue肯定报错 报错…...

探索ChatGPT在程序员日常工作的多种应用

引言 在现代科技迅猛发展的今天&#xff0c;人工智能的应用已经深入到我们生活和工作的各个方面。作为程序员&#xff0c;我们时常面临大量繁杂的任务&#xff0c;从代码编写、错误调试到项目管理和团队协作&#xff0c;每一项都需要花费大量的时间和精力。近年来&#xff0c;…...

算法与数据结构——时间复杂度详解与示例(C#,C++)

文章目录 1. 算法与数据结构概述2. 时间复杂度基本概念3. 时间复杂度分析方法4. 不同数据结构的时间复杂度示例5. 如何通过算法优化来提高时间复杂度6. C#中的时间复杂度示例7. 总结 算法与数据结构是计算机科学的核心&#xff0c;它们共同决定了程序的性能和效率。在实际开发中…...

面试题3:GET 和 POST 有什么区别?

[!]高频面试题。 GET 和 POST 没有本质区别&#xff0c;可以进行相互代替。 1、GET语义&#xff1a;“从服务器获取数据”&#xff1b;POST语义&#xff1a;“往服务器上提交数据”。[设计初衷&#xff0c;不一定要遵守] 2、发请求时&#xff0c;给服务器传递的数据&#xff…...

探索QCS6490目标检测AI应用开发(三):模型推理

作为《探索QCS6490目标检测AI应用开发》文章&#xff0c;紧接上一期&#xff0c;我们介绍如何在应用程序中介绍如何使用解码后的视频帧结合Yolov8n模型推理。 高通 Qualcomm AI Engine Direct 是一套能够针对高通AI应用加速的软件SDK&#xff0c;更多的内容可以访问&#xff1a…...

C# 静态类中构造、字段和属性等的执行顺序,含有单例模式分析

C# 静态类时我们实战项目开发中用的非常多的。有些时候可能他的执行顺序并非如我们认为的那样&#xff0c;那就快速来看一下吧&#xff01; 在C#中&#xff0c;静态类的构造函数是在第一次访问该类的任何成员时执行的。静态构造函数是不可继承的&#xff0c;并且在访问静态类的…...

c++设计模式之一创建型模式

1、创建型模式&#xff08;常见的设计模式&#xff09; Factory 模式&#xff08;工厂模式&#xff0c;被实例化的子类&#xff09; 在面向对象系统设计中经常可以遇到以下的两类问题&#xff1a; 下面是第一类问题和代码示例&#xff1a;我们经常会抽象出一些类的公共接口以…...

上古世纪台服注册账号+下载客户端全方位图文教程

又一款新的MMRPG游戏即将上线啦&#xff0c;游戏名称叫做《上古世纪》游戏采用传统MMO类型游戏的玩法&#xff0c;但是开发商采用了先进的游戏引擎&#xff0c;让玩家们可以享受到极致的视觉体验。同时游戏的背景是建立在大陆分崩离析的基础上。各个部落因为领地的原因纷纷开战…...

【Android】Android中继承Activity、Application和AppCompatActivity的区别

在 Android 开发中&#xff0c;Activity、Application 和 AppCompatActivity 是三个重要的类&#xff0c;它们各自有不同的作用和用途&#xff1a; 1. Activity Activity 是 Android 应用中的一个核心组件&#xff0c;代表了用户界面上的一个单一屏幕或交互界面。每个 Activi…...

SQLite 可以随可执行文件部署在用户机器吗

答案是&#xff1a;可以的。 sqlite 本身就是嵌入式的SQL数据库引擎&#xff0c;不需要单独的服务器进程。sqlite 直接读取和写入普通磁盘文件&#xff0c;sqlite 的整个数据库&#xff08;所有表、索引、触发器等&#xff09;都包含在单个磁盘文件中。所以 sqlite 很适合开发…...

大模型的开源不同于传统的开源软件

大模型的开源与传统的开源软件往往有一些不同之处&#xff0c;主要体现在以下几个方面&#xff1a; 数据和许可证的复杂性&#xff1a; 数据依赖性&#xff1a; 大模型通常需要大量的数据来进行训练&#xff0c;这些数据可能来自各种来源&#xff0c;包括公共数据集、专有数据集…...

基于PHP+MySql的留言管理系统的设计与实现

功能概述 网页留言板管理系统&#xff0c;用户层面分为普通用户和管理员&#xff0c;并设权限&#xff08;即后台留言管理系统普通用户不能访问&#xff0c;别人的留言自己不可以修改删除&#xff0c;未登录不能使用留言功能&#xff09;&#xff0c;功能包括用户登录注册、留…...

单目标应用:基于吸血水蛭优化器(Blood-Sucking Leech Optimizer,BSLO)的微电网优化(MATLAB代码)

一、微电网模型介绍 微电网多目标优化调度模型简介_vmgpqv-CSDN博客 参考文献&#xff1a; [1]李兴莘,张靖,何宇,等.基于改进粒子群算法的微电网多目标优化调度[J].电力科学与工程, 2021, 37(3):7 二、吸血水蛭优化器求解微电网 2.1算法简介 吸血水蛭优化器&#xff08;B…...

嵌入式工程师从0开始,到底该学什么,怎么学

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「嵌入式的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“666”之后私信回复“666”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01;嵌入式是个大筐&#xff0…...

Redis-集群-环境搭建

文章目录 1、清空主从复制和哨兵模式留下的一些文件1.1、删除以rdb后缀名的文件1.2、删除主从复制的配置文件1.3、删除哨兵模式的配置文件 2、appendonly修改回no3、开启daemonize yes4、protect-mode no5、注释掉bind6、制作六个实例的配置文件6.1、制作配置文件redis6379.con…...

ITSG、COST-G、Tongji和WHU Level-2数据产品读取绘图(Matlab)

数据介绍&#xff1a; ICGEM International Center for Global Gravity Field Models (gfz-potsdam.de) ITSG 2018&#xff1a;Institute of Geodesy at Graz University of Technolog&#xff08;格拉茨理工大学大地测量研究所&#xff09; 2018版本&#xff0c;最高60阶球谐…...

linux(ubuntucentos)-安装libreoffice

因为需要在linux支持word文档和pdf之间的转换&#xff0c;调研验证后选择了libreoffice&#xff0c;在不同的服务器进行了安装&#xff0c;记录如下。 说明&#xff1a; 此处下载版本是7.6.7&#xff0c;如果网址不存在&#xff0c;可以访问http://mirrors.ustc.edu.cn/tdf/l…...