2.2.5 服务端的请求处理入口

2.2.5 服务端的请求处理入口

现在请求通道上请求的发送(由处理器执行)和接收(由请求处理线程执行),以及响应的接收(由处理器执行)都有了,只剩下响应的发送由rKafkaApis负责。r客户端请求通过请求处理器交给负责具体业务逻辑处理的rKafkaApisr,rKafkaApis收到请求执行完业务逻辑,r将请求对应的响应结果发送到请求通道的响应队歹ljr中r。r因为rKafkaApis类持有请求通道的引用,所以它可以把响应发送到请求通道中。现在,请求和响应的发送和接收四个步骤就都齐了 。

KafkaAp"is handle ()方法是服务端处理各种请求 的入口 。 不仅仅是客户端( 比如生产者或消费者),Kafka服务端节点之间的通信也会走这个统一的人口( 比如备份副本的拉取请求 ,以及其他内部请求)。相关代码如下:

class KafkaApis(val requestChannel: RequestChannel,
                val replicaManager: ReplicaManager,
                val adminManager: AdminManager,
                val groupCoordinator: GroupCoordinator,
                val txnCoordinator: TransactionCoordinator,
                val controller: KafkaController,
                val zkUtils: ZkUtils,
                val brokerId: Int,
                val config: KafkaConfig,
                val metadataCache: MetadataCache,
                val metrics: Metrics,
                val authorizer: Option[Authorizer],
                val quotas: QuotaManagers,
                brokerTopicStats: BrokerTopicStats,
                val clusterId: String,
                time: Time) extends Logging {}

总结一下服务端接收请求放入请求通道,再到发送响应放入请求通道的过程。处理器接收完从客户端发送过来的NetworkReceive对象,会解析NetworkReceive的内容,再加上当前处理器编号,包装成RequestChannelRequest对象,然后将RequestChannelRequest放入请求通道的请求队列中。

客户端发送的请求对象是CientRequest,但是在经过网络发送给服务端,会被包装成Send对象。服务端通过处理器中选择器的轮询读取客户端的请求,得到的是NetworkReceive对象。服务端需要解析NetworkReceive的内容才-能创建RequestChannelRequest请求对象。

请求处理线程从请求通道中获取请求,并交给KafkaApis去处理。KafkaApis在处理完请求后,会创建响应对象(RequestChannelResponse)并放入请求通道中。响应对象也持有请求对象的引用,因为请求对象中有处理器编号,所以响应对象可以从请求对象中获取处理器编号,确保对请求和响应的处理都是在同一个处理器中完成。相关代码如下:

  private def processCompletedReceives() {
    selector.completedReceives.asScala.foreach { receive =>
      try {
        val openChannel = selector.channel(receive.source)
        // Only methods that are safe to call on a disconnected channel should be invoked on 'openOrClosingChannel'.
        val openOrClosingChannel = if (openChannel != null) openChannel else selector.closingChannel(receive.source)
        val session = RequestChannel.Session(new KafkaPrincipal(KafkaPrincipal.USER_TYPE, openOrClosingChannel.principal.getName), openOrClosingChannel.socketAddress)

        val req = RequestChannel.Request(processor = id, connectionId = receive.source, session = session,
          buffer = receive.payload, startTimeNanos = time.nanoseconds,
          listenerName = listenerName, securityProtocol = securityProtocol)
        requestChannel.sendRequest(req)
        selector.mute(receive.source)
      } catch {
        case e @ (_: InvalidRequestException | _: SchemaException) =>
          // note that even though we got an exception, we can assume that receive.source is valid. Issues with constructing a valid receive object were handled earlier
          error(s"Closing socket for ${receive.source} because of error", e)
          close(selector, receive.source)
      }
    }
  }

KafkaApls处理生产者客户端请求的具体实现会在第6章中分析,从第3章到第5章我们会分析同样也是客户端的消费者。


2.3 小节

本章主要分析了两种版本的生产者客户端以及服务端的网络层实现,重点介绍了客户端的NetworkCllent和l服务端的SocketServer。Java版本的客户端和l服务端的处理器,都使用了选择器模式和Kafka通道(KafkaChannel),而Scala版本的客户端则使用比较原始的阻塞通道(BlocklngChannel)。在客户端-服务端的通信模型中,通常一个客户端会连接到多个服务端,一个服务端也会接受多个客户端的连接,所以使用选择器模式可以使网络通信更加高效。另外,我们还在服务端运用了Reactor模乱,将网络请求和业务处理的钱程进行分离。除此之外,客户端和服务端在很多地方都运用了队列这种数据结构,来对请求或者响应进行排队。队列是一种保证数据被有序处理,井且能够缓存的结构。

在客户端要向服务端发送消息时,我们会获取Cluster集群状态(Java版本)或集群元数据ToplcMetadata(Scala版本),并为消息选择分区,选择分区的主副本作为目标节点。在服务端,SocketServer会接收客户端发送的请求交给请求处理线程和]KafkaApls处理。具体和消息相关的处理逻辑,由KafkaApls以及KafkaServer中的其他组件一起完成。

本章分析的Producer以及后面要分析的Consumer,都不是Kafka的内置服务,而是一种客户端(所以它们都在clients包中)。客户端可以独立于Kafka集群,因此开发客户端应用程序时,只需要提供一个Kafka集群的地址,说明客户端可以和Kafka集群独立开来。图2-22展示了一种典型的生产者、消费者和Kafka集群交互方式,其中Kafka集群还会和ZK互相通信。

在这里插入图片描述

客户端有发送和接收请求的逻辑,服务端同样也有接收和发送的逻辑,因为对于110来说这是双向的:客户端发送请求,就意味着服务端要接收请求;服务端对请求作出响应井发送响应结果给客户端,客户端就要接收响应。

相关推荐
©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页