chrislu
1 year ago
5 changed files with 157 additions and 5 deletions
-
5weed/mq/client/sub_client/subscribe.go
-
2weed/mq/client/sub_client/subscriber.go
-
92weed/mq/coordinator/consumer_group.go
-
36weed/mq/coordinator/coordinator.go
-
17weed/mq/topic/partition.go
@ -0,0 +1,92 @@ |
|||||
|
package coordinator |
||||
|
|
||||
|
import ( |
||||
|
"github.com/seaweedfs/seaweedfs/weed/mq/topic" |
||||
|
"sync" |
||||
|
) |
||||
|
|
||||
|
func (cg *ConsumerGroup) SetMinMaxActiveInstances(min, max int32) { |
||||
|
cg.MinimumActiveInstances = min |
||||
|
cg.MaximumActiveInstances = max |
||||
|
} |
||||
|
|
||||
|
func (cg *ConsumerGroup) AddConsumerGroupInstance(clientId string) *ConsumerGroupInstance { |
||||
|
cgi := &ConsumerGroupInstance{ |
||||
|
ClientId: clientId, |
||||
|
} |
||||
|
cg.ConsumerGroupInstances.Set(clientId, cgi) |
||||
|
return cgi |
||||
|
} |
||||
|
|
||||
|
func (cg *ConsumerGroup) RemoveConsumerGroupInstance(clientId string) { |
||||
|
cg.ConsumerGroupInstances.Remove(clientId) |
||||
|
} |
||||
|
|
||||
|
func (cg *ConsumerGroup) CoordinateIfNeeded() { |
||||
|
emptyInstanceCount, activeInstanceCount := int32(0), int32(0) |
||||
|
for cgi := range cg.ConsumerGroupInstances.IterBuffered() { |
||||
|
if cgi.Val.Partition == nil { |
||||
|
// this consumer group instance is not assigned a partition
|
||||
|
// need to assign one
|
||||
|
emptyInstanceCount++ |
||||
|
} else { |
||||
|
activeInstanceCount++ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var delta int32 |
||||
|
if emptyInstanceCount > 0 { |
||||
|
if cg.MinimumActiveInstances <= 0 { |
||||
|
// need to assign more partitions
|
||||
|
delta = emptyInstanceCount |
||||
|
} else if activeInstanceCount < cg.MinimumActiveInstances && activeInstanceCount+emptyInstanceCount >= cg.MinimumActiveInstances { |
||||
|
// need to assign more partitions
|
||||
|
delta = cg.MinimumActiveInstances - activeInstanceCount |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if cg.MaximumActiveInstances > 0 { |
||||
|
if activeInstanceCount > cg.MaximumActiveInstances { |
||||
|
// need to remove some partitions
|
||||
|
delta = cg.MaximumActiveInstances - activeInstanceCount |
||||
|
} |
||||
|
} |
||||
|
if delta == 0 { |
||||
|
return |
||||
|
} |
||||
|
cg.doCoordinate(activeInstanceCount + delta) |
||||
|
} |
||||
|
|
||||
|
func (cg *ConsumerGroup) doCoordinate(target int32) { |
||||
|
// stop existing instances from processing
|
||||
|
var wg sync.WaitGroup |
||||
|
for cgi := range cg.ConsumerGroupInstances.IterBuffered() { |
||||
|
if cgi.Val.Partition != nil { |
||||
|
wg.Add(1) |
||||
|
go func(cgi *ConsumerGroupInstance) { |
||||
|
defer wg.Done() |
||||
|
// stop processing
|
||||
|
// flush internal state
|
||||
|
// wait for all messages to be processed
|
||||
|
// close the connection
|
||||
|
}(cgi.Val) |
||||
|
} |
||||
|
} |
||||
|
wg.Wait() |
||||
|
|
||||
|
partitions := topic.SplitPartitions(target) |
||||
|
|
||||
|
// assign partitions to new instances
|
||||
|
i := 0 |
||||
|
for cgi := range cg.ConsumerGroupInstances.IterBuffered() { |
||||
|
cgi.Val.Partition = partitions[i] |
||||
|
i++ |
||||
|
wg.Add(1) |
||||
|
go func(cgi *ConsumerGroupInstance) { |
||||
|
defer wg.Done() |
||||
|
// start processing
|
||||
|
// start consuming from the last offset
|
||||
|
}(cgi.Val) |
||||
|
} |
||||
|
wg.Wait() |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
package coordinator |
||||
|
|
||||
|
import ( |
||||
|
cmap "github.com/orcaman/concurrent-map/v2" |
||||
|
"github.com/seaweedfs/seaweedfs/weed/mq/topic" |
||||
|
) |
||||
|
|
||||
|
type ConsumerGroupInstance struct { |
||||
|
ClientId string |
||||
|
// the consumer group instance may not have an active partition
|
||||
|
Partition *topic.Partition |
||||
|
// processed message count
|
||||
|
ProcessedMessageCount int64 |
||||
|
} |
||||
|
type ConsumerGroup struct { |
||||
|
// map a client id to a consumer group instance
|
||||
|
ConsumerGroupInstances cmap.ConcurrentMap[string, *ConsumerGroupInstance] |
||||
|
MinimumActiveInstances int32 |
||||
|
MaximumActiveInstances int32 |
||||
|
} |
||||
|
type TopicConsumerGroups struct { |
||||
|
// map a consumer group name to a consumer group
|
||||
|
ConsumerGroups cmap.ConcurrentMap[string, *ConsumerGroup] |
||||
|
} |
||||
|
|
||||
|
// Coordinator coordinates the instances in the consumer group for one topic.
|
||||
|
// It is responsible for:
|
||||
|
// 1. Assigning partitions to consumer instances.
|
||||
|
// 2. Reassigning partitions when a consumer instance is down.
|
||||
|
// 3. Reassigning partitions when a consumer instance is up.
|
||||
|
type Coordinator struct { |
||||
|
// map client id to subscriber
|
||||
|
Subscribers cmap.ConcurrentMap[string, *ConsumerGroupInstance] |
||||
|
// map topic name to consumer groups
|
||||
|
TopicSubscribers cmap.ConcurrentMap[string, map[string]TopicConsumerGroups] |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue