refine delivery

Browse Source
main
git 2024-06-09 00:03:27 +08:00
parent 5931e3ce8f
commit 8fd65a83e4
Signed by: git
GPG Key ID: 3F65EFFA44207ADD
3 changed files with 17 additions and 150 deletions

View File

@ -13,6 +13,7 @@ import (
clientv3 "go.etcd.io/etcd/client/v3" clientv3 "go.etcd.io/etcd/client/v3"
) )
// Delivery task delivery with ack and nack
type Delivery interface { type Delivery interface {
Ack() Ack()
Nack() Nack()
@ -30,6 +31,7 @@ type deliver struct {
aliveCancel func() aliveCancel func()
} }
// NewDelivery create the task delivery
func NewDelivery(ctx context.Context, client *clientv3.Client, key string, node string) (Delivery, error) { func NewDelivery(ctx context.Context, client *clientv3.Client, key string, node string) (Delivery, error) {
d := &deliver{ d := &deliver{
ctx: ctx, ctx: ctx,
@ -64,15 +66,15 @@ func (d *deliver) assign(key string, node string) error {
return err return err
} }
if !resp.Succeeded { 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 { 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() getResp := resp.Responses[1].GetResponseRange()
if len(getResp.Kvs) == 0 { if len(getResp.Kvs) == 0 || len(getResp.Kvs[0].Value) == 0 {
return fmt.Errorf("have no task %s", key) return fmt.Errorf("task %s have no body", key)
} }
kv := getResp.Kvs[0] kv := getResp.Kvs[0]
@ -91,9 +93,11 @@ func (d *deliver) assign(key string, node string) error {
d.aliveCancel = aliveCancel d.aliveCancel = aliveCancel
d.signature = signature d.signature = signature
d.value = kv.Value
return nil return nil
} }
// Ack acknowledged the task is done
func (d *deliver) Ack() { func (d *deliver) Ack() {
defer d.aliveCancel() defer d.aliveCancel()
@ -102,10 +106,11 @@ func (d *deliver) Ack() {
_, err := d.client.Delete(ctx, d.key, clientv3.WithPrefix()) _, err := d.client.Delete(ctx, d.key, clientv3.WithPrefix())
if err != nil { 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() { func (d *deliver) Nack() {
defer d.aliveCancel() defer d.aliveCancel()
@ -119,10 +124,12 @@ func (d *deliver) Nack() {
} }
} }
// Signature return the task Signature
func (d *deliver) Signature() *tasks.Signature { func (d *deliver) Signature() *tasks.Signature {
return d.signature return d.signature
} }
// Body return the task body
func (d *deliver) Body() []byte { func (d *deliver) Body() []byte {
return d.value return d.value
} }

View File

@ -1,3 +1,4 @@
// Package etcd is broker use etcd
package etcd package etcd
import ( import (
@ -12,6 +13,8 @@ import (
"sync" "sync"
"time" "time"
"golang.org/x/sync/errgroup"
"github.com/RichardKnop/machinery/v2/brokers/errs" "github.com/RichardKnop/machinery/v2/brokers/errs"
"github.com/RichardKnop/machinery/v2/brokers/iface" "github.com/RichardKnop/machinery/v2/brokers/iface"
"github.com/RichardKnop/machinery/v2/common" "github.com/RichardKnop/machinery/v2/common"
@ -20,7 +23,6 @@ import (
"github.com/RichardKnop/machinery/v2/tasks" "github.com/RichardKnop/machinery/v2/tasks"
clientv3 "go.etcd.io/etcd/client/v3" clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency" "go.etcd.io/etcd/client/v3/concurrency"
"golang.org/x/sync/errgroup"
) )
var haveNoTaskErr = errors.New("have no task") var haveNoTaskErr = errors.New("have no task")
@ -37,7 +39,7 @@ type etcdBroker struct {
// New .. // New ..
func New(ctx context.Context, conf *config.Config) (iface.Broker, error) { func New(ctx context.Context, conf *config.Config) (iface.Broker, error) {
etcdConf := clientv3.Config{ etcdConf := clientv3.Config{
Endpoints: []string{conf.Lock}, Endpoints: []string{conf.Broker},
Context: ctx, Context: ctx,
DialTimeout: time.Second * 5, DialTimeout: time.Second * 5,
TLS: conf.TLSConfig, 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) task, err := b.nextTask(getQueue(b.GetConfig(), taskProcessor), consumerTag)
if err != nil { if err != nil {
if !errors.Is(err, haveNoTaskErr) { log.ERROR.Printf("get next task failed: %s", err)
log.ERROR.Print(err)
}
continue continue
} }

View File

@ -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() {
}