RocketMQ 5.1.0 源码详解 | Producer 启动流程

文章目录

  • 初始化DefaultMQProducer实例
  • 启动流程
    • DefaultMQProducer#start
    • DefaultMQProducerImpl#start
    • MQClientInstance#start
    • 启动流程总结
  • 实例内容

初始化DefaultMQProducer实例

初始化一个 DefaultMQProducer 对象的代码如下

// 返回一个producer对象
DefaultMQProducer producer = new DefaultMQProducer();
// 设置组名
producer.setProducerGroup("ProducerGroupName");
// 设置NameServer地址
producer.setNamesrvAddr("127.0.0.1:9876");

在初始化 DefaultMQProducer 时会初始化一个 DefaultMQProducerImpl 实例并赋值给 producer 的成员变量

public DefaultMQProducer() {this(null, MixAll.DEFAULT_PRODUCER_GROUP, null);
}public DefaultMQProducer(final String namespace, final String producerGroup, RPCHook rpcHook) {this.namespace = namespace;this.producerGroup = producerGroup;// 将defaultMQProducerImpl对象保存在成员变量中defaultMQProducerImpl = new DefaultMQProducerImpl(this, rpcHook);
}

同时,在初始化 DefaultMQProducerImpl 实例时也会将 producer 对象作为成员变量保存在 DefaultMQProducerImpl 实例中

构造 defaultMQProducerImpl 的代码如下

public DefaultMQProducerImpl(final DefaultMQProducer defaultMQProducer, RPCHook rpcHook) {// 将defaultMQProducer对象保存在成员变量中this.defaultMQProducer = defaultMQProducer;this.rpcHook = rpcHook;this.asyncSenderThreadPoolQueue = new LinkedBlockingQueue<>(50000);this.defaultAsyncSenderExecutor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors(),1000 * 60,TimeUnit.MILLISECONDS,this.asyncSenderThreadPoolQueue,new ThreadFactory() {private AtomicInteger threadIndex = new AtomicInteger(0);@Overridepublic Thread newThread(Runnable r) {return new Thread(r, "AsyncSenderExecutor_" + this.threadIndex.incrementAndGet());}});if (defaultMQProducer.getBackPressureForAsyncSendNum() > 10) {semaphoreAsyncSendNum = new Semaphore(Math.max(defaultMQProducer.getBackPressureForAsyncSendNum(),10), true);} else {semaphoreAsyncSendNum = new Semaphore(10, true);log.info("semaphoreAsyncSendNum can not be smaller than 10.");}if (defaultMQProducer.getBackPressureForAsyncSendNum() > 1024 * 1024) {semaphoreAsyncSendSize = new Semaphore(Math.max(defaultMQProducer.getBackPressureForAsyncSendNum(),1024 * 1024), true);} else {semaphoreAsyncSendSize = new Semaphore(1024 * 1024, true);log.info("semaphoreAsyncSendSize can not be smaller than 1M.");}
}

因此 DefaultMQProducerImpl 对象能够通过其保存的 producer 对象实例获取到 producer 的所有参数

经过上面简单的设置后此时 producer 被赋值的成员变量有

producerGroup = "ProducerGroupName"
namesrvAddr = "127.0.0.1:9876"
namespaceInitialized = false
defaultMQProducerImpl = new DefaultMQProducerImpl(this, rpcHook) // 这里rpcHook为null

启动流程

DefaultMQProducer#start

只需要执行下面代码即可启动上方初始化的 produer 对象

producer.start();

producerstart() 方法具体的内容如下

public void start() throws MQClientException {// 由于DefaultMQProducer继承了ClientConfig,所以可以直接使用ClientConfig的withNamespace方法this.setProducerGroup(withNamespace(this.producerGroup));// 调用DefaultMQProducerImpl的start方法this.defaultMQProducerImpl.start();// 如果traceDispatcher不为空,则调用traceDispatcher的start方法if (null != traceDispatcher) {try {traceDispatcher.start(this.getNamesrvAddr(), this.getAccessChannel());} catch (MQClientException e) {logger.warn("trace dispatcher start failed ", e);}}
}

