【RocketMQ】路由中心NameServer

news2025/5/31 21:21:38

【RocketMQ】路由中心NameServer

参考资料:

RocketMQ Nameserver 背后的设计理念

RocketMQ之NameServer详解

深入剖析RocketMQ源码-NameServer —— vivo互联网技术

《RocketMQ技术内幕》

文章目录

  • 【RocketMQ】路由中心NameServer
    • NameServer架构设计
    • NameServer工作机制
      • 两种设计注册中心的思路
        • PUSH 模式
        • PULL 模式
      • Nameserver 数据不一致影响分析
        • 对消息发送端的影响
        • 对消息消费端的影响
    • 源码分析
      • NameServer启动核心流程
      • NameServer路由注册、故障剔除
        • 路由元信息
        • 路由注册
          • Broker 发送心跳包
          • NameServer处理心跳包
        • 路由删除
    • 小结

本章主要介绍 RocketMQ 路由管理、服务注册及服务发现的机制,NameServer 是整个 RocketMQ 的“大脑”。相信大家对“服务发现”这个词语并不陌生,分布式服务 SOA 架构体系中会有服务注册中心,分布式服务 SOA 的注册中心主要提供服务调用的解析服务, 指引服务调用方(消费者)找到“远方”的服务提供者,完成网络通信,那么 RocketMQ 的路由中心存储的是什么数据呢?作为一款高性能的消息中间件,如何避免 NameServer 的单点故障,提供高可用性呢?

NameServer架构设计

消息中间件的设计思路一般基于主题的订阅发布机制,消息生产者(Producer)发送某一主题的消息到消息服务器,消息服务器负贵该消息的持久化存储,消息消费者 (Consumer)订阅感兴趣的主题,消息服务器根据订阅信息(路由信息)将消息推送到消费者(PUSH 模式)或者消息消费者主动向消息服务器拉取消息〔PULL模式),从而实现消息生产者与消息消费者解潤。为了避免消息服务器的单点故障导致的整个系统瘫痰,通常会部署多台消息服务器共同承担消息的存储。那消息生产者如何知道消息要发往哪台消息服务器呢?如果某一台消息服务器宕机了,那么生产者如何在不重启服务的情况下感知呢?

Name Server 是专为设计的轻量级名称服务,具有简单、可集群横吐扩展、无状态,节点之间互不通信等特点。整个RocketMQ集群的工作原理如下图所示:

image-20230920212151610

Broker 消息服务器在启动时向所有 NameServer 注册,消息生产者(Producer) 在发送消息之前先从 NameServer 获取 Broker 服务器地址列表,然后根据负载算法从列表中选择一台消息服务器进行消息发送。NameServer 与每台 Broker服务器保持长连接,并间隔30s 检测 Broker 是否存活,如果检测到 Broker 宕机,则从路由注册表中将其移除。但是路由变化不会马上通知消息生产者,为什么要这样设计呢?这是为了降低 NameServer 实现的复杂性,在消息发送端提供容错机制来保证消息发送的高可用性,这部分在后续文章中会有详细的描述。

NameServer 本身的高可用可通过部署多台 NameServer 服务器来实现,但彼此之间互不通信,也就是 NameServer 服务器之间在某一时刻的数据并不会完全相同,但这对消息发送不会造成任何影响,这也是 RocketMQ NameServer 设计的一个亮点,RocketMQ NameServer 设计追求简单高效。

NameServer工作机制

image-20230920212731594

Topic 的注册与发现主要的参与者:Nameserver、Producer、Consumer、Broker。其交互特性与联通性如下:

  • Nameserver:命名服务器,多台机器组成一个集群,每台机器之间互不联通。
  • Broker:Broker 消息服务器,会向 Nameserver 中的每一台 NamServer 每隔 30s 发送心跳包,即 Nameserver 中关于 Topic 路由信息来源于 Broker。正式由于这种注册机制,并且 Nameserver 互不联通,如果出现网络分区等因素,例如 broker-a 与集群中的一台 Nameserver 网络出现中断,这样会出现两台 Nameserver 中的数据出现不一致。具体会有什么影响下文会继续探讨。
  • Producer、Consumer:消息发送者、消息消费者,在同一时间只会连接 Nameserver 集群中的一台服务器,并且会每隔 30s 会定时更新 Topic 的路由信息。

另外 Nameserver 会定时扫描 Broker 的存活状态,其依据之一是如果连续 120s 未收到 Broker 的心跳信息,就会移除 Topic 路由表中关于该 broker 的所有队列信息,这样消息发送者在发送消息时就不会将消息发送到出现故障的 Broker 上,提高消息发送高可用性。

Nameserver 采用的注册中心模式为——PULL 模式,接下来会详细介绍目前主流的注册中心实现思路,从而从架构上如何进行选择。

两种设计注册中心的思路

PUSH 模式

说到服务注册中心,大家肯定会优先想到 Dubbo 的服务注册中心 ZooKeeper,正式由于这种“先入为主”,不少人通常也会有一个疑问:为什么 RocketMQ 的注册中心不直接使用 ZooKeeper,而要自己实现一个 Nameserver 的注册中心呢?

那我们首先来聊一下 Dubbo 的服务注册中心:ZooKeeper,基于 ZooKeeper 的注册中心有一个显著的特点是服务的动态变更,消费者可以实时感知。例如在 Dubbo 中,一个服务进行在线扩容,增加一批的消息服务提供者,消费者能立即感知,并将新的请求负载到新的服务提供者,这种模式在业界有一个专业术语:PUSH 模式。

image-20230920213238658

基于 ZooKeeper 的服务注册中心主要是利于 ZooKeeper 的事件机制,其主要过程如下:

  1. 消息服务提供者在启动时向注册中心进行注册,其主要是在 /dubbo/{serviceName}/providers 目录下创建一个瞬时节点。服务提供者如果宕机该节点就会由于会话关闭而被删除。
  2. 消息消费者在启动时订阅某个服务,其实就是在 /dubbo/{serviceName}/consumers 下创建一个瞬时节点,同时监听 /dubbo/{serviceName}/providers,如果该节点下新增或删除子节点,消费端会收到一个事件,ZooKeeper 会将 providers 当前所有子节点信息推送给消费消费端,消费端收到最新的服务提供者列表,更新消费端的本地缓存,及时生效。

基于 ZooKeeper 的注册中心一个最大的优点是其实时性。但其内部实现非常复杂,ZooKeeper 是基于 CP 模型,可以看出是强一致性,往往就需要吸收其可用性,例如如果 ZooKeeper 集群触发重新选举或网络分区,此时整个 ZooKeeper 集群将无法提供新的注册与订阅服务,影响用户的使用。

服务注册领域服务数据的一致性其实并不是那么重要,例如回到 Dubbo 服务的注册与订阅场景来看,其实客户端(消息消费端)就算获得服务提供者列表不一致,也不会造成什么严重的后果,最多是在一段时间内服务提供者的负载不均衡,只要最终能达到一致即可。

PULL 模式

RocketMQ 的 Nameserver 并没有采用诸如 ZooKeeper 的注册中心,而是选择自己实现,如果大家看过 RocketMQ 的源代码,就会发现该模块就 5~6 个类,总代码不超过 5000 行,简单就意味着高效,基于 PULL 模式的注册中心示例图:

image-20230920213457931

  1. Broker 每 30s 向 Nameserver 发送心跳包,心跳包中包含主题的路由信息(主题的读写队列数、操作权限等),Nameserver 会通过 HashMap 更新 Topic 的路由信息,并记录最后一次收到 Broker 的时间戳。
  2. Nameserver 以每 10s 的频率清除已宕机的 Broker,Nameserver 认为 Broker 宕机的依据是如果当前系统时间戳减去最后一次收到 Broker 心跳包的时间戳大于 120s。
  3. 消息生产者以每 30s 的频率去拉取主题的路由信息,即消息生产者并不会立即感知 Broker 服务器的新增与删除。

PULL 模式的一个典型特征是即使注册中心中存储的路由信息发生变化后,客户端无法实时感知,只能依靠客户端的定时更新更新任务,这样会引发一些问题。例如大促结束后要对集群进行缩容,对集群进行下线,如果是直接停止进程,由于是网络连接直接断开,Nameserver 能立即感知 Broker 的下线,会及时存储在内存中的路由信息,但并不会立即推送给 Producer、Consumer,而是需要等到 Producer 定时向 Nameserver 更新路由信息,那在更新之前,进行消息队列负载时,会选择已经下线的 Broker 上的队列,这样会造成消息发送失败。

在 RocketMQ 中 Nameserver 集群中的节点相互之间不通信,各节点相互独立,实现非常简单,但同样会带来一个问题:Topic 的路由信息在各个节点上会出现不一致。

那 Nameserver 如何解决上述这两个问题呢?RocketMQ 的设计者采取的方案是不解决,即为了保证 Nameserver 的高性能,允许存在这些缺陷,这些缺陷由其使用者去解决。

由于消息发送端无法及时感知路由信息的变化,引入了消息发送重试与故障规避机制来保证消息的发送高可用,这部分内容已经在前面的文章中详细介绍。

Nameserver 数据不一致影响分析

RocketMQ 中的消息发送者、消息消费者在同一时间只会连接到 Nameserver 集群中的某一台机器上,即有可能消息发送者 A 连接到 Namederver-1 上,而消费端 C1 可能连接到 Nameserver-1 上,消费端 C2 可能连接到 Nameserver-2 上,我们分别针对消息发送、消息消费来谈一下数据不一致会产生什么样的影响。

Nameserver 数据不一致示例图如下:

image-20230920214240172

对消息发送端的影响

正如上图所示,Producer-1 连接 Nameserver-1,而 Producer-2 连接 Nameserver-2,例如这个两个消息发送者都需要发送消息到主题 order_topic。由于 Nameserver 中存储的路由信息不一致,对消息发送的影响不大,只是会造成消息分布不均衡,会导致消息大部分会发送到 broker-a 上,只要不出现网络分区的情况,Nameserver 中的数据会最终达到一致,数据不均衡问题会很快得到解决。故从消息发送端来看,Nameserver 中路由数据的不一致性并不会产生严重的问题。

对消息消费端的影响

如果一个消费组 order_consumer 中有两个消费者 c1、c2,同样由于 c1、c2 连接的 Nameserver 不同,两者得到的路由信息会不一致,会出现什么问题呢?我们知道,在 RocketMQ PUSH 模式下会自动进行消息消费队列的负载均衡,我们以平均分配算法为例,来看一下队列的负载情况。

  • c1:在消息队列负载的时查询到 order_topic 的队列数量为 8 个(broker-a、broker-b 各 2 个),查询到该消费组在线的消费者为 2 个,那按照平均分配算法,会分配到 4 个队列,分别为 broker-a:q0、q1、q2、q3。
  • c2:在消息队列负载时查询到 order_topic 的队列个数为 4 个(broker-a),查询到该消费组在线的消费者 2 个,按照平均分配算法,会分配到 2 个队列,由于 c2 在整个消费列表中处于第二个位置,故分配到队列为 broker-a:q2、q3。

将出现的问题一目了然了吧:会出现 broker-b 上的队列分配不到消费者,并且 broker-a 上的 q2、q3 这两个队列会被两个消费者同时消费,造成消息的重复处理,如果消费端实现了幂等,也不会造成太大的影响,无法就是有些队列消息未处理,结合监控机制,这种情况很快能被监控并通知人工进行干预。

当然随着 Nameserver 路由信息最终实现一致,同一个消费组中所有消费组,最终维护的路由信息会达到一致,这样消息消费队列最终会被正常负载,故只要消费端实现幂等,造成的影响也是可控的,不会造成不可估量的损失,就是因为这个原因,RocketMQ 的设计者们为了达到简单、高效之目的,在 Nameserver 的设计上允许出现一些缺陷,给我们做架构设计方案时起到了一个非常好的示范作用,无需做到尽善尽美,懂得抉择、权衡。

源码分析

NameServer启动核心流程

从源码的角度窥探一下 NameServer 启动流程,重点关注 NameServer 相关启动参数.

NameServer 启动类:org.apache.rocketmq.namesrv

Step1:首先来解析配置文件,需要填充 NameServerConfig、 NettyServerConfig 展性值.

image-20230920215427681

image-20230920215457339

Step2:根据启动属性创建 NamesrvController 实例,并初始化该实例,NameServerController 实例为 NameServer 核心控制器

image-20230920215812301

加载KV配置,创建 NetyServer 网络处理对象,然后开启两个定时任务,在 RocketMQ 中此类定时任务统称为心跳检测。

  • 定时任务1:NameServer 每隔10s 扫描一次 Broker,移除处于不激活状态的 Broker。
  • 定时任务2:nameServer 每隔10 分钟打印一次 KV 配置。

Step3: 注册JVM 钩子两数并启动服务器,以便监听 Broker、消息生产者的网络请求。

image-20230920220013864

这里主要是展示一种常用的编程技巧,如果代码中使用了线程池,一种优雅停机的方式就是注册一个JVM钩子两数,在JVM进程关闭之前,先将线程池关闭,及时释放资源。

NameServer路由注册、故障剔除

路由元信息

NameServer 路由实现类:org.apache.rocketmq.namesrv.routeinfo,在了解路由注册之前,我们首先看一下 NameServer 到底存储哪些信息。

image-20230920220435636

  • topicQueueTable:Topie 消息队列路由信息,消息发送时根据路由表进行负载均衡。
  • brokerAddrTable:Broker 基础信息,包含 brokerName、所属集祥名称、主备 Broker 地址。
  • clusterAddrTable: Broker 集群信息,存储集群中所有 Broker 名称。
  • brokerLive Table:Broker 状态信息.NameServer 每次收到心跳包时会替换该信息。
  • filterServer Table: Broker 上的 FilterServer列表,用于类模式消息过滤。
路由注册

RocketMQ 路由注册是通过 Broker 与 NameServer 的心跳功能实现的.Broker启动时向集群中所有的 NameServer 发送心跳语句,每隔30s向集群中所有 NameServer 发送心跳包,NameServer 收到 Broker 心跳包时会更新 brokerLiveTable 缓存中 BrokerLivelnfo 的 lastUpdate Timestamp,然后 Name Server 每隔 10s 扫描 brokerLiveTable,如果连续 120s 没有收到心跳包,NameServer 将移除该 Broker 的路由信息同时关闭 Socket 连接.

Broker 发送心跳包

Broker 发送心跳包的核心代码如下所示。

image-20230920221341549

该方法主要是遍历 NameServer列表,Broker 消息服务器依次向 NameServer 发送心跳包。

发送心跳包具体逻辑,封装请求包头。

image-20230920222200034

RocketMQ 网络传输基于 Netty,具体网络实现细节本书不会过细去剖析,在这里介绍一下网络跟踪方法:每一个请求,RocketMQ 都会定义一个 RequestCode,然后在服务端会对应相应的网络处理器 (processor 包中),只需整库搜索 RequcstCode 即可找到相应的处理逻辑。

NameServer处理心跳包

org.apache.rocketmg.namesrv.processor.DefaultRequestProcessor网络处理器解析请求类梨,如果请求类型为 RequestCode.REGISTER_BROKER,则请求最终转发到 RoutelnfoMan ager#registerBroker

Step1:路由注册需要加写锁,防止并发修改 RoutelnfoManager 中的路由表。首先判断 Broker 所属集群是香存在,如果不存在,则创建,然后将 broker 名加入到集群 Broker 集合中。

image-20230920223631384

Step2:维护 BrokerData 信息,首先从 brokerAddrTable 根据 BrokerName 尝试获取 Broker 信息,如果不有在,则新建 BrokerData 并放入到 brokerAddrTable, registerFirst 设置为 true;如果存在,直接替换原先的,registerFirst设置为 false,表示非第一次注册.

image-20230920223914596

image-20230920223929275

