公众号:码农架构
基于 etcd 的分布式队列、栅栏和 STM
- STM(Software Transactional Memory,软件事务内存)
分布式队列和优先级队列
分布式队列
etcd 通过 github.com/coreos/etcd/contrib/recipes 包提供了分布式队列这种数据结构
- keyPrefix: 队列名
func NewQueue(client *v3.Client, keyPrefix string) *Queue
队列只有两个方法,分别是出队和入队,队列中的元素是字符串类型:
- 如果这个分布式队列当前为空,调用 Dequeue 方法的话,会被阻塞,直到有元素可以出队才返回
// 入队func (q *Queue) Enqueue(val string) error//出队func (q *Queue) Dequeue() (string, error)
既然是分布式的队列,那就意味着,我们可以在一个节点将元素放入队列,在另外一个节点把它取出。
- etcd 的分布式队列是一种多读多写的队列
我们启动一个程序,它会从命令行读取你的命令,然后执行。你可以输入push ,将一个元素入队,输入pop,将一个元素弹出。另外,你还可以使用这个程序启动多个实例,用来模拟分布式的环境:
package mainimport ( "bufio" "flag" "fmt" "log" "os" "strings" "github.com/coreos/etcd/clientv3" recipe "github.com/coreos/etcd/contrib/recipes")var ( addr = flag.String("addr", "http://127.0.0.1:2379", "etcd addresses") queueName = flag.String("name", "my-test-queue", "queue name"))func main() { flag.Parse() // 解析etcd地址 endpoints := strings.Split(*addr, ",") // 创建etcd的client cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints}) if err != nil { log.Fatal(err) } defer cli.Close() // 创建/获取队列 q := recipe.NewQueue(cli, *queueName) // 从命令行读取命令 consolescanner := bufio.NewScanner(os.Stdin) for consolescanner.Scan() { action := consolescanner.Text() items := strings.Split(action, " ") switch items[0] { case "push": // 加入队列 if len(items) != 2 { fmt.Println("must set value to push") continue } q.Enqueue(items[1]) // 入队 case "pop": // 从队列弹出 v, err := q.Dequeue() // 出队 if err != nil { log.Fatal(err) } fmt.Println(v) // 输出出队的元素 case "quit", "exit": //退出 return default: fmt.Println("unknown action") } }}
优先级队列
PriorityQueue
它的用法和队列类似,也提供了出队和入队的操作,只不过,在入队的时候,除了需要把一个值加入到队列,我们还需要提供 uint16 类型的一个整数,作为此值的优先级,优先级高的元素会优先出队。
package mainimport ( "bufio" "flag" "fmt" "log" "os" "strconv" "strings" "github.com/coreos/etcd/clientv3" recipe &