可以看到 DefaultMQProducer 只是一个门面类,具体的实现都是由DefaultMQProducerImpl 去做的


由于 Namespace 的存在,因此在启动 producer 时首先会重新设置 producerGroup,我们需要重点关注经过 withNamespace() 方法处理后返回的生产者组名

public String withNamespace(String resource) {// this.getNamespace()不设置的话返回的是nullreturn NamespaceUtil.wrapNamespace(this.getNamespace(), resource);
}

可见 withNamespace() 方法仅仅是调用了 wrapNamespace() 方法,并将 NamespaceproducerGroup 作为参数一并传入

public static String wrapNamespace(String namespace, String resourceWithOutNamespace) {// 如果namespace为空或者resourceWithOutNamespace为空,则直接返回resourceWithOutNamespaceif (StringUtils.isEmpty(namespace) || StringUtils.isEmpty(resourceWithOutNamespace)) {return resourceWithOutNamespace;}// 如果resourceWithOutNamespace是SystemResource,// 或者resourceWithOutNamespace已经组合了Namespace,则直接返回resourceWithOutNamespaceif (isSystemResource(resourceWithOutNamespace) || isAlreadyWithNamespace(resourceWithOutNamespace, namespace)) {return resourceWithOutNamespace;}String resourceWithoutRetryAndDLQ = withOutRetryAndDLQ(resourceWithOutNamespace);StringBuilder stringBuilder = new StringBuilder();if (isRetryTopic(resourceWithOutNamespace)) {stringBuilder.append(MixAll.RETRY_GROUP_TOPIC_PREFIX);}if (isDLQTopic(resourceWithOutNamespace)) {stringBuilder.append(MixAll.DLQ_GROUP_TOPIC_PREFIX);}// 返回 [RETRY_PREFIX] + [DLQ_PREFIX] + namespace + % + resourceWithoutRetryAndDLQreturn stringBuilder.append(namespace).append(NAMESPACE_SEPARATOR).append(resourceWithoutRetryAndDLQ).toString();}

由于我们并没有设置 producerNamespace,因此会直接返回 producerGroup。造成的效果就是在这个生产者启动过程中第一行代码没有任何效果


traceDispatcher 又是个什么东西?traceDispatcher 的作用是追踪消息的发送和消费的轨迹,它是一个 AsyncTraceDispatcher 对象,它实现了 TraceDispatcher 接口,用于异步地发送追踪消息到 Broker。它可以帮助用户查看每条消息的完整链路数据,包括发送时间、消费时间、存储时间等。我们可以通过使用下面的构造函数构造出一个含有 traceDispatcher 的 DefaultMQProducer 实例

public DefaultMQProducer(final String namespace, final String producerGroup, RPCHook rpcHook,boolean enableMsgTrace, final String customizedTraceTopic) {this.namespace = namespace;this.producerGroup = producerGroup;defaultMQProducerImpl = new DefaultMQProducerImpl(this, rpcHook);//if client open the message trace featureif (enableMsgTrace) {try {AsyncTraceDispatcher dispatcher = new AsyncTraceDispatcher(producerGroup, TraceDispatcher.Type.PRODUCE, customizedTraceTopic, rpcHook);dispatcher.setHostProducer(this.defaultMQProducerImpl);traceDispatcher = dispatcher;this.defaultMQProducerImpl.registerSendMessageHook(new SendMessageTraceHookImpl(traceDispatcher));this.defaultMQProducerImpl.registerEndTransactionHook(new EndTransactionTraceHookImpl(traceDispatcher));} catch (Throwable e) {logger.error("system mqtrace hook init failed ,maybe can't send msg trace data");}}
}

由于我们在初始化 DefaultMQProducer 实例时没有生成 traceDispatcher 实例,因此 null != traceDispatcher 返回 FALSE,不调用 traceDispatcher 的 start 方法

DefaultMQProducerImpl#start

接下来调用 defaultMQProducerImplstart() 方法,方法内容如下:

private ServiceState serviceState = ServiceState.CREATE_JUST;public void start() throws MQClientException {this.start(true);
}public void start(final boolean startFactory) throws MQClientException {// serviceState默认为CREATE_JUSTswitch (this.serviceState) {case CREATE_JUST:// 1. 设置serviceState为START_FAILEDthis.serviceState = ServiceState.START_FAILED;// 2. 检查生产者组名是否合法this.checkConfig();// 3. 如果实例名为默认值则将生产者的instanceName设置为 UtilAll.getPid() + "#" + System.nanoTime()if (!this.defaultMQProducer.getProducerGroup().equals(MixAll.CLIENT_INNER_PRODUCER_GROUP)) {this.defaultMQProducer.changeInstanceNameToPID();}// 4. 使用MQClientManager.getInstance()返回一个单例的MQClientManager对象// defaultMQProducer继承了ClientConfig,因此getOrCreateMQClientInstance方法的参数可以是defaultMQProducer// mQClientFactory是MQClientInstance的一个实例,MQClientInstance是MQClientManager的内部类this.mQClientFactory = MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQProducer, rpcHook);// 5. 注册producer实例:将生产者组名作为key,defaultMQProducerImpl对象作为value保存到MQClientInstance的producerTable中boolean registerOK = mQClientFactory.registerProducer(this.defaultMQProducer.getProducerGroup(), this);if (!registerOK) {// 如果注册失败则将serviceState重设为CREATE_JUSTthis.serviceState = ServiceState.CREATE_JUST;throw new MQClientException("The producer group[" + this.defaultMQProducer.getProducerGroup()+ "] has been created before, specify another name please." + FAQUrl.suggestTodo(FAQUrl.GROUP_NAME_DUPLICATE_URL),null);}// 6. 将defaultMQProducer的createTopicKey作为key,TopicPublishInfo作为value,放入到defaultMQProducerImpl的topicPublishInfoTable中// createTopicKey的默认值为TBW102// topicPublishInfoTable的作用是存储topic的路由信息,包括topic的queue数目、brokerName、brokerId等this.topicPublishInfoTable.put(this.defaultMQProducer.getCreateTopicKey(), new TopicPublishInfo());// 7. 启动MQClientInstance实例if (startFactory) {// MQClientInstance的start方法会启动MQClientInstance的定时任务// 包括定时向所有broker发送心跳、定时清理过期的topic、定时清理过期的consumer、定时清理过期的producermQClientFactory.start();}log.info("the producer [{}] start OK. sendMessageWithVIPChannel={}", this.defaultMQProducer.getProducerGroup(),this.defaultMQProducer.isSendMessageWithVIPChannel());// 8. 如果启动成功则将serviceState设置为RUNNINGthis.serviceState = ServiceState.RUNNING;break;case RUNNING:case START_FAILED:case SHUTDOWN_ALREADY:throw new MQClientException("The producer service state not OK, maybe started once, "+ this.serviceState+ FAQUrl.suggestTodo(FAQUrl.CLIENT_SERVICE_NOT_OK),null);default:break;}// 立即发送心跳到所有brokerthis.mQClientFactory.sendHeartbeatToAllBrokerWithLock();// 开启一个定时任务来处理所有的 Request 状态,对异步的请求根据状态处理回调函数// 这个异步请求指的并不是在 send 中的异步回调机制,而是 Request-Reply 特性,用来模拟 RPC 调用RequestFutureHolder.getInstance().startScheduledTask(this);}

注意上述代码中有一段注释为createTopicKey 的默认值为 TBW102,这个 Topic 在自动创建 topic 时有关键作用

最后的 RequestFutureHolder.getInstance().startScheduledTask(this) 用来扫描和处理过期的异步请求,但是需要注意的是这个异步请求指的并不是在 send 中的异步回调机制,而是 Request-Reply 特性,用来模拟 RPC 调用

RocketMQ 有两种异步请求的方式,一种是在 send 方法中传入一个回调函数,当消息发送成功或失败时,会调用这个回调函数。这种方式不需要等待服务器的响应,只需要等待服务器的确认。

另一种是在 RocketMQ 4.7.0 后加入的 Request-Reply 特性,这种方式是模拟 RPC 调用,需要等待服务器的响应,并返回一个结果。这种方式需要使用 RequestResponseFuture 对象来封装请求和响应的信息。


在使用 getOrCreateMQClientInstance(this.defaultMQProducer, rpcHook) 返回一个 MQClientInstance 对象时,如果 factoryTable 中没有实例的话则会初始化一个新的实例,代码如下:

public MQClientInstance getOrCreateMQClientInstance(final ClientConfig clientConfig, RPCHook rpcHook) {// 最多情况下 clientId = ip@instanceName@unitName@RequestType.STREAM// 默认情况下 clientId = ip@instanceNameString clientId = clientConfig.buildMQClientId();MQClientInstance instance = this.factoryTable.get(clientId);if (null == instance) {instance =new MQClientInstance(clientConfig.cloneClientConfig(),this.factoryIndexGenerator.getAndIncrement(), clientId, rpcHook);MQClientInstance prev = this.factoryTable.putIfAbsent(clientId, instance);if (prev != null) {instance = prev;log.warn("Returned Previous MQClientInstance for clientId:[{}]", clientId);} else {log.info("Created new MQClientInstance for clientId:[{}]", clientId);}}return instance;
}

注意在初始化 MQClientInstance 实例的过程中会初始化一个新的 DefaultMQProducer 实例,与我们一开始就有的 producer 实例不是一个对象

public MQClientInstance(ClientConfig clientConfig, int instanceIndex, String clientId, RPCHook rpcHook) {// ...// 此处实例化内部的producer// 用于消费失败或超时的消息,sendMessageBack回发给broker,放到retry topic中重试消费this.defaultMQProducer = new DefaultMQProducer(MixAll.CLIENT_INNER_PRODUCER_GROUP);this.defaultMQProducer.resetClientConfig(clientConfig);// ...
}

此时新初始化的 DefaultMQProducer 实例的 producerGroup = "CLIENT_INNER_PRODUCER"instanceName = "DEFAULT"

接着,在初始化实例后又执行了 this.defaultMQProducer.resetClientConfig(clientConfig) 这行代码

public void resetClientConfig(final ClientConfig cc) {this.namesrvAddr = cc.namesrvAddr;this.clientIP = cc.clientIP;this.instanceName = cc.instanceName;this.clientCallbackExecutorThreads = cc.clientCallbackExecutorThreads;this.pollNameServerInterval = cc.pollNameServerInterval;this.heartbeatBrokerInterval = cc.heartbeatBrokerInterval;this.persistConsumerOffsetInterval = cc.persistConsumerOffsetInterval;this.pullTimeDelayMillsWhenException = cc.pullTimeDelayMillsWhenException;this.unitMode = cc.unitMode;this.unitName = cc.unitName;this.vipChannelEnabled = cc.vipChannelEnabled;this.useTLS = cc.useTLS;this.socksProxyConfig = cc.socksProxyConfig;this.namespace = cc.namespace;this.language = cc.language;this.mqClientApiTimeout = cc.mqClientApiTimeout;this.decodeReadBody = cc.decodeReadBody;this.decodeDecompressBody = cc.decodeDecompressBody;this.enableStreamRequestType = cc.enableStreamRequestType;
}

可以看到,这段代码将 producer 实例的 ClinetConfig 属性完全拷贝了一份给新创建的 DefaultMQProducer 实例的 ClinetConfig 属性。因此这段代码后新初始化的 DefaultMQProducer 实例的 instanceName 不再是默认值而是和 producer 的一致

MQClientInstance#start

接下来我们来看 mQClientFactory.start() 这部分的代码

public void start() throws MQClientException {// 用synchronized修饰保证线程安全性与内存可见性synchronized (this) {switch (this.serviceState) {case CREATE_JUST:this.serviceState = ServiceState.START_FAILED;// If not specified,looking address from name server// 由于传入了NameServer的地址,因此不进入分支if (null == this.clientConfig.getNamesrvAddr()) {this.mQClientAPIImpl.fetchNameServerAddr();}// Start request-response channel// 1. 启动用于和broker通信的netty客户端this.mQClientAPIImpl.start();// Start various schedule tasks// 2. 启动定时任务,包括心跳,拉取topic路由信息,更新broker信息,清理过期消息等this.startScheduledTask();// 3. Start pull servicethis.pullMessageService.start();// Start rebalance service// 4. 启动负载均衡服务(对MQConsumer有效)this.rebalanceService.start();// Start push service// 5. 启动它内部的producer实例// this.defaultMQProducer是在DefaultMQProducerImpl中// 使用MQClientManager.getInstance().getOrCreateMQClientInstance(this.defaultMQProducer, rpcHook)被初始化的this.defaultMQProducer.getDefaultMQProducerImpl().start(false);log.info("the client factory [{}] start OK", this.clientId);this.serviceState = ServiceState.RUNNING;break;case START_FAILED:throw new MQClientException("The Factory object[" + this.getClientId() + "] has been created before, and failed.", null);default:break;}}
}

这段代码的主要任务是启动和broker通信的 Netty 客户端、启动定时任务、启动拉取消息服务、启动负载均衡服务、启动内部生产者实例

其中启动负载均衡服务只是针对消费者而言的,在生产者启动过程中并无作用

mQClientAPIImpl 对象是 RocketMQ 客户端与 Broker 之间通信的实现类

我们先看启动定时任务这个方法做了什么

private void startScheduledTask() {if (null == this.clientConfig.getNamesrvAddr()) {// 如果没有指定namesrv地址,则定时获取namesrv地址this.scheduledExecutorService.scheduleAtFixedRate(() -> {try {MQClientInstance.this.mQClientAPIImpl.fetchNameServerAddr();} catch (Exception e) {log.error("ScheduledTask fetchNameServerAddr exception", e);}}, 1000 * 10, 1000 * 60 * 2, TimeUnit.MILLISECONDS);}// 定期从NameServer更新topic路由信息this.scheduledExecutorService.scheduleAtFixedRate(() -> {try {MQClientInstance.this.updateTopicRouteInfoFromNameServer();} catch (Exception e) {log.error("ScheduledTask updateTopicRouteInfoFromNameServer exception", e);}}, 10, this.clientConfig.getPollNameServerInterval(), TimeUnit.MILLISECONDS);// 用于定期清除离线Broker,并向所有Broker发送心跳包this.scheduledExecutorService.scheduleAtFixedRate(() -> {try {MQClientInstance.this.cleanOfflineBroker();MQClientInstance.this.sendHeartbeatToAllBrokerWithLock();} catch (Exception e) {log.error("ScheduledTask sendHeartbeatToAllBroker exception", e);}}, 1000, this.clientConfig.getHeartbeatBrokerInterval(), TimeUnit.MILLISECONDS);// 定时持久化消费者当前消费进度(对MQConsumer有效)this.scheduledExecutorService.scheduleAtFixedRate(() -> {try {MQClientInstance.this.persistAllConsumerOffset();} catch (Exception e) {log.error("ScheduledTask persistAllConsumerOffset exception", e);}}, 1000 * 10, this.clientConfig.getPersistConsumerOffsetInterval(), TimeUnit.MILLISECONDS);// 根据当前的积压调优线程池的核心线程数this.scheduledExecutorService.scheduleAtFixedRate(() -> {try {MQClientInstance.this.adjustThreadPool();} catch (Exception e) {log.error("ScheduledTask adjustThreadPool exception", e);}}, 1, 1, TimeUnit.MINUTES);
}

其中的定时持久化消费者当前消费进度任务同样只是针对消费者而言的,在生产者启动过程中并无作用

至此,producer 的启动流程结束

启动流程总结

执行 producer.start()启动一个 producer

  1. 重新设置生产者组名
  2. 调用 defaultMQProducerImplstart() 方法,是进行启动实现的入口
    1. 检查当前状态,如果是 CREATE_JUST 则进入启动流程
    2. 检查生产者组名称是否合法
    3. 更改 producerinstanceName
    4. 创建一个 MQClientInstance 类型的 mQClinetFactory 实例
      1. 创建一个新的 defaultMQProducer 实例作为 mQClinetFactory 实例的成员变量
      2. 新的 defaultMQProducer 实例内部又会创建一个新的 defaultMQProducerImpl 实例
      3. 将新的 defaultMQProducer 实例的 ClinetConfig 属性复制粘贴为 producerClinetConfig 属性
    5. producer 实例放入 mQClinetFactoryproducerTable 中,key为 producer 的生产者组名
    6. defaultMQProducercreateTopicKey 作为key,TopicPublishInfo 作为value,放入到 defaultMQProducerImpltopicPublishInfoTable
    7. 启动 mQClinetFactory
      1. 如果没有 NameServer 地址则尝试获取
      2. 启动用于和 broker 通信的 netty 客户端
      3. 启动定时任务
        1. 如果没有指定 NameServer 地址,则定时获取 NameServer 地址
        2. 定期从NameServer更新topic路由信息
        3. 定期清除离线Broker,并向所有Broker发送心跳包
        4. 定时持久化消费者当前消费进度(对MQConsumer有效)
        5. 定时根据当前的积压调优线程池的核心线程数,但是实现是空的
      4. 启动 pullMessageService 从 broker 拉取消息
      5. 启动消费者客户端的负载均衡服务
      6. 启动 mQClinetFactory 内部的 defaultMQProducerImpl 实例
        1. 检查当前状态,如果是 CREATE_JUST 则进入启动流程
        2. 检查生产者组名称是否合法
        3. 由于其 instanceName 等于 MixAll.CLIENT_INNER_PRODUCER_GROUP,因此不更改
        4. 创建一个 MQClientInstance 类型的 mQClinetFactory 实例
        5. producer 实例放入 mQClinetFactoryproducerTable 中,key为 producer 的生产者组名
        6. defaultMQProducercreateTopicKey 作为key,TopicPublishInfo 作为value,放入到 defaultMQProducerImpltopicPublishInfoTable
        7. 将当前状态设置为 RUNNING
        8. 如果上述都成功,则立即发送心跳到所有的 broker
        9. 启动定时任务扫描和处理过期的异步请求
    8. 将当前状态设置为 RUNNING
    9. 如果上述都成功,则立即发送心跳到所有的 broker
    10. 启动定时任务扫描和处理过期的异步请求
  3. 如果 traceDispatch 不为空则启动 traceDispatcher

实例内容

以生产者组名称为 ProducerGroupName,topic名称为 TopicTest 为例,生产者启动成功后的实例内容如下

DefaultMQProducer producer:1 # 启动入口String namesrvAddr = "127.0.0.1:9876" # NameServer地址String clientIp = "192.168.142.1" # producer的ip地址String producerGroup = "ProducerGroupName" # 生产者组名称String createTopicKey = "TBW102" # 用来自动创建topic的topic名称String instanceName = "14896#9822706678400" # 生产者实例的名称DefaultProducerImpl defaultProducerImpl:1 # 实际启动类DefaultMQProducer defaultMQProducer:1 # 就是最开头的 producerConcurrentMap<String/* topic */, TopicPublishInfo> topicPublishInfoTable:1 = {"TBW102" : TopicPublishInfo} # 存储topic的路由信息MQClientInstance mQClientFactory # 负责管理与 NameServer 和 Broker 的网络连接String clientId = "192.168.142.1@14896#9822706678400" # 为 ip + instanceNameMQClientAPIImpl mQClientAPIImpl # 负责实现与 Broker 之间的通信ConcurrentMap<String, MQProducerInner> producerTable = {"ProducerGroupName" : defaultProducerImpl:1} # MQProducerInner是DefaultProducerImpl的接口ConcurrentMap<String, MQConsumerInner> consumerTable # MQConsumerInner是DefaultConsumerImpl的接口ConcurrentMap<String, HashMap<Long, String>> brokerAddrTable # broker的地址DefaultMQProducer defaultMQProducer:2 # mQClientFactory内部的producer实例String producerGroup = "CLIENT_INNER_PRODUCER"Stirng instanceName = "14896#9822706678400" # 和 producer 的 clientConfig 属性完全一致DefaultProducerImpl defaultProducerImpl:2DefaultMQProducer defaultMQProducer:2ConcurrentMap<String/* topic */, TopicPublishInfo> topicPublishInfoTable:2 = {"TBW102" : TopicPublishInfo} # 存储topic的路由信息

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

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

相关文章

[SQL挖掘机] - 转换机制

一种较为有用的数据转换机制是在查询中修改列的数据类型. 通常, 当处理不同数据类型(如数字)的列时, 可使用仅对一种数据类型(如文本)有效的函数. 当修改某一列的数据类型时, 可简单地采用 column::datatype 格式. 其中, column表示为列名, datatype 表示为将列调整后的数据类型…

python多线程

目录 一.多线程的定义 A.什么是多线程&#xff1f; B.多线程如今遇到的挑战 C.总结 二.python中的多线程 A.python中的多线程底层原理&#xff1a; B.全局解释器锁导致python多线程不能实现真正的并行执行&#xff01; C.总结应用场景 三.java多线程&#xff0c;以及…

【Matlab】判断点和多面体位置关系的两种方法实现

我的主页&#xff1a; 技术邻&#xff1a;小铭的ABAQUS学习的技术邻主页博客园 : HF_SO4的主页哔哩哔哩&#xff1a;小铭的ABAQUS学习的个人空间csdn&#xff1a;qgm1702 博客园文章链接&#xff1a; https://www.cnblogs.com/aksoam/p/17590039.html 分别是向量判别法&…

Ubuntu 22.04下对无线网络作静态ip设置

内容如题所示&#xff0c;最近本人安全毕业&#xff0c;参加工作了&#xff0c;此处应有鲜花和掌声&#xff0c;哈哈哈。但新的生活总是有很多的小问题&#xff0c;坎坎坷坷&#xff0c;所以&#xff0c;我继续记录工作和学习生活中遇到的问题。 今天带我的云哥给了我一个ip&am…

Python入门【变量的作用域(全局变量和局部变量)、参数的传递、浅拷贝和深拷贝、参数的几种类型 】(十一)

&#x1f44f;作者简介&#xff1a;大家好&#xff0c;我是爱敲代码的小王&#xff0c;CSDN博客博主,Python小白 &#x1f4d5;系列专栏&#xff1a;python入门到实战、Python爬虫开发、Python办公自动化、Python数据分析、Python前后端开发 &#x1f4e7;如果文章知识点有错误…

无涯教程-jQuery - wrapInner( html )方法函数

wrapInner(html)方法使用HTML结构包装每个匹配元素(包括文本节点)的内部子内容。 wrapInner( html ) - 语法 selector.wrapInner( html ) 这是此方法使用的所有参数的描述- html - 将动态创建并环绕目标的HTML字符串。 wrapInner( html ) - 示例 以下是一个简单的示例…

Jupyter Notbook无法刷新.bashrc中的环境变量

Jupyter Notbook无法刷新.bashrc中的环境变量 原因解决方法 原因 在Linux系统中&#xff0c;在.bashrc中添加环境变量后&#xff0c;打开jupyter notebook发现无法加载添加的环境变量。这是因为.bashrc只对bash起作用&#xff0c;如果使用GUI软件直接访问&#xff0c;是无法加…

51单片机:数码管和矩阵按键

目录 一:动态数码管模块 1:介绍 2:共阴极和共阳极 A:共阴极 B:共阳极 C:转化表 3:74HC138译码器 4:74HC138译码器控制动态数码管 5:数码管显示完整代码 二:矩阵按键模块 1:介绍 2:原理图 3:矩阵按键代码 一:动态数码管模块 1:介绍 LED数码管&#xff1a;数码管是一种…

NLP实践——Llama-2 多轮对话prompt构建

NLP实践——Llama-2 多轮对话prompt构建 1. 问题提出2. prompt的正确形式3. 效果测试4. 结尾 1. 问题提出 最近&#xff0c;META开源了Llama-2模型&#xff0c;受到了广泛的关注和好评&#xff0c;然而&#xff0c;在官方给的使用说明中&#xff0c;并没有对使用方法进行特别细…

Verilog语法学习——LV10_使用函数实现数据大小端转换

LV10_使用函数实现数据大小端转换 题目来源于牛客网 [牛客网在线编程_Verilog篇_Verilog快速入门 (nowcoder.com)](https://www.nowcoder.com/exam/oj?page1&tabVerilog篇&topicId301) 题目 描述 在数字芯片设计中&#xff0c;经常把实现特定功能的模块编写成函数&…

ES自定义分词,对数字进行分词

需求&#xff1a;需要将下面类似的数据分词为&#xff1a;GB,T,32403,1,2015 "text": "GB/T 32403.1-2015"1、调研 现在用的ik分词器效果 POST _analyze {"analyzer": "ik_max_word","text": "GB/T 32403.1-2015&qu…

Java 反射

反射 Java 的反射&#xff08; reflection &#xff09;机制是指在程序的运行状态中&#xff0c;可以构造任意一个类的对象&#xff0c;可以了解任意一个对象所属的类&#xff0c;可以了解任意一个类的成员变量和方法&#xff0c;可以调用任意一个对象的属性和方法。这种动态获…

一.安装k8s环境

1.初始操作 默认3台服务器都执行 # 关闭防火墙 systemctl stop firewalld systemctl disable firewalld# 关闭selinux sed -i s/enforcing/disabled/ /etc/selinux/config # 永久 setenforce 0 # 临时# 关闭swap swapoff -a # 临时 sed -ri s/.*swap.*/#&/ /etc/fstab…

QT--day3(定时器事件、对话框)

头文件代码&#xff1a; #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QTimerEvent> //定时器事件处理时间头文件 #include <QTime> //时间类 #include <QtTextToSpeech> #include <QPushButton> #include <QLabel&g…

【数据结构】实验六:队列

实验六 队列 一、实验目的与要求 1&#xff09;熟悉C/C语言&#xff08;或其他编程语言&#xff09;的集成开发环境&#xff1b; 2&#xff09;通过本实验加深对队列的理解&#xff0c;熟悉基本操作&#xff1b; 3&#xff09; 结合具体的问题分析算法时间复杂度。 二、…

ubuntu23.04 flush DNS caches

如何在Ubuntu 23.04中刷新DNS缓存 现在&#xff0c;如果你运行的是Ubuntu 23.04&#xff0c;"系统解决 "的方法将不再适用于你。让我们检查一下你目前的缓存大小。打开你的Ubuntu终端&#xff0c;运行以下command&#xff1a; resolvectl statistics现在&#xff0c…

mysql主从同步怎么跳过错误

今天介绍两种mysql主从同步跳过错误的方法&#xff1a; 一、两种方法介绍 1、跳过指定数量的事务&#xff1a; mysql>slave stop; mysql>SET GLOBAL SQL_SLAVE_SKIP_COUNTER 1 #跳过一个事务 mysql>slave start2、修改mysql的配置文件&#xff0c;通过slav…

【QT 网络云盘客户端】——实现文件属性窗口

目录 文件属性对话框 设置字体样式 获取文件的信息 显示文件属性对话框 当我们点击文件中的属性&#xff0c;则会弹出一个属性对话框&#xff1a; 实现过程&#xff1a; 0.设置 属性 菜单项的槽函数。 1.鼠获取鼠标选中的QListWidgetItem,它包含 图标和文件名 2.根据文件…

POLARDB IMCI 白皮书 云原生HTAP 数据库系统 一 数据压缩和打包处理与数据更新

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

十一、数据结构——树(Tree)的基本概念

数据结构之树(Tree) 目录 树的基本概念树的分类树的基本操作树的应用结语 树的基本概念 树是一种重要的数据结构&#xff0c;它在计算机科学中被广泛应用。树的特点是以分层的方式存储数据&#xff0c;具有层次结构&#xff0c;类似于现实生活中的树状结构。在树中&#xff…