├── README.md ├── connect.go ├── errors.go ├── example ├── example.go └── pangudashu-Vs-bradfitz.go ├── memcache.go ├── pool.go ├── protocal.go ├── server.go └── tools.go /README.md: -------------------------------------------------------------------------------- 1 | # Memcached Client for Golang 2 | golang版本的memcached客户端,使用二进制协议,支持分布式,支持连接池,支持多种数据格式 3 | 4 | ### 特性 5 | * 支持多server集群 6 | * 与memcached使用二进制协议通信 7 | * 支持连接池 8 | * 存储value支持golang基本数据类型:string、[]byte、int、int8、int16、int32、int64、bool、uint8、uint16、uint32、uint64、float32、float64、map、结构体,不需要单独转为string存储 9 | * Replace、Increment/Decrement、Delete、Append/Prepend命令支持cas原子操作 10 | 11 | ### 分布式集群 12 | 默认开启分布式集群,key按照一致性哈希算法分配到各server,当server无法连接时如果设置了SetRemoveBadServer(true)则自动被剔除server列表,等到恢复正常时再重新加入server列表 13 | 14 | ### 性能 15 | 与[github.com/bradfitz/gomemcache](https://github.com/bradfitz/gomemcache)项目(beego cache用的这个)比较,测试方式:启动一个http服务,每次请求调用一次memcached的Get操作。[测试脚本example/pangudashu-Vs-bradfitz.go](https://github.com/pangudashu/memcache/blob/master/example/pangudashu-Vs-bradfitz.go) 16 | 17 | 用ab分别测试请求: 18 | 19 | ab -c 200 -n 10000 http://127.0.0.1:9955/bradfitz_foo 20 | ab -c 200 -n 10000 http://127.0.0.1:9955/pangudashu_foo 21 | 22 | github.com/bradfitz/gomemcache结果: 23 | 24 | Document Path: /bradfitz_foo 25 | Document Length: 295 bytes 26 | 27 | Concurrency Level: 200 28 | Time taken for tests: 1.982 seconds 29 | Complete requests: 10000 30 | Failed requests: 0 31 | Write errors: 0 32 | Total transferred: 4135782 bytes 33 | HTML transferred: 2954130 bytes 34 | Requests per second: 5046.27 [#/sec] (mean) 35 | Time per request: 39.633 [ms] (mean) 36 | Time per request: 0.198 [ms] (mean, across all concurrent requests) 37 | Transfer rate: 2038.11 [Kbytes/sec] received 38 | 39 | Connection Times (ms) 40 | min mean[+/-sd] median max 41 | Connect: 0 4 40.2 1 1006 42 | Processing: 0 33 16.6 31 251 43 | Waiting: 0 31 16.2 29 251 44 | Total: 0 36 43.4 33 1039 45 | 46 | Percentage of the requests served within a certain time (ms) 47 | 50% 33 48 | 66% 40 49 | 75% 45 50 | 80% 48 51 | 90% 56 52 | 95% 64 53 | 98% 73 54 | 99% 79 55 | 100% 1039 (longest request) 56 | 57 | github.com/pangudashu/memcache结果: 58 | 59 | Document Path: /pangudashu_foo 60 | Document Length: 295 bytes 61 | 62 | Concurrency Level: 200 63 | Time taken for tests: 1.140 seconds 64 | Complete requests: 10000 65 | Failed requests: 0 66 | Write errors: 0 67 | Total transferred: 4144455 bytes 68 | HTML transferred: 2960325 bytes 69 | Requests per second: 8771.33 [#/sec] (mean) 70 | Time per request: 22.802 [ms] (mean) 71 | Time per request: 0.114 [ms] (mean, across all concurrent requests) 72 | Transfer rate: 3550.04 [Kbytes/sec] received 73 | 74 | Connection Times (ms) 75 | min mean[+/-sd] median max 76 | Connect: 0 4 41.3 2 1003 77 | Processing: 0 15 9.9 13 218 78 | Waiting: 0 13 9.5 11 211 79 | Total: 0 19 42.5 16 1024 80 | 81 | Percentage of the requests served within a certain time (ms) 82 | 50% 16 83 | 66% 19 84 | 75% 22 85 | 80% 24 86 | 90% 29 87 | 95% 34 88 | 98% 40 89 | 99% 46 90 | 100% 1024 (longest request) 91 | 92 | ### 使用 93 | ##### 下载 94 | 95 | go get github.com/pangudashu/memcache 96 | 97 | ##### 导入 98 | 99 | package main 100 | 101 | import( 102 | "fmt" 103 | "github.com/pangudashu/memcache" 104 | ) 105 | 106 | func main(){ 107 | 108 | /** 109 | * server配置 110 | * Address string //host:port 111 | * Weight int //权重 112 | * InitConn int: //初始化连接数 < MaxCnt 113 | * MaxConn int: //最大连接数 114 | * IdleTime time.Duration: //空闲连接有效期 115 | */ 116 | 117 | s1 := &memcache.Server{Address: "127.0.0.1:12000", Weight: 50} 118 | s2 := &memcache.Server{Address: "127.0.0.1:12001", Weight: 20} 119 | s3 := &memcache.Server{Address: "127.0.0.1:12002", Weight: 20} 120 | s4 := &memcache.Server{Address: "127.0.0.1:12003", Weight: 10} 121 | 122 | //初始化连接池 123 | mc, err := memcache.NewMemcache([]*memcache.Server{s1, s2, s3, s4}) 124 | if err != nil { 125 | fmt.Println(err) 126 | return 127 | } 128 | 129 | //设置是否自动剔除无法连接的server,默认不开启(建议开启) 130 | //如果开启此选项被踢除的server如果恢复正常将会再次被加入server列表 131 | mc.SetRemoveBadServer(true) 132 | //设置连接、读写超时 133 | mc.SetTimeout(time.Second*2, time.Second, time.Second) 134 | 135 | mc.Set("test_key",true) 136 | fmt.Println(mc.Get("test_key")) 137 | 138 | //... 139 | 140 | mc.Close() 141 | } 142 | 143 | ##### 示例 144 | [example/example.go](https://github.com/pangudashu/memcache/blob/master/example/example.go) 145 | 146 | ### 命令列表 147 | ###### Get 148 | 149 | 根据key检索一个元素 150 | 151 | 【说明】 152 | Get(key string [, format_struct interface{} ])(value interface{}, cas uint64, err error) 153 | 154 | 【参数】 155 | key 要检索的元素的key 156 | format 用于存储的value为map、结构体时,返回值将直接反序列化到format 157 | 158 | 【返回值】 159 | value为interface,取具体存储的值需要断言 160 | 存储的value为map、结构体时,value将返回nil 161 | 162 | type User struct { 163 | //... 164 | } 165 | 166 | var user User 167 | if _, _, e := mc.Get("pangudashu_struct", &user); e != nil { 168 | fmt.Println(e) 169 | } else { 170 | fmt.Println(user) 171 | } 172 | 173 | 174 | ###### Set 175 | 176 | 向一个新的key下面增加一个元素 177 | 178 | 【说明】 179 | Set(key string, value interface{} [, expire ...uint32 ]) (res bool, err error) 180 | 181 | 【参数】 182 | key 用于存储值的键名 183 | value 存储的值,可以为string、[]byte、int、int8、int16、int32、int64、bool、uint8、uint16、uint32、uint64、float32、float64、map、struct等类型 184 | expire 过期时间,默认0 185 | 186 | 【返回值】 187 | 设置成功res返回true,err返回nil,否则res返回false,err返回memcache.ErrNotStord 188 | 189 | 【注意】 190 | int类型长度与系统位数相关,所以实际存储转为string,建议尽量使用具体长度的类型:int8、int16、int32、int64替换 191 | 192 | //demo 193 | var value uint32 = 360000000000 194 | mc.Set("test_value", value, 1800) 195 | 196 | ###### Add 197 | 198 | 向一个新的key下面增加一个元素,与Set类似,但是如果 key已经在服务端存在,此操作会失败 199 | 200 | 【说明】 201 | Add(key string, value interface{} [, expire uint32 ]) (res bool, err error) 202 | 203 | 【参数】 204 | 同Set 205 | 206 | 【返回值】 207 | 同Set。 208 | 如果key已经存在,res返回false,err返回memcache.ErrKeyExists 209 | 210 | ###### Replace 211 | 212 | 替换已存在key下的元素,类似Set,但是如果服务端不存在key,操作将失败 213 | 214 | 【说明】 215 | Replace(key string, value interface{} [, expire uint64 [, cas uint64 ]]) (res bool, err error) 216 | 217 | 【参数】 218 | key 用于存储值的键名 219 | value 存储的值 220 | expire 过期时间 221 | cas 数据版本号,原子替换,如果数据在此操作前已被其它客户端更新,则替换失败 222 | 223 | _,cas,_ := mc.Get("test_key") 224 | 225 | res, er := mc.Replace("test_key", "new value~", 0, cas) //每次更新操作数据的cas都会变,所以如果这个值在Get后被其它client更新了则返回false,err返回memcache.ErrKeyExists 226 | 227 | ###### Delete 228 | 229 | 删除一个元素 230 | 231 | 【说明】 232 | Delete(key string [, cas uint64 ]) (res bool, err error) 233 | 234 | 【参数】 235 | key 要删除的key 236 | cas 数据版本号,如果数据在此操作前已被其它客户端更新,则删除失败 237 | 238 | 【返回值】 239 | 成功时返回 true,或者在失败时返回 false,如果key不存在err返回 memcache.ErrNotFound 240 | 241 | ###### Increment 242 | 243 | 增加数值元素的值,如果key不存在则操作失败 244 | 245 | 【说明】 246 | Increment(key string [, delta int [, cas int ]]) (res bool, err error) 247 | 248 | 【参数】 249 | key 要增加值的元素的key 250 | delta 要将元素的值增加的大小,默认1 251 | cas 数据版本号,只有当服务端cas没有变化时此操作才成功 252 | 253 | 【返回值】 254 | 成功时返回 true,或者在失败时返回 false,如果key不存在则初始化为0,如果cas版本号已变err返回memcache.ErrKeyExists 255 | 256 | 【注意】 257 | Increment/Decrement只能操作value类型为int的值,其它任何类型均无法操作。(原因是memcached中在Incr/Decr处理时首先使用strtoull将value转为unsigned long long再进行加减操作,所以只有将数值存为字符串strtoull才能将其转为合法的数值) 258 | 259 | ###### Decrement 260 | 261 | 减小数值元素的值 262 | 263 | 【说明】 264 | Decrement(key string [, delta int [, cas int ]]) (res bool, err error) 265 | 266 | 【参数】 267 | 同Increment 268 | 269 | ###### Flush 270 | 271 | 删除缓存中的所有元素 272 | 273 | 【说明】 274 | Flush([ delay uint32 ]) (res bool, err error) 275 | 276 | 【参数】 277 | delay 在flush所有元素之前等待的时间(单位秒) 278 | 279 | 【返回值】 280 | 成功时返回 true, 或者在失败时返回 false 281 | 282 | ###### Append 283 | 284 | 向已存在string元素后追加数据 285 | 286 | 【说明】 287 | Append(key string, value string [, cas uint64 ]) (res bool, err error) 288 | 289 | 【参数】 290 | key 用于存储值的键名 291 | value 将要追加的值 292 | 293 | 【返回值】 294 | 成功时返回 false, 或者在失败时返回 false。 如果key不存在err返回memcache.ErrNotFound 295 | 296 | 【注意】 297 | Append/Prepend只能操作string类型,尽管操作其它类型时也能转化为string,但是将导致数据原来的类型失效,也就是说Append/Prement能够成功,但是Get时将出错 298 | 299 | ###### Prepend 300 | 301 | 向已存在string元素前追加数据 302 | 303 | 【说明】 304 | Prepend(key string, value string [, cas uint64 ]) (res bool, err error) 305 | 306 | 同Append 307 | 308 | ###### Version 309 | 310 | 获取memcached服务端版本 311 | 312 | 【说明】 313 | Version(server *memcache.Server) (v string, err error) 314 | 315 | 316 | 【参数】 317 | server server配置结构 318 | 319 | 【返回值】 320 | memcached version 321 | 322 | ### 错误编码 323 | * ErrNotConn : Can't connect to server 324 | * ErrNotFound : Key not found 325 | * ErrKeyExists : Key exists 326 | * ErrInval : Invalid arguments 327 | * ErrNotStord : Item not stored 328 | * ErrDeltaBadVal : Increment/Decrement on non-numberic value 329 | * ErrMem : Out of memery 330 | * ErrInvalValue : Unkown value type 331 | * ErrInvalFormat : Invalid format struct 332 | * ErrNoFormat : Format struct empty 333 | * ErrUnkown : Unkown error 334 | -------------------------------------------------------------------------------- /connect.go: -------------------------------------------------------------------------------- 1 | package memcache 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/binary" 7 | "errors" 8 | "io" 9 | "net" 10 | "strconv" 11 | "strings" 12 | "time" 13 | ) 14 | 15 | type Connection struct { 16 | c net.Conn 17 | buffered bufio.ReadWriter 18 | lastActiveTime time.Time 19 | } 20 | 21 | var ( 22 | dialTimeout time.Duration 23 | writeTimeout time.Duration 24 | readTimeout time.Duration 25 | ) 26 | 27 | func connect(address string) (conn *Connection, err error) { /*{{{*/ 28 | var network string 29 | if strings.Contains(address, "/") { 30 | network = "unix" 31 | } else { 32 | network = "tcp" 33 | } 34 | var nc net.Conn 35 | 36 | if dialTimeout > 0 { 37 | nc, err = net.DialTimeout(network, address, dialTimeout) 38 | } else { 39 | nc, err = net.Dial(network, address) 40 | } 41 | if err != nil { 42 | return nil, ErrNotConn 43 | } 44 | return newConnection(nc), nil 45 | } /*}}}*/ 46 | 47 | func newConnection(c net.Conn) *Connection { /*{{{*/ 48 | return &Connection{ 49 | c: c, 50 | buffered: *bufio.NewReadWriter( 51 | bufio.NewReader(c), 52 | bufio.NewWriter(c), 53 | ), 54 | lastActiveTime: time.Now(), 55 | } 56 | } /*}}}*/ 57 | 58 | func (this *Connection) readResponse() (*response, error) { /*{{{*/ 59 | b := make([]byte, 24) 60 | 61 | if readTimeout > 0 { 62 | this.c.SetReadDeadline(time.Now().Add(readTimeout)) 63 | } 64 | 65 | if _, err := this.buffered.Read(b); err != nil { 66 | //if err == io.EOF { 67 | return nil, ErrBadConn 68 | //} else { 69 | // return nil, err 70 | //} 71 | } 72 | 73 | response_header := this.parseHeader(b) 74 | 75 | if response_header.magic != MAGIC_RES { 76 | return nil, errors.New("invalid magic") 77 | } 78 | 79 | res := &response{header: response_header} 80 | 81 | if response_header.bodylen > 0 { 82 | if readTimeout > 0 { 83 | this.c.SetReadDeadline(time.Now().Add(readTimeout)) 84 | } 85 | 86 | res.bodyByte = make([]byte, response_header.bodylen) 87 | _, err := io.ReadFull(this.buffered, res.bodyByte) 88 | if err != nil { 89 | return nil, ErrBadConn 90 | } 91 | } 92 | 93 | return res, nil 94 | } /*}}}*/ 95 | 96 | func (this *Connection) flushBufferToServer() error { /*{{{*/ 97 | if writeTimeout > 0 { 98 | this.c.SetWriteDeadline(time.Now().Add(writeTimeout)) 99 | } 100 | return this.buffered.Flush() 101 | } /*}}}*/ 102 | 103 | func (this *Connection) get(key string, format ...interface{}) (res *response, err error) { /*{{{*/ 104 | header := &request_header{ 105 | magic: MAGIC_REQ, 106 | opcode: OP_GET, 107 | keylen: uint16(len(key)), 108 | extlen: 0x00, 109 | datatype: TYPE_RAW_BYTES, 110 | status: 0x00, 111 | bodylen: uint32(len(key)), 112 | opaque: 0x00, 113 | cas: 0x00, 114 | } 115 | if err := this.writeHeader(header); err != nil { 116 | return nil, err 117 | } 118 | 119 | this.buffered.WriteString(key) 120 | 121 | if err := this.flushBufferToServer(); err != nil { 122 | return nil, ErrBadConn 123 | } 124 | 125 | resp, err := this.readResponse() 126 | if err != nil { 127 | return resp, err 128 | } 129 | 130 | if err := this.checkResponseError(resp.header.status); err != nil { 131 | return resp, err 132 | } 133 | 134 | if resp.header.bodylen > 0 { 135 | flags := binary.BigEndian.Uint32(resp.bodyByte[:resp.header.extlen]) 136 | res_value, err := this.formatValueFromByte(value_type_t(flags), resp.bodyByte[resp.header.extlen:], format...) 137 | if err != nil { 138 | res_value = nil 139 | } 140 | 141 | resp.body = res_value 142 | return resp, err 143 | } else { 144 | return resp, errors.New("unkown error") 145 | } 146 | } /*}}}*/ 147 | 148 | func (this *Connection) delete(key string, cas ...uint64) (res bool, err error) { /*{{{*/ 149 | var set_cas uint64 = 0 150 | if len(cas) > 0 { 151 | set_cas = cas[0] 152 | } 153 | header := &request_header{ 154 | magic: MAGIC_REQ, 155 | opcode: OP_DELETE, 156 | keylen: uint16(len(key)), 157 | extlen: 0x00, 158 | datatype: TYPE_RAW_BYTES, 159 | status: 0x00, 160 | bodylen: uint32(len(key)), 161 | opaque: 0x00, 162 | cas: set_cas, 163 | } 164 | 165 | if err := this.writeHeader(header); err != nil { 166 | return false, err 167 | } 168 | this.buffered.Write([]byte(key)) 169 | 170 | if err := this.flushBufferToServer(); err != nil { 171 | return false, ErrBadConn 172 | } 173 | 174 | resp, err := this.readResponse() 175 | if err != nil { 176 | return false, err 177 | } 178 | 179 | if err := this.checkResponseError(resp.header.status); err != nil { 180 | return false, err 181 | } 182 | 183 | return true, nil 184 | } /*}}}*/ 185 | 186 | func (this *Connection) numberic(opcode opcode_t, key string, args ...interface{}) (res bool, err error) { /*{{{*/ 187 | var delta = 1 188 | var cas = 0 189 | 190 | switch len(args) { 191 | case 1: 192 | delta = args[0].(int) 193 | case 2: 194 | delta = args[0].(int) 195 | cas = args[1].(int) 196 | } 197 | 198 | header := &request_header{ 199 | magic: MAGIC_REQ, 200 | opcode: opcode, 201 | keylen: uint16(len(key)), 202 | extlen: 0x14, 203 | datatype: TYPE_RAW_BYTES, 204 | status: 0x00, 205 | bodylen: uint32(len(key) + 0x14), 206 | opaque: 0x00, 207 | cas: uint64(cas), 208 | } 209 | extra_byte := make([]byte, 0x14) 210 | binary.BigEndian.PutUint64(extra_byte[0:8], uint64(delta)) 211 | binary.BigEndian.PutUint64(extra_byte[8:16], 0x0000000000000000 /*uint64(initial)*/) 212 | binary.BigEndian.PutUint32(extra_byte[16:20], 0x00000000 /*uint32(expiration) If the expiration value is all one-bits (0xffffffff), the operation will fail with NOT_FOUND*/) 213 | 214 | if err := this.writeHeader(header); err != nil { 215 | return false, err 216 | } 217 | 218 | this.buffered.Write(extra_byte) 219 | this.buffered.Write([]byte(key)) 220 | 221 | if err := this.flushBufferToServer(); err != nil { 222 | return false, ErrBadConn 223 | } 224 | 225 | resp, err := this.readResponse() 226 | if err != nil { 227 | return false, err 228 | } 229 | 230 | if err := this.checkResponseError(resp.header.status); err != nil { 231 | return false, err 232 | } 233 | 234 | return true, nil 235 | } /*}}}*/ 236 | 237 | func (this *Connection) store(opcode opcode_t, key string, value interface{}, timeout uint32, cas uint64) (res bool, err error) { /*{{{*/ 238 | val, data_type := this.getValueTypeByte(value) 239 | if val == nil { 240 | return false, ErrInvalValue 241 | } 242 | 243 | header := &request_header{ 244 | magic: MAGIC_REQ, 245 | opcode: opcode, 246 | keylen: uint16(len(key)), 247 | extlen: 0x08, 248 | datatype: TYPE_RAW_BYTES, 249 | status: 0x00, 250 | bodylen: uint32(len(key) + 0x08 + len(val)), 251 | opaque: 0x00, 252 | cas: cas, //If the Data Version Check (CAS) is nonzero, the requested operation MUST only succeed if the item exists and has a CAS value identical to the provided value. 253 | } 254 | 255 | if err := this.writeHeader(header); err != nil { 256 | return false, err 257 | } 258 | 259 | extra_byte := make([]byte, 8) 260 | binary.BigEndian.PutUint32(extra_byte[0:4], uint32(data_type)) //uint32 flags 261 | binary.BigEndian.PutUint32(extra_byte[4:8], timeout) //uint32 expiration 262 | 263 | this.buffered.Write(extra_byte) 264 | this.buffered.Write([]byte(key)) 265 | this.buffered.Write(val) 266 | 267 | if err := this.flushBufferToServer(); err != nil { 268 | return false, ErrBadConn 269 | } 270 | 271 | resp, err := this.readResponse() 272 | if err != nil { 273 | return false, err 274 | } 275 | 276 | if err := this.checkResponseError(resp.header.status); err != nil { 277 | return false, err 278 | } 279 | 280 | return true, nil 281 | } /*}}}*/ 282 | 283 | func (this *Connection) appends(opcode opcode_t, key string, value string, cas ...uint64) (res bool, err error) { /*{{{*/ 284 | var set_cas uint64 = 0 285 | if len(cas) > 0 { 286 | set_cas = cas[0] 287 | } 288 | 289 | header := &request_header{ 290 | magic: MAGIC_REQ, 291 | opcode: opcode, 292 | keylen: uint16(len(key)), 293 | extlen: 0x00, 294 | datatype: TYPE_RAW_BYTES, 295 | status: 0x00, 296 | bodylen: uint32(len(key) + len(value)), 297 | opaque: 0x00, 298 | cas: set_cas, 299 | } 300 | 301 | if err := this.writeHeader(header); err != nil { 302 | return false, err 303 | } 304 | this.buffered.Write([]byte(key + value)) 305 | 306 | if err := this.flushBufferToServer(); err != nil { 307 | return false, ErrBadConn 308 | } 309 | 310 | resp, err := this.readResponse() 311 | if err != nil { 312 | return false, err 313 | } 314 | 315 | if err := this.checkResponseError(resp.header.status); err != nil { 316 | return false, err 317 | } 318 | 319 | return true, nil 320 | } /*}}}*/ 321 | 322 | func (this *Connection) flush(delay ...uint32) (res bool, err error) { /*{{{*/ 323 | var set_delay uint32 = 0 324 | if len(delay) > 0 { 325 | set_delay = delay[0] 326 | } 327 | 328 | header := &request_header{ 329 | magic: MAGIC_REQ, 330 | opcode: OP_FLUSH, 331 | keylen: 0x00, 332 | extlen: 0x04, 333 | datatype: TYPE_RAW_BYTES, 334 | status: 0x00, 335 | bodylen: 0x00000004, 336 | opaque: 0x00, 337 | cas: 0x00, 338 | } 339 | 340 | if err := this.writeHeader(header); err != nil { 341 | return false, err 342 | } 343 | 344 | extra_byte := make([]byte, 4) 345 | binary.BigEndian.PutUint32(extra_byte, set_delay) 346 | 347 | this.buffered.Write(extra_byte) 348 | 349 | if err := this.flushBufferToServer(); err != nil { 350 | return false, ErrBadConn 351 | } 352 | 353 | resp, err := this.readResponse() 354 | if err != nil { 355 | return false, err 356 | } 357 | 358 | if err := this.checkResponseError(resp.header.status); err != nil { 359 | return false, err 360 | } 361 | 362 | return true, nil 363 | } /*}}}*/ 364 | 365 | func (this *Connection) noop() (res bool, err error) { /*{{{*/ 366 | header := &request_header{ 367 | magic: MAGIC_REQ, 368 | opcode: OP_NOOP, 369 | keylen: 0x00, 370 | extlen: 0x00, 371 | datatype: TYPE_RAW_BYTES, 372 | status: 0x00, 373 | bodylen: 0x00000000, 374 | opaque: 0x00, 375 | cas: 0x00, 376 | } 377 | 378 | if err := this.writeHeader(header); err != nil { 379 | return false, err 380 | } 381 | 382 | if err := this.flushBufferToServer(); err != nil { 383 | return false, ErrBadConn 384 | } 385 | 386 | resp, err := this.readResponse() 387 | if err != nil { 388 | return false, err 389 | } 390 | 391 | if err := this.checkResponseError(resp.header.status); err != nil { 392 | return false, err 393 | } 394 | 395 | return true, nil 396 | } /*}}}*/ 397 | 398 | func (this *Connection) version() (v string, err error) { /*{{{*/ 399 | header := &request_header{ 400 | magic: MAGIC_REQ, 401 | opcode: OP_VERSION, 402 | keylen: 0x00, 403 | extlen: 0x00, 404 | datatype: TYPE_RAW_BYTES, 405 | status: 0x00, 406 | bodylen: 0x00000000, 407 | opaque: 0x00, 408 | cas: 0x00, 409 | } 410 | 411 | if err := this.writeHeader(header); err != nil { 412 | return "", err 413 | } 414 | 415 | if err := this.flushBufferToServer(); err != nil { 416 | return "", ErrBadConn 417 | } 418 | 419 | resp, err := this.readResponse() 420 | if err != nil { 421 | return "", err 422 | } 423 | 424 | if err := this.checkResponseError(resp.header.status); err != nil { 425 | return "", err 426 | } 427 | 428 | if resp.header.bodylen > 0 { 429 | return string(resp.bodyByte), nil 430 | } else { 431 | return "", nil 432 | } 433 | } /*}}}*/ 434 | 435 | //check server returned status 436 | func (this *Connection) checkResponseError(status status_t) (err error) { /*{{{*/ 437 | switch status { 438 | case STATUS_SUCCESS: 439 | return nil 440 | case STATUS_KEY_ENOENT: 441 | return ErrNotFound 442 | case STATUS_KEY_EEXISTS: 443 | return ErrKeyExists 444 | case STATUS_E2BIG: 445 | return ErrBig 446 | case STATUS_EINVAL: 447 | return ErrInval 448 | case STATUS_NOT_STORED: 449 | return ErrNotStord 450 | case STATUS_DELTA_BADVAL: 451 | return ErrDeltaBadVal 452 | case STATUS_AUTH_ERROR: 453 | return ErrAuthError 454 | case STATUS_AUTH_CONTINUE: 455 | return ErrAuthContinue 456 | case STATUS_UNKNOWN_COMMAND: 457 | return ErrCmd 458 | case STATUS_ENOMEM: 459 | return ErrMem 460 | default: 461 | return ErrUnkown 462 | } 463 | } /*}}}*/ 464 | 465 | func (this *Connection) parseHeader(b []byte) *response_header { /*{{{*/ 466 | return &response_header{ 467 | magic: magic_t(b[0]), 468 | opcode: opcode_t(b[1]), 469 | keylen: uint16(binary.BigEndian.Uint16(b[2:4])), 470 | extlen: uint8(b[4]), 471 | datatype: type_t(b[5]), 472 | status: status_t(binary.BigEndian.Uint16(b[6:8])), 473 | bodylen: uint32(binary.BigEndian.Uint32(b[8:12])), 474 | opaque: uint32(binary.BigEndian.Uint32(b[12:16])), 475 | cas: uint64(binary.BigEndian.Uint64(b[16:24])), 476 | } 477 | } /*}}}*/ 478 | 479 | func (this *Connection) writeHeader(header *request_header) error { /*{{{*/ 480 | bin_buf := make([]byte, 24) 481 | 482 | bin_buf[0] = byte(header.magic) 483 | bin_buf[1] = byte(header.opcode) 484 | 485 | binary.BigEndian.PutUint16(bin_buf[2:4], header.keylen) 486 | bin_buf[4] = byte(header.extlen) 487 | bin_buf[5] = byte(header.datatype) 488 | binary.BigEndian.PutUint16(bin_buf[6:8], uint16(header.status)) 489 | binary.BigEndian.PutUint32(bin_buf[8:12], header.bodylen) 490 | binary.BigEndian.PutUint32(bin_buf[12:16], header.opaque) 491 | binary.BigEndian.PutUint64(bin_buf[16:24], header.cas) 492 | 493 | len, err := this.buffered.Write(bin_buf) 494 | 495 | if err != nil { 496 | return err 497 | } 498 | 499 | if len < 24 { 500 | return errors.New("write request header error") 501 | } 502 | 503 | return nil 504 | } /*}}}*/ 505 | 506 | func (this *Connection) getValueTypeByte(value interface{}) (body_bin []byte, value_type value_type_t) { /*{{{*/ 507 | switch v := value.(type) { 508 | case []byte: 509 | value_type = VALUE_TYPE_BYTE 510 | body_bin = v 511 | case int: //转为字符串处理 512 | value_type = VALUE_TYPE_INT 513 | s := strconv.Itoa(v) 514 | body_bin = []byte(s) 515 | case int8: 516 | value_type = VALUE_TYPE_INT8 517 | body_bin = make([]byte, 1) 518 | body_bin[0] = byte(v) 519 | case int16: 520 | value_type = VALUE_TYPE_INT16 521 | body_bin = make([]byte, 2) 522 | binary.LittleEndian.PutUint16(body_bin, uint16(v)) 523 | case int32: 524 | value_type = VALUE_TYPE_INT32 525 | body_bin = make([]byte, 4) 526 | binary.LittleEndian.PutUint32(body_bin, uint32(v)) 527 | case int64: 528 | value_type = VALUE_TYPE_INT64 529 | body_bin = make([]byte, 8) 530 | binary.LittleEndian.PutUint64(body_bin, uint64(v)) 531 | case uint8: 532 | value_type = VALUE_TYPE_UINT8 533 | body_bin = make([]byte, 1) 534 | body_bin[0] = byte(v) 535 | case uint16: 536 | value_type = VALUE_TYPE_UINT16 537 | body_bin = make([]byte, 2) 538 | binary.LittleEndian.PutUint16(body_bin, v) 539 | case uint32: 540 | value_type = VALUE_TYPE_UINT32 541 | body_bin = make([]byte, 4) 542 | binary.LittleEndian.PutUint32(body_bin, v) 543 | case uint64: 544 | value_type = VALUE_TYPE_UINT64 545 | body_bin = make([]byte, 8) 546 | binary.LittleEndian.PutUint64(body_bin, v) 547 | case float32: 548 | value_type = VALUE_TYPE_FLOAT32 549 | body_bin = Float32ToByte(v) 550 | case float64: 551 | value_type = VALUE_TYPE_FLOAT64 552 | body_bin = Float64ToByte(v) 553 | case string: 554 | value_type = VALUE_TYPE_STRING 555 | body_bin = []byte(v) 556 | case bool: 557 | value_type = VALUE_TYPE_BOOL 558 | body_bin = make([]byte, 1) 559 | if value.(bool) { 560 | body_bin[0] = uint8(1) 561 | } else { 562 | body_bin[0] = uint8(0) 563 | } 564 | default: //其它数据类型:map、struct等统一尝试转byte (性能不高) 565 | value_type = VALUE_TYPE_BIN 566 | b, err := StructToByte(value) 567 | 568 | if err != nil { 569 | return nil, value_type 570 | } 571 | 572 | body_bin = b 573 | } 574 | 575 | return body_bin, value_type 576 | } /*}}}*/ 577 | 578 | func (this *Connection) formatValueFromByte(value_type value_type_t, data []byte, format ...interface{}) (value interface{}, err error) { /*{{{*/ 579 | switch value_type { 580 | case VALUE_TYPE_BYTE: 581 | value = data 582 | case VALUE_TYPE_INT: 583 | data = bytes.Trim(data, " ") 584 | s := string(data) 585 | value, err = strconv.Atoi(s) 586 | case VALUE_TYPE_INT8: 587 | value = int8(data[0]) 588 | case VALUE_TYPE_INT16: 589 | value = int16(binary.LittleEndian.Uint16(data)) 590 | case VALUE_TYPE_INT32: 591 | value = int32(binary.LittleEndian.Uint32(data)) 592 | case VALUE_TYPE_INT64: 593 | value = int64(binary.LittleEndian.Uint64(data)) 594 | case VALUE_TYPE_UINT8: 595 | value = uint8(data[0]) 596 | case VALUE_TYPE_UINT16: 597 | value = binary.LittleEndian.Uint16(data) 598 | case VALUE_TYPE_UINT32: 599 | value = binary.LittleEndian.Uint32(data) 600 | case VALUE_TYPE_UINT64: 601 | value = binary.LittleEndian.Uint64(data) 602 | case VALUE_TYPE_FLOAT32: 603 | value = ByteToFloat32(data) 604 | case VALUE_TYPE_FLOAT64: 605 | value = ByteToFloat64(data) 606 | case VALUE_TYPE_STRING: 607 | value = string(data) 608 | case VALUE_TYPE_BOOL: 609 | if uint8(data[0]) == 1 { 610 | value = true 611 | } else { 612 | value = false 613 | } 614 | default: 615 | if len(format) == 0 { 616 | err = ErrNoFormat 617 | } else { 618 | if e := ByteToStruct(data, format[0]); e != nil { 619 | err = ErrInvalFormat 620 | } 621 | 622 | } 623 | } 624 | 625 | return value, err 626 | } /*}}}*/ 627 | 628 | func (this *Connection) Close() { /*{{{**/ 629 | if this.c != nil { 630 | this.c.Close() 631 | this.c = nil 632 | } 633 | } /*}}}*/ 634 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package memcache 2 | 3 | import "errors" 4 | 5 | //connection error 6 | var ( 7 | ErrBadConn = errors.New("Connect closed") 8 | ErrNotConn = errors.New("Can't connect to server") 9 | ) 10 | 11 | //memcached server returned error 12 | var ( 13 | ErrNotFound = errors.New("Key not found") 14 | ErrKeyExists = errors.New("Key exists") 15 | ErrBig = errors.New("Value too long") 16 | ErrInval = errors.New("Invalid arguments") 17 | ErrNotStord = errors.New("Item not stored") 18 | ErrDeltaBadVal = errors.New("Increment/Decrement on non-numberic value") 19 | ErrAuthError = errors.New("Auth error") 20 | ErrAuthContinue = errors.New("Auth continue") 21 | ErrCmd = errors.New("Unkown commond") 22 | ErrMem = errors.New("Out of memery") 23 | ErrUnkown = errors.New("Unkown error") 24 | ) 25 | 26 | var ( 27 | ErrInvalValue = errors.New("Unkown value type") 28 | ErrInvalFormat = errors.New("Invalid format struct") 29 | ErrNoFormat = errors.New("Format struct empty") 30 | ) 31 | -------------------------------------------------------------------------------- /example/example.go: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Memcached Client for Golang | 4 | +----------------------------------------------------------------------+ 5 | | https://github.com/pangudashu/memcache | 6 | +----------------------------------------------------------------------+ 7 | | @Author : pangudashu | 8 | | @Email: qp2624@163.com | 9 | | @Date: 2016-02-20 | 10 | +----------------------------------------------------------------------+ 11 | */ 12 | 13 | package main 14 | 15 | import ( 16 | "fmt" 17 | "github.com/pangudashu/memcache" 18 | "reflect" 19 | ) 20 | 21 | type SonT struct { 22 | Sid int 23 | } 24 | 25 | type User struct { 26 | Id int64 27 | Count float64 28 | Sex bool 29 | Name string 30 | List map[int]interface{} 31 | Son *SonT 32 | } 33 | 34 | func main() { 35 | 36 | //server配置 37 | s1 := &memcache.Server{Address: "127.0.0.1:12000", Weight: 50} 38 | s2 := &memcache.Server{Address: "127.0.0.1:12001", Weight: 20} 39 | s3 := &memcache.Server{Address: "127.0.0.1:12002", Weight: 20} 40 | s4 := &memcache.Server{Address: "127.0.0.1:12003", Weight: 10} 41 | 42 | //初始化连接池 43 | mc, err := memcache.NewMemcache([]*memcache.Server{s1, s2, s3, s4}) 44 | if err != nil { 45 | fmt.Println(err) 46 | return 47 | } 48 | 49 | //设置是否自动剔除无法连接的server,默认不开启(建议开启) 50 | //如果开启此选项被踢除的server如果恢复正常将会再次被加入server列表 51 | mc.SetRemoveBadServer(true) 52 | 53 | cmd_set(mc) 54 | cmd_append(mc) 55 | cmd_increment(mc) 56 | cmd_get(mc) 57 | 58 | //clear pool 59 | mc.Close() 60 | } 61 | 62 | func cmd_set(mc *memcache.Memcache) { 63 | /* 64 | +----------------------------------------------------------------------+ 65 | | Commond: Set | 66 | +----------------------------------------------------------------------+ 67 | | @param key string | 68 | | @param value interface{} | 69 | | @param expiration uint32 (*可选,默认值:0) 过期时间 | 70 | | @param cas uint64 (*可选,默认值:0) 数据版本号,用于cas原子操作 | 71 | +----------------------------------------------------------------------+ 72 | | @return result bool 操作成功:true 失败:false | 73 | | @return err error 操作成功:nil | 74 | +----------------------------------------------------------------------+ 75 | */ 76 | fmt.Println("\n+----------------------------------[Set]------------------------------------+\n") 77 | 78 | fmt.Println(mc.Set("pangudashu_bool", true, 0)) //bool 79 | fmt.Println(mc.Set("pangudashu_string", "[string]this is string~", 0)) //string 80 | fmt.Println(mc.Set("pangudashu_bytes", []byte("[byte]this is []byte~"), 0)) //[]byte 81 | fmt.Println(mc.Set("pangudashu_int", 1024, 0)) //int 82 | fmt.Println(mc.Set("pangudashu_int8", int8(-128), 0)) //int8 83 | fmt.Println(mc.Set("pangudashu_int16", int16(-3400), 0)) //int16 84 | fmt.Println(mc.Set("pangudashu_int32", int32(-429496729), 0)) //int32 85 | fmt.Println(mc.Set("pangudashu_int64", int64(-8589934591), 0)) //int64 86 | fmt.Println(mc.Set("pangudashu_uint8", uint8(130), 0)) //uint8 87 | fmt.Println(mc.Set("pangudashu_uint16", uint16(1300), 0)) //uint16 88 | fmt.Println(mc.Set("pangudashu_uint32", uint32(130000000), 0)) //uint32 89 | fmt.Println(mc.Set("pangudashu_uint64", uint64(1300000000000000), 0)) //uint64 90 | fmt.Println(mc.Set("pangudashu_float32", float32(3.14), 0)) //float32 91 | fmt.Println(mc.Set("pangudashu_float64", float64(3.1415926), 0)) //float64 92 | 93 | user := User{ 94 | Id: 7, 95 | Count: 10000.888, 96 | Sex: true, 97 | Name: "盘古大叔", 98 | List: make(map[int]interface{}), 99 | Son: &SonT{ 100 | Sid: 80009, 101 | }, 102 | } 103 | user.List[1001] = "北京1" 104 | user.List[1002] = "北京2" 105 | 106 | fmt.Println(mc.Set("pangudashu_struct", user, 0)) //struct 107 | } 108 | 109 | func cmd_append(mc *memcache.Memcache) { 110 | fmt.Println("\n+----------------------------------[Append/Prepend]------------------------------------+\n") 111 | 112 | fmt.Println(mc.Append("pangudashu_string", "<=后置字符串")) //string 113 | fmt.Println(mc.Prepend("pangudashu_string", "前置字符串=>")) //string 114 | } 115 | 116 | func cmd_increment(mc *memcache.Memcache) { 117 | fmt.Println("\n+----------------------------------[Increment/Decrement]------------------------------------+\n") 118 | //Increment/Decrement只能操作value类型为int的值,其它任何类型均无法操作。 119 | //原因是memcached中在Incr/Decr处理时首先使用strtoull将value转为unsigned long long再进行加减操作, 120 | //所以只有将数值存为字符串strtoull才能将其转为合法的数值 121 | 122 | fmt.Println(mc.Increment("pangudashu_int", 100)) //int 123 | fmt.Println(mc.Decrement("pangudashu_int", 50)) //int 124 | } 125 | 126 | func cmd_get(mc *memcache.Memcache) { 127 | /* 128 | +----------------------------------------------------------------------+ 129 | | Commond: Get | 130 | +----------------------------------------------------------------------+ 131 | | @param key string | 132 | | @param format_struct interface{} (*类型为struct时必选) | 133 | +----------------------------------------------------------------------+ 134 | | @return value interface{} 操作失败:nil | 135 | | @return cas uint64 操作成功:>0 失败:0 | 136 | | @return err error 操作成功:nil | 137 | +----------------------------------------------------------------------+ 138 | */ 139 | 140 | fmt.Println("\n+----------------------------------[Get]------------------------------------+\n") 141 | 142 | var val [15]interface{} 143 | var ver [15]uint64 144 | var err [15]error 145 | 146 | val[1], ver[1], err[1] = mc.Get("pangudashu_bool") //bool 147 | val[2], ver[2], err[2] = mc.Get("pangudashu_string") //string 148 | val[3], ver[3], err[3] = mc.Get("pangudashu_bytes") //[]byte 149 | val[4], ver[4], err[4] = mc.Get("pangudashu_int") //int 150 | val[5], ver[5], err[5] = mc.Get("pangudashu_int8") //int8 151 | val[6], ver[6], err[6] = mc.Get("pangudashu_int16") //int16 152 | val[7], ver[7], err[7] = mc.Get("pangudashu_int32") //int32 153 | val[8], ver[8], err[8] = mc.Get("pangudashu_int64") //int64 154 | val[9], ver[9], err[9] = mc.Get("pangudashu_uint8") //uint8 155 | val[10], ver[10], err[10] = mc.Get("pangudashu_uint16") //uint16 156 | val[11], ver[11], err[11] = mc.Get("pangudashu_uint32") //uint32 157 | val[12], ver[12], err[12] = mc.Get("pangudashu_uint64") //uint64 158 | val[13], ver[13], err[13] = mc.Get("pangudashu_float32") //float32 159 | val[14], ver[14], err[14] = mc.Get("pangudashu_float64") //float64 160 | 161 | for i := 1; i < len(val); i++ { 162 | fmt.Println("No.", i, "\n\t【value】", val[i], "\n\t【value type】", reflect.TypeOf(val[i]), "\n\t【cas】", ver[i], "\n\t【error】", err[i]) 163 | } 164 | 165 | var user User 166 | if _, _, e := mc.Get("pangudashu_struct", &user); e != nil { 167 | fmt.Println(e) 168 | } else { 169 | fmt.Println("获取存储的结构体:\n\t", user) 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /example/pangudashu-Vs-bradfitz.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | bradfitz_memcache "github.com/bradfitz/gomemcache/memcache" 5 | pangudashu_memcache "github.com/pangudashu/memcache" 6 | "log" 7 | "net/http" 8 | "time" 9 | ) 10 | 11 | var bradfitz_mc *bradfitz_memcache.Client 12 | var pangudashu_mc *pangudashu_memcache.Memcache 13 | 14 | func newPangudashuMemcache() { 15 | s1 := &pangudashu_memcache.Server{Address: "127.0.0.1:12000", InitConn: 10, Weight: 50, IdleTime: time.Second * 5} 16 | 17 | pangudashu_mc, _ = pangudashu_memcache.NewMemcache([]*pangudashu_memcache.Server{s1}) 18 | 19 | pangudashu_mc.SetTimeout(time.Second, time.Second, time.Second) 20 | pangudashu_mc.SetRemoveBadServer(true) 21 | log.Println(pangudashu_mc.Set("qp_1", "存储value支持golang基本数据类型:string、[]byte、int、int8、int16、int32、int64、bool、uint8、uint16、uint32、uint64、float32、float64、map、结构体,不需要单独转为string存储,Replace、Increment/Decrement、Delete、Append/Prepend命令支持cas原子操作")) 22 | } 23 | 24 | func newBradfitzMemcache() { 25 | bradfitz_mc = bradfitz_memcache.New("127.0.0.1:12000") 26 | 27 | item := &bradfitz_memcache.Item{ 28 | Key: "qp_2", 29 | Value: []byte("存储value支持golang基本数据类型:string、[]byte、int、int8、int16、int32、int64、bool、uint8、uint16、uint32、uint64、float32、float64、map、结构体,不需要单独转为string存储,Replace、Increment/Decrement、Delete、Append/Prepend命令支持cas原子操作"), 30 | } 31 | log.Println(bradfitz_mc.Set(item)) 32 | } 33 | 34 | func main() { 35 | newPangudashuMemcache() 36 | newBradfitzMemcache() 37 | 38 | http.HandleFunc("/pangudashu_foo", pangudashuHandler) 39 | http.HandleFunc("/bradfitz_foo", bradfitzHandler) 40 | 41 | log.Fatal(http.ListenAndServe(":9955", nil)) 42 | } 43 | 44 | func pangudashuHandler(w http.ResponseWriter, r *http.Request) { 45 | v, _, _ := pangudashu_mc.Get("qp_1") 46 | w.Write([]byte(v.(string))) 47 | } 48 | 49 | func bradfitzHandler(w http.ResponseWriter, r *http.Request) { 50 | v, _ := bradfitz_mc.Get("qp_2") 51 | w.Write(v.Value) 52 | } 53 | -------------------------------------------------------------------------------- /memcache.go: -------------------------------------------------------------------------------- 1 | package memcache 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | "time" 7 | ) 8 | 9 | type Memcache struct { 10 | nodes *Nodes 11 | manager *serverManager 12 | 13 | sync.RWMutex //保证操作nodes的原子性 14 | } 15 | 16 | type serverManager struct { 17 | serverList []*Server 18 | badServerNotice chan bool 19 | isRmBadServer bool 20 | } 21 | 22 | var ( 23 | badTryCnt = 4 24 | defaultMaxConn = 128 25 | defaultInitConn = 8 26 | defaultIdleTime = time.Hour * 2 27 | ) 28 | 29 | func NewMemcache(server_list []*Server) (mem *Memcache, err error) { /*{{{*/ 30 | if server_list == nil { 31 | return nil, errors.New("Server is nil or address is empty") 32 | } 33 | 34 | mem = &Memcache{} 35 | 36 | //create connect pool 37 | for _, server := range server_list { 38 | if server == nil || server.Address == "" { 39 | return nil, errors.New("Server is nil or address is empty") 40 | } 41 | if server.MaxConn == 0 { 42 | server.MaxConn = defaultMaxConn 43 | } 44 | if server.InitConn == 0 { 45 | server.InitConn = defaultInitConn 46 | } 47 | if server.IdleTime == 0 { 48 | server.IdleTime = defaultIdleTime 49 | } 50 | server.isActive = true 51 | } 52 | 53 | mem.manager = &serverManager{ 54 | serverList: server_list, 55 | } 56 | 57 | //create server hash node 58 | mem.nodes = createServerNode(server_list) 59 | return mem, nil 60 | } /*}}}*/ 61 | 62 | //设置是否移除不可用server 63 | func (this *Memcache) SetRemoveBadServer(option bool) { /*{{{*/ 64 | if option == false { 65 | return 66 | } 67 | this.manager.isRmBadServer = option 68 | this.manager.badServerNotice = make(chan bool) 69 | 70 | go this.monitorBadServer() 71 | } /*}}}*/ 72 | 73 | func (this *Memcache) SetTimeout(dial, read, write time.Duration) { /*{{{*/ 74 | dialTimeout = dial 75 | readTimeout = read 76 | writeTimeout = write 77 | } /*}}}*/ 78 | 79 | func (this *Memcache) Get(key string, format ...interface{}) (value interface{}, cas uint64, err error) { /*{{{*/ 80 | this.RLock() 81 | defer this.RUnlock() 82 | 83 | var res *response 84 | server := this.nodes.getServerByKey(key) 85 | if server == nil { 86 | return nil, 0, ErrNotConn 87 | } 88 | 89 | for i := 0; i < badTryCnt; i++ { 90 | conn, e := server.pool.Get() 91 | if e != nil && e == ErrNotConn { 92 | this.sendBadServerNotice() 93 | return nil, 0, e 94 | } 95 | 96 | res, err = conn.get(key, format...) 97 | 98 | if err == ErrBadConn { 99 | server.pool.Release(conn) 100 | } else { 101 | server.pool.Put(conn) 102 | break 103 | } 104 | } 105 | 106 | if res != nil { 107 | return res.body, res.header.cas, err 108 | } else { 109 | return nil, 0, err 110 | } 111 | } /*}}}*/ 112 | 113 | func (this *Memcache) Set(key string, value interface{}, expire ...uint32) (res bool, err error) { /*{{{*/ 114 | this.RLock() 115 | defer this.RUnlock() 116 | var timeout uint32 = 0 117 | 118 | if len(expire) > 0 { 119 | timeout = expire[0] 120 | } 121 | server := this.nodes.getServerByKey(key) 122 | if server == nil { 123 | return false, ErrNotConn 124 | } 125 | 126 | for i := 0; i < badTryCnt; i++ { 127 | conn, e := server.pool.Get() 128 | if e != nil && e == ErrNotConn { 129 | this.sendBadServerNotice() 130 | return false, e 131 | } 132 | 133 | res, err = conn.store(OP_SET, key, value, timeout, 0) 134 | 135 | if err == ErrBadConn { 136 | server.pool.Release(conn) 137 | } else { 138 | server.pool.Put(conn) 139 | break 140 | } 141 | } 142 | 143 | return res, err 144 | } /*}}}*/ 145 | 146 | func (this *Memcache) Add(key string, value interface{}, expire ...uint32) (res bool, err error) { /*{{{*/ 147 | this.RLock() 148 | defer this.RUnlock() 149 | var timeout uint32 = 0 150 | 151 | if len(expire) > 0 { 152 | timeout = expire[0] 153 | } 154 | server := this.nodes.getServerByKey(key) 155 | if server == nil { 156 | return false, ErrNotConn 157 | } 158 | 159 | for i := 0; i < badTryCnt; i++ { 160 | conn, e := server.pool.Get() 161 | if e != nil && e == ErrNotConn { 162 | this.sendBadServerNotice() 163 | return false, e 164 | } 165 | 166 | res, err = conn.store(OP_ADD, key, value, timeout, 0) 167 | if err == ErrBadConn { 168 | server.pool.Release(conn) 169 | } else { 170 | server.pool.Put(conn) 171 | break 172 | } 173 | } 174 | 175 | return res, err 176 | } /*}}}*/ 177 | 178 | func (this *Memcache) Replace(key string, value interface{}, args ...uint64) (res bool, err error) { /*{{{*/ 179 | this.RLock() 180 | defer this.RUnlock() 181 | var timeout uint32 = 0 182 | var cas uint64 = 0 183 | 184 | switch len(args) { 185 | case 1: 186 | timeout = uint32(args[0]) 187 | case 2: 188 | timeout = uint32(args[0]) 189 | cas = args[1] 190 | } 191 | server := this.nodes.getServerByKey(key) 192 | if server == nil { 193 | return false, ErrNotConn 194 | } 195 | 196 | for i := 0; i < badTryCnt; i++ { 197 | conn, e := server.pool.Get() 198 | if e != nil && e == ErrNotConn { 199 | this.sendBadServerNotice() 200 | return false, e 201 | } 202 | 203 | res, err = conn.store(OP_REPLACE, key, value, timeout, cas) 204 | if err == ErrBadConn { 205 | server.pool.Release(conn) 206 | } else { 207 | server.pool.Put(conn) 208 | break 209 | } 210 | } 211 | return res, err 212 | } /*}}}*/ 213 | 214 | func (this *Memcache) Delete(key string, cas ...uint64) (res bool, err error) { /*{{{*/ 215 | this.RLock() 216 | defer this.RUnlock() 217 | server := this.nodes.getServerByKey(key) 218 | if server == nil { 219 | return false, ErrNotConn 220 | } 221 | 222 | for i := 0; i < badTryCnt; i++ { 223 | conn, e := server.pool.Get() 224 | if e != nil && e == ErrNotConn { 225 | this.sendBadServerNotice() 226 | return false, e 227 | } 228 | 229 | res, err = conn.delete(key, cas...) 230 | 231 | if err == ErrBadConn { 232 | server.pool.Release(conn) 233 | } else { 234 | server.pool.Put(conn) 235 | break 236 | } 237 | } 238 | 239 | return res, err 240 | } /*}}}*/ 241 | 242 | func (this *Memcache) Increment(key string, args ...interface{}) (res bool, err error) { /*{{{*/ 243 | this.RLock() 244 | defer this.RUnlock() 245 | server := this.nodes.getServerByKey(key) 246 | if server == nil { 247 | return false, ErrNotConn 248 | } 249 | 250 | for i := 0; i < badTryCnt; i++ { 251 | conn, e := server.pool.Get() 252 | if e != nil && e == ErrNotConn { 253 | this.sendBadServerNotice() 254 | return false, e 255 | } 256 | 257 | res, err = conn.numberic(OP_INCREMENT, key, args...) 258 | 259 | if err == ErrBadConn { 260 | server.pool.Release(conn) 261 | } else { 262 | server.pool.Put(conn) 263 | break 264 | } 265 | } 266 | 267 | return res, err 268 | } /*}}}*/ 269 | 270 | func (this *Memcache) Decrement(key string, args ...interface{}) (res bool, err error) { /*{{{*/ 271 | this.RLock() 272 | defer this.RUnlock() 273 | server := this.nodes.getServerByKey(key) 274 | if server == nil { 275 | return false, ErrNotConn 276 | } 277 | 278 | for i := 0; i < badTryCnt; i++ { 279 | conn, e := server.pool.Get() 280 | if e != nil && e == ErrNotConn { 281 | this.sendBadServerNotice() 282 | return false, e 283 | } 284 | 285 | res, err = conn.numberic(OP_DECREMENT, key, args...) 286 | 287 | if err == ErrBadConn { 288 | server.pool.Release(conn) 289 | } else { 290 | server.pool.Put(conn) 291 | break 292 | } 293 | } 294 | 295 | return res, err 296 | } /*}}}*/ 297 | 298 | func (this *Memcache) Append(key string, value string, cas ...uint64) (res bool, err error) { /*{{{*/ 299 | this.RLock() 300 | defer this.RUnlock() 301 | server := this.nodes.getServerByKey(key) 302 | if server == nil { 303 | return false, ErrNotConn 304 | } 305 | 306 | for i := 0; i < badTryCnt; i++ { 307 | conn, e := server.pool.Get() 308 | if e != nil && e == ErrNotConn { 309 | this.sendBadServerNotice() 310 | return false, e 311 | } 312 | 313 | res, err = conn.appends(OP_APPEND, key, value, cas...) 314 | 315 | if err == ErrBadConn { 316 | server.pool.Release(conn) 317 | } else { 318 | server.pool.Put(conn) 319 | break 320 | } 321 | } 322 | 323 | return res, err 324 | } /*}}}*/ 325 | 326 | func (this *Memcache) Prepend(key string, value string, cas ...uint64) (res bool, err error) { /*{{{*/ 327 | this.RLock() 328 | defer this.RUnlock() 329 | server := this.nodes.getServerByKey(key) 330 | if server == nil { 331 | return false, ErrNotConn 332 | } 333 | 334 | for i := 0; i < badTryCnt; i++ { 335 | conn, e := server.pool.Get() 336 | if e != nil && e == ErrNotConn { 337 | this.sendBadServerNotice() 338 | return false, e 339 | } 340 | 341 | res, err = conn.appends(OP_PREPEND, key, value, cas...) 342 | 343 | if err == ErrBadConn { 344 | server.pool.Release(conn) 345 | } else { 346 | server.pool.Put(conn) 347 | break 348 | } 349 | } 350 | 351 | return res, err 352 | } /*}}}*/ 353 | 354 | func (this *Memcache) Flush(server *Server, delay ...uint32) (res bool, err error) { /*{{{*/ 355 | for i := 0; i < badTryCnt; i++ { 356 | conn, e := server.pool.Get() 357 | if e != nil && e == ErrNotConn { 358 | this.sendBadServerNotice() 359 | return false, e 360 | } 361 | 362 | res, err = conn.flush(delay...) 363 | 364 | if err == ErrBadConn { 365 | server.pool.Release(conn) 366 | } else { 367 | server.pool.Put(conn) 368 | break 369 | } 370 | } 371 | return res, err 372 | } /*}}}*/ 373 | 374 | func (this *Memcache) Version(server *Server) (v string, err error) { /*{{{*/ 375 | for i := 0; i < badTryCnt; i++ { 376 | conn, e := server.pool.Get() 377 | if e != nil && e == ErrNotConn { 378 | this.sendBadServerNotice() 379 | return "", e 380 | } 381 | 382 | v, err = conn.version() 383 | 384 | if err == ErrBadConn { 385 | server.pool.Release(conn) 386 | } else { 387 | server.pool.Put(conn) 388 | break 389 | } 390 | } 391 | 392 | return v, err 393 | } /*}}}*/ 394 | 395 | func (this *Memcache) sendBadServerNotice() { /*{{{*/ 396 | if this.manager.isRmBadServer == false { 397 | return 398 | } 399 | 400 | select { 401 | case this.manager.badServerNotice <- true: 402 | default: 403 | } 404 | } /*}}}*/ 405 | 406 | func (this *Memcache) monitorBadServer() { /*{{{*/ 407 | for { 408 | select { 409 | case <-this.manager.badServerNotice: 410 | this.doDealBadServer() 411 | case <-time.After(time.Second * 120): 412 | this.doDealBadServer() 413 | } 414 | } 415 | } /*}}}*/ 416 | 417 | func (this *Memcache) doDealBadServer() { /*{{{*/ 418 | var res map[*Server]chan bool 419 | var isReload bool = false 420 | 421 | res = make(map[*Server]chan bool, len(this.manager.serverList)) 422 | for _, s := range this.manager.serverList { 423 | res[s] = make(chan bool, 1) 424 | go this.checkServerActive(s, res[s]) 425 | } 426 | 427 | for s, ch := range res { 428 | switch <-ch { 429 | case true: 430 | if s.isActive == false { 431 | s.pool.Close() 432 | s.pool = nil 433 | isReload = true 434 | } 435 | s.isActive = true 436 | case false: 437 | if s.isActive == true { 438 | isReload = true 439 | } 440 | s.isActive = false 441 | } 442 | 443 | } 444 | 445 | if isReload == false { 446 | return 447 | } 448 | //有server状态发生变化,重新生成node 449 | new_server_list := make([]*Server, 0) 450 | for _, s := range this.manager.serverList { 451 | if s.isActive == true { 452 | new_server_list = append(new_server_list, s) 453 | } 454 | } 455 | //create server hash node 456 | new_nodes := createServerNode(new_server_list) 457 | 458 | this.Lock() 459 | this.nodes = new_nodes 460 | this.Unlock() 461 | } /*}}}*/ 462 | 463 | func (this *Memcache) checkServerActive(server *Server, ch chan bool) { /*{{{*/ 464 | conn, e := server.pool.Get() 465 | if e != nil && e == ErrNotConn { 466 | //can't connect to server 467 | ch <- false 468 | return 469 | } 470 | 471 | res, err := conn.noop() 472 | 473 | if err == ErrBadConn { 474 | server.pool.Release(conn) 475 | } else { 476 | server.pool.Put(conn) 477 | } 478 | 479 | if err == nil && res == true { 480 | ch <- true 481 | return 482 | } 483 | 484 | //noop failed ,then try dial server 485 | if new_connection, err := connect(server.Address); err == ErrNotConn { 486 | ch <- false 487 | } else { 488 | ch <- true 489 | new_connection.Close() 490 | } 491 | } /*}}}*/ 492 | 493 | func (this *Memcache) Close() { 494 | for _, s := range this.manager.serverList { 495 | s.pool.Close() 496 | } 497 | } 498 | -------------------------------------------------------------------------------- /pool.go: -------------------------------------------------------------------------------- 1 | package memcache 2 | 3 | import ( 4 | "sync" 5 | "time" 6 | ) 7 | 8 | //连接池 9 | type ConnectionPool struct { 10 | pool chan *Connection 11 | address string 12 | maxCnt int 13 | totalCnt int 14 | idleTime time.Duration 15 | 16 | sync.Mutex 17 | } 18 | 19 | func open(address string, maxCnt int, initCnt int, idelTime time.Duration) (pool *ConnectionPool) { 20 | pool = &ConnectionPool{ 21 | pool: make(chan *Connection, maxCnt), 22 | address: address, 23 | maxCnt: maxCnt, 24 | idleTime: idelTime, 25 | } 26 | 27 | for i := 0; i < initCnt; i++ { 28 | conn, err := connect(address) 29 | if err != nil { 30 | continue 31 | } 32 | pool.totalCnt++ 33 | pool.pool <- conn 34 | } 35 | return pool 36 | } 37 | 38 | func (this *ConnectionPool) Get() (conn *Connection, err error) { 39 | for { 40 | conn, err = this.get() 41 | 42 | if err != nil { 43 | return nil, err 44 | } 45 | 46 | if conn.lastActiveTime.Add(this.idleTime).UnixNano() > time.Now().UnixNano() { 47 | break 48 | } else { 49 | this.Release(conn) 50 | } 51 | } 52 | conn.lastActiveTime = time.Now() 53 | return conn, err 54 | } 55 | 56 | func (this *ConnectionPool) get() (conn *Connection, err error) { 57 | select { 58 | case conn = <-this.pool: 59 | return conn, nil 60 | default: 61 | } 62 | 63 | this.Lock() 64 | defer this.Unlock() 65 | 66 | if this.totalCnt >= this.maxCnt { 67 | //阻塞,直到有可用连接 68 | conn = <-this.pool 69 | return conn, nil 70 | } 71 | 72 | //create new connect 73 | conn, err = connect(this.address) 74 | if err != nil { 75 | return nil, err 76 | } 77 | this.totalCnt++ 78 | 79 | return conn, nil 80 | } 81 | 82 | func (this *ConnectionPool) Put(conn *Connection) { 83 | if conn == nil { 84 | return 85 | } 86 | 87 | this.pool <- conn 88 | } 89 | 90 | func (this *ConnectionPool) Release(conn *Connection) { 91 | conn.Close() 92 | this.Lock() 93 | this.totalCnt-- 94 | this.Unlock() 95 | } 96 | 97 | //clear pool 98 | func (this *ConnectionPool) Close() { 99 | for i := 0; i < len(this.pool); i++ { 100 | conn := <-this.pool 101 | conn.Close() 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /protocal.go: -------------------------------------------------------------------------------- 1 | package memcache 2 | 3 | type magic_t uint8 4 | 5 | const ( 6 | MAGIC_REQ magic_t = 0x80 7 | MAGIC_RES magic_t = 0x81 8 | ) 9 | 10 | type opcode_t uint8 11 | 12 | const ( 13 | OP_GET opcode_t = 0x00 14 | OP_SET opcode_t = 0x01 15 | OP_ADD opcode_t = 0x02 16 | OP_REPLACE opcode_t = 0x03 17 | OP_DELETE opcode_t = 0x04 18 | OP_INCREMENT opcode_t = 0x05 19 | OP_DECREMENT opcode_t = 0x06 20 | OP_FLUSH opcode_t = 0x08 21 | OP_NOOP opcode_t = 0x0a 22 | OP_VERSION opcode_t = 0x0b 23 | OP_GETK opcode_t = 0x0c 24 | OP_APPEND opcode_t = 0x0e 25 | OP_PREPEND opcode_t = 0x0f 26 | ) 27 | 28 | type status_t uint16 29 | 30 | const ( 31 | STATUS_SUCCESS status_t = 0x00 //no error 32 | STATUS_KEY_ENOENT status_t = 0x01 //Key not found 33 | STATUS_KEY_EEXISTS status_t = 0x02 //Key exists 34 | STATUS_E2BIG status_t = 0x03 //Value too long 35 | STATUS_EINVAL status_t = 0x04 //Invalid arguments 36 | STATUS_NOT_STORED status_t = 0x05 //Item not stored 37 | STATUS_DELTA_BADVAL status_t = 0x06 //Incr/Decr on non-numberic value 38 | STATUS_AUTH_ERROR status_t = 0x20 // 39 | STATUS_AUTH_CONTINUE status_t = 0x21 // 40 | STATUS_UNKNOWN_COMMAND status_t = 0x81 //Unkown opcode 41 | STATUS_ENOMEM status_t = 0x82 //Out of memery 42 | ) 43 | 44 | type type_t uint8 45 | 46 | const ( 47 | TYPE_RAW_BYTES type_t = 0x00 48 | ) 49 | 50 | //自定义flags 51 | type value_type_t uint32 52 | 53 | const ( 54 | VALUE_TYPE_INT value_type_t = 0x00000000 //0 /*change Type 0 => int to sure memcached can right deal Incr/Decr*/ 55 | VALUE_TYPE_BIN value_type_t = 0x00000001 //1 56 | VALUE_TYPE_BYTE value_type_t = 0x00000002 //2 57 | VALUE_TYPE_INT8 value_type_t = 0x00000004 //4 58 | VALUE_TYPE_INT16 value_type_t = 0x00000008 //8 59 | VALUE_TYPE_INT32 value_type_t = 0x00000010 //16 60 | VALUE_TYPE_INT64 value_type_t = 0x00000020 //32 61 | VALUE_TYPE_UINT8 value_type_t = 0x00000040 //64 62 | VALUE_TYPE_UINT16 value_type_t = 0x00000080 //128 63 | VALUE_TYPE_UINT32 value_type_t = 0x00000100 //256 64 | VALUE_TYPE_UINT64 value_type_t = 0x00000200 //512 65 | VALUE_TYPE_FLOAT32 value_type_t = 0x00000400 //1024 66 | VALUE_TYPE_FLOAT64 value_type_t = 0x00000800 //2048 67 | VALUE_TYPE_STRING value_type_t = 0x00001000 //4096 68 | VALUE_TYPE_BOOL value_type_t = 0x00002000 //8192 69 | ) 70 | 71 | //request header 72 | type request_header struct { 73 | magic magic_t 74 | opcode opcode_t 75 | keylen uint16 76 | extlen uint8 77 | datatype type_t 78 | status status_t 79 | bodylen uint32 80 | opaque uint32 81 | cas uint64 82 | } 83 | 84 | //response header 85 | type response_header struct { 86 | magic magic_t 87 | opcode opcode_t 88 | keylen uint16 89 | extlen uint8 90 | datatype type_t 91 | status status_t 92 | bodylen uint32 93 | opaque uint32 94 | cas uint64 95 | } 96 | 97 | type response struct { 98 | header *response_header 99 | bodyByte []byte 100 | body interface{} 101 | } 102 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package memcache 2 | 3 | import ( 4 | "crypto/md5" 5 | "hash/crc32" 6 | "math" 7 | "strconv" 8 | "time" 9 | ) 10 | 11 | const ( 12 | VIRTUAL_NODE_PER = 160 13 | ) 14 | 15 | type Server struct { 16 | Address string 17 | Weight int 18 | MaxConn int 19 | InitConn int 20 | IdleTime time.Duration 21 | isActive bool 22 | pool *ConnectionPool 23 | nodeList []uint32 24 | } 25 | 26 | type Nodes struct { 27 | serverNodeMap map[uint32]*Server //hask_key => Server 28 | nodeList []uint32 29 | nodeCnt int 30 | } 31 | 32 | func createServerNode(servers []*Server) *Nodes { /*{{{*/ 33 | nodes := &Nodes{} 34 | 35 | total_weight := 0 36 | for _, v := range servers { 37 | if v.Weight > 0 { 38 | total_weight += v.Weight 39 | } else { 40 | v.Weight = 1 41 | total_weight++ 42 | } 43 | } 44 | 45 | total_node := (VIRTUAL_NODE_PER / 4) * len(servers) 46 | 47 | nodes.serverNodeMap = make(map[uint32]*Server, (VIRTUAL_NODE_PER+4)*len(servers)) 48 | nodes.nodeList = make([]uint32, (VIRTUAL_NODE_PER+4)*len(servers)) 49 | 50 | cnt := 0 51 | for _, s := range servers { 52 | //create connection pool 53 | if s.pool == nil { 54 | s.pool = open(s.Address, s.MaxConn, s.InitConn, s.IdleTime) 55 | } 56 | 57 | //计算实际分配的虚拟节点数 58 | node_cnt := int(math.Ceil(float64(total_node) * (float64(s.Weight) / float64(total_weight)))) 59 | 60 | s.nodeList = make([]uint32, node_cnt*4) 61 | 62 | for i := 0; i < node_cnt; i++ { 63 | node_position := createKetamaHash(s.Address, i) 64 | for j, node := range node_position { 65 | nodes.serverNodeMap[node] = s 66 | nodes.nodeList[cnt] = node 67 | 68 | s.nodeList[i*4+j] = node 69 | cnt++ 70 | } 71 | } 72 | } 73 | quickSort(nodes.nodeList, 0, len(nodes.nodeList)-1) 74 | 75 | bad_node := 0 76 | for _, v := range nodes.nodeList { 77 | if v == 0 { 78 | bad_node++ 79 | } else { 80 | break 81 | } 82 | } 83 | if bad_node > 0 { 84 | nodes.nodeList = nodes.nodeList[bad_node:] 85 | } 86 | 87 | nodes.nodeCnt = len(nodes.nodeList) 88 | 89 | return nodes 90 | } /*}}}*/ 91 | 92 | //每个address节对应4个虚拟node 93 | func createKetamaHash(key string, i int) []uint32 { /*{{{*/ 94 | addr := key + "#" + strconv.Itoa(i) 95 | 96 | code_byte := md5.Sum([]byte(addr)) 97 | 98 | hashs := make([]uint32, 4) 99 | for n := 0; n < 4; n++ { 100 | hashs[n] = (uint32(code_byte[3+n*4]&0xFF) << 24) | (uint32(code_byte[2+n*4]&0xFF) << 16) | (uint32(code_byte[1+n*4]&0xFF) << 8) | uint32(code_byte[0+n*4]&0xFF) 101 | } 102 | return hashs 103 | } /*}}}*/ 104 | 105 | func quickSort(array []uint32, low, high int) { /*{{{*/ 106 | if low >= high { 107 | return 108 | } 109 | 110 | key := array[low] 111 | tmpLow := low 112 | tmpHigh := high 113 | for { 114 | for array[tmpHigh] > key { 115 | tmpHigh-- 116 | } 117 | for array[tmpLow] <= key && tmpLow < tmpHigh { 118 | tmpLow++ 119 | } 120 | 121 | if tmpLow >= tmpHigh { 122 | break 123 | } 124 | array[tmpLow], array[tmpHigh] = array[tmpHigh], array[tmpLow] 125 | } 126 | //将key放到中间 127 | array[tmpLow], array[low] = array[low], array[tmpLow] 128 | 129 | quickSort(array, low, tmpLow-1) 130 | quickSort(array, tmpLow+1, high) 131 | } /*}}}*/ 132 | 133 | var hash_crc32_table = crc32.MakeTable(0xFFFFFFFF) 134 | 135 | func (nodes *Nodes) getServerByKey(key string) *Server { /*{{{*/ 136 | hash_key := crc32.Checksum([]byte(key), hash_crc32_table) 137 | 138 | node := nodes.getNodeByHash(hash_key) 139 | 140 | if node == 0 { 141 | return nil 142 | } else { 143 | return nodes.serverNodeMap[node] 144 | } 145 | } /*}}}*/ 146 | 147 | //折半查找 148 | func (nodes *Nodes) getNodeByHash(hash_key uint32) (node uint32) { /*{{{*/ 149 | if nodes.nodeCnt < 1 { 150 | return 0 151 | } 152 | //大于最后一个节点分配给第一个节点 153 | if hash_key > nodes.nodeList[nodes.nodeCnt-1] { 154 | return nodes.nodeList[0] 155 | } 156 | 157 | left := 0 158 | right := nodes.nodeCnt 159 | index := 0 160 | for { 161 | mid_index := (right + left) / 2 162 | if hash_key == nodes.nodeList[mid_index] { 163 | index = mid_index 164 | break 165 | } 166 | if hash_key > nodes.nodeList[mid_index] { 167 | left = mid_index 168 | } else { 169 | right = mid_index 170 | } 171 | 172 | if right-left == 1 { 173 | index = right 174 | break 175 | } 176 | } 177 | return nodes.nodeList[index] 178 | } /*}}}*/ 179 | -------------------------------------------------------------------------------- /tools.go: -------------------------------------------------------------------------------- 1 | package memcache 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "encoding/gob" 7 | "errors" 8 | "math" 9 | ) 10 | 11 | //float 32/64 -> []byte 12 | func Float32ToByte(float float32) []byte { 13 | bits := math.Float32bits(float) 14 | bytes := make([]byte, 4) 15 | binary.LittleEndian.PutUint32(bytes, bits) 16 | 17 | return bytes 18 | } 19 | 20 | func ByteToFloat32(bytes []byte) float32 { 21 | bits := binary.LittleEndian.Uint32(bytes) 22 | 23 | return math.Float32frombits(bits) 24 | } 25 | 26 | func Float64ToByte(float float64) []byte { 27 | bits := math.Float64bits(float) 28 | bytes := make([]byte, 8) 29 | binary.LittleEndian.PutUint64(bytes, bits) 30 | 31 | return bytes 32 | } 33 | 34 | func ByteToFloat64(bytes []byte) float64 { 35 | bits := binary.LittleEndian.Uint64(bytes) 36 | 37 | return math.Float64frombits(bits) 38 | } 39 | 40 | func StructToByte(value interface{}) (b []byte, err error) { 41 | var buf bytes.Buffer 42 | encoder := gob.NewEncoder(&buf) 43 | if err = encoder.Encode(value); err != nil { 44 | return nil, errors.New("encode fail") 45 | } 46 | 47 | return buf.Bytes(), nil 48 | } 49 | 50 | func ByteToStruct(b []byte, value interface{}) (err error) { 51 | buf := bytes.NewBuffer(b) 52 | 53 | decoder := gob.NewDecoder(buf) 54 | if err = decoder.Decode(value); err != nil { 55 | return errors.New("decode fail") 56 | } 57 | 58 | return nil 59 | } 60 | --------------------------------------------------------------------------------