1.ES核心源码(二):创建索引和主节点
2.redis cluster 计ç®slotå¨åªä¸ªèç¹
3.游戏引擎随笔 0x20:UE5 Nanite 源码解析之渲染篇:BVH 与 Cluster 的 Culling
4.Redis集群架构剖析(4):槽位迁移,重新分配
ES核心源码(二):创建索引和主节点
在ElasticSearch系统中,写请求的流程引发了一个关键问题:主节点(master node)在数据写入过程中是否扮演了关键角色?让我们深入源码探讨这个话题,解答疑问。
首先,ElasticSearch的真封神私服源码核心在于如何高效地管理和存储数据。其主节点的职责之一是在索引创建和管理过程中提供协调服务。当用户发起创建索引的请求时,流程从接收HTTP请求开始,具体在`org.elasticsearch.ty4.Netty4HttpRequestHandler`中进行。随后,请求经过`RestController`处理,这个组件负责将请求检验和分发至相应的服务。
在分发请求过程中,关键在于请求对象的结构——它分为Action和Request。Action描述了请求的类型,如新建、删除等操作。在新建索引的请求中,系统通过URI匹配发现需要使用`TransportCreateIndexAction`来处理。这个Action继承自`TransportMasterNodeAction`,意味着其设计目标就是与主节点进行交互。
`TransportMasterNodeAction`的执行逻辑在于,它通过`transportService.sendRequest`方法向主节点发起请求。如果当前节点是主节点,该操作会直接在内部执行;若非主节点,分时短线王源码则通过网络请求主节点完成。
关于主节点如何通知其他节点这一问题,答案在于请求的分发机制。当请求到达主节点后,如果当前节点是主节点,它会通过一系列内部操作生成新的集群状态信息,并通过`org.elasticsearch.action.admin.indices.create.TransportCreateIndexAction#masterOperation`执行索引创建的逻辑。这个过程中,关键步骤是通过`clusterService.submitStateUpdateTask`将索引创建任务包装为集群状态更新任务,然后通过`MasterService#runTasks`方法向集群中的其他节点分发集群状态信息。
集群状态的分发通过`ZenDiscovery`服务完成,具体实现为`publish`方法。这个流程确保了主节点在集群中的协调作用,使得创建索引的操作能够有效地在集群范围内进行。
关于主节点如何验证索引创建的合法性,答案是通过自创建索引并随后删除的方式完成。这样,主节点确保了新索引符合集群的规则和需求。
总结起来,创建索引的请求首先通过Bulk请求的形式执行,先发起对主节点的请求。主节点验证索引创建请求后,内部生成新的集群状态信息,执行索引创建任务。主分片所在的元气骑士源码掉落节点根据集群状态信息创建对应的索引,从而完成了索引的创建过程。整个流程中,主节点扮演了协调和验证的关键角色,确保了索引创建的正确性和集群的一致性。
redis cluster 计ç®slotå¨åªä¸ªèç¹
å设clusteræ5个nodeï¼é£ä¹client请æ±clusterå ¶å®æ¯éæºè¯·æ±äºä¸ä¸ªèç¹ï¼å设请æ±äºnode1ï¼è¯·æ±çkeyç»è¿ CRC( key ) % ä¼è®¡ç®åºè¿ä¸ªkeyè½å¨åªä¸ä¸ªslotä¸ï¼ç¶åä¼è·åsoltånodeçå ³ç³»ï¼è¿ä¸ªå ³ç³»ä¿åå¨clusterNodeè¿ä¸ªæ°æ®ç»æä¸ï¼å¦æè¿ä¸ªslotå¨å½ånodeä¸ï¼å°±æ§è¡å½ä»¤ï¼å¦åå°±è¿åä¸ä¸ªmovedä¿¡æ¯ï¼ä¸»è¦å æ¬slotåç®æ nodeä¿¡æ¯ï¼åç±clientéååé请æ±å°ç®æ nodeãè¿éå¨soltè¿ç§»çæ¶åï¼æä¸ä¸ªaskéå®åã大æ¦å°±æ¯è¿ä¸ªææå§ï¼æä¹ä¸æ¯å¾æ¸ æ¥ï¼ä½ åèåèå§
ç½é¡µé¾æ¥
游戏引擎随笔 0x:UE5 Nanite 源码解析之渲染篇:BVH 与 Cluster 的 Culling
在UE5 Nanite的渲染深度中,一个关键组件是其独特的剔除策略,特别是通过高效的BVH(Bounded Volume Hierarchy)和Cluster Culling技术。Nanite的目标在于智能地控制GPU资源,避免不必要的三角形绘制,确保每一点计算都被最大化利用。
首先,Nanite的渲染流程中,异步数据传输和GPU初始化完成后,进入CullRasterize阶段,其中的PersistentCulling pass至关重要。它分为两个步骤: BVH Node Culling 和 Cluster Culling,每个阶段都利用多线程并行处理,实现了GPU性能的极致发挥。
在Node Culling中,每个线程处理8个节点,通过Packed Node数据结构,确保数据的一致性和同步性。每组个线程间通过MPMC Job Queue协同工作,保证了负载均衡,避免了GPU资源的插件如何找到源码浪费。GroupNodeMask和NodeReadyMask等优化策略,确保了节点处理的高效性和准确性。
核心部分是TGS GroupNodeData,它接收并处理来自候选节点的Packed Node,进行实例数据、动态数据和BVH节点数据的整合。通过Frustum Culling,仅保留可见的节点,非叶节点的计数更新和候选Cluster的生成,都在这个过程中完成。
叶节点的Cluster Culling更为精细,通过计算Screen Rect,判断是否适合渲染。当遇到硬件光栅化需求时,Nanite会利用上一帧的LocalToClip矩阵进行HZB遮挡剔除,确保每个Cluster的可见性和正确性。
在硬件光栅化中,VisibleClusterOffset的计算和Cluster的有序写入,体现了UE5团队对性能的精心调教。而软光栅化则采取相反的存储策略,确保了渲染的高效执行。
尽管Nanite在百万面模型处理上展现出惊人的0.5ms速度,但它并非无懈可击,如不支持Forward Rendering。然而,游资追风指标源码随着UE5技术的不断迭代,Nanite的潜力和优化空间将继续扩展,推动着游戏开发的创新边界。
总之,UE5 Nanite的渲染篇是技术与艺术的完美融合,通过深度剖析其渲染流程,我们不仅能领略到高效剔除策略的魅力,更能感受到Unreal团队在性能优化上的匠心独运。深入源码,解锁游戏引擎的内在魔力,让我们一起期待Nanite在未来的更多可能。
Redis集群架构剖析(4):槽位迁移,重新分配
在前一篇Redis集群架构剖析中,我们了解到集群如何处理来自redis-cli的指令,但都是在cluster槽位不变的情况下。那么,为什么槽位会发生变化呢?集群可能进行节点增删,在第二篇中我们得知,只有当所有节点都分配到槽位时,Redis cluster才会处于online状态。在开始之前,先思考以下问题:
集群在重新分配过程中,无需下线,且源节点和目标节点均可继续处理命令请求。下面我们将探讨Redis是如何实现这一过程的。
重新分配的操作是将指定给某个节点(源节点)的槽位重新分配给另一个节点(目标节点),同时相关槽位所属的键值对也会从源节点移动到目标节点。
例如,原本由、和组成的集群,现在加入一个新的节点。此时,原本分配给的槽位~中的~槽位将重新分配给节点。重新分配的动作在CLUSTER MEET这个节点时完成。
Redis cluster的重新分配操作由Redis的集群管理软件redis-trib负责执行,Redis提供进行重新分配所需的命令,redis-trib则通过向源节点和目标节点发送指令来进行重新分配的操作。
下面是一个槽位重新分配的流程图,需要注意的是其中的第三和第四步,先迁移value再迁移key,这在后面会有作用。
如果这个slot存储了多个键值对,会重复执行步骤4中的第二个指令和步骤5。
在迁移过程中,可能存在redis-cli发送请求来请求数据的情况。此时,可以联想到上一篇文章,如果请求到不是本节点的槽位,节点会告诉redis-cli应该去哪个节点找到对应的槽位。这个思路是否可以借鉴?其实,在设计分布式系统时,这个问题还是非常重要的。要么直接禁止访问,要么设计一个机制,让迁移和请求同时存在。显然,Redis选择了后者。
当客户端向源节点发送一个与数据库键有关的命令,且命令要处理的数据库键恰好属于正在被迁移的槽时:
下面是节点收到请求后是否要发送ASK的流程图
这个ASK和MOVED一样,也是返回Redirected到某个节点,如果需要看到ASK错误,需要用单机redis请求。
在细究ASK的实现细节之前,我们先看看cluster是用什么数据结构来记录哪些槽位在源节点,哪些正在迁移到目标节点。
在重新分配的实现过程中,我们知道最开始有两个动作,分别是目标节点准备导入槽,源节点准备将槽导出。这涉及到两个指令,分别对应两个数据结构。
clusterState结构的importing_slots_from数组记录了当前节点正在从其他节点导入的槽:
如果importing_slots_from[i]不为NULL,而是指向一个clusterNode结构,那么表示正在从这个clusterNode节点导入槽i。
例如,如果加入集群,将上的重新分配给,会执行CLUSTER SETSLOT IMPORTING 的节点ID。那么的importing_slots_from就会变成下图所示,也就是重新分配实现过程的第一步,的importing_slots_from[]会指向节点。
clusterState结构的migrating_slots_to数组记录了当前节点正在迁移至其他节点的槽:
如果migrating_slots_to[i]不为NULL,而是指向一个clusterNode结构,那么表示正在导入到这个clusterNode节点。
例如,继续上面的importing,到了重新分配实现过程的第二步,给发送指令CLUSTER SETSLOT MIGRATING 的节点ID,那么的migrating_slots_to会变成下图所示:
在前面了解到如果请求的命令对应的键不在源节点上,在迁移的目标节点上,源节点就会返回一个ASK错误。接到ASK错误的客户端就会根据错误提供的IP地址和端口号,转向正在导入槽的目标节点,然后首先会向目标节点发送一个ASKING命令,之后才会重新发送原本想要执行的命令。下面是一个简单的转向后,请求ASKING的示意图。
ASKING命令唯一要做的就是打开发送该命令的客户端的REDIS_ASKING标识,以下是这个命令实现的伪代码:
回想一下,之前槽位不存在请求节点的时候,节点会向客户端返回一个MOVED错误。但是,如果节点的clusterState.importing_slots_from[i]显示节点正在导入槽i,并且发送命令的客户端带有REDIS_ASKING标识,那么节点将执行这个关于槽i的命令一次,以下是流程图:
当客户端接收到ASK错误并转向到正在导入槽的节点时,客户端会先向节点发送一个ASKING命令,然后才重新发送想要执行的命令。这是因为如果客户端不发送ASKING命令,而直接发送想要执行的命令,客户端发送的命令将被节点拒绝执行,并返回MOVED错误。
例如,在上面的例子中,我们向节点请求槽,因为是在导入槽,所以如果我们没有发送一个ASKING的命令,会返回一个MOVED的错误,并转到,因为槽还分配在上。如果在请求之前,发送了ASKING命令,那么就会执行这个命令。
注意:REDIS_ASKING标识是一次性标识,当节点执行了一个带有REDIS_ASKING标识的客户端发送的命令之后,客户端的REDIS_ASKING标识就移除了。
ASK错误和MOVED错误都会导致客户端转向,它们之间的区别如下:
通过这篇文档,我们了解到节点在槽转移时,集群是如何处理重新分配的,数据结构又是如何存储的。这是针对数据的一种异常情况。还有一个是针对节点的异常,例如部署的redis节点挂掉了,原本存的槽即使知道导向这个节点,但这个节点也没有回复的能力了。那么,我们该怎么做呢?是否需要备份这个数据?似乎就是我挂了,你顶我。针对这个异常行为,我们下节分析。