refine delivery
parent
5931e3ce8f
commit
8fd65a83e4
|
@ -13,6 +13,7 @@ import (
|
|||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
)
|
||||
|
||||
// Delivery task delivery with ack and nack
|
||||
type Delivery interface {
|
||||
Ack()
|
||||
Nack()
|
||||
|
@ -30,6 +31,7 @@ type deliver struct {
|
|||
aliveCancel func()
|
||||
}
|
||||
|
||||
// NewDelivery create the task delivery
|
||||
func NewDelivery(ctx context.Context, client *clientv3.Client, key string, node string) (Delivery, error) {
|
||||
d := &deliver{
|
||||
ctx: ctx,
|
||||
|
@ -64,15 +66,15 @@ func (d *deliver) assign(key string, node string) error {
|
|||
return err
|
||||
}
|
||||
if !resp.Succeeded {
|
||||
return fmt.Errorf("key %s not exist or already assign ", key)
|
||||
return fmt.Errorf("task %s not exist or already assign", key)
|
||||
}
|
||||
|
||||
if len(resp.Responses) < 2 {
|
||||
return fmt.Errorf("have no resp %s", key)
|
||||
return fmt.Errorf("task %s tnx resp invalid, count=%d", key, len(resp.Responses))
|
||||
}
|
||||
getResp := resp.Responses[1].GetResponseRange()
|
||||
if len(getResp.Kvs) == 0 {
|
||||
return fmt.Errorf("have no task %s", key)
|
||||
if len(getResp.Kvs) == 0 || len(getResp.Kvs[0].Value) == 0 {
|
||||
return fmt.Errorf("task %s have no body", key)
|
||||
}
|
||||
kv := getResp.Kvs[0]
|
||||
|
||||
|
@ -91,9 +93,11 @@ func (d *deliver) assign(key string, node string) error {
|
|||
|
||||
d.aliveCancel = aliveCancel
|
||||
d.signature = signature
|
||||
d.value = kv.Value
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ack acknowledged the task is done
|
||||
func (d *deliver) Ack() {
|
||||
defer d.aliveCancel()
|
||||
|
||||
|
@ -102,10 +106,11 @@ func (d *deliver) Ack() {
|
|||
|
||||
_, err := d.client.Delete(ctx, d.key, clientv3.WithPrefix())
|
||||
if err != nil {
|
||||
log.ERROR.Printf("ack task %s err: %s", d.value, err)
|
||||
log.ERROR.Printf("ack task %s err: %s", d.key, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Nack negatively acknowledge the delivery of task should handle again
|
||||
func (d *deliver) Nack() {
|
||||
defer d.aliveCancel()
|
||||
|
||||
|
@ -119,10 +124,12 @@ func (d *deliver) Nack() {
|
|||
}
|
||||
}
|
||||
|
||||
// Signature return the task Signature
|
||||
func (d *deliver) Signature() *tasks.Signature {
|
||||
return d.signature
|
||||
}
|
||||
|
||||
// Body return the task body
|
||||
func (d *deliver) Body() []byte {
|
||||
return d.value
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// Package etcd is broker use etcd
|
||||
package etcd
|
||||
|
||||
import (
|
||||
|
@ -12,6 +13,8 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/RichardKnop/machinery/v2/brokers/errs"
|
||||
"github.com/RichardKnop/machinery/v2/brokers/iface"
|
||||
"github.com/RichardKnop/machinery/v2/common"
|
||||
|
@ -20,7 +23,6 @@ import (
|
|||
"github.com/RichardKnop/machinery/v2/tasks"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
"go.etcd.io/etcd/client/v3/concurrency"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
var haveNoTaskErr = errors.New("have no task")
|
||||
|
@ -37,7 +39,7 @@ type etcdBroker struct {
|
|||
// New ..
|
||||
func New(ctx context.Context, conf *config.Config) (iface.Broker, error) {
|
||||
etcdConf := clientv3.Config{
|
||||
Endpoints: []string{conf.Lock},
|
||||
Endpoints: []string{conf.Broker},
|
||||
Context: ctx,
|
||||
DialTimeout: time.Second * 5,
|
||||
TLS: conf.TLSConfig,
|
||||
|
@ -116,9 +118,7 @@ func (b *etcdBroker) StartConsuming(consumerTag string, concurrency int, taskPro
|
|||
|
||||
task, err := b.nextTask(getQueue(b.GetConfig(), taskProcessor), consumerTag)
|
||||
if err != nil {
|
||||
if !errors.Is(err, haveNoTaskErr) {
|
||||
log.ERROR.Print(err)
|
||||
}
|
||||
log.ERROR.Printf("get next task failed: %s", err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
package etcd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/RichardKnop/machinery/v2/log"
|
||||
"go.etcd.io/etcd/api/v3/mvccpb"
|
||||
clientv3 "go.etcd.io/etcd/client/v3"
|
||||
)
|
||||
|
||||
type checkFunc func(*mvccpb.KeyValue) error
|
||||
|
||||
// deleteRevKey deletes a key by revision, returning false if key is missing
|
||||
func deleteRevKey(ctx context.Context, kv clientv3.KV, key string, rev int64) error {
|
||||
cmp := clientv3.Compare(clientv3.ModRevision(key), "=", rev)
|
||||
req := clientv3.OpDelete(key)
|
||||
txnresp, err := kv.Txn(ctx).If(cmp).Then(req).Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 有写入或者删除竞争
|
||||
if !txnresp.Succeeded {
|
||||
return fmt.Errorf("delete key failed: %s: %w", key, haveNoTaskErr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func waitPrefixEvents(ctx context.Context, c *clientv3.Client, prefix string, rev int64) (*clientv3.Event, error) {
|
||||
wc := c.Watch(ctx, prefix, clientv3.WithPrefix(), clientv3.WithRev(rev))
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, haveNoTaskErr
|
||||
|
||||
case wresp := <-wc:
|
||||
for _, ev := range wresp.Events {
|
||||
if ev.Type != mvccpb.PUT {
|
||||
continue
|
||||
}
|
||||
return ev, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getItem(ctx context.Context, c *clientv3.Client, prefix string, opts []clientv3.OpOption, check checkFunc) (*mvccpb.KeyValue, error) {
|
||||
resp, err := c.Get(ctx, prefix, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// nothing yet; wait on elements
|
||||
if len(resp.Kvs) == 0 {
|
||||
ev, err := waitPrefixEvents(ctx, c, prefix, resp.Header.Revision)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := check(ev.Kv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := deleteRevKey(ctx, c, string(ev.Kv.Key), ev.Kv.ModRevision); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ev.Kv, nil
|
||||
}
|
||||
|
||||
kv := resp.Kvs[0]
|
||||
if err := check(kv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := deleteRevKey(ctx, c, string(kv.Key), kv.ModRevision); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return kv, nil
|
||||
}
|
||||
|
||||
func getFirstItem(ctx context.Context, c *clientv3.Client, prefix string) ([]byte, error) {
|
||||
// 按创建时间排序, 只返回单个数据
|
||||
opts := clientv3.WithFirstRev()
|
||||
|
||||
check := func(*mvccpb.KeyValue) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
kv, err := getItem(ctx, c, prefix, opts, check)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return kv.Value, nil
|
||||
}
|
||||
|
||||
func getFirstETAItem(ctx context.Context, c *clientv3.Client, prefix string) ([]byte, error) {
|
||||
// 按创建key排序, 只返回单个数据
|
||||
opts := clientv3.WithFirstKey()
|
||||
|
||||
check := func(kv *mvccpb.KeyValue) error {
|
||||
k := string(kv.Key)
|
||||
if len(k) < 55 {
|
||||
log.ERROR.Print("not valid eta key: %s", k)
|
||||
return fmt.Errorf("not valid eta key: %s", k)
|
||||
}
|
||||
|
||||
nsStr := k[36:55]
|
||||
ns, err := strconv.ParseInt(nsStr, 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse key: %w", err)
|
||||
}
|
||||
|
||||
t := time.Unix(0, ns)
|
||||
if t.After(time.Now()) {
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
return haveNoTaskErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
kv, err := getItem(ctx, c, prefix, opts, check)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return kv.Value, nil
|
||||
}
|
||||
|
||||
func putItem() {
|
||||
|
||||
}
|
Loading…
Reference in New Issue