Step3:如果 Broker 为 Master,并且 Broker Topic 配置信息发生变化或者是初次注册, 则需要创建或更新 Topic 路由元数据,填充 topicQueueTable,其实就是为默认主题自动注册路由信息,其中包含 MixAIl.DEFAULT TOPIC 的路由信息.当消息生产者发送主题时, 如果该主题未创建并且 BrokerConfig 的 autoCreate TopicEnable 为 true 时,将返回 MixAll.DEFAULT TOPIC 的路由信息.

根据 TopieConfig 创建 QueueData 数据结构,然后更新 topicQueueTable.

Step4:更新 BrokerLivelnfo,存活 Broker 信息表,BrokeLivelnfo 是执行路由删除的重要依据。

设计亮点:NameServe 与Broker 保持长连接,Broker 状态存储在 brokerLiveTable 中, NameServer 每收到一个心跳包,将更新 brokerLiveTable 中关于 Broker 的状态信息以及路由表(topicQueueTable、brokerAddrTable、 brokerLiveTable、 filterServer Table)。更新上述路由表(Hash Table) 使用了锁粒度软少的读写锁,允许多个消惠发送者(Producer) 并发读, 保证消息发送时的高并发。但同一时刻 NameServer 只处理一个 Broker 心跳包,多个心跳包请求串行执行。这也是读写锁经典使用场景。

路由删除

根据上面章节的介绍,Broker 每隔30s 向 NameServer 发送一个心跳包,心跳包中包含 Brokerld、 Broker 地址、Broker 名称、Broker 所属集群名称、Broker 关联的 FilterServer 列表。

但是如果 Broker 宕机,NameServer 无法收到心跳包,此时 NameServer 如何来剔除这些失效的 Broker 呢?Name Server 会每隔 10s扫描 brokerLiveTable 状态表,如果 BrokerLive 的 lastUpdate Timestamp 的时间截距当前时间超过120s,则认为 Broker 失效,移除该 Broker, 关闭与 Broker 连接,并同时更新 topicQueueTable、 brokerAddrTable、 brokerLiveTable、 filterServer Table。

RocktMQ 有两个触发点来触发路由删除。

  • NameServer 定时扫描 brokerLiveTable 检测上次心跳包与当前系统时间的时间差, 如果时间戳大于120s,则需要移除该 Broker 信息。
  • Broker 在正常被关闭的情况下,会执行 unregisterBroker 指令。

由于不管是何种方式触发的路由删除,路由删除的方法都是一样的,就是从 topic- Queue Table、 brokerAddr Table、 brokerLiveTable、 filterServerTable 删除与该 Broker 相关的信息,但 RocketMQ 这两种方式维护路由信息时会抽取公共代码,本文将以第一种方式展开分析。

image-20230920225135803

我们应该不会忘记 scanNotActiveBroker 在 NameServer 中每 10s 执行一次.逻辑也很简单,過历 brokerLivelnfo 路由表(HashMap),检测 BrokerLivelnfo 的 lastUpdateTimestamp 上次收到心跳包的时间如果超过当前时间 120s, NameServer 则认为该 Broker已不可用, 故需要将它移除,关闭 Channel,然后删除与该 Broker 相关的路由信息,路由表维护过程, 需要申请写锁。

Step1:申请写锁,根据 brokerAddress 从 brokerLive Table、 filterServer Table 移除。

Step2:维护 brokerAddrTable。

遍历从 HashMap中,找到具体的 Broker,从 BrokerData 中移除,如果移除后在 BrokcrData 中不再包含其他 Broker,则在 brokerAddrTable 中移除该 brokerName 对应的条目。

Step3:根据 BrokerName,从 clusterAddrTable 中找到 Broker 并从集群中移除.如果移除后,集群中不包含任何 Broker,则将该集群从 clusterAddrTable 中移除.

Step4:根据 brokerName,遍历所有主题的队列,如果队列中包含了当前 Broker 的队列,则移除,如果 topie 只包含待移除 Broker 的队列的话,从路由表中删除该 topic。

Step5:释放锁,完成路由删除。

小结

本文主要介绍了 NameServer 路由功能,包含路由元数据、路由注册与发现机制。

image-20230920225916463

我们在了解了NameServer的设计原理之后,也可以回过头思考下在设计过程中一些值得我们学习的小技巧,在此我抛砖引玉提出两点:

  • 启动流程注册JVM钩子用于优雅停机。这是一个编程技巧,我们在实际开发过程中,如果有使用线程池或者一些常驻线程任务时,可以考虑通过注册JVM钩子的方式,在JVM关闭前释放资源或者完成一些事情来保证优雅停机。
  • 更新路由表时需要通过加锁防止并发操作,这里使用的是锁粒度较少的读写锁,允许多个消息发送者并发读,保证消息发送时的高并发,但同一时刻NameServer只处理一个Broker心跳包,多个心跳包请求串行执行,这也是读写锁经典使用场景。

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

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

相关文章

上海亚商投顾:沪指震荡调整 两市成交金额跌破6000亿

上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 一.市场情绪 三大指数昨日集体调整,创业板指续创3年多以来新低。ST板块继续走强,*ST柏龙、ST恒久等…

笔记2.2:网络应用基本原理

一. 网络应用的体系结构 (1)客户机/服务器结构(Client-Server, C/S) (2)点对点结构(Peer-to-Peer,P2P) (3)混合结构(Hybrid&#x…

leetcode646. 最长数对链(java)

最长数对链 题目描述贪心解法二 动态规划 dp 题目描述 难度 - 中等 leetcode646. 最长数对链(java) 给你一个由 n 个数对组成的数对数组 pairs &#xff0c;其中 pairs[i] [lefti, righti] 且 lefti < righti 。 现在&#xff0c;我们定义一种 跟随 关系&#xff0c;当且仅…

数字散斑干涉测量仿真研究

一、引言 数字散斑干涉技术(digital speckle pattern interferometry&#xff0c;DSPI)是一种测量物体表面微小变形的测量技术&#xff0c;在生物医学检测、缺陷无损检测、精密制造、材料与结构力学参数评估等领域起着日益重要的作用&#xff0c;具有实时性、高精度、非接触、…

npm、yarn、pnpm如何清除缓存?

前端工程化创建项目会经常使用各种安装包管理工具&#xff0c;安装各种前端依赖包。例如&#xff0c;npm、yarn、pnpm等。时间一长&#xff0c;各种安装包管理工具的在安装依赖时&#xff0c;留下的缓存文件就会变得很大&#xff0c;以至于影响系统的运行&#xff0c;因此必要时…

kdxf speex

speex import subprocess# 定义FFmpeg命令 ffmpeg_command [ffmpeg,#-f, dshow,-i, rd:\tug\iat_mp3_16k.mp3,-acodec, speex,-ar, 16000,-ab, 16000,-ac, 1,output.spx ] spxcmdspxTest -enc 16000 7 1280 D:\\tug\iat_pcm_16k.pcm 1.speex #ffmpeg -f dshow -i audio"…

易优cms安装插件时显示空白一片处理方法

检查网站权限是否自己改过&#xff0c;看下图操作

camera驱动层和硬件层简介

目录 Camera Driver 1. V4L2 框架简介 1&#xff09;关键结构体 2&#xff09;模块初始化 3&#xff09;处理用户空间请求 2. 高通KMD框架详解 1&#xff09;概览 2&#xff09;核心模块解析 3&#xff09;模块初始化 4&#xff09;处理UMD CSL请求 Camera Hardware…

孤网双机并联逆变器下垂控制策略MATLAB仿真模型

微❤关注“电气仔推送”获得资料 主体模块&#xff1a; 建议使用MATLAB2021b及以上版本打开&#xff01; 功率计算模块、下垂控制模块、电压电流双环控制模块 系统输出有功功率: 系统输出无功功率&#xff1a; 系统频率变化曲线: 参考文献&#xff1a; 微电网并网运行模式下…

智慧燃气管网监控预警系统探析

关键词&#xff1a;智慧燃气、智能管网、智慧燃气系统、智能燃气、燃气监控、数据挖掘 “智慧城市”是我国当前城市发展的新理念&#xff0c;“智慧城市”离不开天然气等清洁能源的普及和发展&#xff0c;在这一理念的指引下&#xff0c;我国城市的燃气使用普及率和使用范围都…

2023-09-20 Teaching Note for Class 1

文章目录 1. Sign-in Result2. Content of Course3. History of Computer4. Free Topic 1. Sign-in Result Let’s take a look at the sign-in result Everybody is here. 2. Content of Course the course consists of eight modules 3. History of Computer 4. Free To…

求生之路2服务器搭建插件安装及详细的游戏参数配置教程windows

求生之路2服务器搭建插件安装及详细的游戏参数配置教程windows 大家好我是艾西&#xff0c;最近研究了下 l4d2&#xff08;求生之路2&#xff09;这款游戏的搭建以及架设过程。今天就给喜欢l4d2这款游戏的小伙伴们分享下怎么搭建架设一个自己的服务器。毕竟自己当服主是热爱游…

基于matlab实现的光折射反射(不同界面)程序

完整程序: %平面电磁波在不同介质界面上入射、反射、折射仿真 %ReadMe!!!在下述说明的用户输入区内输入入射角和两介质折射率&#xff0c; %输出反射折射示意图与反射折射系数随入射角变化的曲线 %—————————————————————————————————————…

我想设计一套游戏的奖励系统,有什么值得注意的?

游戏上&#xff1a; 游戏成就系统的价值 游戏中的成就可以延长游戏时间&#xff0c;让玩家不仅仅是将游戏通关&#xff0c;而是必须完成游戏内所有挑战及发现秘密&#xff0c;这些成就可以与游戏本身的目标一致&#xff0c;也可以独立于游戏的主要或次要目标之外&#xff0c;…

flink集群与资源@k8s源码分析-资源III 声明式资源管理

1 资源 资源分析分3部分,资源请求,资源提供,声明式资源管理,本文是第三部分声明式资源管理 2 检查资源需求/检查资源声明 检查资源需求/检查资源声明是flink声明式资源管理的核心方法 上面的资源场景分为两类,提出资源需求和提供资源, 检查资源请求/检查资源声明是交…

Unity HDRP 录制透明通道序列帧

一、插件 使用插件为Unity Recorder&#xff0c;可在PackageManager搜索Recorder安装 然后打开插件面板 二、Image Sequence 添加Image Sequence&#xff0c;摄像机选择主摄即可&#xff0c;图片格式选PNG&#xff0c;并勾选Include Alpha 此时点击 START RECORDING 即可…

Linux常用命令 - 用户管理命令

用户管理命令 useradd 功能&#xff1a;添加用户。 选项功能-d指定用户主目录-m创建用户的主目录-g指定用户的用户组-s指定用户的shell-D显示useradd命令当前的默认值 不同的linux系统默认选项不一样&#xff0c;比如Ubuntu系统useradd命令默认只创建用户&#xff0c;不会创…

Spring Boot虚拟线程的性能还不如Webflux?

早上看到一篇关于Spring Boot虚拟线程和Webflux性能对比的文章&#xff0c;觉得还不错。内容较长&#xff0c;抓重点给大家介绍一下这篇文章的核心内容&#xff0c;方便大家快速阅读。 测试场景 作者采用了一个尽可能贴近现实操作的场景&#xff1a; 从授权头信息中提取JWT验证…

SpringBoot3基础:最简项目示例

说明 本文建立一个最基本的SpringBoot3项目&#xff0c;依赖项仅包含 spring-web&#xff08;SpringMVC&#xff09;。 备注&#xff1a;SpringBoot3需要JDK17支持&#xff0c;配置方法参考&#xff1a; SpringBoot3项目中配置JDK17 项目结构图示 POM <?xml version&qu…

飞行动力学 - 第31节-荷兰滚模态机理 之 基础点摘要

飞行动力学 - 第31节-荷兰滚模态机理 之 基础点摘要 1. 荷兰滚模态2. “荷兰滚”的由来3. 荷兰滚模态机理4. 滚转力矩方程5. 参考资料 1. 荷兰滚模态 紧随滚转收敛模态的振荡运动被称为荷兰滚模态&#xff0c;包括侧滑与航向运动。 2. “荷兰滚”的由来 与一种叫荷兰滚(利用…