阿里面试官:RocketMQ与Kafka中如何实现事务?

RocketMQ 和 Kafka 有什么区别? RocketMQ 和 Kafka 相比,在架构上做了减法,在功能上做了加法跟 Kafka 的架构相比,RocketMQ 简化了协调节点和分区以及备份模型。同时增强了消息过滤、消息回溯和事务能力,加入了延迟队列,死信队列等新特性。 阅读详情

RocketMQ的事务是如何实现的?

首先我们来看 RocketMQ 的事务。我在之前的课程中,已经给大家讲解过 RocketMQ 事务的大致流程,这里我们再一起通过代码,重温一下这个流程。

public class CreateOrderService {
    @Inject
    private OrderDao orderDao;
    //注入订单表的DAO
    @Inject
    private ExecutorService executorService;
    //注入一个ExecutorService
    private TransactionMQProducer producer;
    //初始化transactionListener和producer
    @Init
    public void init() throws MQClientException {
        TransactionListener transactionListener = createTransactionListener();
        producer = new TransactionMQProducer("myGroup");
        producer.setExecutorService(executorService);
        producer.setTransactionListener(transactionListener);
        producer.start();
    }
    //创建订单服务的请求入口
    @PUT
    @RequestMapping(...)
    public Boolean createOrder(@RequestBody CreateOrderRequest request) {
        //根据创建订单请求创建一条消息
        Message msg = createMessage(request);
        //发送事务消息
        SendResult sendResult = producer.sendMessageInTransaction(msg, request);
        //返回:事务是否成功
        return sendResult.getSendStatus() == SendStatus.SEND_OK;
    }
    private TransactionListener createTransactionListener() {
        return new TransactionListener() {
            @Override
            public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
                CreateOrderRequest request = (CreateOrderRequest ) arg;
                try {
                    //执行本地事务创建订单
                    orderDao.createOrderInDB(request);
                    //如果没抛异常说明执行成功,提交事务消息
                    return LocalTransactionState.COMMIT_MESSAGE;
                }
                catch (Throwable t) {
                    //失败则直接回滚事务消息
                    return LocalTransactionState.ROLLBACK_MESSAGE;
                }
            }
            //反查本地事务
            @Override
            public LocalTransactionState checkLocalTransaction(MessageExt msg) {
                //从消息中获得订单ID
                String orderId = msg.getUserProperty("orderId");
                //去数据库中查询订单号是否存在,如果存在则提交事务;
                //如果不存在,可能是本地事务失败了,也可能是本地事务还在执行,所以返回UNKNOW//(PS:这里RocketMQ有个拼写错误:UNKNOW)
                return orderDao.isOrderIdExistsInDB(orderId)?
                LocalTransactionState.COMMIT_MESSAGE: LocalTransactionState.UNKNOW;
            }
        }
        ;
    }
    //....
}

在这个流程中,我们提供一个创建订单的服务,功能就是在数据库中插入一条订单记录,并发送一条创建订单的消息,要求写数据库和发消息这两个操作在一个事务内执行,要么都成功,要么都失败。在这段代码中,我们首先在 init() 方法中初始化了 transactionListener和发生 RocketMQ 事务消息的变量 producer。真正提供创建订单服务的方法是createOrder(),在这个方法里面,我们根据请求的参数创建一条消息,然后调用RocketMQ producer 发送事务消息,并返回事务执行结果。

之后的 createTransactionListener() 方法是在 init() 方法中调用的,这里面直接构造一个匿名类,来实现 RocketMQ 的 TransactionListener 接口,这个接口需要实现两个方法:

  • executeLocalTransaction:执行本地事务,在这里我们直接把订单数据插入到数据库中,并返回本地事务的执行结果。
  • checkLocalTransaction:反查本地事务,在这里我们的处理是,在数据库中查询订单号是否存在,如果存在则提交事务,如果不存在,可能是本地事务失败了,也可能是本地事务还在执行,所以返回 UNKNOW。

这样,就使用 RocketMQ 的事务消息功能实现了一个创建订单的分布式事务。接下来我们一起通过 RocketMQ 的源代码来看一下,它的事务消息是如何实现的。

首先看一下在 producer 中,是如何来发送事务消息的:

