123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- // Copyright (C) 2015 The GoHBase Authors. All rights reserved.
- // This file is part of GoHBase.
- // Use of this source code is governed by the Apache License 2.0
- // that can be found in the COPYING file.
- package gohbase
- import (
- "encoding/binary"
- "fmt"
- "sync"
- "time"
- "github.com/cznic/b"
- "github.com/golang/protobuf/proto"
- log "github.com/sirupsen/logrus"
- "github.com/tsuna/gohbase/hrpc"
- "github.com/tsuna/gohbase/pb"
- "github.com/tsuna/gohbase/region"
- "github.com/tsuna/gohbase/zk"
- "golang.org/x/time/rate"
- )
- const (
- standardClient = iota
- adminClient
- defaultRPCQueueSize = 100
- defaultFlushInterval = 20 * time.Millisecond
- defaultZkRoot = "/hbase"
- defaultZkTimeout = 30 * time.Second
- defaultEffectiveUser = "root"
- // metaBurst is maximum number of request allowed at once.
- metaBurst = 10
- // metaLimit is rate at which to throttle requests to hbase:meta table.
- metaLimit = rate.Limit(100)
- )
- // Client a regular HBase client
- type Client interface {
- Scan(s *hrpc.Scan) hrpc.Scanner
- Get(g *hrpc.Get) (*hrpc.Result, error)
- Put(p *hrpc.Mutate) (*hrpc.Result, error)
- Delete(d *hrpc.Mutate) (*hrpc.Result, error)
- Append(a *hrpc.Mutate) (*hrpc.Result, error)
- Increment(i *hrpc.Mutate) (int64, error)
- CheckAndPut(p *hrpc.Mutate, family string, qualifier string,
- expectedValue []byte) (bool, error)
- Close()
- }
- // RPCClient is core client of gohbase. It's exposed for testing.
- type RPCClient interface {
- SendRPC(rpc hrpc.Call) (proto.Message, error)
- }
- // Option is a function used to configure optional config items for a Client.
- type Option func(*client)
- // A Client provides access to an HBase cluster.
- type client struct {
- clientType int
- regions keyRegionCache
- // Maps a hrpc.RegionInfo to the *region.Client that we think currently
- // serves it.
- clients clientRegionCache
- metaRegionInfo hrpc.RegionInfo
- adminRegionInfo hrpc.RegionInfo
- // The maximum size of the RPC queue in the region client
- rpcQueueSize int
- // zkClient is zookeeper for retrieving meta and admin information
- zkClient zk.Client
- // The root zookeeper path for Hbase. By default, this is usually "/hbase".
- zkRoot string
- // The zookeeper session timeout
- zkTimeout time.Duration
- // The timeout before flushing the RPC queue in the region client
- flushInterval time.Duration
- // The user used when accessing regions.
- effectiveUser string
- // metaLookupLimiter is used to throttle lookups to hbase:meta table
- metaLookupLimiter *rate.Limiter
- // How long to wait for a region lookup (either meta lookup or finding
- // meta in ZooKeeper). Should be greater than or equal to the ZooKeeper
- // session timeout.
- regionLookupTimeout time.Duration
- // regionReadTimeout is the maximum amount of time to wait for regionserver reply
- regionReadTimeout time.Duration
- done chan struct{}
- closeOnce sync.Once
- }
- // NewClient creates a new HBase client.
- func NewClient(zkquorum string, options ...Option) Client {
- return newClient(zkquorum, options...)
- }
- func newClient(zkquorum string, options ...Option) *client {
- log.WithFields(log.Fields{
- "Host": zkquorum,
- }).Debug("Creating new client.")
- c := &client{
- clientType: standardClient,
- regions: keyRegionCache{regions: b.TreeNew(region.CompareGeneric)},
- clients: clientRegionCache{
- regions: make(map[hrpc.RegionClient]map[hrpc.RegionInfo]struct{}),
- },
- rpcQueueSize: defaultRPCQueueSize,
- flushInterval: defaultFlushInterval,
- metaRegionInfo: region.NewInfo(
- 0,
- []byte("hbase"),
- []byte("meta"),
- []byte("hbase:meta,,1"),
- nil,
- nil),
- zkRoot: defaultZkRoot,
- zkTimeout: defaultZkTimeout,
- effectiveUser: defaultEffectiveUser,
- metaLookupLimiter: rate.NewLimiter(metaLimit, metaBurst),
- regionLookupTimeout: region.DefaultLookupTimeout,
- regionReadTimeout: region.DefaultReadTimeout,
- done: make(chan struct{}),
- }
- for _, option := range options {
- option(c)
- }
- //Have to create the zkClient after the Options have been set
- //since the zkTimeout could be changed as an option
- c.zkClient = zk.NewClient(zkquorum, c.zkTimeout)
- return c
- }
- // RpcQueueSize will return an option that will set the size of the RPC queues
- // used in a given client
- func RpcQueueSize(size int) Option {
- return func(c *client) {
- c.rpcQueueSize = size
- }
- }
- // ZookeeperRoot will return an option that will set the zookeeper root path used in a given client.
- func ZookeeperRoot(root string) Option {
- return func(c *client) {
- c.zkRoot = root
- }
- }
- // ZookeeperTimeout will return an option that will set the zookeeper session timeout.
- func ZookeeperTimeout(to time.Duration) Option {
- return func(c *client) {
- c.zkTimeout = to
- }
- }
- // RegionLookupTimeout will return an option that sets the region lookup timeout
- func RegionLookupTimeout(to time.Duration) Option {
- return func(c *client) {
- c.regionLookupTimeout = to
- }
- }
- // RegionReadTimeout will return an option that sets the region read timeout
- func RegionReadTimeout(to time.Duration) Option {
- return func(c *client) {
- c.regionReadTimeout = to
- }
- }
- // EffectiveUser will return an option that will set the user used when accessing regions.
- func EffectiveUser(user string) Option {
- return func(c *client) {
- c.effectiveUser = user
- }
- }
- // FlushInterval will return an option that will set the timeout for flushing
- // the RPC queues used in a given client
- func FlushInterval(interval time.Duration) Option {
- return func(c *client) {
- c.flushInterval = interval
- }
- }
- // Close closes connections to hbase master and regionservers
- func (c *client) Close() {
- c.closeOnce.Do(func() {
- close(c.done)
- if c.clientType == adminClient {
- if ac := c.adminRegionInfo.Client(); ac != nil {
- ac.Close()
- }
- }
- c.clients.closeAll()
- })
- }
- func (c *client) Scan(s *hrpc.Scan) hrpc.Scanner {
- return newScanner(c, s)
- }
- func (c *client) Get(g *hrpc.Get) (*hrpc.Result, error) {
- pbmsg, err := c.SendRPC(g)
- if err != nil {
- return nil, err
- }
- r, ok := pbmsg.(*pb.GetResponse)
- if !ok {
- return nil, fmt.Errorf("sendRPC returned not a GetResponse")
- }
- return hrpc.ToLocalResult(r.Result), nil
- }
- func (c *client) Put(p *hrpc.Mutate) (*hrpc.Result, error) {
- return c.mutate(p)
- }
- func (c *client) Delete(d *hrpc.Mutate) (*hrpc.Result, error) {
- return c.mutate(d)
- }
- func (c *client) Append(a *hrpc.Mutate) (*hrpc.Result, error) {
- return c.mutate(a)
- }
- func (c *client) Increment(i *hrpc.Mutate) (int64, error) {
- r, err := c.mutate(i)
- if err != nil {
- return 0, err
- }
- if len(r.Cells) != 1 {
- return 0, fmt.Errorf("increment returned %d cells, but we expected exactly one",
- len(r.Cells))
- }
- val := binary.BigEndian.Uint64(r.Cells[0].Value)
- return int64(val), nil
- }
- func (c *client) mutate(m *hrpc.Mutate) (*hrpc.Result, error) {
- pbmsg, err := c.SendRPC(m)
- if err != nil {
- return nil, err
- }
- r, ok := pbmsg.(*pb.MutateResponse)
- if !ok {
- return nil, fmt.Errorf("sendRPC returned not a MutateResponse")
- }
- return hrpc.ToLocalResult(r.Result), nil
- }
- func (c *client) CheckAndPut(p *hrpc.Mutate, family string,
- qualifier string, expectedValue []byte) (bool, error) {
- cas, err := hrpc.NewCheckAndPut(p, family, qualifier, expectedValue)
- if err != nil {
- return false, err
- }
- pbmsg, err := c.SendRPC(cas)
- if err != nil {
- return false, err
- }
- r, ok := pbmsg.(*pb.MutateResponse)
- if !ok {
- return false, fmt.Errorf("sendRPC returned a %T instead of MutateResponse", pbmsg)
- }
- if r.Processed == nil {
- return false, fmt.Errorf("protobuf in the response didn't contain the field "+
- "indicating whether the CheckAndPut was successful or not: %s", r)
- }
- return r.GetProcessed(), nil
- }
|