Akka这样一个scala世界里的明星,给我们提供了各种各样吸引人的功能和特性,尤其在分布式、高并发领域。但就像任何其他优秀的框架,它的实现也必然会有其复杂性,在Roland Kuhn(Akka Tech Lead)的带领下,Akka的实现原理吸收了各个领域内成熟、领先的理论。尤其是Akka里cluster的实现,更是体现了非常多的优秀理论和实战经验。
但由于它目前还处在实验阶段,在使用过程中还是会有可能碰到这样或那样的问题,下面就以Akka 2.3.1为例,详细分析我们碰到的一个bug。
1)场景描述
集群里有两台机器SeedNode1(10.10.10.110) 和 SeedNode2(10.10.10.220),Akka的配置文件application.conf里相关配置如下:
seed-nodes = [
"akka.tcp://ClusterSystem@10.10.10.110:2551",
"akka.tcp://ClusterSystem@10.10.10.220:2552"]
我们先启动SeedNode1,等一会启动SeedNode2,发现SeedNode2和SeedNode1的TCP链路是连上了,但就是无法正常进行工作。但如果先让SeedNode2先启动,然后再启动SeedNode1,则
没有问题,集群可正常启动。
2)分析
为了更好方便大家理解,下面先介绍一下cluster和remote的相关实现细节,这样才能前后串起来。
2.1)cluster的启动
要使用一个cluster首先要启动它,所以我们先从启动这个步骤的实现开始进行分析。Akka集群的启动首先就是要启动一种叫做种子节点(SeedNode)的节点们。只有种子节点启动成功,其他节点才能选择任意一个种子节点加入集群。
种子节点默认可配置多个,它们之间没有任何区别,种子节点的启动分以下几种情况:
1.某种子节点启动,它首先判断自己的ip是否在种子节点配置列表中,如果在并且是第一个,则它在一个规定时间内(默认是5秒),向其他种子节点发送‘InitJoin’消息,如果有确认消息返回,则加入第一个返回确认的种子节点所在的cluster中,否则,它自己将创建一个新的cluster。(这些任务由FirstSeedNodeProcess这个Actor完成,任务完成后它就销毁自己)
2.某种子节点启动,它首先判断自己的ip是否在种子节点配置中,但不是第一个,则它向其他种子节点发送消息,如果在一个规定时间内(默认是5秒)没有收到任何确认消息,则它将不断重试,直到有一个种子节点返回正确的确认消息,然后就加入这个种子节点所在的cluster中。(这里注意以下,它不会自己创建一个新cluster)。(这些任务由JoinSeedNodeProcess这个Actor完成,任务完成后它就销毁自己)
从上面的分析,我们可以得出下面的一些结论:
#.一个集群第一次启动成功,那一定是种子节点配置列表中排在第一位的节点,由它来创建出集群。但是随着时间的推移,排在第一的种子节点有可能重启了,那这个时候,它将首选加入到其他种子节点去。
#一个种子节点可以加入任何一个其他节点,不用非得都加到排第一位的节点上。
下面我们举例说明,有种子节点1、2、3:
* 1. seed2启动, 但是没有收到seed1 或seed3的确认。
* 2. seed3启动,没有收到seed1 的确认消息(seed2处在’inactive’状态)。
* 3. seed1 启动,创建cluster并加入到自己中。
* 4. seed2 重试加入过程,收到seed1的确认, 加入到seed1。
* 5. seed3重试加入过程,先收到seed2的确认, 加入到seed2。
2.2)remote通讯链路的上行、下行实现
2.2.1)上行路径(listen启动的全过程)
由于上行路径较复杂,所以画了几张图辅助说明:
(建立listen的步骤)
(接收一个新链路请求)
(接收一个新链路处于等待握手状态)
1###可以把Remoting这个非常重要的类作为通讯模块的入口,它在启动的时候(start方法里)会向 EndpointManager这个Actor发送Listen消息,启动底层通讯NettyTransport的listen操作。
2###由AkkaProtocolTransport类来包一层NettyTransport,所以,先调用的是AkkaProtocolTransport的listen方法,这个方法里产生一个upstreamListenerPromise,这个promise最后会被成赋值为ActorAssociationEventListener(EndpointManager的实例),而这个promise的作用是为了设置AkkaProtocolManager的associationListener属性为EndpointManager的实例。
3###NettyTransport在linsten过程中,会返回一个associationListenerPromise,这个promise会通过调用interceptListen方法而被赋值ActorAssociationEventListener(AkkaProtocolManager的实例)。
而这个promise有两个作用:
***把建立起来的通讯Channel(监听端口的)置为可读状态(setReadable),以便接收后续进入的消息。
***作为TcpServerHandler的构造参数传入(_associationListenerFuture),TcpServerHandler实例(它其实是
netty里SimpleChannelUpstreamHandler的一个扩展)里最重要的方法是onConnect这个回调方法。当有外部链接建立成功,
onConnect方法会被调用,紧接着会调用initInbound方法,然后在该promise处等待,直到promise被成功赋值。
4###当上面initInbound方法里的promise被成功唤醒,它就会调用init方法。
5###init方法里首先会创建一个TcpAssociationHandle实例(包含一个readHandlerPromise),这个Promise在这里等待被唤醒(它被后面7处的操作唤醒而设置channel(新链接的)置为可读状态(setReadable),同时在netty中注册该channel的listen为ProtocolStateActor实例),然后会向AkkaProtocolManager实例发送InboundAssociation消息(这个消息里包含一个TcpAssociationHandle实例)。
6###AkkaProtocolManager实例收到InboundAssociation消息,创建一个ProtocolStateActor实例(调用inboundProps构造方法),这个实例的构造函数里包含两个重要的参数TcpAssociationHandle实例、EndpointManager的实例;
7###ProtocolStateActor实例的这种构造方法会把TcpAssociationHandle实例里的readHandlerPromise设置值而唤醒它。
8###ProtocolStateActor实例初始化后会等待在接受握手的状态中(WaitHandshake),这个时候如果接收到网络报文,decode后发现是Associate消息,则调用notifyInboundHandler方法。在这个方法中会向EndpointManager实例发送InboundAssociation(new AkkaProtocolHandle(...))消息,notifyInboundHandler方法也创建了一个readHandlerPromise,它作为参数放在发往EndpointManager实例的消息里,然后等待被赋值。
9###EndpointManager实例收到InboundAssociation消息后,根据addressToWritable(EndpointPolicy规则的集合)进行一些必要的判断,如果符合要求则调用createAndRegisterEndpoint方法,这个方法最主要是创建EndpointWriter实例并注册这个实例。不符合则进行相关动作,如保存这个InboundAssociation消息,等待后续条件合适再处理。
10###在创建EndpointWriter实例的preStart方法里,判断是否已经存在AkkaProtocolHandle实例,如果已经存在则创建一个EndpointReader实例,并把它作为值设置给步骤7里的readHandlerPromise,使readHandlerPromise这个Promise的future被唤醒。
11###ProtocolStateActor实例的readHandlerPromise被唤醒后,会向自己发送一条HandleListenerRegistered(EndpointReader实例)的消息,接收到这个消息后,它会修改自己状态机里的状态数据为ListenerReady。后续所有接受的网络数据包就会被正常的decode和分发了。
2.2.2)下行路径
作为发送端(client),当seed节点A向seed节点B发送InitJoin消息时,调用链如下:
1###向处在accepting状态中的EndpointManager实例发送'Send(message, senderOption, recipientRef, _)'
2###EndpointManager实例调用createAndRegisterWritingEndpoint方法,创建一个ReliableDeliverySupervisor实例(在EndpointWriter实例之上封了一层,以加强可靠性)。
并且向addressToWritable这个HashMap里添加一条记录。
3###ReliableDeliverySupervisor实例会创建一个EndpointWriter实例,在其preStart方法里,由于传入的AkkaProtocolHandle为None,所以会调用transport.associate(remoteAddress, ...),同时EndpointWriter实例进入Initializing状态。
4###上面的transport是AkkaProtocolTransport实例,它会向AkkaProtocolManager实例的发送一个AssociateUnderlyingRefuseUid消息
5###AkkaProtocolManager实例收到AssociateUnderlyingRefuseUid消息后,调用createOutboundStateActor方法,该方法调用ProtocolStateActor.outboundProps的构造方法。
6###ProtocolStateActor实例的outboundProps构造方法,会调用NettyTransport实例的associate方法,它会调用NettyFutureBridge(bootstrap.connect(socketAddress)进行真正的网络连接。
7.1###如果无法成功建立连接,则向外发送异常,这个异常会最终被EndpointManager实例捕获。
8###EndpointManager实例捕获异常后,根据异常情况进行处理,如果是链接失败异常则调用markAsFailed修改addressToWritable相关配置。
7.2###如果成功建立连接,则InitJoin消息会发送对对方机器。
3)bug具体原因分析
通过上面的cluster集群启动过程的分析和remoting的实现过程,可以用来具体分析一下我们的问题场景。 我们是先启动SeedNode1,它启动后会调用remoting的下行路径向SeedNode2发送 ’InitJoin‘消息,它在发送几次后,还没收到响应则自己创建了集群。等我们再启动SeedNode2的时候,SeedNode2会向SeedNode1发起链接,走的是SeedNode1的上行路径,于是bug发生了。
它具体原因就在下行链路的处理环节8###中没有捕获ConnectException异常,也就没有对addressToWritable相关配置进行调整。这就使得上行链路的处理环节9###无法正常往下进行。
该bug在今年4月份被修复,2.3.2及其之后的版本都没有问题,具体修复请查看https://github.com/akka/akka/commit/672e7f947c9d4e3499bb3667a7230685546b7f7b,
虽然就是新增了一个对ConnectException异常的捕获,但分析这个bug的原因过程,还是有收获的,应该能对使用Akka的remoting、cluster模块的相关朋友有帮助。
相关推荐
c904417ef980d9da9eabe32d217623a2.part1
game_patch_1.30.21.13210.pak
Java多线程,线程安全(同步锁、异步锁)
图书管理系统前端静态资源
教育机构绩效管理与绩效考核制度
华为MA5626/MA562空库文件
可以通过这个简易的demo来,锻炼刚开始接触JAVA的朋友们。 首先需要有JAVA开发环境,安装了JDK。 此代码展示了如何设置游戏面板、加载图像资源、初始化游戏状态、处理键盘输入以改变方向、更新游戏状态、检测碰撞和苹果收集等基本功能1。请注意,为了运行这个程序,你需要准备相应的图片资源(dot.png, food.png, head.png),并将其放置在正确的路径下(这里假设是src/resources/目录)。如果你没有这些图片文件,可以使用任何你喜欢的图片代替,或者直接绘制矩形作为替代
"单相Boost PFC电路双闭环控制策略仿真研究:电感电流内环与输出电压双环控制的运行特性及功率因数校正效果展示",单相boost PFC电路仿真 功率因数校正 采用双闭环控制方式 电感电流内环+输出电压双环控制 电路运行特性良好,如效果图所示 ,核心关键词:单相boost PFC电路仿真; 功率因数校正; 双闭环控制方式; 电感电流内环; 输出电压双环控制; 电路运行特性。,"双闭环控制单相Boost PFC电路仿真:电感电流与输出电压协同优化"
改进A星算法:优化路径规划,剔除冗余节点,平滑转折点,对比优化前后结果,改进A星算法 剔除冗余节点,光滑转折点 对比优化前后路径。 ,核心关键词:改进A星算法; 剔除冗余节点; 光滑转折点; 对比优化前后路径。,"优化A星算法:剔除冗余节点,平滑转折点,对比优化路径效果"
项目已获导师指导并通过的高分毕业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行!可以放心下载 技术组成 语言:java 开发环境:idea 数据库:MySql8.0 部署环境:Tomcat(建议用 7.x 或者 8.x 版本),maven 数据库工具:navicat
基于滑膜控制的无人车辆多车道变换轨迹跟踪与路径规划——MATLAB仿真实现,基于滑膜控制无人车辆轨迹跟踪控制 复现滑膜控制 多车道变,MATLAB仿真 路径规划 无人船无人机 SMC控制 Sliding mode controller for trajectory tracking ,基于滑膜控制的无人车辆轨迹跟踪; 复现滑膜控制; 多车道变换; MATLAB仿真; 无人船无人机路径规划; SMC控制; Sliding mode controller。,基于滑膜控制的无人车辆多车道轨迹跟踪控制及仿真研究
基于定子磁链定向矢量控制策略的双馈风力发电系统研究:直接转矩输入与双PWM变换器控制,双馈风力发电系统,双pwm变器控制系统,采用直接转矩输入代替风力发电机。 (1)转子侧采用基于定子磁链定向的矢量控制策略,对d轴进行定向,采用双闭环控制结构,外环为速度环,内环为电流控制环。 (2)网侧采用基于dq解耦直接功率控制,使转子侧以单位功率因数消耗能量,功率因数为1。 (3)右侧加了转子电流过流保护电路(crowbar),并设置了一些参数突变,以便研究了双馈风力发电机在外界干扰下各转矩、功率、电压等波形变化。 附带说明 ,双馈风力发电系统; 双PWM变换器控制; 矢量控制策略; 功率因数1; 转子电流过流保护; 波形变化。,双馈风力发电系统:双PWM变换器直接转矩控制技术研究
2025年小学《义务教育道德与法治课程标准(2022年版)》试卷附含答案.docx
2025年义务教育新课程标准生物(2022年版)必考试题含答案.docx
单相ANPC仿真-逆变器通用-matlab/SIMetrix
数字农场项目建设方案.pptx
python国产进口电影票房榜单数据可视化(可视化大屏)
1.项目简介 基于JavaEE超市管理系统-可用于毕设-课程设计-练手学习。本系统利用网络沟通、计算机信息存储管理,有着与传统的方式所无法替代的优点。比如计算检索速度特别快、可靠性特别高、存储容量特别大、保密性特别好、可保存时间特别长、成本特别低等。 2.项目技术栈 • 数据库:MySQL • 开发工具:IDEA • 数据连接池:Druid • Web容器:Apache Tomcat • 项目管理工具:Maven • 版本控制工具:Git • 后端技术:Servlet+JDBC+Web+JSP • 前端框架:LayUI、HTML、CSS、Jquery 3.项目运行部分截图 4.项目配套万字文档 1.项目简介 根据系统需求与系统功能的分析,可以把系统总体分为:学生信息管理、专业信息管理、课程信息管理、学院信息管理等几大功能模块。每个模块分别定义了多个功能。可用下图的总体功能模块图表示 2.项目技术栈 • 数据库:MySQL • 开发工具:IDEA或Eclipse • 数据连接池:Druid • Web容器:Apache Tomcat •
1、文件内容:acpid-sysvinit-2.0.19-9.el7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/acpid-sysvinit-2.0.19-9.el7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、安装指导:私信博主,全程指导安装
数字化校园系统开发定制各个子功能清单模板.docx