package clientv3

import (
    pb "github.com/coreos/etcd/etcdserver/etcdserverpb"   //protobuffer  
    "golang.org/x/net/context"  
    "google.golang.org/grpc"   //google  rpc 框架
)

type (
    CompactResponse pb.CompactionResponse  //带压缩的响应
    PutResponse     pb.PutResponse   //添加响应
    GetResponse     pb.RangeResponse  // 带区间的响应
    DeleteResponse  pb.DeleteRangeResponse  // 删除带区间数据的响应
    TxnResponse     pb.TxnResponse    //带事务的响应
)

type KV interface {
    // Put puts a key-value pair into etcd.
        //添加键值对到etcd中
    // Note that key,value can be plain bytes array and string is
    // an immutable representation of that bytes array.
       //注意:键值对可以是字节数组或者字符串  字符串是原子性的字节数组
    // To get a string of bytes, do string([]byte(0x10, 0x20)).
        //
    Put(ctx context.Context, key, val string, opts ...OpOption) (*PutResponse, error)

    // Get retrieves keys.
        //获取键对应的值
    // By default, Get will return the value for "key", if any.
        //默认 获取键对应的值  在任何情况下
    // When passed WithRange(end), Get will return the keys in the range [key, end).
        //当opts 使用了 WithRange(end),将得到键对应的区间[key, end)之间的值
    // When passed WithFromKey(), Get returns keys greater than or equal to key.
             //opts 使用了WithFromKey(),得到大于等于当前key 对应的value
    // When passed WithRev(rev) with rev > 0, Get retrieves keys at the given revision;
                //当opts 使用了 WithRev(rev),如果版本号rev>0 获取指定的版本号对应的value
    // if the required revision is compacted, the request will fail with ErrCompacted .
    // When passed WithLimit(limit), the number of returned keys is bounded by limit.
             //当opts 使用了 WithLimit(limit),将得到键对应的区间[key最小值  默认的   end [limit   )的值   例如:  key为 foo   实际的key为 fooN。。。。到foolimit  之间对应的value
    // When passed WithSort(), the keys will be sorted.
             //当opts 使用了 WithSort(),得到的值将排序  按照字典的顺序来排序
    Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error)

    // Delete deletes a key, or optionally using WithRange(end), [key, end).
       //  删除一个键值对    更常使用 WithRange(end), [key, end).
    Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error)

    // Compact compacts etcd KV history before the given rev.
               // 压缩etcd kv历史数据  通常再给出版本号之前
    Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error)

    // Do applies a single Op on KV without a transaction.
        //do应用于单一kv操作项,通常是没有事务的
    // Do is useful when declaring operations to be issued at a later time
       //
    // whereas Get/Put/Delete are for better suited for when the operation
    // should be immediately issued at time of declaration.

    // Do applies a single Op on KV without a transaction.
    // Do is useful when creating arbitrary operations to be issued at a
    // later time; the user can range over the operations, calling Do to
    // execute them. Get/Put/Delete, on the other hand, are best suited
    // for when the operation should be issued at the time of declaration.
    Do(ctx context.Context, op Op) (OpResponse, error)

    // Txn creates a transaction.
        //创建事务
    Txn(ctx context.Context) Txn
}
//响应结构体
type OpResponse struct {
    put *PutResponse
    get *GetResponse
    del *DeleteResponse
}

func (op OpResponse) Put() *PutResponse    { return op.put }
func (op OpResponse) Get() *GetResponse    { return op.get }
func (op OpResponse) Del() *DeleteResponse { return op.del }
//kv存储服务客户端的包装
type kv struct {
    remote pb.KVClient
}
//创建kv 服务  带着指定的客户端
func NewKV(c *Client) KV {
    return &kv{remote: RetryKVClient(c)}
}
//创建一个kv服务客户端  带着指定的protobuffer 客户端
func NewKVFromKVClient(remote pb.KVClient) KV {
    return &kv{remote: remote}
}
//kv结构体实现了 kv接口
func (kv *kv) Put(ctx context.Context, key, val string, opts ...OpOption) (*PutResponse, error) {
    r, err := kv.Do(ctx, OpPut(key, val, opts...))
    return r.put, toErr(ctx, err)
}

