上节说到,当负载均衡队列建立的时候,会在注册中心,进行登记。建立多个队列之后,注册中心会维护这样一个数据结构,并把相应的状态置为true:
这个注册中心是如何被使用的,我们在FlowController构造方法的最后可以看到这样一段代码:
构造了一个线程池,然后往其中提交了 NioAsyncLoadBalanceClientTask 类型的任务,来看看任务做什么的:
通过注册中心,获取到所有的client,然后调用它的 communicate 方法,进入到client.communicate()方法:
这里我们需要关注两个方法: 建立连接的 establishConnection() 和收发数据的loadBalanceSession.communicate(),注意这个方法是放在do-while循环内的。
establishConnection()
这个 SocketChannel 也是博主第一次见到,放个传送门一起来学习下:
传统的IO是基于字节流和字符流操作,是阻塞式,而NIO(non-blocking IO)是基于Channel和Buffer,是非阻塞式,使用Selector用于监听多个通道的事件。单个线程可以监听多个数据通道。
Channel和IO中的流相似,只不过流是单向的,InputStream只能输入不能输出,OutputStream又只能输出不能做输入,而Channel就厉害了,既可以用输出,又可以输入,又当爹又当妈的。
Channel的主要实现有:
FileChannel: 文件的数据读写
DatagramChannel: UDP的数据读写
SocketChannel: TCP的数据读写,一般客户端实现
ServerSocketChannel: 一般是服务器实现。
所以这里有了客户端实现,那肯定的我们也需要找到服务端实现。这个我们在后边关注。
这里建立连接,就是 client 新建一个channel,在后续数据交换的时候使用。
连接建立之后,新建一个session,LoadBalanceSession,调用session的 communicate 方法。
loadBalanceSession.communicate()
NIFI把一个客户端的一次负载均衡事务分成了多个阶段 phase,
session初始化的时候是 RECOMMEND_PROTOCOL_VERSION 阶段,
来到 communicate 方法:
大致可以看出来,根据当前session所处的事务阶段 phase 的不同,来发送不同的数据,只要write的数据不为0,这个方法就返回true.进入到 getDataFrame 方法:
进去这些方法可以看出,每次都会将 phase 修改成新的值。结合着之前说的,整个方法是放在do-while循环中的,因此会将所有的 phase 走完。都某些阶段时候,需要先读响应,然后再判断是不是需要继续往下走,比如
这里发送一个检查空间的请求,然后把 phase 设置成 RECEIVE_SPACE_RESPONSE,下一次循环,就会执行到这里:
会从 channel 中读数据出来,判断响应 是不是 SPACE_AVAILABLE 如果是,接下来会执行getNextFlowFile方法,这个方法会把等待负载均衡的把flowfile放入一个list当中,通过getFlowFileContent方法把要发送的flowfile准备好。
这里 client的流程我们基本都整理完了,接下来看下服务端是如何实现的。
ConnectionLoadBalanceServer
session在clustered包下的client目录,在server目录下,可以看到ConnectionLoadBalanceServer类,进入start()方法:
先是通过 createServerSocket 方法构建好soeket
AcceptConnection 实现了runnable方法,要启动一个线程,这个线程的run方法如下:
监听socket, 当有连接进来的时候,会构建一个 CommunicateAction 对象,放入一个list当中,该对象也实现了Runnable 方法:
主要是调用 receiveFlowFiles 方法
将接收到的数据,转成 flowFile。 在completeTransaction 方法中将flowfile路由到指定的队列:
至此我们看到了十分熟悉的 flowFileRepository.updateRepository() 方法。
server的创建也是在FlowController中完成的:
至此 我们大概整理清楚了flowfile的负载均衡是如何实现的了。