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

聊聊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配合使用&#xff0c;我们通常在一个POJO里面进行配置&#xff1a; Data Configuration ConfigurationProperties(pre…...

三、组件与数据交互

一、组件基础 1、单文件组件 第一步&#xff1a;引入组件 import ComponentTest from ./components/ComponentTest.vue 第二步&#xff1a;挂载组件 components: {ComponentTest } 第三步&#xff1a;显示组件 <ComponentTest></ComponentTest><!-- 父组件 --…...

#define 宏定义看这一篇文章就够了

前言&#xff1a;在c/c学习的过程中&#xff0c;宏定义&#xff08;#define&#xff09;是作为初学者学习到的为数不多的预处理指令&#xff0c;在学习的时候我们被告知他可以帮助我们更高效的写程序&#xff0c;可以增加程序的可读性&#xff0c;但宏定义&#xff08;#define&…...

LeetCode算法栈—验证图书取出顺序

验证图书取出顺序 目录 验证图书取出顺序 题解&#xff1a; 代码&#xff1a; 运行结果&#xff1a; 验证图书取出顺序 现在图书馆有一堆图书需要放入书架&#xff0c;并且图书馆的书架是一种特殊的数据结构&#xff0c;只能按照 一定 的顺序 放入 和 拿取 书籍。 给定一个…...

PAM从入门到精通(十八)

接前一篇文章&#xff1a;PAM从入门到精通&#xff08;十七&#xff09; 本文参考&#xff1a; 《The Linux-PAM Application Developers Guide》 PAM 的应用开发和内部实现源码分析 先再来重温一下PAM系统架构&#xff1a; 更加形象的形式&#xff1a; 六、整体流程示例 2.…...

【区间 DP】热门区间 DP 运用题

题目描述 这是 LeetCode 上的 「312. 戳气球」 &#xff0c;难度为 「困难」。 Tag : 「区间 DP」、「动态规划」 有 n 个气球&#xff0c;编号为 0 到 n - 1&#xff0c;每个气球上都标有一个数字&#xff0c;这些数字存在数组 nums 中。 现在要求你戳破所有的气球。戳破第 i …...

正则表达式,日期选择器时间限制,报错原因

目录 一、正则表达式 1、表达式含义 2、书写表达式 二、时间限制 1、原始日期选择器改造 2、禁止选择未来时间 3、从...到...两个日期选择器的时间限制 三、Uncaught (in promise) Error报错 一、正则表达式 1、表达式含义 &#xff08;1&#xff09;/^([a-zA-Z0-9_.…...

YOLOv7 改进原创 HFAMPAN 结构,信息高阶特征对齐融合和注入,全局融合多级特征,将全局信息注入更高级别

💡本篇内容:YOLOv7 改进原创 HFAMPAN 结构,信息高阶特征对齐融合和注入,全局融合多级特征,将全局信息注入更高级别 💡🚀🚀🚀本博客 改进源代码改进 适用于 YOLOv7 按步骤操作运行改进后的代码即可 💡本文提出改进 原创 方式:二次创新,YOLOv7 专属 论文理…...

django建站过程(1)

django建站过程&#xff08;1&#xff09; 使用pycharm创建过程运行项目创建数据库创建超级用户登录生成的后台&#xff1a;界面本地化 准备以django,bootstrap来做一个过程记录&#xff0c;文章主要阐述过程的细节。 使用pycharm创建过程 创建项目“schoolapps”&#xff0c;…...

使用 Typhoeus 和 Ruby 编写的爬虫程序

以下是一个使用 Typhoeus 和 Ruby 编写的爬虫程序&#xff0c;用于爬取 &#xff0c;同时使用了 jshk.com.cn/get_proxy 这段代码获取代理&#xff1a; #!/usr/bin/env rubyrequire typhoeus require jsondef get_proxyurl "https://www.duoip.cn/get_proxy"respon…...

Git 安装和基础命令、IDEA 基础操作

目录 总结命令&#xff1a;1、安装&#xff1a;1、安装2、配置环境变量&#xff1a; 2、Git操作&#xff1a;1、初始化&#xff1a;1、姓名邮箱&#xff1a;2、初始化仓库&#xff1a;3、工作区和暂存区分析 2、提交文件3、查看版本库状态4、安装小乌龟git不显示图标 5、查看提…...