func (kv *kv) Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error) {
    r, err := kv.Do(ctx, OpGet(key, opts...))
    return r.get, toErr(ctx, err)
}

func (kv *kv) Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error) {
    r, err := kv.Do(ctx, OpDelete(key, opts...))
    return r.del, toErr(ctx, err)
}

func (kv *kv) Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error) {
    resp, err := kv.remote.Compact(ctx, OpCompact(rev, opts...).toRequest())
    if err != nil {
        return nil, toErr(ctx, err)
    }
    return (*CompactResponse)(resp), err
}

func (kv *kv) Txn(ctx context.Context) Txn {
    return &txn{
        kv:  kv,
        ctx: ctx,
    }
}

func (kv *kv) Do(ctx context.Context, op Op) (OpResponse, error) {
    for {
        resp, err := kv.do(ctx, op)
        if err == nil {
            return resp, nil
        }

        if isHaltErr(ctx, err) {
            return resp, toErr(ctx, err)
        }
        // do not retry on modifications
        if op.isWrite() {
            return resp, toErr(ctx, err)
        }
    }
}

func (kv *kv) do(ctx context.Context, op Op) (OpResponse, error) {
    var err error
    switch op.t {
    // TODO: handle other ops
    case tRange:
        var resp *pb.RangeResponse
        resp, err = kv.remote.Range(ctx, op.toRangeRequest(), grpc.FailFast(false))
        if err == nil {
            return OpResponse{get: (*GetResponse)(resp)}, nil
        }
    case tPut:
        var resp *pb.PutResponse
        r := &pb.PutRequest{Key: op.key, Value: op.val, Lease: int64(op.leaseID), PrevKv: op.prevKV}
        resp, err = kv.remote.Put(ctx, r)
        if err == nil {
            return OpResponse{put: (*PutResponse)(resp)}, nil
        }
    case tDeleteRange:
        var resp *pb.DeleteRangeResponse
        r := &pb.DeleteRangeRequest{Key: op.key, RangeEnd: op.end, PrevKv: op.prevKV}
        resp, err = kv.remote.DeleteRange(ctx, r)
        if err == nil {
            return OpResponse{del: (*DeleteResponse)(resp)}, nil
        }
    default:
        panic("Unknown op")
    }
    return OpResponse{}, err
}