public TransactionSendResult sendMessageInTransaction(final Message msg,final LocalTransactionExecuter locthrows MQClientException {
    TransactionListener transactionListener = getCheckListener();
    if (null == localTransactionExecuter && null == transactionListener) {
        throw new MQClientException("tranExecutor is null", null);
    }
    Validators.checkMessage(msg, this.defaultMQProducer);
    SendResult sendResult = null;
    //这里给消息添加了属性,标明这是一个事务消息,也就是半消息
    MessageAccessor.putProperty(msg, MessageConst.PROPERTY_TRANSACTION_PREPARED, "true"
    MessageAccessor.putProperty(msg, MessageConst.PROPERTY_PRODUCER_GROUP, this.defaultM
    //调用发送普通消息的方法,发送这条半消息
    try {
        sendResult = this.send(msg);
    }
    catch (Exception e) {
        throw new MQClientException("send message Exception", e);
    }
    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
                if (null != transactionId && !"".equals(transactionId)) {
                    msg.setTransactionId(transactionId);
                }
                //执行本地事务
                if (null != localTransactionExecuter) {
                    localTransactionState = localTransactionExecuter.executeLocalTransac
                    } else if (transactionListener != null) {
                        log.debug("Used new transaction API");
                        localTransactionState = transactionListener.executeLocalTransaction
                        }
                        if (null == localTransactionState) {
                            localTransactionState = LocalTransactionState.UNKNOW;
                        }
                        if (localTransactionState != LocalTransactionState.COMMIT_MESSAGE) {
                            log.info("executeLocalTransactionBranch return {}", localTransaction
                            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:
                localTransactionState = LocalTransactionState.ROLLBACK_MESSAGE;
                break;
                default:
                break;
            }
            //根据事务消息和本地事务的执行结果localTransactionState,决定提交或回滚事务消息
            //这里给Broker发送提交或回滚事务的RPC请求。
            try {
                this.endTransaction(sendResult, localTransactionState, localException);
            }
            catch (Exception e) {
                log.warn("local transaction execute " + localTransactionState + ", but end broke
}
TransactionSendResult transactionSendResult = new TransactionSendResult();transactionSendResult.setSendStatus(sendResult.getSendStatus());transactionSendResult.setMessageQueue(sendResult.getMessageQueue());transactionSendResult.setMsgId(sendResult.getMsgId());
transactionSendResult.setQueueOffset(sendResult.getQueueOffset());transactionSendResult.setTransactionId(sendResult.getTransactionId());transactionSendResult.setLocalTransactionState(localTransactionState);
return transactionSendResult;

这段代码的实现逻辑是这样的:首先给待发送消息添加了一个属性PROPERTY_TRANSACTION_PREPARED,标明这是一个事务消息,也就是半消息,然后会像发送普通消息一样去把这条消息发送到 Broker 上。如果发送成功了,就开始调用我们之前提供的接口 TransactionListener 的实现类中,执行本地事务的方法executeLocalTransaction() 来执行本地事务,在我们的例子中就是在数据库中插入一条订单记录。

最后,根据半消息发送的结果和本地事务执行的结果,来决定提交或者回滚事务。在实现方法 endTransaction() 中,producer 就是给 Broker 发送了一个单向的 RPC 请求,告知Broker 完成事务的提交或者回滚。由于有事务反查的机制来兜底,这个 RPC 请求即使失败或者丢失,也都不会影响事务最终的结果。最后构建事务消息的发送结果,并返回。

以上,就是 RocketMQ 在 Producer 这一端事务消息的实现,然后我们再看一下 Broker这一端,它是怎么来处理事务消息和进行事务反查的。

Broker 在处理 Producer 发送消息的请求时,会根据消息中的属性判断一下,这条消息是普通消息还是半消息:

// ...
if (traFlag != null && Boolean.parseBoolean(traFlag)) {
    // ...
    putMessageResult = this.brokerController.getTransactionalMessageService().prepareMes
} else {
    putMessageResult = this.brokerController.getMessageStore().putMessage(msgInner);
}
// ...

这段代码在org.apache.rocketmq.broker.processor.SendMessageProcessor#sendMessage 方法中,然后我们跟进去看看真正处理半消息的业务逻辑,这段处理逻辑在类org.apache.rocketmq.broker.transaction.queue.TransactionalMessageBridge 中:

public PutMessageResult putHalfMessage(MessageExtBrokerInner messageInner) {
    return store.putMessage(parseHalfMessageInner(messageInner));
}
private MessageExtBrokerInner parseHalfMessageInner(MessageExtBrokerInner msgInner) {
    //记录消息的主题和队列,到新的属性中
    MessageAccessor.putProperty(msgInner, MessageConst.PROPERTY_REAL_TOPIC, msgInner.getMessageAccessor.putProperty(msgInner, MessageConst.PROPERTY_REAL_QUEUE_ID,
    String.valueOf(msgInner.getQueueId()));
    msgInner.setSysFlag(
    MessageSysFlag.resetTransactionValue(msgInner.getSysFlag(), MessageSysFlag.TRANS//替换消息的主题和队列为:RMQ_SYS_TRANS_HALF_TOPIC,0
    msgInner.setTopic(TransactionalMessageUtil.buildHalfTopic());
    msgInner.setQueueId(0);
    msgInner.setPropertiesString(MessageDecoder.messageProperties2String(msgInner.getProreturn msgInner;
}

我们可以看到,在这段代码中,RocketMQ 并没有把半消息保存到消息中客户端指定的那个队列中,而是记录了原始的主题队列后,把这个半消息保存在了一个特殊的内部主题RMQ_SYS_TRANS_HALF_TOPIC 中,使用的队列号固定为 0。这个主题和队列对消费者是不可见的,所以里面的消息永远不会被消费。这样,就保证了在事务提交成功之前,这个半消息对消费者来说是消费不到的。

然后我们再看一下,RocketMQ 是如何进行事务反查的:在 Broker 的TransactionalMessageCheckService 服务中启动了一个定时器,定时从半消息队列中读出所有待反查的半消息,针对每个需要反查的半消息,Broker 会给对应的 Producer 发一个要求执行事务状态反查的 RPC 请求,这部分的逻辑在方法org.apache.rocketmq.broker.transaction.AbstractTransactionalMessageCheckListener#sendCheckMessage 中,根据 RPC 返回响应中的反查结果,来决定这个半消息是需要提交还是回滚,或者后续继续来反查。

最后,提交或者回滚事务实现的逻辑是差不多的,首先把半消息标记为已处理,如果是提交事务,那就把半消息从半消息队列中复制到这个消息真正的主题和队列中去,如果要回滚事务,这一步什么都不需要做,最后结束这个事务。这部分逻辑的实现在org.apache.rocketmq.broker.processor.EndTransactionProcessor 这个类中。

Kafka的事务和Exactly Once可以解决什么问题?

接下来我们再说一下 Kafka 的事务。之前我们讲事务的时候说过,Kafka 的事务解决的问题和 RocketMQ 是不太一样的。RocketMQ 中的事务,它解决的问题是,确保执行本地事务和发消息这两个操作,要么都成功,要么都失败。并且,RocketMQ 增加了一个事务反查的机制,来尽量提高事务执行的成功率和数据一致性。

而 Kafka 中的事务,它解决的问题是,确保在一个事务中发送的多条消息,要么都成功,要么都失败。注意,这里面的多条消息不一定要在同一个主题和分区中,可以是发往多个主题和分区的消息。当然,你可以在 Kafka 的事务执行过程中,加入本地事务,来实现和RocketMQ 中事务类似的效果,但是 Kafka 是没有事务反查机制的。

Kafka 的这种事务机制,单独来使用的场景不多。更多的情况下被用来配合 Kafka 的幂等机制来实现 Kafka 的 Exactly Once 语义。我在之前的课程中也强调过,这里面的 ExactlyOnce,和我们通常理解的消息队列的服务水平中的 Exactly Once 是不一样的。

我们通常理解消息队列的服务水平中的 Exactly Once,它指的是,消息从生产者发送到Broker,然后消费者再从 Broker 拉取消息,然后进行消费。这个过程中,确保每一条消息恰好传输一次,不重不丢。我们之前说过,包括 Kafka 在内的几个常见的开源消息队列,都只能做到 At Least Once,也就是至少一次,保证消息不丢,但有可能会重复。做不到Exactly Once。

 

那 Kafka 中的 Exactly Once 又是解决的什么问题呢?它解决的是,在流计算中,用 Kafka作为数据源,并且将计算结果保存到 Kafka 这种场景下,数据从 Kafka 的某个主题中消费,在计算集群中计算,再把计算结果保存在 Kafka 的其他主题中。这样的过程中,保证每条消息都被恰好计算一次,确保计算结果正确。

 

举个例子,比如,我们把所有订单消息保存在一个 Kafka 的主题 Order 中,在 Flink 集群中运行一个计算任务,统计每分钟的订单收入,然后把结果保存在另一个 Kafka 的主题Income 里面。要保证计算结果准确,就要确保,无论是 Kafka 集群还是 Flink 集群中任何节点发生故障,每条消息都只能被计算一次,不能重复计算,否则计算结果就错了。这里面有一个很重要的限制条件,就是数据必须来自 Kafka 并且计算结果都必须保存到 Kafka中,才可以享受到 Kafka 的 Excactly Once 机制。

可以看到,Kafka 的 Exactly Once 机制,是为了解决在“读数据 - 计算 - 保存结果”这样的计算过程中数据不重不丢,而不是我们通常理解的使用消息队列进行消息生产消费过程中的 Exactly Once。

Kafka的事务是如何实现的?

那 Kafka 的事务又是怎么实现的呢?它的实现原理和 RocketMQ 的事务是差不多的,都是基于两阶段提交来实现的,但是实现的过程更加复杂。

首先说一下,参与 Kafka 事务的几个角色,或者说是模块。为了解决分布式事务问题,Kafka 引入了事务协调者这个角色,负责在服务端协调整个事务。这个协调者并不是一个独立的进程,而是 Broker 进程的一部分,协调者和分区一样通过选举来保证自身的可用性。

和 RocketMQ 类似,Kafka 集群中也有一个特殊的用于记录事务日志的主题,这个事务日志主题的实现和普通的主题是一样的,里面记录的数据就是类似于“开启事务”“提交事务”这样的事务日志。日志主题同样也包含了很多的分区。在 Kafka 集群中,可以存在多个协调者,每个协调者负责管理和使用事务日志中的几个分区。这样设计,其实就是为了能并行执行多个事务,提升性能。

 

下面说一下 Kafka 事务的实现流程。

首先,当我们开启事务的时候,生产者会给协调者发一个请求来开启事务,协调者在事务日志中记录下事务 ID。

然后,生产者在发送消息之前,还要给协调者发送请求,告知发送的消息属于哪个主题和分区,这个信息也会被协调者记录在事务日志中。接下来,生产者就可以像发送普通消息一样来发送事务消息,这里和 RocketMQ 不同的是,RocketMQ 选择把未提交的事务消息保存在特殊的队列中,而 Kafka 在处理未提交的事务消息时,和普通消息是一样的,直接发给 Broker,保存在这些消息对应的分区中,Kafka 会在客户端的消费者中,暂时过滤未提交的事务消息。

消息发送完成后,生产者给协调者发送提交或回滚事务的请求,由协调者来开始两阶段提交,完成事务。第一阶段,协调者把事务的状态设置为“预提交”,并写入事务日志。到这里,实际上事务已经成功了,无论接下来发生什么情况,事务最终都会被提交。

之后便开始第二阶段,协调者在事务相关的所有分区中,都会写一条“事务结束”的特殊消息,当 Kafka 的消费者,也就是客户端,读到这个事务结束的特殊消息之后,它就可以把之前暂时过滤的那些未提交的事务消息,放行给业务代码进行消费了。最后,协调者记录最后一条事务日志,标识这个事务已经结束了。

我把整个事务的实现流程,绘制成一个简单的时序图放在这里,便于你理解。

 

总结一下 Kafka 这个两阶段的流程,准备阶段,生产者发消息给协调者开启事务,然后消息发送到每个分区上。提交阶段,生产者发消息给协调者提交事务,协调者给每个分区发一条“事务结束”的消息,完成分布式事务提交。

总结

本文分别讲解了 Kafka 和 RocketMQ 是如何来实现事务的。你可以看到,它们在实现事务过程中的一些共同的地方,它们都是基于两阶段提交来实现的事务,都利用了特殊的主题中的队列和分区来记录事务日志。

不同之处在于对处于事务中的消息的处理方式,RocketMQ 是把这些消息暂存在一个特殊的队列中,待事务提交后再移动到业务队列中;而 Kafka 直接把消息放到对应的业务分区中,配合客户端过滤来暂时屏蔽进行中的事务消息。

同时你需要了解,RocketMQ 和 Kafka 的事务,它们的适用场景是不一样的,RocketMQ的事务适用于解决本地事务和发消息的数据一致性问题,而 Kafka 的事务则是用于实现它的 Exactly Once 机制,应用于实时计算的场景中。

 

想要了解更多Java架构技术的,可以关注我一下,我后续也会整理更多关于架构技术这一块的知识点分享出来,里面会分享一些:spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化,并发编程这些成为架构师必备的知识体系.更多交流和获取方式可以滴滴博主哦

MQ三巨头RocketMQ、Kafka、RabbitMQ 该怎么选型? 消息中间件选型面试指南:Kafka、RocketMQ和RabbitMQ如何选择?本文针对面试中常见的消息中间件选型问题,分析了三大主流消息中间件的核心差异与适用场景。Kafka适用于高吞吐大数据场景,RocketMQ在可靠性与吞吐量间平衡,适合电商金融等核心业务,RabbitMQ则擅长复杂路由的企业级应用。文章提出"3步决策法":先明确业务核心需求(吞吐优先还是功能优先),再验证非功能需求(可靠性、扩展性等),最后结合团队技术栈。面试回答时应重点说明业务需求与技术特性的匹配逻辑,而非简单 阅读详情

相关推荐

美团面试:对比分析 RocketMQ、Kafka、RabbitMQ 三大MQ常见问题?

美团面试:对比分析 RocketMQ、Kafka、RabbitMQ 三大MQ常见问题?

架构师尼恩 1950

Stream Processing: Apache Kafka的Exactly-once的定义 原理和实现

2018年,Apache Kafka以一种特殊的设计和方法实现了强语义的exactly-once和事务性。热泪盈眶啊! 这篇文章将讲解kafka中exactly-once和事务操作的原理,具体为(1)exactly-once在kafka中的定义。(2)数据生产者的幂等操作和kafka的事务性支持。(3)exactly-once的流处理。 1. 什么是恰好一次exactly-once exact...

ChainingBlocks 5322

Kafka与RocketMQ深度对比:存储模型、事务消息与选型指南

消息中间件是分布式系统异步解耦与流量削峰的核心组件,其底层存储模型直接决定了吞吐量、堆积能力与运维复杂度。Kafka基于分区追加写日志,天生擅长高吞吐与流式数据处理;RocketMQ采用CommitLog与ConsumeQueue两级结构,在业务消息场景下对海量topic更友好。事务消息与顺序消息是业务系统的关键能力,RocketMQ的半消息机制与顺序消费接口开箱即用,Kafka则更偏流处理原子性。面对延迟排查、重复消费、集群部署等工程实践问题,掌握两者的差异与定位,才能在日志管道、实时数仓、订单交易等场景

weixin_29055137的博客 143

【kafka系列】Exactly Once语义

即使在生产者重试、Broker故障或消费者重启等场景下,也能避免数据重复或丢失。正确配置后,Kafka可支持金融支付、实时对账等对数据一致性要求极高的场景。

技术改变世界 1123

消息队列选型实战:Kafka、RabbitMQ、RocketMQ核心场景与决策框架

消息队列作为现代分布式系统的核心组件,其核心价值在于实现应用解耦、异步处理和流量削峰。从技术原理上看,不同消息队列的设计哲学决定了其性能边界和适用场景。例如,基于发布订阅模型和分区机制的系统,通常为高吞吐、持久化的数据流场景而优化;而实现了高级消息队列协议(AMQP)的代理,则更侧重于提供丰富的消息路由模式和可靠投递保证。在技术选型时,需综合评估**业务场景驱动**和**团队能力匹配**两大关键因素。对于需要构建实时数据管道、处理海量日志流的场景,高吞吐和持久化能力是首要考量;而对于涉及复杂路由逻辑、要求强

weixin_34392435的博客 399

MQ那点破事,消息丢失、重复消费、消费顺序、堆积、事务、高可用....

大家好,我是 Tom哥~马上要开启国庆小长假了,祝大家节日快乐,吃喝玩乐走起~为了便于大家查找问题,了解全貌,整理个目录,我们可以快速全局了解关于消息队列,面试官一般会问哪些问题。本篇文章的目录:消息队列的应用场景?答案:1、异步处理  2、流量削峰填谷  3、应用解耦  4、消息通讯异步处理。将一个请求链路中的非核心流程,拆分出来,异步处理,减少主流程链路的处理逻辑,缩短RT,提升吞吐量。如:注册新用户发短信通知。削峰填谷。避免流量暴涨,打垮下游系统,前面会加个消息队列,平滑

公众号:微观技术 6196

Kafka消息队列怎么保证exactlyOnce以及顺序消费

一个Topic可以有多个Paritition,相当于把一个Topic里面的N个消息数据进行一个分片存储。消费端去消费的时候会从指定的Paritition里面去获取。在同一个消费组中,一个消费者可以消费多个Partition中的数据。当我们向某个Topic发送消息的时候,在Kafka的Broker上会通过Partition分区的机制来实现消息的物理存储。在Kafka里面,每个Paritition分区的消息本身就是按顺序来存储的。而消费者这边只需要去消费这个分区,就可以实现消息的顺序处理。

qq_46130027的博客 576

【Kafka】Kafka 实现 Exactly-once (ack机制、高水位)

这个acks参数在kafka的使用中,是非常核心以及关键的一个参数,决定了很多东西。Kafka producer有三种ack机制 初始化producer时在config中进行配置ack等于0:意味着producer不等待broker同步完成的确认,继续发送下一条(批)信息提供了最低的延迟。但是最弱的持久性,当服务器发生故障时,就很可能发生数据丢失。例如leader已经死亡,producer不知情,还会继续发送消息broker接收不到数据就会数据丢失。ack等于1(默认)

九师兄 1448

Kafka的幂等性和事务

目录幂等性1. 简介2. 生产者幂等性事务1. 简介 幂等性 1. 简介 Kafka在 0.11 版本引入了一项重大特性,幂等性。所谓的幂等性就是指 Producer 不论向 Server 发送多少次重复数据,Server 端都只会持久化一条。 拿 http 举例来说,一次或多次请求,得到的响应是一致的(网络超时等问题除外),换句话说,就是执行多次操作与执行一次操作的影响是一样的。 如果,某个系统是不具备幂等性的,如果用户重复提交了某个表格,就可能会造成不良影响。例如:用户在浏览器上点击了多次提交订单按钮

程序园@大Null 1484

伪距单点定位推导+DOP值分析(附matlab仿真代码)

伪距单点定位公式推导、DOP值分析与matlab仿真分析

shannanshouyin的博客 2514

别再调参了!用Simulink手把手教你搞定FOC电机速度环PI控制器(附模型下载)

本文详细介绍了如何使用Simulink搭建FOC电机速度环PI控制器,通过模最优准则和系统化调试方法,避免传统手动调参的繁琐过程。文章包含从传递函数推导到参数自动整定的完整流程,并提供了实战调试技巧与异常处理指南,帮助工程师快速实现电机控制系统的稳定运行。

weixin_42519985的博客 415

kafka 事务_阿里面试官:RocketMQ与Kafka中如何实现事务?

本文已收录GitHub,更有互联网大厂面试真题,面试攻略,高效学习资料等RocketMQ的事务是如何实现的?首先我们来看 RocketMQ 的事务。我在之前的课程中,已经给大家讲解过 RocketMQ 事务的大致流程,这里我们再一起通过代码,重温一下这个流程。public class CreateOrderService { @Inject private OrderDao ord...

weixin_33542801的博客 384

RK3568下YT8531/YT8521 PHY移植避坑指南:RGMII与设备树详解

嵌入式Linux网络调试中,PHY芯片作为MAC与物理介质之间的桥梁,其驱动配置直接影响网络稳定性。RGMII接口的延时补偿、PHY地址设定以及复位时序是设备树配置中的关键环节,稍有差错便会出现链路已通却无法通信的现象。通过理解PHYLIB框架与mdio总线枚举原理,开发者可以快速定位寄存器级故障。在RK3568等工业级平台中,裕太微YT8531/YT8521凭借高性价比与内核主线支持,成为替代传统方案的热门选择。从设备树模板、内核配置到常见坑点,系统梳理千兆以太网PHY的移植与调试方法,帮助开发者少走弯路

weixin_29189381的博客 213

【137期】面试官问:RocketMQ 与 Kafka 对比,谈谈两者的差异?

学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。下方即为我手绘的MyBtis知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的MyBtis知识脑图原件(包括上方的面试解析xmind文档)

2401_84009130的博客 1039

AMD显卡跑Ollama大模型?手把手教你搞定Windows下GPU识别与加速(以RX 9600XT为例)

本文详细指导如何在Windows系统下使用AMD显卡(如RX 9600XT)运行Ollama大模型,解决GPU识别与加速问题。通过获取非官方Ollama版本、替换ROCm库文件等步骤,实现高效的GPU加速推理,提升大语言模型的运行效率。

weixin_28707365的博客 485

BioEmu:微软黑科技炸场!生成式AI重构蛋白质模拟:千倍效率碾压传统计算,新药研发周期砍半

BioEmu 是微软推出的生成式深度学习系统,可在单个 GPU 上每小时生成数千种蛋白质结构样本,支持模拟动态变化、预测热力学性质,并显著降低计算成本。

士多啤梨先生の博客 1100

【面朝大厂】面试官:RocketMQ与Kafka对比,谈谈两者的差异

在Java后端、中间件及大数据面试中,RocketMQ与Kafka的对比是高频考点。二者虽同为Apache顶级项目,但定位迥异:RocketMQ源于阿里电商交易链路,强调可靠性、事务消息与延迟消息,适合支付、订单等业务场景;Kafka起源于LinkedIn日志采集,聚焦高吞吐、持久化日志与流处理,广泛用于日志监控、实时计算。架构上,RocketMQ采用NameServer轻量注册,Kafka依赖ZooKeeper或KRaft管理元数据;存储模型上,RocketMQ使用统一CommitLog+ConsumeQ

油墨香^-^的博客 206

Coze智能体开发:扣子空间制作PPT功能

扣子空间内置了丰富的场景模板,涵盖人文历史、学术分享、研究报告、数据分析等场景,可以满足不同用户的需求。 你只需输入与制作 PPT 相关的提示词,Agent 就能基于这些提示词展示思考的过程,并自动生成完整的 PPT。

王国平的博客 2054

【137期】面试官问:RocketMQ 与 Kafka 对比,谈谈两者的差异?(1)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。 既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!由于文件比较多,这里只是将部分目录截图出来,全套

2301_82244509的博客 1319

rk3506上移植lvgl应用

本文介绍在RK3506开发板上移植和运行LVGL图形库的方法。

m0_60805802的博客 1164

【数字IC/FPGA】低功耗设计

低功耗设计方法功耗的类型低功耗设计方法静态低功耗技术多阈值工艺方法电源门控方法体偏置动态低功耗技术多电压域(Multi-Voltage Domain)预计算门控时钟门级优化技术毛刺的消除逻辑级优化物理级优化控制输入向量低功耗Soc系统的动态管理动态电压及频率调节技术低功耗操作系统存储器功耗控制总结 功耗的类型 CMOS电路中的功耗由两部分构成,第一部分为负载电容充放电时引起的功耗,称为动态功耗,另一部分为漏电流引起的功耗,称为静态功耗。其中动态功耗包括翻转功耗和短路功耗。翻转功耗是数字电路中信号翻转时电容充

qq_40268672的博客 3073

面试官问:RocketMQ与Kafka实践上对比,谈谈两者的差异?

Java精选面试题(微信小程序):5000+道面试题和选择题,包含Java基础、并发、JVM、线程、MQ系列、Redis、Spring系列、Elasticsearch、Docker、K8s、Flink、Spark、架构设计、大厂真题等,在线随时刷题!淘宝内部的交易系统使用了淘宝自主研发的Notify消息中间件,使用Mysql作为消息存储媒介,可完全水平扩容,为了进一步降低成本,我们认为存储部分可以...

Java精选 226

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

消息中间件选型的艺术:如何在RocketMQ、Kafka、RabbitMQ中做出正确决策

最近在社群里看到一个有趣的现象:很多技术同学在面试时,简历上写了使用某款消息中间件的经验,结果面试官追问"为什么选这款而不选那款?"时,往往答不上来。这让我想起自己多年前面试时的窘境——我能熟练使用RocketMQ,却说不清楚为什么团队当初选了它而不是Kafka。 作为一名在分布式系统领域摸爬滚打十年的老兵,我深刻体会到:技术选型不是技术比武,而是业务需求的精准匹配。今天,我想从实战角度,深入剖析RocketMQ、Kafka、RabbitMQ三大主流消息中间件的选型逻辑,帮助大家建立系统化的技术决策思维。

沉淀所学,分享所思,让热爱与成长同行。商务记录AI实战经验,助力开发者快速成长。 热爱AI,希望做出有影响力的技术成果。 技术点亮生活,分享连接价值与机会。 专注AI落地实战,陪你走好技术每一步。 1万+

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

上一篇: Java多线程你真的理解透彻了吗?带你玩转一次多线程!
下一篇: 10 个牛逼的单行代码编程技巧,你会用吗?
Java架构技术官
博客等级 码龄6年 9粉丝 58原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值