// Lock locks the mutex with a cancelable context. If the context is canceled // while trying to acquire the lock, the mutex tries to clean its stale lock entry. func(m *Mutex)Lock(ctx context.Context)error { s := m.s client := m.s.Client()
// 下面的 m.pfx 就是 prefix,是传进来的前缀,后面的 s.Lease() 会返回一个租约,是一个 int64 的整数,和 session 有关系 //mykex 先理解为是 / prefix/leaseid 这样的结构 m.myKey = fmt.Sprintf("%s%x", m.pfx, s.Lease()) // 这里定义一个 cmp 方法,比较上面 m.myKey 的 CreateRevision 是否为 0,等于 0 表示目前不存在该 key,需要执行 Put 操作,不等于 0 表示对应的 key 已经创建了,需要执行 Get 操作 cmp := v3.Compare(v3.CreateRevision(m.myKey), "=", 0) // put self in lock waiters via myKey; oldest waiter holds lock put := v3.OpPut(m.myKey, "", v3.WithLease(s.Lease())) // reuse key in case this session already holds the lock // 获取key是否已设置成锁 get := v3.OpGet(m.myKey) // fetch current holder to complete uncontended path with only one RPC // 获取当前锁的真正持有者 getOwner := v3.OpGet(m.pfx, v3.WithFirstCreate()...) // Txn 事务,判断 cmp 的条件是否成立,成立执行 Then,不成立执行 Else,最终执行 Commit() resp, err := client.Txn(ctx).If(cmp).Then(put, getOwner).Else(get, getOwner).Commit() if err != nil { return err } // if true; (put, getOwner) m.myRev = resp.Header.Revision // else if (get, getOwner) if !resp.Succeeded { m.myRev = resp.Responses[0].GetResponseRange().Kvs[0].CreateRevision } // if no key on prefix / the minimum rev is key, already hold the lock // 获取当前实际拿到锁的KEY ownerKey := resp.Responses[1].GetResponseRange().Kvs // 比较如果当前没有人获得锁(第一次场景) // 或者锁的 owner 的 CreateRevision 等于当前的 key 的 CreateRevision,则表示m.myKey即为拿到锁的key,不用新建,直接使用即可 iflen(ownerKey) == 0 || ownerKey[0].CreateRevision == m.myRev { m.hdr = resp.Header returnnil }
// WithFirstCreate gets the key with the oldest creation revision in the request range. funcWithFirstCreate() []OpOption { return withTop(SortByCreateRevision, SortAscend) }
// withTop gets the first key over the get's prefix given a sort order funcwithTop(target SortTarget, order SortOrder) []OpOption { return []OpOption{WithPrefix(), WithSort(target, order), WithLimit(1)} }
// WithPrefix enables 'Get', 'Delete', or 'Watch' requests to operate // on the keys with matching prefix. For example, 'Get(foo, WithPrefix())' // can return 'foo1', 'foo2', and so on. funcWithPrefix()OpOption { // 返回所有满足 prefix 匹配的 key-value,和 etcdctl get key --prefix 功能一样 returnfunc(op *Op) { iflen(op.key) == 0 { op.key, op.end = []byte{0}, []byte{0} return } op.end = getPrefix(op.key) } }