kv.go的更多相关文章

  1. KV存储系统

    现在的KV存储系统都是分布式的,首先介绍Zookeeper——针对大型分布式系统的高可靠的协调系统. 开发分布式系统是件很困难的事情,其中的困难主要体现在分布式系统的“部分失败”.“部分失败”是指信息 ...

  2. 计算机程序的思维逻辑 (60) - 随机读写文件及其应用 - 实现一个简单的KV数据库

    57节介绍了字节流, 58节介绍了字符流,它们都是以流的方式读写文件,流的方式有几个限制: 要么读,要么写,不能同时读和写 不能随机读写,只能从头读到尾,且不能重复读,虽然通过缓冲可以实现部分重读,但 ...

  3. Redis与KV存储(RocksDB)融合之编码方式

    Redis与KV存储(RocksDB)融合之编码方式 简介 Redis 是目前 NoSQL 领域的当红炸子鸡,它象一把瑞士军刀,小巧.锋利.实用,特别适合解决一些使用传统关系数据库难以解决的问题.Re ...

  4. 从零开始山寨Caffe·柒:KV数据库

    你说你会关系数据库?你说你会Hadoop? 忘掉它们吧,我们既不需要网络支持,也不需要复杂关系模式,只要读写够快就行.    ——论数据存储的本质 浅析数据库技术 内存数据库——STL的map容器 关 ...

  5. Ping CAP CTO、Codis作者谈redis分布式解决方案和分布式KV存储

    此文根据[QCON高可用架构群]分享内容,由群内[编辑组]志愿整理,转发请注明出处. 苏东旭,Ping CAP CTO,Codis作者 开源项目Codis的co-author黄东旭,之前在豌豆荚从事i ...

  6. Red KV数据 庫设計模式

    转:http://blog.nosqlfan.com/html/3033.html NoSQL带给我们的东西很多,高性能,水平扩展性,还有不一样的思维方式.本文来自@hoterran的个人博客运维与开 ...

  7. 浅析LRU(K-V)缓存

    LRU(Least Recently Used)算法是缓存技术中的一种常见思想,顾名思义,最近最少使用,也就是说有两个维度来衡量,一个是时间(最近),一个频率(最少).如果需要按优先级来对缓存中的K- ...

  8. 基于KV Data Model实现Table Data Model

    HBase对外暴露出来的是一个表格数据模型,如下图所示 rowkey应用程序可以自己设计.每一个Cell可以保存多个版本的数据,由timestamp标示版本.应用程序可以自己指定timestamp,如 ...

  9. 基于淘宝开源Tair分布式KV存储引擎的整合部署

    一.前言 Tair支撑了淘宝几乎所有系统的缓存信息(Tair = Taobao Pair,Pair即Key-Value键值对),内置了三个存储引擎:mdb(默认,类似于Memcache).rdb(类似 ...

  10. KV总结

    今天没事又重新写了一遍.很多注释是自己犯糊涂后来又终于跨过去的备忘. // ImgEff.js function ImgEff(div,time){ //构造函数,需要传入参数div的id和时间 // ...

随机推荐

  1. 从小故事来谈nginx负载均衡

    负载均衡 负载均衡是任何一个有一定规模的互联网企业都会考虑的问题,负载方式很多,有依靠硬件实现的,也有依靠软件实现负载的. 今天来聊聊使用软件来负载的方式 你可能听过各自负载的方式,比如常见的ngin ...

  2. remove Nth Node from linked list从链表中删除倒数第n个元素

    Given a linked list, remove the nth node from the end of list and return its head. For example, Give ...

  3. javascript学习(三)——常用方法(2)

    一.兼容性较高的浏览器页面关闭 //关闭网页,不支持火狐(火狐返回上次浏览页面)   //FireFox非window.open()等弹出页面,需要在地址栏中输入about:config, 然后将do ...

  4. 小议 HashMap

    大家都知道,在Java里对对象的操作是基于引用的.而当我们需要对一组对象操作的时候,就需要有接收这一组引用的容器.平时我们最常用的就是数组.在Java里可以定义一个对象数组来完成许多操作.可是,数组长 ...

  5. jbpm 工作流(二)

    1           概述 本文主要介绍如何将JBPM+Struts+Spring+Hibernate整合在一块.并通过一个简单实例来说明.此实例为一个申请审批的简单流程,并将申请人和审批人记录到数 ...

  6. 多重影分身——C#中多线程的使用一(基础)

    首先明确几个概念: 什么是进程? 当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源. 而一个进程又是由多个线程所组成的. 一个程序通常只有一个进程(不包括exe ...

  7. 一步一步设置Joomla!开发环境

    转载自:http://h2appy.blog.51cto.com/609721/373414 虽然是英文,可是写的非常浅显易懂,再配合截图,更是明了. http://docs.joomla.org/S ...

  8. DjangoRestFramework实践笔记

    1.Restful服务的实现方式一共三种:function based view,class based view,viewset+router,这三种实现方式的封装重度依序升高,越往后越适合典型CU ...

  9. WebRTC技术调研

    相关网址: 协议:https://www.w3.org/TR/webrtc/ https://apprtc.webrtc.org/ https://apprtc.appspot.com/ https: ...

  10. require、缓存

    什么是require? -Node使用CommonJS模块规范,内置require函数用于加载模块文件 -require的基本功能是>读入并执行一个javascript文件,然后返回该模块的ex ...