聊聊RocketMQMessageListener的实现机制

本文主要研究一下RocketMQMessageListener的实现机制

示例

@Service
@RocketMQMessageListener(nameServer = "${demo.rocketmq.myNameServer}", topic = "${demo.rocketmq.topic.user}", consumerGroup = "user_consumer")
public class UserConsumer implements RocketMQListener<User> {@Overridepublic void onMessage(User message) {System.out.printf("######## user_consumer received: %s ; age: %s ; name: %s \n", message, message.getUserAge(), message.getUserName());}
}

实现了RocketMQListener接口的类,再配合@RocketMQMessageListener注解就可以实现对rocketmq消息的消费

RocketMQListener

rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/core/RocketMQListener.java

public interface RocketMQListener<T> {void onMessage(T message);
}

RocketMQListener接口定义了onMessage方法

RocketMQMessageListener

rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/annotation/RocketMQMessageListener.java

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RocketMQMessageListener {String NAME_SERVER_PLACEHOLDER = "${rocketmq.name-server:}";String ACCESS_KEY_PLACEHOLDER = "${rocketmq.consumer.access-key:}";String SECRET_KEY_PLACEHOLDER = "${rocketmq.consumer.secret-key:}";String TRACE_TOPIC_PLACEHOLDER = "${rocketmq.consumer.customized-trace-topic:}";String ACCESS_CHANNEL_PLACEHOLDER = "${rocketmq.access-channel:}";/*** Consumers of the same role is required to have exactly same subscriptions and consumerGroup to correctly achieve* load balance. It's required and needs to be globally unique.*** See <a href="http://rocketmq.apache.org/docs/core-concept/">here</a> for further discussion.*/String consumerGroup();/*** Topic name.*/String topic();/*** Control how to selector message.** @see SelectorType*/SelectorType selectorType() default SelectorType.TAG;/*** Control which message can be select. Grammar please see {@link SelectorType#TAG} and {@link SelectorType#SQL92}*/String selectorExpression() default "*";/*** Control consume mode, you can choice receive message concurrently or orderly.*/ConsumeMode consumeMode() default ConsumeMode.CONCURRENTLY;/*** Control message mode, if you want all subscribers receive message all message, broadcasting is a good choice.*/MessageModel messageModel() default MessageModel.CLUSTERING;/*** Max consumer thread number.* This property control consumer thread pool executor maximumPoolSize see* {@link ConsumeMessageService#updateCorePoolSize(int)}* @see <a href="https://github.com/apache/rocketmq-spring/issues/546">issues#546</a>*/int consumeThreadMax() default 64;/*** consumer thread number.*/int consumeThreadNumber() default 20;/*** Max re-consume times.** In concurrently mode, -1 means 16;* In orderly mode, -1 means Integer.MAX_VALUE.*/int maxReconsumeTimes() default -1;/*** Maximum amount of time in minutes a message may block the consuming thread.*/long consumeTimeout() default 15L;/*** Timeout for sending reply messages.*/int replyTimeout() default 3000;/*** The property of "access-key".*/String accessKey() default ACCESS_KEY_PLACEHOLDER;/*** The property of "secret-key".*/String secretKey() default SECRET_KEY_PLACEHOLDER;/*** Switch flag instance for message trace.*/boolean enableMsgTrace() default false;/*** The name value of message trace topic.If you don't config,you can use the default trace topic name.*/String customizedTraceTopic() default TRACE_TOPIC_PLACEHOLDER;/*** The property of "name-server".*/String nameServer() default NAME_SERVER_PLACEHOLDER;/*** The property of "access-channel".*/String accessChannel() default ACCESS_CHANNEL_PLACEHOLDER;/*** The property of "tlsEnable" default false.*/String tlsEnable() default "false";/*** The namespace of consumer.*/String namespace() default "";/*** Message consume retry strategy in concurrently mode.** -1,no retry,put into DLQ directly* 0,broker control retry frequency* >0,client control retry frequency*/int delayLevelWhenNextConsume() default 0;/*** The interval of suspending the pull in orderly mode, in milliseconds.** The minimum value is 10 and the maximum is 30000.*/int suspendCurrentQueueTimeMillis() default 1000;/*** Maximum time to await message consuming when shutdown consumer, in milliseconds.* The minimum value is 0*/int awaitTerminationMillisWhenShutdown() default 1000;/*** The property of "instanceName".*/String instanceName() default "DEFAULT";
}

RocketMQMessageListener注解定义了consumerGroup、topic、selectorType、selectorExpression、consumeMode、messageModel、consumeThreadMax、consumeThreadNumber、maxReconsumeTimes、consumeTimeout、replyTimeout、accessKey、secretKey、enableMsgTrace、customizedTraceTopic、nameServer、accessChannel、tlsEnable、namespace、delayLevelWhenNextConsume、suspendCurrentQueueTimeMillis、awaitTerminationMillisWhenShutdown、instanceName属性

RocketMQMessageListenerBeanPostProcessor

rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/annotation/RocketMQMessageListenerBeanPostProcessor.java

public class RocketMQMessageListenerBeanPostProcessor implements ApplicationContextAware, BeanPostProcessor, InitializingBean {private ApplicationContext applicationContext;private AnnotationEnhancer enhancer;private ListenerContainerConfiguration listenerContainerConfiguration;@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {Class<?> targetClass = AopUtils.getTargetClass(bean);RocketMQMessageListener ann = targetClass.getAnnotation(RocketMQMessageListener.class);if (ann != null) {RocketMQMessageListener enhance = enhance(targetClass, ann);if (listenerContainerConfiguration != null) {listenerContainerConfiguration.registerContainer(beanName, bean, enhance);}}return bean;}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}@Overridepublic void afterPropertiesSet() throws Exception {buildEnhancer();this.listenerContainerConfiguration = this.applicationContext.getBean(ListenerContainerConfiguration.class);}private void buildEnhancer() {if (this.applicationContext != null) {Map<String, AnnotationEnhancer> enhancersMap =this.applicationContext.getBeansOfType(AnnotationEnhancer.class, false, false);if (enhancersMap.size() > 0) {List<AnnotationEnhancer> enhancers = enhancersMap.values().stream().sorted(new OrderComparator()).collect(Collectors.toList());this.enhancer = (attrs, element) -> {Map<String, Object> newAttrs = attrs;for (AnnotationEnhancer enh : enhancers) {newAttrs = enh.apply(newAttrs, element);}return attrs;};}}}private RocketMQMessageListener enhance(AnnotatedElement element, RocketMQMessageListener ann) {if (this.enhancer == null) {return ann;} else {return AnnotationUtils.synthesizeAnnotation(this.enhancer.apply(AnnotationUtils.getAnnotationAttributes(ann), element), RocketMQMessageListener.class, null);}}public interface AnnotationEnhancer extends BiFunction<Map<String, Object>, AnnotatedElement, Map<String, Object>> {}
}

RocketMQMessageListenerBeanPostProcessor实现了ApplicationContextAware、BeanPostProcessor、InitializingBean接口,其中postProcessAfterInitialization方法判断bean有没有RocketMQMessageListener注解,如果有的话则通过enhance进行增强,另外会通过listenerContainerConfiguration的registerContainer进行注册
其afterPropertiesSet方法会执行buildEnhancer方法,该方法会获取AnnotationEnhancer的bean实例,然后排序好,最后构造AnnotationEnhancer,其作用就是把这些enhancers挨个apply上去

registerContainer

rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/autoconfigure/ListenerContainerConfiguration.java

    public void registerContainer(String beanName, Object bean, RocketMQMessageListener annotation) {Class<?> clazz = AopProxyUtils.ultimateTargetClass(bean);if (RocketMQListener.class.isAssignableFrom(bean.getClass()) && RocketMQReplyListener.class.isAssignableFrom(bean.getClass())) {throw new IllegalStateException(clazz + " cannot be both instance of " + RocketMQListener.class.getName() + " and " + RocketMQReplyListener.class.getName());}if (!RocketMQListener.class.isAssignableFrom(bean.getClass()) && !RocketMQReplyListener.class.isAssignableFrom(bean.getClass())) {throw new IllegalStateException(clazz + " is not instance of " + RocketMQListener.class.getName() + " or " + RocketMQReplyListener.class.getName());}String consumerGroup = this.environment.resolvePlaceholders(annotation.consumerGroup());String topic = this.environment.resolvePlaceholders(annotation.topic());boolean listenerEnabled =(boolean) rocketMQProperties.getConsumer().getListeners().getOrDefault(consumerGroup, Collections.EMPTY_MAP).getOrDefault(topic, true);if (!listenerEnabled) {log.debug("Consumer Listener (group:{},topic:{}) is not enabled by configuration, will ignore initialization.",consumerGroup, topic);return;}validate(annotation);String containerBeanName = String.format("%s_%s", DefaultRocketMQListenerContainer.class.getName(),counter.incrementAndGet());GenericApplicationContext genericApplicationContext = (GenericApplicationContext) applicationContext;genericApplicationContext.registerBean(containerBeanName, DefaultRocketMQListenerContainer.class,() -> createRocketMQListenerContainer(containerBeanName, bean, annotation));DefaultRocketMQListenerContainer container = genericApplicationContext.getBean(containerBeanName,DefaultRocketMQListenerContainer.class);if (!container.isRunning()) {try {container.start();} catch (Exception e) {log.error("Started container failed. {}", container, e);throw new RuntimeException(e);}}log.info("Register the listener to container, listenerBeanName:{}, containerBeanName:{}", beanName, containerBeanName);}

ListenerContainerConfiguration的registerContainer方法会根据注解信息及对应的bean构造DefaultRocketMQListenerContainer并注册到genericApplicationContext,同时执行其start方法

DefaultRocketMQListenerContainer

rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/support/DefaultRocketMQListenerContainer.java

public class DefaultRocketMQListenerContainer implements InitializingBean,RocketMQListenerContainer, SmartLifecycle, ApplicationContextAware {private DefaultMQPushConsumer consumer;private RocketMQListener rocketMQListener;@Overridepublic void start() {if (this.isRunning()) {throw new IllegalStateException("container already running. " + this.toString());}try {consumer.start();} catch (MQClientException e) {throw new IllegalStateException("Failed to start RocketMQ push consumer", e);}this.setRunning(true);log.info("running container: {}", this.toString());}@Overridepublic void afterPropertiesSet() throws Exception {initRocketMQPushConsumer();this.messageType = getMessageType();this.methodParameter = getMethodParameter();log.debug("RocketMQ messageType: {}", messageType);}private void initRocketMQPushConsumer() throws MQClientException {if (rocketMQListener == null && rocketMQReplyListener == null) {throw new IllegalArgumentException("Property 'rocketMQListener' or 'rocketMQReplyListener' is required");}Assert.notNull(consumerGroup, "Property 'consumerGroup' is required");Assert.notNull(nameServer, "Property 'nameServer' is required");Assert.notNull(topic, "Property 'topic' is required");RPCHook rpcHook = RocketMQUtil.getRPCHookByAkSk(applicationContext.getEnvironment(),this.rocketMQMessageListener.accessKey(), this.rocketMQMessageListener.secretKey());boolean enableMsgTrace = rocketMQMessageListener.enableMsgTrace();if (Objects.nonNull(rpcHook)) {consumer = new DefaultMQPushConsumer(consumerGroup, rpcHook, new AllocateMessageQueueAveragely(),enableMsgTrace, this.applicationContext.getEnvironment().resolveRequiredPlaceholders(this.rocketMQMessageListener.customizedTraceTopic()));consumer.setVipChannelEnabled(false);} else {log.debug("Access-key or secret-key not configure in " + this + ".");consumer = new DefaultMQPushConsumer(consumerGroup, enableMsgTrace,this.applicationContext.getEnvironment().resolveRequiredPlaceholders(this.rocketMQMessageListener.customizedTraceTopic()));}consumer.setNamespace(namespace);String customizedNameServer = this.applicationContext.getEnvironment().resolveRequiredPlaceholders(this.rocketMQMessageListener.nameServer());if (customizedNameServer != null) {consumer.setNamesrvAddr(customizedNameServer);} else {consumer.setNamesrvAddr(nameServer);}if (accessChannel != null) {consumer.setAccessChannel(accessChannel);}consumer.setConsumeThreadMax(consumeThreadMax);consumer.setConsumeThreadMin(consumeThreadNumber);consumer.setConsumeTimeout(consumeTimeout);consumer.setMaxReconsumeTimes(maxReconsumeTimes);consumer.setAwaitTerminationMillisWhenShutdown(awaitTerminationMillisWhenShutdown);consumer.setInstanceName(instanceName);switch (messageModel) {case BROADCASTING:consumer.setMessageModel(org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel.BROADCASTING);break;case CLUSTERING:consumer.setMessageModel(org.apache.rocketmq.remoting.protocol.heartbeat.MessageModel.CLUSTERING);break;default:throw new IllegalArgumentException("Property 'messageModel' was wrong.");}switch (selectorType) {case TAG:consumer.subscribe(topic, selectorExpression);break;case SQL92:consumer.subscribe(topic, MessageSelector.bySql(selectorExpression));break;default:throw new IllegalArgumentException("Property 'selectorType' was wrong.");}switch (consumeMode) {case ORDERLY:consumer.setMessageListener(new DefaultMessageListenerOrderly());break;case CONCURRENTLY:consumer.setMessageListener(new DefaultMessageListenerConcurrently());break;default:throw new IllegalArgumentException("Property 'consumeMode' was wrong.");}//if String is not is equal "true" TLS mode will represent the as default value falseconsumer.setUseTLS(new Boolean(tlsEnable));if (rocketMQListener instanceof RocketMQPushConsumerLifecycleListener) {((RocketMQPushConsumerLifecycleListener) rocketMQListener).prepareStart(consumer);} else if (rocketMQReplyListener instanceof RocketMQPushConsumerLifecycleListener) {((RocketMQPushConsumerLifecycleListener) rocketMQReplyListener).prepareStart(consumer);}}//......
}    

DefaultRocketMQListenerContainer的start方法(SmartLifecycle)执行consumer的start方法;其afterPropertiesSet方法(InitializingBean)会执行initRocketMQPushConsumer方法来创建consumer
initRocketMQPushConsumer方法主要是创建DefaultMQPushConsumer,设置messageModel,根据selectorType执行subscribe方法,根据consumeMode来设置messageListener(DefaultMessageListenerOrderly或者DefaultMessageListenerConcurrently),最后针对RocketMQPushConsumerLifecycleListener执行prepareStart方法

DefaultMessageListenerOrderly

rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/support/DefaultRocketMQListenerContainer.java