做一个最新版的淘宝客返利程序源码有多难?

我们都知道淘宝客返利程序成为了很多人的创业和赚钱的工具。这种程序允许通过推广淘宝商品来获得佣金。然而&#xff0c;你知道构建这样一个淘宝客返利程序有多难吗&#xff1f;今天我们就从最基本的API说起&#xff0c;现在我将介绍构建一个最新版淘宝客返利程序所需的关键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全能学习面试指南&#xff1a;https://javaxiaobear.cn 今天我们主要学习如何正确停止一个线程&#xff1f;以及为什么用 volatile 标记位的停止方法是错误的&#xff1f; 首先&#xff0c;我们来复习如何启动一个线程&#xff0c;想要启动线程需要调用 Thread 类的 start…...

pytorch nn.Embedding 读取gensim训练好的词/字向量(有例子)

最近在跑深度学习模型&#xff0c;发现Embedding随机性太强导致模型结果有出入&#xff0c;因此考虑固定初始随机向量&#xff0c;既提前训练好词/字向量&#xff0c;不多说上代码&#xff01;&#xff01; 1、利用gensim训练字向量&#xff08;词向量自行修改&#xff09; #…...

2.1.1BFS中的Flood Fill和最短路模型

1.池塘计数 农夫约翰有一片 N ∗ M N∗M N∗M 的矩形土地。 最近&#xff0c;由于降雨的原因&#xff0c;部分土地被水淹没了。 现在用一个字符矩阵来表示他的土地。 每个单元格内&#xff0c;如果包含雨水&#xff0c;则用”W”表示&#xff0c;如果不含雨水&#xff0c;…...

Mysql 新增更新、删除新增、忽略

当主键或唯一键冲突时&#xff0c;Mysql可以进行更新、删除新增、忽略插入等操作。 1.更新 当主键或唯一键冲突时&#xff0c;可以指定更新内容。 INSERT INTO table_name (column_name, column_name, column_name) VALUES (column_value, column_value,column_value) ON DUPL…...

Node-模块系统的用法

题记 node.js模块系统的用法&#xff0c;以下是具体操作过程和代码 为了让Node.js的文件可以相互调用&#xff0c;Node.js提供了一个简单的模块系统。 模块是Node.js 应用程序的基本组成部分&#xff0c;文件和模块是一一对应的。 一个 Node.js 文件就是一个模块&#xff0c;这…...

XSS攻击(1), 测试XSS漏洞, 获取cookie

XSS漏洞, 测试XSS漏洞, 获取cookie 一, 概念: XSS(Cross-Site Scripting), 跨站攻击脚本, XSS漏洞发生在前端, 依赖于浏览器的解析引擎, 让前端执行攻击代码. XSS其实也算注入类的攻击, XSS代码注入需要有JavaScript编程基础. 二, 目的: XSS&#xff08;跨站脚本&#xff0…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

ABAP设计模式之---“简单设计原则(Simple Design)”

“Simple Design”&#xff08;简单设计&#xff09;是软件开发中的一个重要理念&#xff0c;倡导以最简单的方式实现软件功能&#xff0c;以确保代码清晰易懂、易维护&#xff0c;并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计&#xff0c;遵循“让事情保…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析

Java求职者面试指南&#xff1a;Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问&#xff08;基础概念问题&#xff09; 1. 请解释Spring框架的核心容器是什么&#xff1f;它在Spring中起到什么作用&#xff1f; Spring框架的核心容器是IoC容器&#…...

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…...

R 语言科研绘图第 55 期 --- 网络图-聚类

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…...

c# 局部函数 定义、功能与示例

C# 局部函数&#xff1a;定义、功能与示例 1. 定义与功能 局部函数&#xff08;Local Function&#xff09;是嵌套在另一个方法内部的私有方法&#xff0c;仅在包含它的方法内可见。 • 作用&#xff1a;封装仅用于当前方法的逻辑&#xff0c;避免污染类作用域&#xff0c;提升…...

Linux安全加固:从攻防视角构建系统免疫

Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...