ConsumerGroup中的leader消费者会通过syncGroupRequest将分区的分配结果发送给GroupCoordinator,GroupCoordinator会根据这个分配结果形成SyncGroupResponse返回给所有的消费者。消费者受到解析后得到分区的分配结果。prepareStoreGroup()方法实现了根据分区分配结果创建消息消息的功能。
def prepareStoreGroup(group: GroupMetadata,
groupAssignment: Map[String, Array[Byte]],
responseCallback: Short => Unit): DelayedStore = {
// 获取对应offsets topic Partition使用消息版本格式
val (magicValue, timestamp) = getMessageFormatVersionAndTimestamp(partitionFor(group.groupId))
// 创建记录GroupMetadata信息的消息,消息的value是分区的分配结果
val message = new Message(
key = GroupMetadataManager.groupMetadataKey(group.groupId),
bytes = GroupMetadataManager.groupMetadataValue(group, groupAssignment),
timestamp = timestamp,
magicValue = magicValue)
// 获取consumer Group对应的__CONSUMER_OFFSETS分区
val groupMetadataPartition = new TopicPartition(TopicConstants.GROUP_METADATA_TOPIC_NAME, partitionFor(group.groupId))
// __CONSUMER_OFFSET分区和消息集合的对应关系
val groupMetadataMessageSet = Map(groupMetadataPartition ->
new ByteBufferMessageSet(config.offsetsTopicCompressionCodec, message))
val generationId = group.generationId
// set the callback function to insert the created group into cache after log append completed
// Callback会在上述消息成功追加到__CONSUMER_OFFSET中之后被调用
def putCacheCallback(responseStatus: Map[TopicPartition, PartitionResponse]) {
// the append response should only contain the topics partition
if (responseStatus.size != 1 || ! responseStatus.contains(groupMetadataPartition))
throw new IllegalStateException("Append status %s should only have one partition %s"
.format(responseStatus, groupMetadataPartition))
// construct the error status in the propagated assignment response
// in the cache
// 根据消息追加结果更新错误码
val status = responseStatus(groupMetadataPartition)
var responseCode = Errors.NONE.code
if (status.errorCode != Errors.NONE.code) {
debug("Metadata from group %s with generation %d failed when appending to log due to %s"
.format(group.groupId, generationId, Errors.forCode(status.errorCode).exceptionName))
// transform the log append error code to the corresponding the commit status error code
responseCode = if (status.errorCode == Errors.UNKNOWN_TOPIC_OR_PARTITION.code) {
Errors.GROUP_COORDINATOR_NOT_AVAILABLE.code
} else if (status.errorCode == Errors.NOT_LEADER_FOR_PARTITION.code) {
Errors.NOT_COORDINATOR_FOR_GROUP.code
} else if (status.errorCode == Errors.REQUEST_TIMED_OUT.code) {
Errors.REBALANCE_IN_PROGRESS.code
} else if (status.errorCode == Errors.MESSAGE_TOO_LARGE.code
|| status.errorCode == Errors.RECORD_LIST_TOO_LARGE.code
|| status.errorCode == Errors.INVALID_FETCH_SIZE.code) {
error("Appending metadata message for group %s generation %d failed due to %s, returning UNKNOWN error code to the client"
.format(group.groupId, generationId, Errors.forCode(status.errorCode).exceptionName))
Errors.UNKNOWN.code
} else {
error("Appending metadata message for group %s generation %d failed due to unexpected error: %s"
.format(group.groupId, generationId, status.errorCode))
status.errorCode
}
}
responseCallback(responseCode)
}
DelayedStore(groupMetadataMessageSet, putCacheCallback)
}
prepareStoreGroup并没有追加消息的代码,它仅仅是创建了DelayedStore对象,其中封装了消息和回调函数。真正实现追加消息操作的是GroupMetadataManager.store()方法,会调用ReplicaManager.appendMessages()方法追加消息,appendMessages中的参数,第二个参数是requiredAcks,默认是-1,即isr中所有副本已经同步了才认为消息成功追加并返回。第三个参数是internalTopicsAllowed,是true,表示可以向kafka内部topic追加消息。
def store(delayedAppend: DelayedStore) {
// call replica manager to append the group message
replicaManager.appendMessages(
config.offsetCommitTimeoutMs.toLong,
config.offsetCommitRequiredAcks,
true, // allow appending to internal offset topic
delayedAppend.messageSet,
delayedAppend.callback)
}
prepareStoreGroup中传入了回调函数
delayedGroupStore = Some(groupManager.prepareStoreGroup(group, assignment, (errorCode: Short) => {
group synchronized {
// another member may have joined the group while we were awaiting this callback,
// so we must ensure we are still in the AwaitingSync state and the same generation
// when it gets invoked. if we have transitioned to another state, then do nothing
// 检测Consumer Group状态和年代信息
if (group.is(AwaitingSync) && generationId == group.generationId) {
if (errorCode != Errors.NONE.code) {
resetAndPropagateAssignmentError(group, errorCode)
maybePrepareRebalance(group)
} else {
setAndPropagateAssignment(group, assignment)
group.transitionTo(Stable)
}
}
}
}))
setAndPropagateAssignment主要如下:
private def setAndPropagateAssignment(group: GroupMetadata, assignment: Map[String, Array[Byte]]) {
assert(group.is(AwaitingSync))
// 将分配结果更新到GroupMetadata维护的每个MemberMetadata中
group.allMemberMetadata.foreach(member => member.assignment = assignment(member.memberId))
propagateAssignment(group, Errors.NONE.code)
}
private def propagateAssignment(group: GroupMetadata, errorCode: Short) {
for (member <- group.allMemberMetadata) {
if (member.awaitingSyncCallback != null) {
// 调用每个membermetadata的回调函数,具体操作时创建syncGroupResponse对象并添加到requestChannels中等待发送
member.awaitingSyncCallback(member.assignment, errorCode)
member.awaitingSyncCallback = null
// reset the session timeout for members after propagating the member's assignment.
// This is because if any member's session expired while we were still awaiting either
// the leader sync group or the storage callback, its expiration will be ignored and no
// future heartbeat expectations will not be scheduled.
// 开启等待下次心跳的延迟任务。
completeAndScheduleNextHeartbeatExpiration(group, member)
}
}
}