chrislu
12 months ago
2 changed files with 182 additions and 0 deletions
@ -0,0 +1,181 @@ |
|||
package pub_client |
|||
|
|||
import ( |
|||
"context" |
|||
"fmt" |
|||
"github.com/seaweedfs/seaweedfs/weed/glog" |
|||
"github.com/seaweedfs/seaweedfs/weed/pb" |
|||
"github.com/seaweedfs/seaweedfs/weed/pb/mq_pb" |
|||
"sort" |
|||
"sync" |
|||
"time" |
|||
) |
|||
|
|||
type EachPartitionError struct { |
|||
*mq_pb.BrokerPartitionAssignment |
|||
Err error |
|||
generation int |
|||
} |
|||
|
|||
type EachPartitionPublishJob struct { |
|||
*mq_pb.BrokerPartitionAssignment |
|||
stopChan chan bool |
|||
wg sync.WaitGroup |
|||
generation int |
|||
} |
|||
func (p *TopicPublisher) StartSchedulerThread(bootstrapBrokers []string) error { |
|||
|
|||
if err := p.doEnsureConfigureTopic(bootstrapBrokers); err != nil { |
|||
return err |
|||
} |
|||
|
|||
generation := 0 |
|||
var errChan chan EachPartitionError |
|||
for { |
|||
glog.V(0).Infof("lookup partitions gen %d topic %s/%s", generation, p.namespace, p.topic) |
|||
if assignments, err := p.doLookupTopicPartitions(bootstrapBrokers); err == nil { |
|||
generation++ |
|||
glog.V(0).Infof("start generation %d", generation) |
|||
if errChan == nil { |
|||
errChan = make(chan EachPartitionError, len(assignments)) |
|||
} |
|||
p.onEachAssignments(generation, assignments, errChan) |
|||
} else { |
|||
glog.Errorf("lookup topic %s/%s: %v", p.namespace, p.topic, err) |
|||
time.Sleep(5 * time.Second) |
|||
continue |
|||
} |
|||
|
|||
// wait for any error to happen. If so, consume all remaining errors, and retry
|
|||
for { |
|||
select { |
|||
case eachErr := <-errChan: |
|||
glog.Errorf("gen %d publish to topic %s/%s partition %v: %v", eachErr.generation, p.namespace, p.topic, eachErr.Partition, eachErr.Err) |
|||
if eachErr.generation < generation { |
|||
continue |
|||
} |
|||
break |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
func (p *TopicPublisher) onEachAssignments(generation int, assignments []*mq_pb.BrokerPartitionAssignment, errChan chan EachPartitionError) { |
|||
// TODO assuming this is not re-configured so the partitions are fixed.
|
|||
sort.Slice(assignments, func(i, j int) bool { |
|||
return assignments[i].Partition.RangeStart < assignments[j].Partition.RangeStart |
|||
}) |
|||
var jobs []*EachPartitionPublishJob |
|||
hasExistingJob := len(p.jobs) == len(assignments) |
|||
for i, assignment := range assignments { |
|||
if assignment.LeaderBroker == "" { |
|||
continue |
|||
} |
|||
if hasExistingJob { |
|||
var existingJob *EachPartitionPublishJob |
|||
existingJob = p.jobs[i] |
|||
if existingJob.BrokerPartitionAssignment.LeaderBroker == assignment.LeaderBroker { |
|||
existingJob.generation = generation |
|||
jobs = append(jobs, existingJob) |
|||
continue |
|||
} else { |
|||
if existingJob.LeaderBroker != "" { |
|||
close(existingJob.stopChan) |
|||
existingJob.LeaderBroker = "" |
|||
existingJob.wg.Wait() |
|||
} |
|||
} |
|||
} |
|||
|
|||
// start a go routine to publish to this partition
|
|||
job := &EachPartitionPublishJob{ |
|||
BrokerPartitionAssignment: assignment, |
|||
stopChan: make(chan bool, 1), |
|||
generation: generation, |
|||
} |
|||
job.wg.Add(1) |
|||
go func(job *EachPartitionPublishJob) { |
|||
defer job.wg.Done() |
|||
if err := p.doPublishToPartition(job); err != nil { |
|||
errChan <- EachPartitionError{assignment, err, generation} |
|||
} |
|||
}(job) |
|||
jobs = append(jobs, job) |
|||
} |
|||
p.jobs = jobs |
|||
} |
|||
|
|||
func (p *TopicPublisher) doPublishToPartition(job *EachPartitionPublishJob) error { |
|||
|
|||
return nil |
|||
} |
|||
|
|||
func (p *TopicPublisher) doEnsureConfigureTopic(bootstrapBrokers []string) (err error) { |
|||
if len(bootstrapBrokers) == 0 { |
|||
return fmt.Errorf("no bootstrap brokers") |
|||
} |
|||
var lastErr error |
|||
for _, brokerAddress := range bootstrapBrokers { |
|||
err = pb.WithBrokerGrpcClient(false, |
|||
brokerAddress, |
|||
p.grpcDialOption, |
|||
func(client mq_pb.SeaweedMessagingClient) error { |
|||
_, err := client.ConfigureTopic(context.Background(), &mq_pb.ConfigureTopicRequest{ |
|||
Topic: &mq_pb.Topic{ |
|||
Namespace: p.namespace, |
|||
Name: p.topic, |
|||
}, |
|||
PartitionCount: p.config.CreateTopicPartitionCount, |
|||
}) |
|||
return err |
|||
}) |
|||
if err == nil { |
|||
return nil |
|||
} else { |
|||
lastErr = err |
|||
} |
|||
} |
|||
|
|||
if lastErr != nil { |
|||
return fmt.Errorf("configure topic %s/%s: %v", p.namespace, p.topic, err) |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
func (p *TopicPublisher) doLookupTopicPartitions(bootstrapBrokers []string) (assignments []*mq_pb.BrokerPartitionAssignment, err error) { |
|||
if len(bootstrapBrokers) == 0 { |
|||
return nil, fmt.Errorf("no bootstrap brokers") |
|||
} |
|||
var lastErr error |
|||
for _, brokerAddress := range bootstrapBrokers { |
|||
err := pb.WithBrokerGrpcClient(false, |
|||
brokerAddress, |
|||
p.grpcDialOption, |
|||
func(client mq_pb.SeaweedMessagingClient) error { |
|||
lookupResp, err := client.LookupTopicBrokers(context.Background(), |
|||
&mq_pb.LookupTopicBrokersRequest{ |
|||
Topic: &mq_pb.Topic{ |
|||
Namespace: p.namespace, |
|||
Name: p.topic, |
|||
}, |
|||
}) |
|||
glog.V(0).Infof("lookup topic %s/%s: %v", p.namespace, p.topic, lookupResp) |
|||
|
|||
if err != nil { |
|||
return err |
|||
} |
|||
|
|||
assignments = lookupResp.BrokerPartitionAssignments |
|||
|
|||
return nil |
|||
}) |
|||
if err == nil { |
|||
return assignments, nil |
|||
} else { |
|||
lastErr = err |
|||
} |
|||
} |
|||
|
|||
return nil, fmt.Errorf("lookup topic %s/%s: %v", p.namespace, p.topic, lastErr) |
|||
|
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue