聊聊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()
相关文章:
聊聊RocketMQMessageListener的实现机制
序 本文主要研究一下RocketMQMessageListener的实现机制 示例 Service RocketMQMessageListener(nameServer "${demo.rocketmq.myNameServer}", topic "${demo.rocketmq.topic.user}", consumerGroup "user_consumer") public class UserC…...
ConfigurationProperties注解详解
ConfigurationProperties和Value注解用于获取配置文件中的属性定义并绑定到Java Bean或属性中 一个简单的例子 ConfigurationProperties需要和Configuration配合使用,我们通常在一个POJO里面进行配置: Data Configuration ConfigurationProperties(pre…...
三、组件与数据交互
一、组件基础 1、单文件组件 第一步:引入组件 import ComponentTest from ./components/ComponentTest.vue 第二步:挂载组件 components: {ComponentTest } 第三步:显示组件 <ComponentTest></ComponentTest><!-- 父组件 --…...
#define 宏定义看这一篇文章就够了
前言:在c/c学习的过程中,宏定义(#define)是作为初学者学习到的为数不多的预处理指令,在学习的时候我们被告知他可以帮助我们更高效的写程序,可以增加程序的可读性,但宏定义(#define&…...
LeetCode算法栈—验证图书取出顺序
验证图书取出顺序 目录 验证图书取出顺序 题解: 代码: 运行结果: 验证图书取出顺序 现在图书馆有一堆图书需要放入书架,并且图书馆的书架是一种特殊的数据结构,只能按照 一定 的顺序 放入 和 拿取 书籍。 给定一个…...
PAM从入门到精通(十八)
接前一篇文章:PAM从入门到精通(十七) 本文参考: 《The Linux-PAM Application Developers Guide》 PAM 的应用开发和内部实现源码分析 先再来重温一下PAM系统架构: 更加形象的形式: 六、整体流程示例 2.…...
【区间 DP】热门区间 DP 运用题
题目描述 这是 LeetCode 上的 「312. 戳气球」 ,难度为 「困难」。 Tag : 「区间 DP」、「动态规划」 有 n 个气球,编号为 0 到 n - 1,每个气球上都标有一个数字,这些数字存在数组 nums 中。 现在要求你戳破所有的气球。戳破第 i …...
正则表达式,日期选择器时间限制,报错原因
目录 一、正则表达式 1、表达式含义 2、书写表达式 二、时间限制 1、原始日期选择器改造 2、禁止选择未来时间 3、从...到...两个日期选择器的时间限制 三、Uncaught (in promise) Error报错 一、正则表达式 1、表达式含义 (1)/^([a-zA-Z0-9_.…...
YOLOv7 改进原创 HFAMPAN 结构,信息高阶特征对齐融合和注入,全局融合多级特征,将全局信息注入更高级别
💡本篇内容:YOLOv7 改进原创 HFAMPAN 结构,信息高阶特征对齐融合和注入,全局融合多级特征,将全局信息注入更高级别 💡🚀🚀🚀本博客 改进源代码改进 适用于 YOLOv7 按步骤操作运行改进后的代码即可 💡本文提出改进 原创 方式:二次创新,YOLOv7 专属 论文理…...
django建站过程(1)
django建站过程(1) 使用pycharm创建过程运行项目创建数据库创建超级用户登录生成的后台:界面本地化 准备以django,bootstrap来做一个过程记录,文章主要阐述过程的细节。 使用pycharm创建过程 创建项目“schoolapps”,…...
使用 Typhoeus 和 Ruby 编写的爬虫程序
以下是一个使用 Typhoeus 和 Ruby 编写的爬虫程序,用于爬取 ,同时使用了 jshk.com.cn/get_proxy 这段代码获取代理: #!/usr/bin/env rubyrequire typhoeus require jsondef get_proxyurl "https://www.duoip.cn/get_proxy"respon…...
Git 安装和基础命令、IDEA 基础操作
目录 总结命令:1、安装:1、安装2、配置环境变量: 2、Git操作:1、初始化:1、姓名邮箱:2、初始化仓库:3、工作区和暂存区分析 2、提交文件3、查看版本库状态4、安装小乌龟git不显示图标 5、查看提…...
做一个最新版的淘宝客返利程序源码有多难?
我们都知道淘宝客返利程序成为了很多人的创业和赚钱的工具。这种程序允许通过推广淘宝商品来获得佣金。然而,你知道构建这样一个淘宝客返利程序有多难吗?今天我们就从最基本的API说起,现在我将介绍构建一个最新版淘宝客返利程序所需的关键API…...
day5:Node.js 第三方库
day5:Node.js 第三方库 文章目录 day5:Node.js 第三方库使用 Express.js 构建 Web 应用安装 Express第一个 Express 框架实例第二个 Express 框架实例Node.js 连接 MySQL查询数据插入数据更新数据删除数据使用 Express.js 构建 Web 应用 Express框架是Node.js生态系统中的一…...
如何正确停止线程?为什么 volatile 标记位的停止方法是错误的?
Java全能学习面试指南:https://javaxiaobear.cn 今天我们主要学习如何正确停止一个线程?以及为什么用 volatile 标记位的停止方法是错误的? 首先,我们来复习如何启动一个线程,想要启动线程需要调用 Thread 类的 start…...
pytorch nn.Embedding 读取gensim训练好的词/字向量(有例子)
最近在跑深度学习模型,发现Embedding随机性太强导致模型结果有出入,因此考虑固定初始随机向量,既提前训练好词/字向量,不多说上代码!! 1、利用gensim训练字向量(词向量自行修改) #…...
2.1.1BFS中的Flood Fill和最短路模型
1.池塘计数 农夫约翰有一片 N ∗ M N∗M N∗M 的矩形土地。 最近,由于降雨的原因,部分土地被水淹没了。 现在用一个字符矩阵来表示他的土地。 每个单元格内,如果包含雨水,则用”W”表示,如果不含雨水,…...
Mysql 新增更新、删除新增、忽略
当主键或唯一键冲突时,Mysql可以进行更新、删除新增、忽略插入等操作。 1.更新 当主键或唯一键冲突时,可以指定更新内容。 INSERT INTO table_name (column_name, column_name, column_name) VALUES (column_value, column_value,column_value) ON DUPL…...
Node-模块系统的用法
题记 node.js模块系统的用法,以下是具体操作过程和代码 为了让Node.js的文件可以相互调用,Node.js提供了一个简单的模块系统。 模块是Node.js 应用程序的基本组成部分,文件和模块是一一对应的。 一个 Node.js 文件就是一个模块,这…...
XSS攻击(1), 测试XSS漏洞, 获取cookie
XSS漏洞, 测试XSS漏洞, 获取cookie 一, 概念: XSS(Cross-Site Scripting), 跨站攻击脚本, XSS漏洞发生在前端, 依赖于浏览器的解析引擎, 让前端执行攻击代码. XSS其实也算注入类的攻击, XSS代码注入需要有JavaScript编程基础. 二, 目的: XSS(跨站脚本࿰…...
快速掌握今日热榜:一站式聚合全网热门头条的终极指南
快速掌握今日热榜:一站式聚合全网热门头条的终极指南 【免费下载链接】TopList 今日热榜,一个获取各大热门网站热门头条的聚合网站,使用Go语言编写,多协程异步快速抓取信息,预览:https://mo.fish 项目地址: https://…...
Golang怎么读取环境变量_Golang如何用os.Getenv获取系统环境变量【基础】
os.Getenv读不到变量是因为进程只继承启动时的环境快照,需确认变量已生效;其返回空字符串无法区分“未设置”和“值为空”,应改用os.LookupEnv判断存在性。os.Getenv 读不到变量?先确认它真在进程环境里Go 程序启动时会拷贝父进程…...
OneNote到Markdown迁移的最佳解决方案:如何用开源工具实现10倍效率提升
OneNote到Markdown迁移的最佳解决方案:如何用开源工具实现10倍效率提升 【免费下载链接】onenote-md-exporter ConsoleApp to export OneNote notebooks to Markdown formats 项目地址: https://gitcode.com/gh_mirrors/on/onenote-md-exporter 面对OneNote笔…...
红魔7s Pro变砖别慌!保姆级9008线刷救砖教程(附高通驱动+工具包)
红魔7s Pro变砖急救指南:从崩溃到重生的全流程解析 当你的红魔7s Pro突然变成一块"砖头",屏幕漆黑一片,按键毫无反应,那种瞬间涌上心头的焦虑感我完全理解。作为一名经历过无数次救砖操作的老玩家,我想告诉你…...
终极RDP Wrapper指南:免费解锁Windows远程桌面完整功能
终极RDP Wrapper指南:免费解锁Windows远程桌面完整功能 【免费下载链接】rdpwrap RDP Wrapper Library 项目地址: https://gitcode.com/gh_mirrors/rd/rdpwrap 你是否曾因Windows家庭版无法使用远程桌面而烦恼?或者希望在企业环境中实现多用户同时…...
保姆级教程:用Intel Realsense D435i和Aruco Marker搞定Dobot Magician手眼标定(附常见报错解决)
从零开始实现Dobot Magician与Realsense D435i的高精度手眼标定 当你第一次将Dobot Magician机械臂与Intel Realsense D435i深度相机组合使用时,手眼标定可能是最令人头疼的环节之一。作为机器人视觉系统的核心步骤,精确的手眼标定直接决定了后续抓取、分…...
分类数据集 - CT图像脊柱骨折检测图像分类数据集下载
数据集介绍:CT图像脊柱骨折检测图像分类数据集,真实临床采集高质量脊柱CT断层扫描图片数据;适用实际项目应用:CT图像脊柱骨折检测图像分类项目,脊柱创伤辅助诊断系统,以及作为通用脊柱骨折检测数据集场景数…...
从收藏废人到知识管理高手,就差这8个工具
🗂️你收藏的那500篇文章99%你不会再看第二次收藏 ≠ 学到 看过 ≠ 记住 信息管理才是真正的竞争力这8个工具,帮你把"收藏夹吃灰"变成真正属于自己的知识体系全部附网址知识管理必备🧠 2026必收藏我们这一代人,有一个…...
PICO4手势交互开发避坑实录:MRTK3 + PICO SDK 2.3.0 完整配置与手部模型修复指南
PICO4手势交互开发深度实战:MRTK3与PICO SDK 2.3.0全链路问题诊断与优化 当MRTK3遇上PICO4的最新SDK,开发者往往会面临一系列意料之外的兼容性挑战。从手部模型翻转、关节错位到射线方向异常,这些问题不仅影响用户体验,更可能直接…...
告别联网焦虑!用HLK-V20-SUIT离线语音模块给STM32设备加个‘嘴’(附完整烧录避坑指南)
STM32离线语音交互实战:HLK-V20-SUIT模块从定制到部署全解析 在工业自动化产线嘈杂环境中,工人喊出"启动传送带"却因网络延迟导致设备无响应;在偏远地区智能灌溉系统前,农户对着设备重复指令却因信号微弱无法触发操作—…...