    public class DefaultMessageListenerOrderly implements MessageListenerOrderly {@SuppressWarnings("unchecked")@Overridepublic ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {for (MessageExt messageExt : msgs) {log.debug("received msg: {}", messageExt);try {long now = System.currentTimeMillis();handleMessage(messageExt);long costTime = System.currentTimeMillis() - now;log.debug("consume {} cost: {} ms", messageExt.getMsgId(), costTime);} catch (Exception e) {log.warn("consume message failed. messageId:{}, topic:{}, reconsumeTimes:{}", messageExt.getMsgId(), messageExt.getTopic(), messageExt.getReconsumeTimes(), e);context.setSuspendCurrentQueueTimeMillis(suspendCurrentQueueTimeMillis);return ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT;}}return ConsumeOrderlyStatus.SUCCESS;}}

DefaultMessageListenerOrderly实现了MessageListenerOrderly的consumeMessage接口,它内部遍历msgs,挨个执行handleMessage,有异常的话则设置context.setSuspendCurrentQueueTimeMillis(suspendCurrentQueueTimeMillis),然后返回ConsumeOrderlyStatus.SUSPEND_CURRENT_QUEUE_A_MOMENT

DefaultMessageListenerConcurrently

rocketmq-spring-boot/src/main/java/org/apache/rocketmq/spring/support/DefaultRocketMQListenerContainer.java

