github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/go-hbase/client.go (about) 1 package hbase 2 3 import ( 4 "bytes" 5 "crypto/md5" 6 "encoding/binary" 7 "encoding/hex" 8 "fmt" 9 "sync" 10 "time" 11 12 pb "github.com/insionng/yougam/libraries/golang/protobuf/proto" 13 "github.com/insionng/yougam/libraries/juju/errors" 14 "github.com/insionng/yougam/libraries/ngaut/go-zookeeper/zk" 15 "github.com/insionng/yougam/libraries/ngaut/log" 16 "github.com/insionng/yougam/libraries/pingcap/go-hbase/proto" 17 ) 18 19 const ( 20 zkRootRegionPath = "/meta-region-server" 21 zkMasterAddrPath = "/master" 22 23 magicHeadByte = 0xff 24 magicHeadSize = 1 25 idLengthSize = 4 26 md5HexSize = 32 27 servernameSeparator = "," 28 rpcTimeout = 30000 29 pingTimeout = 30000 30 callTimeout = 5000 31 defaultMaxActionRetries = 3 32 // Some operations can take a long time such as disable of big table. 33 // numRetries is for 'normal' stuff... Multiply by this factor when 34 // want to wait a long time. 35 retryLongerMultiplier = 31 36 socketDefaultRetryWaitMs = 200 37 defaultRetryWaitMs = 100 38 // always >= any unix timestamp(hbase version) 39 beyondMaxTimestamp = "99999999999999" 40 ) 41 42 var ( 43 hbaseHeaderBytes []byte = []byte("HBas") 44 metaTableName []byte = []byte("hbase:meta") 45 metaRegionName []byte = []byte("hbase:meta,,1") 46 ) 47 48 var retryPauseTime = []int64{1, 2, 3, 5, 10, 20, 40, 100, 100, 100, 100, 200, 200} 49 50 type RegionInfo struct { 51 Server string 52 StartKey []byte 53 EndKey []byte 54 Name string 55 Ts string 56 TableNamespace string 57 TableName string 58 Offline bool 59 Split bool 60 } 61 62 type tableInfo struct { 63 tableName string 64 families []string 65 } 66 67 // export client interface 68 type HBaseClient interface { 69 Get(tbl string, g *Get) (*ResultRow, error) 70 Put(tbl string, p *Put) (bool, error) 71 Delete(tbl string, d *Delete) (bool, error) 72 TableExists(tbl string) (bool, error) 73 DropTable(t string) error 74 DisableTable(t string) error 75 EnableTable(t string) error 76 CreateTable(t *TableDescriptor, splits [][]byte) error 77 ServiceCall(table string, call *CoprocessorServiceCall) (*proto.CoprocessorServiceResponse, error) 78 LocateRegion(table, row []byte, useCache bool) (*RegionInfo, error) 79 GetRegions(table []byte, useCache bool) ([]*RegionInfo, error) 80 Split(tblOrRegion, splitPoint string) error 81 CleanRegionCache(table []byte) 82 CleanAllRegionCache() 83 Close() error 84 } 85 86 // hbase client implemetation 87 var _ HBaseClient = (*client)(nil) 88 89 type client struct { 90 mu sync.RWMutex // for read/update region info 91 zkClient *zk.Conn 92 zkHosts []string 93 zkRoot string 94 prefetched map[string]bool 95 cachedConns map[string]*connection 96 cachedRegionInfo map[string]map[string]*RegionInfo 97 maxRetries int 98 rootServerName *proto.ServerName 99 masterServerName *proto.ServerName 100 } 101 102 func serverNameToAddr(server *proto.ServerName) string { 103 return fmt.Sprintf("%s:%d", server.GetHostName(), server.GetPort()) 104 } 105 106 func cachedConnKey(addr string, srvType ServiceType) string { 107 return fmt.Sprintf("%s|%d", addr, srvType) 108 } 109 110 func NewClient(zkHosts []string, zkRoot string) (HBaseClient, error) { 111 cl := &client{ 112 zkHosts: zkHosts, 113 zkRoot: zkRoot, 114 cachedConns: make(map[string]*connection), 115 cachedRegionInfo: make(map[string]map[string]*RegionInfo), 116 prefetched: make(map[string]bool), 117 maxRetries: defaultMaxActionRetries, 118 } 119 120 err := cl.init() 121 if err != nil { 122 return nil, errors.Trace(err) 123 } 124 125 return cl, nil 126 } 127 128 func (c *client) decodeMeta(data []byte) (*proto.ServerName, error) { 129 if data[0] != magicHeadByte { 130 return nil, errors.New("unknown packet") 131 } 132 133 var n int32 134 err := binary.Read(bytes.NewBuffer(data[1:]), binary.BigEndian, &n) 135 if err != nil { 136 return nil, errors.Trace(err) 137 } 138 139 dataOffset := magicHeadSize + idLengthSize + int(n) 140 data = data[(dataOffset + 4):] 141 142 var mrs proto.MetaRegionServer 143 err = pb.Unmarshal(data, &mrs) 144 if err != nil { 145 return nil, errors.Trace(err) 146 } 147 148 return mrs.GetServer(), nil 149 } 150 151 // init and get root region server addr and master addr 152 func (c *client) init() error { 153 zkclient, _, err := zk.Connect(c.zkHosts, time.Second*30) 154 if err != nil { 155 return errors.Trace(err) 156 } 157 c.zkClient = zkclient 158 159 res, _, _, err := c.zkClient.GetW(c.zkRoot + zkRootRegionPath) 160 if err != nil { 161 return errors.Trace(err) 162 } 163 164 c.rootServerName, err = c.decodeMeta(res) 165 if err != nil { 166 return errors.Trace(err) 167 } 168 169 log.Debug("connect root region server...", c.rootServerName) 170 serverAddr := serverNameToAddr(c.rootServerName) 171 conn, err := newConnection(serverAddr, ClientService) 172 if err != nil { 173 return errors.Trace(err) 174 } 175 176 // Set buffered regionserver conn. 177 cachedKey := cachedConnKey(serverAddr, ClientService) 178 c.cachedConns[cachedKey] = conn 179 180 res, _, _, err = c.zkClient.GetW(c.zkRoot + zkMasterAddrPath) 181 if err != nil { 182 return errors.Trace(err) 183 } 184 185 c.masterServerName, err = c.decodeMeta(res) 186 if err != nil { 187 return errors.Trace(err) 188 } 189 190 return nil 191 } 192 193 // get connection 194 func (c *client) getConn(addr string, srvType ServiceType) (*connection, error) { 195 connKey := cachedConnKey(addr, srvType) 196 c.mu.RLock() 197 conn, ok := c.cachedConns[connKey] 198 c.mu.RUnlock() 199 200 if ok { 201 return conn, nil 202 } 203 204 var err error 205 conn, err = newConnection(addr, srvType) 206 if err != nil { 207 return nil, errors.Errorf("create new connection failed - %v", errors.ErrorStack(err)) 208 } 209 c.mu.Lock() 210 c.cachedConns[connKey] = conn 211 c.mu.Unlock() 212 return conn, nil 213 } 214 215 func (c *client) getAdminConn(addr string) (*connection, error) { 216 return c.getConn(addr, AdminService) 217 } 218 219 func (c *client) getClientConn(addr string) (*connection, error) { 220 return c.getConn(addr, ClientService) 221 } 222 223 func (c *client) getMasterConn() (*connection, error) { 224 return c.getConn(serverNameToAddr(c.masterServerName), MasterService) 225 } 226 227 func (c *client) doAction(conn *connection, req pb.Message) (chan pb.Message, error) { 228 cl := newCall(req) 229 err := conn.call(cl) 230 if err != nil { 231 return nil, errors.Trace(err) 232 } 233 234 return cl.responseCh, nil 235 } 236 237 func (c *client) adminAction(req pb.Message) (chan pb.Message, error) { 238 conn, err := c.getMasterConn() 239 if err != nil { 240 return nil, errors.Trace(err) 241 } 242 return c.doAction(conn, req) 243 } 244 245 func (c *client) regionAction(addr string, req pb.Message) (chan pb.Message, error) { 246 conn, err := c.getAdminConn(addr) 247 if err != nil { 248 return nil, errors.Trace(err) 249 } 250 return c.doAction(conn, req) 251 } 252 253 // http://stackoverflow.com/questions/27602013/correct-way-to-get-region-name-by-using-hbase-api 254 func (c *client) createRegionName(table, startKey []byte, id string, newFormat bool) []byte { 255 if len(startKey) == 0 { 256 startKey = make([]byte, 1) 257 } 258 259 b := bytes.Join([][]byte{table, startKey, []byte(id)}, []byte{','}) 260 261 if newFormat { 262 m := md5.Sum(b) 263 mhex := []byte(hex.EncodeToString(m[:])) 264 b = append(bytes.Join([][]byte{b, mhex}, []byte{'.'}), '.') 265 } 266 267 return b 268 } 269 270 func (c *client) parseRegion(rr *ResultRow) (*RegionInfo, error) { 271 regionInfoCol, ok := rr.Columns["info:regioninfo"] 272 if !ok { 273 return nil, errors.Errorf("Unable to parse region location (no regioninfo column): %#v", rr) 274 } 275 276 offset := bytes.Index(regionInfoCol.Value, []byte("PBUF")) + 4 277 regionInfoBytes := regionInfoCol.Value[offset:] 278 279 var info proto.RegionInfo 280 err := pb.Unmarshal(regionInfoBytes, &info) 281 if err != nil { 282 return nil, errors.Errorf("Unable to parse region location: %#v", err) 283 } 284 285 ri := &RegionInfo{ 286 StartKey: info.GetStartKey(), 287 EndKey: info.GetEndKey(), 288 Name: bytes.NewBuffer(rr.Row).String(), 289 TableNamespace: string(info.GetTableName().GetNamespace()), 290 TableName: string(info.GetTableName().GetQualifier()), 291 Offline: info.GetOffline(), 292 Split: info.GetSplit(), 293 } 294 295 if v, ok := rr.Columns["info:server"]; ok { 296 ri.Server = string(v.Value) 297 } 298 299 return ri, nil 300 } 301 302 func (c *client) getMetaRegion() *RegionInfo { 303 return &RegionInfo{ 304 StartKey: []byte{}, 305 EndKey: []byte{}, 306 Name: string(metaRegionName), 307 Server: serverNameToAddr(c.rootServerName), 308 } 309 } 310 311 func (c *client) getCachedLocation(table, row []byte) *RegionInfo { 312 c.mu.RLock() 313 defer c.mu.RUnlock() 314 315 tableStr := string(table) 316 if regions, ok := c.cachedRegionInfo[tableStr]; ok { 317 for _, region := range regions { 318 if (len(region.EndKey) == 0 || 319 bytes.Compare(row, region.EndKey) < 0) && 320 (len(region.StartKey) == 0 || 321 bytes.Compare(row, region.StartKey) >= 0) { 322 return region 323 } 324 } 325 } 326 327 return nil 328 } 329 330 func (c *client) updateRegionCache(table []byte, region *RegionInfo) { 331 c.mu.Lock() 332 defer c.mu.Unlock() 333 334 tableStr := string(table) 335 if _, ok := c.cachedRegionInfo[tableStr]; !ok { 336 c.cachedRegionInfo[tableStr] = make(map[string]*RegionInfo) 337 } 338 c.cachedRegionInfo[tableStr][region.Name] = region 339 } 340 341 func (c *client) CleanRegionCache(table []byte) { 342 c.mu.Lock() 343 defer c.mu.Unlock() 344 delete(c.cachedRegionInfo, string(table)) 345 } 346 347 func (c *client) CleanAllRegionCache() { 348 c.mu.Lock() 349 defer c.mu.Unlock() 350 c.cachedRegionInfo = map[string]map[string]*RegionInfo{} 351 } 352 353 func (c *client) LocateRegion(table, row []byte, useCache bool) (*RegionInfo, error) { 354 // If user wants to locate metaregion, just return it. 355 if bytes.Equal(table, metaTableName) { 356 return c.getMetaRegion(), nil 357 } 358 359 // Try to get location from cache. 360 if useCache { 361 if r := c.getCachedLocation(table, row); r != nil { 362 return r, nil 363 } 364 } 365 366 // If cache missed or not using cache, try to get and update region info. 367 metaRegion := c.getMetaRegion() 368 conn, err := c.getClientConn(metaRegion.Server) 369 if err != nil { 370 return nil, errors.Trace(err) 371 } 372 373 regionRow := c.createRegionName(table, row, beyondMaxTimestamp, true) 374 375 call := newCall(&proto.GetRequest{ 376 Region: &proto.RegionSpecifier{ 377 Type: proto.RegionSpecifier_REGION_NAME.Enum(), 378 Value: metaRegionName, 379 }, 380 Get: &proto.Get{ 381 Row: regionRow, 382 Column: []*proto.Column{&proto.Column{ 383 Family: []byte("info"), 384 }}, 385 ClosestRowBefore: pb.Bool(true), 386 }, 387 }) 388 389 err = conn.call(call) 390 if err != nil { 391 return nil, errors.Trace(err) 392 } 393 394 response := <-call.responseCh 395 396 switch r := response.(type) { 397 case *proto.GetResponse: 398 res := r.GetResult() 399 if res == nil { 400 return nil, errors.Errorf("Empty region: [table=%s] [row=%q] [region_row=%q]", table, row, regionRow) 401 } 402 403 rr := NewResultRow(res) 404 region, err := c.parseRegion(rr) 405 if err != nil { 406 return nil, errors.Trace(err) 407 } 408 409 c.updateRegionCache(table, region) 410 return region, nil 411 case *exception: 412 return nil, errors.New(r.msg) 413 default: 414 log.Warnf("Unknown response - %T - %v", r, r) 415 } 416 417 return nil, errors.Errorf("Couldn't find the region: [table=%s] [row=%q] [region_row=%q]", table, row, regionRow) 418 } 419 420 func (c *client) GetRegions(table []byte, useCache bool) ([]*RegionInfo, error) { 421 var regions []*RegionInfo 422 startKey := []byte("") 423 // Get first region. 424 region, err := c.LocateRegion(table, []byte(startKey), useCache) 425 if err != nil { 426 return nil, errors.Errorf("couldn't find any region: [table=%s] [useCache=%t]", table, useCache) 427 } 428 regions = append(regions, region) 429 startKey = region.EndKey 430 for len(startKey) > 0 { 431 region, err = c.LocateRegion(table, []byte(startKey), useCache) 432 if err != nil { 433 return nil, errors.Trace(err) 434 } 435 regions = append(regions, region) 436 startKey = region.EndKey 437 } 438 return regions, nil 439 } 440 441 func (c *client) Close() error { 442 if c.zkClient != nil { 443 c.zkClient.Close() 444 } 445 446 for _, conn := range c.cachedConns { 447 err := conn.close() 448 if err != nil { 449 return errors.Trace(err) 450 } 451 } 452 453 return nil 454 }