技术干货 | 源码解析 Github 上 14.1k Star 的 RocketMQ

简介: 站在发送方视角,通过源码,来分析在事务消息发送中 RocketMQ 是如何工作的。

封面图0423头条.jpg

前言

Apache RocketMQ 作为广为人知的开源消息中间件,诞生于阿里巴巴,于 2016 年捐赠给了 Apache。从 RocketMQ 4.0 到如今最新的 v4.7.1,不论是在阿里巴巴内部还是外部社区,都赢得了广泛的关注和好评。

本文将站在发送方视角,通过阅读 RocketMQ Producer 源码,来分析在事务消息发送中 RocketMQ 是如何工作的。

需要说明的是,本文所贴代码,均来自 4.7.1 版本的 RocketMQ 源码。本文中所讨论的发送,仅指从 Producer 发送到 Broker 的过程,并不包含 Broker 将消息投递到 Consumer 的过程。

宏观概览

RocketMQ 事务消息发送流程:

3.png

结合源码来看,RocketMQ 的事务消息 TransactionMQProducer 的 sendMessageInTransaction 方法,实际调用了 DefaultMQProducerImpl 的 sendMessageInTransaction 方法。我们进入 sendMessageInTransaction 方法,整个事务消息的发送流程清晰可见。

首先,做发送前检查,并填入必要参数,包括设 prepare 事务消息。

源码清单-1

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;MessageAccessor.putProperty(msg, MessageConst.PROPERTY_TRANSACTION_PREPARED, "true");MessageAccessor.putProperty(msg, MessageConst.PROPERTY_PRODUCER_GROUP, this.defaultMQProducer.getProducerGroup());

进入发送处理流程:

源码清单-2

    try {sendResult = this.send(msg);} catch (Exception e) {throw new MQClientException("send message Exception", e);}

根据 broker 返回的处理结果决策本地事务是否执行,半消息发送成功则开始本地事务执行:

源码清单-3

    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");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:  // 当备broker状态不可用时,半消息要回滚,不执行本地事务localTransactionState = LocalTransactionState.ROLLBACK_MESSAGE;break;default:break;}

本地事务执行结束,根据本地事务状态进行二阶段处理:

源码清单-4

    try {this.endTransaction(sendResult, localTransactionState, localException);} catch (Exception e) {log.warn("local transaction execute " + localTransactionState + ", but end broker transaction failed", e);}// 组装发送结果// ...return transactionSendResult;
}

接下来,我们深入每个阶段代码分析。

深扒内幕

Ⅰ阶段发送

重点分析 send 方法。进入 send 方法后,我们发现,RocketMQ 的事务消息的一阶段,使用了 SYNC 同步模式:

源码清单-5