    public class DefaultMessageListenerConcurrently implements MessageListenerConcurrently {@SuppressWarnings("unchecked")@Overridepublic ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {for (MessageExt messageExt : msgs) {log.debug("received msg: {}", messageExt);try {long now = System.currentTimeMillis();handleMessage(messageExt);long costTime = System.currentTimeMillis() - now;log.debug("consume {} cost: {} ms", messageExt.getMsgId(), costTime);} catch (Exception e) {log.warn("consume message failed. messageId:{}, topic:{}, reconsumeTimes:{}", messageExt.getMsgId(), messageExt.getTopic(), messageExt.getReconsumeTimes(), e);context.setDelayLevelWhenNextConsume(delayLevelWhenNextConsume);return ConsumeConcurrentlyStatus.RECONSUME_LATER;}}return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}}

DefaultMessageListenerConcurrently实现了MessageListenerConcurrently接口的consumeMessage方法,它内部遍历msgs,挨个执行handleMessage,有异常的话设置context.setDelayLevelWhenNextConsume(delayLevelWhenNextConsume),然后返回ConsumeConcurrentlyStatus.RECONSUME_LATER

handleMessage

    private void handleMessage(MessageExt messageExt) throws MQClientException, RemotingException, InterruptedException {if (rocketMQListener != null) {rocketMQListener.onMessage(doConvertMessage(messageExt));} else if (rocketMQReplyListener != null) {Object replyContent = rocketMQReplyListener.onMessage(doConvertMessage(messageExt));Message<?> message = MessageBuilder.withPayload(replyContent).build();org.apache.rocketmq.common.message.Message replyMessage = MessageUtil.createReplyMessage(messageExt, convertToBytes(message));DefaultMQProducer producer = consumer.getDefaultMQPushConsumerImpl().getmQClientFactory().getDefaultMQProducer();producer.setSendMsgTimeout(replyTimeout);producer.send(replyMessage, new SendCallback() {@Override public void onSuccess(SendResult sendResult) {if (sendResult.getSendStatus() != SendStatus.SEND_OK) {log.error("Consumer replies message failed. SendStatus: {}", sendResult.getSendStatus());} else {log.debug("Consumer replies message success.");}}@Override public void onException(Throwable e) {log.error("Consumer replies message failed. error: {}", e.getLocalizedMessage());}});}}

handleMessage方法则是委托给了rocketMQListener.onMessage(doConvertMessage(messageExt)),即回调业务自定义的RocketMQListener

start

org/apache/rocketmq/client/consumer/DefaultMQPushConsumer.java