public SendResult send(Message msg,long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {return this.sendDefaultImpl(msg, CommunicationMode.SYNC, null, timeout);
}

这一点很容易理解,毕竟事务消息是要根据一阶段发送结果来决定要不要执行本地事务的,所以一定要阻塞等待 broker 的 ack。

我们进入 DefaultMQProducerImpl.java 中去看 sendDefaultImpl 方法的实现,通过读这个方法的代码,来尝试了解在事务消息的一阶段发送过程中 producer 的行为。

值得注意的是,这个方法并非为事务消息定制,甚至不是为 SYNC 同步模式定制的,因此读懂了这段代码,基本可以对 RocketMQ 的消息发送机制有了一个较为全面的认识。

这段代码逻辑非常通畅,不忍切片。为了节省篇幅,将代码中较为繁杂但信息量不大的部分以注释代替,尽可能保留流程的完整性。个人认为较为重要或是容易被忽略的部分,以注释标出,后文还有部分细节的详细解读。

源码清单-6

private SendResult sendDefaultImpl(Message msg,final CommunicationMode communicationMode,final SendCallback sendCallback,final long timeout) throws MQClientException, RemotingException, MQBrokerException, InterruptedException {this.makeSureStateOK();// 一、消息有效性校验。见后文Validators.checkMessage(msg, this.defaultMQProducer);final long invokeID = random.nextLong();long beginTimestampFirst = System.currentTimeMillis();long beginTimestampPrev = beginTimestampFirst;long endTimestamp = beginTimestampFirst;// 获取当前topic的发送路由信息,主要是要broker,如果没找到则从namesrv获取TopicPublishInfo topicPublishInfo = this.tryToFindTopicPublishInfo(msg.getTopic());if (topicPublishInfo != null && topicPublishInfo.ok()) {boolean callTimeout = false;MessageQueue mq = null;Exception exception = null;SendResult sendResult = null;// 二、发送重试机制。见后文int timesTotal = communicationMode == CommunicationMode.SYNC ? 1 + this.defaultMQProducer.getRetryTimesWhenSendFailed() : 1;int times = 0;String[] brokersSent = new String[timesTotal];for (; times < timesTotal; times++) {// 第一次发送是mq == null, 之后都是有broker信息的String lastBrokerName = null == mq ? null : mq.getBrokerName();// 三、rocketmq发送消息时如何选择队列?——broker异常规避机制 MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);if (mqSelected != null) {mq = mqSelected;brokersSent[times] = mq.getBrokerName();try {beginTimestampPrev = System.currentTimeMillis();if (times > 0) {//Reset topic with namespace during resend.msg.setTopic(this.defaultMQProducer.withNamespace(msg.getTopic()));}long costTime = beginTimestampPrev - beginTimestampFirst;if (timeout < costTime) {callTimeout = true;break;}// 发送核心代码sendResult = this.sendKernelImpl(msg, mq, communicationMode, sendCallback, topicPublishInfo, timeout - costTime);endTimestamp = System.currentTimeMillis();// rocketmq 选择 broker 时的规避机制,开启 sendLatencyFaultEnable == true 才生效this.updateFaultItem(mq.getBrokerName(), endTimestamp - beginTimestampPrev, false);switch (communicationMode) {// 四、RocketMQ的三种CommunicationMode。见后文case ASYNC: // 异步模式return null;case ONEWAY: // 单向模式return null;case SYNC: // 同步模式if (sendResult.getSendStatus() != SendStatus.SEND_OK) {if (this.defaultMQProducer.isRetryAnotherBrokerWhenNotStoreOK()) {continue;}}return sendResult;default:break;}} catch (RemotingException e) {// ...// 自动重试} catch (MQClientException e) {// ...// 自动重试} catch (MQBrokerException e) {// ...// 仅返回码==NOT_IN_CURRENT_UNIT==205 时自动重试// 其他情况不重试,抛异常} catch (InterruptedException e) {// ...// 不重试,抛异常}} else {break;}}if (sendResult != null) {return sendResult;}// 组装返回的info信息,最后以MQClientException抛出// ... ...// 超时场景抛RemotingTooMuchRequestExceptionif (callTimeout) {throw new RemotingTooMuchRequestException("sendDefaultImpl call timeout");}// 填充MQClientException异常信息// ...}validateNameServerSetting();throw new MQClientException("No route info of this topic: " + msg.getTopic() + FAQUrl.suggestTodo(FAQUrl.NO_TOPIC_ROUTE_INFO),null).setResponseCode(ClientErrorCode.NOT_FOUND_TOPIC_EXCEPTION);
}

一、消息有效性校验

源码清单-7

 Validators.checkMessage(msg, this.defaultMQProducer);

在此方法中校验消息的有效性,包括对 topic 和消息体的校验。topic 的命名必须符合规范,且避免使用内置的系统消息 TOPIC。消息体长度 > 0 && 消息体长度 <= 102410244 = 4M 。

源码清单-8

public static void checkMessage(Message msg, DefaultMQProducer defaultMQProducer)throws MQClientException {if (null == msg) {throw new MQClientException(ResponseCode.MESSAGE_ILLEGAL, "the message is null");}// topicValidators.checkTopic(msg.getTopic());Validators.isNotAllowedSendTopic(msg.getTopic());// bodyif (null == msg.getBody()) {throw new MQClientException(ResponseCode.MESSAGE_ILLEGAL, "the message body is null");}if (0 == msg.getBody().length) {throw new MQClientException(ResponseCode.MESSAGE_ILLEGAL, "the message body length is zero");}if (msg.getBody().length > defaultMQProducer.getMaxMessageSize()) {throw new MQClientException(ResponseCode.MESSAGE_ILLEGAL,"the message body size over max value, MAX: " + defaultMQProducer.getMaxMessageSize());}
}

二、发送重试机制

Producer 在消息发送不成功时,会自动重试,最多发送次数 = retryTimesWhenSendFailed + 1 = 3 次 。

值得注意的是,并非所有异常情况都会重试,从以上源码中可以提取到的信息告诉我们,在以下三种情况下,会自动重试:

  • 发生 RemotingException,MQClientException 两种异常之一时
  • 发生 MQBrokerException 异常,且 ResponseCode 是 NOT_IN_CURRENT_UNIT = 205 时
  • SYNC 模式下,未发生异常且发送结果状态非 SEND_OK

在每次发送消息之前,会先检查是否在前面这两步就已经耗时超长(超时时长默认 3000ms),若是,则不再继续发送并且直接返回超时,不再重试。这里说明了 2 个问题:

  • producer 内部自动重试对业务应用而言是无感知的,应用看到的发送耗时是包含所有重试的耗时在内的;
  • 一旦超时意味着本次消息发送已经以失败告终,原因是超时。这个信息最后会以 RemotingTooMuchRequestException 的形式抛出。

这里需要指出的是,在 RocketMQ 官方文档中指出,发送超时时长是 10s,即 10000ms,网上许多人对 rocketMQ 的超时时间解读也认为是 10s。然而代码中却明明白白写着 3000ms,最终我 debug 之后确认,默认超时时间确实是 3000ms。

三、broker 的异常规避机制

源码清单-9

MessageQueue mqSelected = this.selectOneMessageQueue(topicPublishInfo, lastBrokerName);  

这行代码是发送前选择 queue 的过程。

这里涉及 RocketMQ 消息发送高可用的的一个核心机制,latencyFaultTolerance。这个机制是 Producer 负载均衡的一部分,通过 sendLatencyFaultEnable 的值来控制,默认是 false 关闭状态,不启动 broker 故障延迟机制,值为 true 时启用 broker 故障延迟机制,可由 Producer 主动打开。

选择队列时,开启异常规避机制,则根据 broker 的工作状态避免选择当前状态不佳的 broker 代理,不健康的 broker 会在一段时间内被规避,不开启异常规避机制时,则按顺序选取下一个队列,但在重试场景下会尽量选择不同于上次发送 broker 的 queue。每次消息发送都会通过 updateFaultItem 方法来维护 broker 的状态信息。

源码清单-10

public void updateFaultItem(final String brokerName, final long currentLatency, boolean isolation) {if (this.sendLatencyFaultEnable) {// 计算延迟多久,isolation表示是否需要隔离该broker,若是,则从30s往前找第一个比30s小的延迟值,再按下标判断规避的周期,若30s,则是10min规避;// 否则,按上一次发送耗时来决定规避时长;long duration = computeNotAvailableDuration(isolation ? 30000 : currentLatency);this.latencyFaultTolerance.updateFaultItem(brokerName, currentLatency, duration);}
}  

深入到 selectOneMessageQueue 方法内部一探究竟:

源码清单-11

public MessageQueue selectOneMessageQueue(final TopicPublishInfo tpInfo, final String lastBrokerName) {if (this.sendLatencyFaultEnable) {// 开启异常规避try {int index = tpInfo.getSendWhichQueue().getAndIncrement();for (int i = 0; i < tpInfo.getMessageQueueList().size(); i++) {int pos = Math.abs(index++) % tpInfo.getMessageQueueList().size();if (pos < 0)pos = 0;// 按顺序取下一个message queue作为发送的queueMessageQueue mq = tpInfo.getMessageQueueList().get(pos);// 当前queue所在的broker可用,且与上一个queue的broker相同,// 或者第一次发送,则使用这个queueif (latencyFaultTolerance.isAvailable(mq.getBrokerName())) {if (null == lastBrokerName || mq.getBrokerName().equals(lastBrokerName))return mq;}}final String notBestBroker = latencyFaultTolerance.pickOneAtLeast();int writeQueueNums = tpInfo.getQueueIdByBroker(notBestBroker);if (writeQueueNums > 0) {final MessageQueue mq = tpInfo.selectOneMessageQueue();if (notBestBroker != null) {mq.setBrokerName(notBestBroker);mq.setQueueId(tpInfo.getSendWhichQueue().getAndIncrement() % writeQueueNums);}return mq;} else {latencyFaultTolerance.remove(notBestBroker);}} catch (Exception e) {log.error("Error occurred when selecting message queue", e);}return tpInfo.selectOneMessageQueue();}// 不开启异常规避,则随机自增选择Queuereturn tpInfo.selectOneMessageQueue(lastBrokerName);
}

四、RocketMQ 的三种 CommunicationMode

源码清单-12

 public enum CommunicationMode {SYNC,ASYNC,ONEWAY,
}

以上三种模式指的都是消息从发送方到达 broker 的阶段,不包含 broker 将消息投递给订阅方的过程。三种模式的发送方式的差异:

单向模式:ONEWAY。消息发送方只管发送,并不关心 broker 处理的结果如何。这种模式下,由于处理流程少,发送耗时非常小,吞吐量大,但不能保证消息可靠不丢,常用于流量巨大但不重要的消息场景,例如心跳发送等。

异步模式:ASYNC。消息发送方发送消息到 broker 后,无需等待 broker 处理,拿到的是 null 的返回值,而由一个异步的线程来做消息处理,处理完成后以回调的形式告诉发送方发送结果。异步处理时如有异常,返回发送方失败结果之前,会经过内部重试(默认 3 次,发送方不感知)。这种模式下,发送方等待时长较小,吞吐量较大,消息可靠,用于流量大但重要的消息场景。

同步模式:SYNC。消息发送方需等待 broker 处理完成并明确返回成功或失败,在消息发送方拿到消息发送失败的结果之前,也会经历过内部重试(默认 3 次,发送方不感知)这种模式下,发送方会阻塞等待消息处理结果,等待时长较长,消息可靠,用于流量不大但重要的消息场景。需要强调的是,事务消息的一阶段半事务消息的处理是同步模式。

在 sendKernelImpl 方法中也可以看到具体的实现差异。ONEWAY 模式最为简单,不做任何处理。负责发送的 sendMessage 方法参数中,相比同步模式,异步模式多了回调方法、包含 topic 发送路由元信息的 topicPublishInfo、包含发送 broker 信息的 instance、包含发送队列信息的 producer、重试次数。另外,异步模式下,会对有压缩的消息先做 copy。

源码清单-13

    switch (communicationMode) {case ASYNC:Message tmpMessage = msg;boolean messageCloned = false;if (msgBodyCompressed) {//If msg body was compressed, msgbody should be reset using prevBody.//Clone new message using commpressed message body and recover origin massage.//Fix bug:https://github.com/apache/rocketmq-externals/issues/66tmpMessage = MessageAccessor.cloneMessage(msg);messageCloned = true;msg.setBody(prevBody);}if (topicWithNamespace) {if (!messageCloned) {tmpMessage = MessageAccessor.cloneMessage(msg);messageCloned = true;}msg.setTopic(NamespaceUtil.withoutNamespace(msg.getTopic(), this.defaultMQProducer.getNamespace()));}long costTimeAsync = System.currentTimeMillis() - beginStartTime;if (timeout < costTimeAsync) {throw new RemotingTooMuchRequestException("sendKernelImpl call timeout");}sendResult = this.mQClientFactory.getMQClientAPIImpl().sendMessage(brokerAddr,mq.getBrokerName(),tmpMessage,requestHeader,timeout - costTimeAsync,communicationMode,sendCallback,topicPublishInfo,this.mQClientFactory,this.defaultMQProducer.getRetryTimesWhenSendAsyncFailed(),context,this);break;case ONEWAY:case SYNC:long costTimeSync = System.currentTimeMillis() - beginStartTime;if (timeout < costTimeSync) {throw new RemotingTooMuchRequestException("sendKernelImpl call timeout");}sendResult = this.mQClientFactory.getMQClientAPIImpl().sendMessage(brokerAddr,mq.getBrokerName(),msg,requestHeader,timeout - costTimeSync,communicationMode,context,this);break;default:assert false;break;} 

官方文档中有这样一张图,十分清晰的描述了异步通信的详细过程:

4.png

Ⅱ 阶段发送

源码清单-3 体现了本地事务的执行,localTransactionState 将本地事务执行结果与事务消息二阶段的发送关联起来。

值得注意的是,如果一阶段的发送结果是 SLAVENOTAVAILABLE,即便 broker 不可用时,也会将 localTransactionState 置为 Rollback,此时将不会执行本地事务。之后由 endTransaction 方法负责二阶段提交,见源码清单-4。具体到 endTransaction 的实现:

源码清单-14

public void endTransaction(final SendResult sendResult,final LocalTransactionState localTransactionState,final Throwable localException) throws RemotingException, MQBrokerException, InterruptedException, UnknownHostException {final MessageId id;if (sendResult.getOffsetMsgId() != null) {id = MessageDecoder.decodeMessageId(sendResult.getOffsetMsgId());} else {id = MessageDecoder.decodeMessageId(sendResult.getMsgId());}String transactionId = sendResult.getTransactionId();final String brokerAddr = this.mQClientFactory.findBrokerAddressInPublish(sendResult.getMessageQueue().getBrokerName());EndTransactionRequestHeader requestHeader = new EndTransactionRequestHeader();requestHeader.setTransactionId(transactionId);requestHeader.setCommitLogOffset(id.getOffset());switch (localTransactionState) {case COMMIT_MESSAGE:requestHeader.setCommitOrRollback(MessageSysFlag.TRANSACTION_COMMIT_TYPE);break;case ROLLBACK_MESSAGE:requestHeader.setCommitOrRollback(MessageSysFlag.TRANSACTION_ROLLBACK_TYPE);break;case UNKNOW:requestHeader.setCommitOrRollback(MessageSysFlag.TRANSACTION_NOT_TYPE);break;default:break;}requestHeader.setProducerGroup(this.defaultMQProducer.getProducerGroup());requestHeader.setTranStateTableOffset(sendResult.getQueueOffset());requestHeader.setMsgId(sendResult.getMsgId());String remark = localException != null ? ("executeLocalTransactionBranch exception: " + localException.toString()) : null;// 采用oneway的方式发送二阶段消息this.mQClientFactory.getMQClientAPIImpl().endTransactionOneway(brokerAddr, requestHeader, remark,this.defaultMQProducer.getSendMsgTimeout());
}

在二阶段发送时,之所以用 oneway 的方式发送,个人理解这正是因为事务消息有一个特殊的可靠机制——回查。

消息回查

当 Broker 经过了一个特定的时间,发现依然没有得到事务消息的二阶段是否要提交或者回滚的确切信息,Broker 不知道 Producer 发生了什么情况(可能 producer 挂了,也可能 producer 发了 commit 但网络抖动丢了,也可能……于是主动发起回查。

事务消息的回查机制,更多的是在 broker 端的体现。RocketMQ 的 broker 以 Half 消息、Op 消息、真实消息三个不同的 topic 来将不同发送阶段的事务消息进行了隔离,使得 Consumer 只能看到最终确认 commit 需要投递出去的消息。其中详细的实现逻辑在本文中暂不多赘述,后续可另开一篇专门来从 Broker 视角来解读。

回到 Producer 的视角,当收到了 Broker 的回查请求,Producer 将根据消息检查本地事务状态,根据结果决定提交或回滚,这就要求 Producer 必须指定回查实现,以备不时之需。当然,正常情况下,并不推荐主动发送 UNKNOW 状态,这个状态毫无疑问会给 broker 带来额外回查开销,只在出现不可预知的异常情况时才启动回查机制,是一种比较合理的选择。

另外,4.7.1 版本的事务回查并非无限回查,而是最多回查 15 次:

源码清单-15

/*** The maximum number of times the message was checked, if exceed this value, this message will be discarded.*/
@ImportantField
private int transactionCheckMax = 15;

附录

官方给出 Producer 的默认参数如下(其中超时时长的参数,在前文中也已经提到,debug 的结果是默认 3000ms,并非 10000ms):

5.png


作者介绍(源淇).png

原文链接

本文为阿里云原创内容,未经允许不得转载。

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

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

相关文章

编译后没有taget文件夹_matconvnet安装、编译、配置

一、安装&#xff08;可见大神windows下编译Matconvnet的方法(CPU和GPU)&#xff09;1.&#xff08;本人&#xff09;安装matlab2015b、Visual Studio 20152.官网Home - MatConvNet下载matconvnet工具包&#xff0c;我的名字是matconvnet-1.0-beta25&#xff0c;然后解压文件到…

面对大规模 K8s 集群,如何先于用户发现问题?

简介&#xff1a; 怎样才能在复杂的大规模场景中&#xff0c;做到真正先于用户发现问题呢&#xff1f;下面我会带来我们在管理大规模 ASI 集群过程中对于快速发现问题的一些经验和实践&#xff0c;希望能对大家有所启发。 作者 | 彭南光&#xff08;光南&#xff09; 来源 | 阿…

第7届UBBF在迪拜举办 加强网络设施建设将加速产业发展成为共识

今天&#xff0c;由联合国宽带委员会和华为共同举办的第7届全球超宽带高峰论坛&#xff08;UBBF 2021&#xff09;在迪拜开幕。作为固网领域全球最大的峰会&#xff0c;今年的UBBF以“联接&#xff0c;新增长”为主题&#xff0c;全球领先的运营商、设备商共同围绕“网络基础设…

使用 rocketmq-spring-boot-starter 来配置、发送和消费 RocketMQ 消息

简介&#xff1a; 本文将 rocktmq-spring-boot 的设计实现做一个简单的介绍&#xff0c;读者可以通过本文了解将 RocketMQ Client 端集成为 spring-boot-starter 框架的开发细节&#xff0c;然后通过一个简单的示例来一步一步的讲解如何使用这个 spring-boot-starter 工具包来配…

回归的误差服从正态分布吗_盘点10大回归类型:总有一款深得你心

全文共2507字&#xff0c;预计学习时长5分钟除了统计模型和其他的一些算法&#xff0c;回归是机器学习成功运行的重要构成要素。回归的核心是寻找变量之间的关系&#xff0c;而机器学习需要根据这种关系来预测结果。显然&#xff0c;任何称职的机器学习工程师都应重视回归&…

What‘s new in dubbo-go v1.5.6

简介&#xff1a; dubbogo 社区近期发布了 dubbogo v1.5.6。该版本和 dubbo 2.7.8 对齐&#xff0c;提供了命令行工具&#xff0c;并提供了多种加载配置的方式。 作者 | 铁城 dubbo-go 社区 committer 来源 | 阿里巴巴云原生公众号 dubbogo 社区近期发布了 dubbogo v1.5.6。该…

华为彭松:基于C.A.F模型构建联接竞争力,创造新增长

10月19日&#xff0c;第七届全球超宽带高峰论坛&#xff08;Ultra-Broadband Forum 2021&#xff09;在迪拜开幕。期间&#xff0c;华为运营商BG Marketing与解决方案销售部总裁彭松发表了题为“联接&#xff0c;新增长”的主题演讲&#xff0c;定义并深入探讨了C.A.F&#xff…

关于写文章的一点经验

简介&#xff1a; 过去的一年&#xff0c;借着《如何画好一张架构图&#xff1f;》、《2020总结&#xff08;个人篇&#xff09;&#xff1a;关于个人成长的再认知》以及《2020 总结&#xff08;团队篇&#xff09;&#xff1a;招之即来&#xff0c;来之即战&#xff0c;战之必…

倒计时 3 天!1024 程序员节全日程曝光,105 场深度演讲点燃数字经济新时代

湘江之滨&#xff0c;岳麓山下&#xff0c;一年前&#xff0c;我们于此完成了一场备受业界关注的硬核技术与开源文化深度融合的大型技术大会——长沙中国1024程序员节&#xff0c;国内顶尖技术专家学者齐聚千年书院&#xff0c;九大操作系统掌门人共话开源技术创新、操作系统新…

13新功能_新功能简介|MySQL8.0数据查询脱敏

数据库管理员会负责维护数据的隐私和完整性。针对数据的脱敏&#xff0c;通常的方案是&#xff1a;应用端实现或者引入加密机等。不过现在MySQL8.0实现了数据脱敏这个功能&#xff0c;可以减少应用的复杂性、减少开发的工作量&#xff0c;也能友好的保护了数据的隐私和完整性。…

贝壳基于 Flink 的实时计算演进之路

简介&#xff1a; 贝壳找房在实时计算之路上的平台建设以及实时数仓应用。 摘要&#xff1a;贝壳找房大数据平台实时计算负责人刘力云带来的分享内容是贝壳找房的实时计算演进之路&#xff0c;内容如下&#xff1a; 发展历程平台建设实时数仓及其应用场景事件驱动场景未来规划G…

python动态规划详解_python----动态规划

不能放弃治疗,每天都要进步&#xff01;&#xff01; 什么时候使用动态规划呢&#xff1f; 1. 求一个问题的最优解 2. 大问题可以分解为子问题&#xff0c;子问题还有重叠的更小的子问题 3. 整体问题最优解取决于子问题的最优解&#xff08;状态转移方程&#xff09; 4. 从上往…

Flink 在唯品会的实践

简介&#xff1a; Flink 在唯品会的容器化实践应用以及产品化经验。 唯品会自 2017 年开始基于 k8s 深入打造高性能、稳定、可靠、易用的实时计算平台&#xff0c;支持唯品会内部业务在平时以及大促的平稳运行。现平台支持 Flink、Spark、Storm 等主流框架。本文主要分享 Flink…

1024 程序员节专题论坛来袭,聚焦企业级开源数据库 openGauss

技术驱动下&#xff0c;现代企业快速发展&#xff0c;产生海量的数据。被称为基础软件三驾马车之一的数据库&#xff0c;一直处于 IT 系统的核心地位&#xff0c;并在技术发展中不断变化。基础数据是“十四五”的重点关注方向&#xff0c;中国数据库正在快速发展崛起&#xff0…

6 张图带你彻底搞懂分布式事务 XA 模式

简介&#xff1a; XA 协议是由 X/Open 组织提出的分布式事务处理规范&#xff0c;主要定义了事务管理器 TM 和局部资源管理器 RM 之间的接口。目前主流的数据库&#xff0c;比如 oracle、DB2 都是支持 XA 协议的。 作者 | 朱晋君 来源 | 阿里巴巴云原生公众号 XA 协议是由 X/O…

龙蜥降世,神龙升级,阿里云投入 20 亿发力操作系统

作者 | 贾凯强、伍杏玲 出品 | CSDN云计算&#xff08;ID&#xff1a;CSDNcloud&#xff09;10 月 20 日&#xff0c;阿里巴巴云栖大会继续在杭州进行&#xff0c;与开幕第一天的主论坛不同&#xff0c;第二天活动的主论坛更加聚焦与技术领域和技术实践。20 日上午&#…

连续三年入围 Gartner 容器竞争格局,阿里云容器服务新布局首次公开

简介&#xff1a; 近日&#xff0c;国际知名信息技术咨询机构 Gartner 发布 2021 年容器竞争格局报告&#xff0c;阿里云成为国内唯一连续三年入选的中国企业&#xff0c;产品丰富度与成熟度持续保持全球领先水平。 来源 | 阿里巴巴云原生公众号 近日&#xff0c;国际知名信息…

双向可控硅触发电路图大全

双向可控硅触发电路图一&#xff1a; 为了提高效率&#xff0c;使触发脉冲与交流电压同步&#xff0c;要求每隔半个交流电的周期输出一个触发脉冲&#xff0c;且触发脉冲电压应大于4V&#xff0c;脉冲宽度应大于20us.图中BT为变压器&#xff0c;TPL521-2为光电耦合器&#xff…

视图计算背后的技术架构思考

简介&#xff1a; 5G时代海量视图计算场景&#xff0c;阿里云边缘计算节点聚焦视频上云和处理方向&#xff0c;阿里云高级技术专家为您解读海量视图计算背后的技术与架构能力。 作者&#xff1a;胡帆 数据载体、算力分布正在根本性变化 视频和图片因其强大的信息承载力&…

Graph + AI 2021全球峰会圆满落幕 TigerGraph企业版3.2发布

中国上海&#xff0c;2021年10月22日——由企业级可扩展图分析平台TigerGraph主办的“图创未来无界精彩”Graph AI 2021中国峰会于前日圆满落幕。本次峰会超3500人参与&#xff0c;较往届增长340%&#xff0c;与会者包括来自耐克、特斯拉、联合利华、西门子、通用电气等上百家…