    /*** This method gets internal infrastructure readily to serve. Instances must call this method after configuration.** @throws MQClientException if there is any client error.*/@Overridepublic void start() throws MQClientException {setConsumerGroup(NamespaceUtil.wrapNamespace(this.getNamespace(), this.consumerGroup));this.defaultMQPushConsumerImpl.start();if (null != traceDispatcher) {try {traceDispatcher.start(this.getNamesrvAddr(), this.getAccessChannel());} catch (MQClientException e) {log.warn("trace dispatcher start failed ", e);}}}

DefaultMQPushConsumer的start方法先执行setConsumerGroup,然后委托给了defaultMQPushConsumerImpl.start(),如果有traceDispatcher的话,则执行traceDispatcher.start方法
defaultMQPushConsumerImpl.start()方法会触发MQClientInstance.start(),后者会触发pullMessageService.start()以及rebalanceService.start()(会在doRebalance的时候触发defaultMQPushConsumerImpl.executePullRequestImmediately,往pullRequestQueue放pullRequest),前者会从pullRequestQueue获取pullRequest然后执行DefaultMQPushConsumerImpl.pullMessage方法,里头是执行pullAPIWrapper.pullKernelImpl,然后通过pullCallback往processQueue.putMessage,再触发consumeMessageService.submitConsumeRequest,它会回调listener.consumeMessage来消费消息

小结

RocketMQMessageListenerBeanPostProcessor实现了ApplicationContextAware、BeanPostProcessor、InitializingBean接口,其中postProcessAfterInitialization方法判断bean有没有RocketMQMessageListener注解,如果有的话则通过enhance进行增强,另外会通过listenerContainerConfiguration的registerContainer进行注册

ListenerContainerConfiguration的registerContainer方法会根据注解信息及对应的bean构造DefaultRocketMQListenerContainer并注册到genericApplicationContext,同时执行其start方法
DefaultRocketMQListenerContainer的start方法(SmartLifecycle)执行consumer的start方法;其afterPropertiesSet方法(InitializingBean)会执行initRocketMQPushConsumer方法来创建consumer
start方法主要是执行pullMessageService.start()以及rebalanceService.start(),前者负责从pullRequestQueue获取pullRequest然后拉取消息放到processQueue,然后触发回调listener.consumeMessage来消费消息;后者负责rebalance,一开始会触发defaultMQPushConsumerImpl.executePullRequestImmediately,即往pullRequestQueue放pullRequest
pushConsumer本质上还是基于pull的模式来的,从RocketMQMessageListenerBeanPostProcessor --> DefaultRocketMQListenerContainer.start --> DefaultMQPushConsumer.start --> defaultMQPushConsumerImpl.start() --> pullMessageService.start()以及rebalanceService.start()

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

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

相关文章

云安全—分布式基础

0x00 前言 云必然是依赖于分布式技术来进行实现的&#xff0c;所以有必要学习和来了解分布式相关的内容 0x01 分布式计算 1.基本概述 分布式计算的定义&#xff1a;通过网络互联的计算机都具有一定的计算能力&#xff0c;他们之间互相传递数据&#xff0c;实现信息共享&…

adb 操作命令(adb调试QT项目使用到的命令)

1.adb连接串口 获取root权限 adb root && adb remount && adb shell2.测试串口命令 stty -F /dev/ttyS4 cs8 -parenb -cstopb -echoecho "12345\n" > /dev/ttyS8cat /dev/ttyS4 &3.软件在安卓系统上的名字已经活动名称&#xff08;下面是示…

用3D扫描生成合成数据

合成数据集&#xff08;Synthetic Datasets&#xff09;正在成为计算机视觉模型训练的标准部分。 虽然新工具使合成数据集变得更容易访问&#xff0c;但除了标准机器学习过程之外&#xff0c;许多工具还需要对 3D 建模有基本的了解。 最简单的捷径是从现实世界中获取现有对象并…

Vue中的v-if和v-show指令有什么区别?

在Vue中,v-if和v-show是两个常用的指令,用于根据条件控制元素的显示和隐藏。虽然它们都可以根据条件来切换元素的可见性,但它们的实现和行为有一些区别。 1:编译时机: v-if是在编译阶段进行条件判断,如果条件为false,则在DOM中不会渲染该元素及其子元素;v-show则是在运…

基于材料生成优化的BP神经网络(分类应用) - 附代码

基于材料生成优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于材料生成优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.材料生成优化BP神经网络3.1 BP神经网络参数设置3.2 材料生成算法应用 4.测试结果…

28 mysql 数据记录的 存储更新删除

前言 前面 我们探讨了 索引记录的 新增, 更新, 删除 这里 我们来看一下 mysql 的核心数据记录的 新增更新删除 这里 来看一下 增删改 的相关实现 数据记录 和 索引记录 的处理方式是一致的 mysql 数据记录的存储 新增部分参见 自增长主键的实现 以及 记录的插入 mysql…

keep-alive 是 Vue 的一个内置组件,用于缓存其他组件的实例,以避免重复渲染和销毁,它可以在需要频繁切换的组件之间提供性能优化

目录 keep-alive 使用 keep-alive 的示例代码&#xff1a; 手动清除组件缓存的示例代码&#xff1a; keep-alive 组件有以下几个优点&#xff1a; keep-alive 的原理&#xff1a; 使用 keep-alive 组件&#xff0c;你可以包裹需要缓存的组件&#xff0c;然后这些组件在切…

1.3 矩阵

一、向量与矩阵 下面是三个向量 u \boldsymbol u u、 v \boldsymbol v v、 w \boldsymbol w w&#xff1a; u [ 1 − 1 0 ] v [ 0 1 − 1 ] w [ 0 0 1 ] \boldsymbol u\begin{bmatrix}\,\,\,\,1\\-1\\\,\,\,\,0\end{bmatrix}\kern 10pt\boldsymbol v\begin{bmatrix}\,\,\,…

RemObjects Elements 12.0 Crack

Elements 是一个现代多功能软件开发工具链。 它支持六种流行的编程语言&#xff1a;Oxygene (Object Pascal)、C#、Java、Mercury (Visual Basic.NET™)、Go 和 Swift&#xff0c;适用于所有现代平台。 使用 Elements&#xff0c;您可以为您喜欢的任何平台进行编程- 无论是单…

基于Ubuntu Server编译YTM32 SDK工程

基于Ubuntu Server编译YTM32 SDK工程 文章目录 基于Ubuntu Server编译YTM32 SDK工程需求下载软件包安装与配置配置虚拟机联网模式启用ssh连接启用ftp连接安装armgcc编译工具链确认make工具 验证 需求 在Linux系统环境下搭建SDK的编译环境&#xff1a; 方便加入到持续集成工具…

中间件安全-CVE复现IISApacheTomcatNginx漏洞复现

目录 中间件安全&CVE复现&IIS&Apache&Tomcat&Nginx漏洞复现中间件-IIS安全问题中间件-Nginx安全问题漏洞复现Nginx 解析漏洞复现Nginx 文件名逻辑漏洞 中间件-Apache-RCE&目录遍历&文件解析等安全问题漏洞复现漏洞复现CVE_2021_42013 RCE代码执行&…

TCP/IP(二十二)TCP 实战抓包分析(六)TCP 快速建立连接

一 TCP Fast Open 快速建立连接 说明&#xff1a; 之前讲解TCP 相关知识点遗漏了这个知识点,补充上 ① TFO简介 ② 请求 Fast Open Cookie过程 "原理图" ③ 真正开始 TCP Fast Open 重点&#xff1a; TFO 使 SYN包 可以包含payload 数据 ④ 抓包分析 1、…

AD20原理图库的制作

1、打开“51单片机最小系统”的工程文件。 2、创建原理图库文件&#xff1a;单击“文件”菜单&#xff0c;选择“新的”选项中的“库”选项&#xff0c;再选择“原理图库”&#xff0c;进入原理图库元件的编辑界面。 3、保存原理图库文件&#xff1a;选择“文件”菜单&#xff…

Pytorch ddp切换forward函数 验证ddp是否生效

DDP及其在pytorch中应用 ddp默认调用forward函数&#xff0c;有些模型无法使用forward函数&#xff0c;可以对模型包装一下。 class modelWraper(nn.Module):def __init__(self, model):super().__init__()self.model modeldef forward(self, *args, **kwargs):return self.…

What‘s new in Arana v0.2.0

Arana 定位于云原生数据库代理&#xff0c;它可以以 sidecar 模式部署为数据库服务网格&#xff0c;项目地址是 https://github.com/arana-db/arana 。Arana 提供透明的数据访问能力&#xff0c;当用户在使用时&#xff0c;可以不用关心数据库的 “分片” 细节&#xff0c;像使…

CPU飙高问题排查命令

1. 远程客户端连接服务器,top命令查看cpu占用最高的进程id 2. (top -H -p 进程pid) 命令: 找出进程里面线程占用CPU高的线程有哪些 ? 3. (printf 0x%x\n 线程id) 线程id转16进制 4. (./jstack PID | grep TID(十六进制) -A 30)

关于Redis(Redisson)超时问题的分析

概述 生产环境中流量高峰期会出现短时间的redis异常&#xff0c;主要报错如下&#xff1a; Redis server response timeoutRedisTimeoutException: Command execution timeout for command: (PING)Command still hasn’t been written into connection! 根据redisson官方所述…

设计模式之建造者模式

什么是建造者模式 建造者模式是一种创建型设计模式&#xff0c;它提供了一种创建对象的最佳方式。这种模式将一个复杂的构建与其表示相分离&#xff0c;使得同样的构建过程可以创建不同的表示。 举一个简单的例子&#xff1a;假设我们要创建一个复杂的对象&#xff0c;例如一…

【Edabit 算法 ★☆☆☆☆☆】Maximum Edge of a Triangle

【Edabit 算法 ★☆☆☆☆☆】Maximum Edge of a Triangle algorithms math numbers Instructions Create a function that finds the maximum range of a triangle’s third edge, where the side lengths are all integers. Examples nextEdge(8, 10) // 17 nextEdge(5, 7…