github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/go-hbase/conn.go (about)

     1  package hbase
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"io"
     7  	"net"
     8  	"strings"
     9  	"sync"
    10  
    11  	pb "github.com/insionng/yougam/libraries/golang/protobuf/proto"
    12  	"github.com/insionng/yougam/libraries/juju/errors"
    13  	"github.com/insionng/yougam/libraries/ngaut/log"
    14  	"github.com/insionng/yougam/libraries/pingcap/go-hbase/iohelper"
    15  	"github.com/insionng/yougam/libraries/pingcap/go-hbase/proto"
    16  )
    17  
    18  type ServiceType byte
    19  
    20  const (
    21  	MasterMonitorService = iota + 1
    22  	MasterService
    23  	MasterAdminService
    24  	AdminService
    25  	ClientService
    26  	RegionServerStatusService
    27  )
    28  
    29  // convert above const to protobuf string
    30  var ServiceString = map[ServiceType]string{
    31  	MasterMonitorService:      "MasterMonitorService",
    32  	MasterService:             "MasterService",
    33  	MasterAdminService:        "MasterAdminService",
    34  	AdminService:              "AdminService",
    35  	ClientService:             "ClientService",
    36  	RegionServerStatusService: "RegionServerStatusService",
    37  }
    38  
    39  type idGenerator struct {
    40  	n  int
    41  	mu *sync.RWMutex
    42  }
    43  
    44  func newIdGenerator() *idGenerator {
    45  	return &idGenerator{
    46  		n:  0,
    47  		mu: &sync.RWMutex{},
    48  	}
    49  }
    50  
    51  func (a *idGenerator) get() int {
    52  	a.mu.RLock()
    53  	v := a.n
    54  	a.mu.RUnlock()
    55  	return v
    56  }
    57  
    58  func (a *idGenerator) incrAndGet() int {
    59  	a.mu.Lock()
    60  	a.n++
    61  	v := a.n
    62  	a.mu.Unlock()
    63  	return v
    64  }
    65  
    66  type connection struct {
    67  	mu           sync.Mutex
    68  	addr         string
    69  	conn         net.Conn
    70  	bw           *bufio.Writer
    71  	idGen        *idGenerator
    72  	serviceType  ServiceType
    73  	in           chan *iohelper.PbBuffer
    74  	ongoingCalls map[int]*call
    75  }
    76  
    77  func processMessage(msg []byte) ([][]byte, error) {
    78  	buf := pb.NewBuffer(msg)
    79  	payloads := make([][]byte, 0)
    80  
    81  	// Question: why can we ignore this error?
    82  	for {
    83  		hbytes, err := buf.DecodeRawBytes(true)
    84  		if err != nil {
    85  			// Check whether error is `unexpected EOF`.
    86  			if strings.Contains(err.Error(), "unexpected EOF") {
    87  				break
    88  			}
    89  
    90  			log.Errorf("Decode raw bytes error - %v", errors.ErrorStack(err))
    91  			return nil, errors.Trace(err)
    92  		}
    93  
    94  		payloads = append(payloads, hbytes)
    95  	}
    96  
    97  	return payloads, nil
    98  }
    99  
   100  func readPayloads(r io.Reader) ([][]byte, error) {
   101  	nBytesExpecting, err := iohelper.ReadInt32(r)
   102  	if err != nil {
   103  		return nil, errors.Trace(err)
   104  	}
   105  
   106  	if nBytesExpecting > 0 {
   107  		buf, err := iohelper.ReadN(r, nBytesExpecting)
   108  		// Question: why should we return error only when we get an io.EOF error?
   109  		if err != nil && ErrorEqual(err, io.EOF) {
   110  			return nil, errors.Trace(err)
   111  		}
   112  
   113  		payloads, err := processMessage(buf)
   114  		if err != nil {
   115  			return nil, errors.Trace(err)
   116  		}
   117  
   118  		if len(payloads) > 0 {
   119  			return payloads, nil
   120  		}
   121  	}
   122  	return nil, errors.New("unexpected payload")
   123  }
   124  
   125  func newConnection(addr string, srvType ServiceType) (*connection, error) {
   126  	conn, err := net.Dial("tcp", addr)
   127  	if err != nil {
   128  		return nil, errors.Trace(err)
   129  	}
   130  	if _, ok := ServiceString[srvType]; !ok {
   131  		return nil, errors.Errorf("unexpected service type [serviceType=%d]", srvType)
   132  	}
   133  	c := &connection{
   134  		addr:         addr,
   135  		bw:           bufio.NewWriter(conn),
   136  		conn:         conn,
   137  		in:           make(chan *iohelper.PbBuffer, 20),
   138  		serviceType:  srvType,
   139  		idGen:        newIdGenerator(),
   140  		ongoingCalls: map[int]*call{},
   141  	}
   142  
   143  	err = c.init()
   144  	if err != nil {
   145  		return nil, errors.Trace(err)
   146  	}
   147  
   148  	return c, nil
   149  }
   150  
   151  func (c *connection) init() error {
   152  	err := c.writeHead()
   153  	if err != nil {
   154  		return errors.Trace(err)
   155  	}
   156  
   157  	err = c.writeConnectionHeader()
   158  	if err != nil {
   159  		return errors.Trace(err)
   160  	}
   161  
   162  	go func() {
   163  		err := c.processMessages()
   164  		if err != nil {
   165  			log.Warnf("process messages failed - %v", errors.ErrorStack(err))
   166  			return
   167  		}
   168  	}()
   169  	go c.dispatch()
   170  	return nil
   171  }
   172  
   173  func (c *connection) processMessages() error {
   174  	for {
   175  		msgs, err := readPayloads(c.conn)
   176  		if err != nil {
   177  			return errors.Trace(err)
   178  		}
   179  
   180  		var rh proto.ResponseHeader
   181  		err = pb.Unmarshal(msgs[0], &rh)
   182  		if err != nil {
   183  			return errors.Trace(err)
   184  		}
   185  
   186  		callId := rh.GetCallId()
   187  		c.mu.Lock()
   188  		call, ok := c.ongoingCalls[int(callId)]
   189  		if !ok {
   190  			c.mu.Unlock()
   191  			return errors.Errorf("Invalid call id: %d", callId)
   192  		}
   193  		delete(c.ongoingCalls, int(callId))
   194  		c.mu.Unlock()
   195  
   196  		exception := rh.GetException()
   197  		if exception != nil {
   198  			call.complete(errors.Errorf("Exception returned: %s\n%s", exception.GetExceptionClassName(), exception.GetStackTrace()), nil)
   199  		} else if len(msgs) == 2 {
   200  			call.complete(nil, msgs[1])
   201  		}
   202  	}
   203  }
   204  
   205  func (c *connection) writeHead() error {
   206  	buf := bytes.NewBuffer(nil)
   207  	buf.Write(hbaseHeaderBytes)
   208  	buf.WriteByte(0)
   209  	buf.WriteByte(80)
   210  	_, err := c.conn.Write(buf.Bytes())
   211  	return errors.Trace(err)
   212  }
   213  
   214  func (c *connection) writeConnectionHeader() error {
   215  	buf := iohelper.NewPbBuffer()
   216  	service := pb.String(ServiceString[c.serviceType])
   217  
   218  	err := buf.WritePBMessage(&proto.ConnectionHeader{
   219  		UserInfo: &proto.UserInformation{
   220  			EffectiveUser: pb.String("pingcap"),
   221  		},
   222  		ServiceName: service,
   223  	})
   224  	if err != nil {
   225  		return errors.Trace(err)
   226  	}
   227  
   228  	err = buf.PrependSize()
   229  	if err != nil {
   230  		return errors.Trace(err)
   231  	}
   232  
   233  	_, err = c.conn.Write(buf.Bytes())
   234  	if err != nil {
   235  		return errors.Trace(err)
   236  	}
   237  
   238  	return nil
   239  }
   240  
   241  func (c *connection) dispatch() {
   242  	for {
   243  		select {
   244  		case buf := <-c.in:
   245  			// TODO: add error check.
   246  			c.bw.Write(buf.Bytes())
   247  			if len(c.in) == 0 {
   248  				c.bw.Flush()
   249  			}
   250  		}
   251  	}
   252  }
   253  
   254  func (c *connection) call(request *call) error {
   255  	id := c.idGen.incrAndGet()
   256  	rh := &proto.RequestHeader{
   257  		CallId:       pb.Uint32(uint32(id)),
   258  		MethodName:   pb.String(request.methodName),
   259  		RequestParam: pb.Bool(true),
   260  	}
   261  
   262  	request.id = uint32(id)
   263  
   264  	bfrh := iohelper.NewPbBuffer()
   265  	err := bfrh.WritePBMessage(rh)
   266  	if err != nil {
   267  		return errors.Trace(err)
   268  	}
   269  
   270  	bfr := iohelper.NewPbBuffer()
   271  	err = bfr.WritePBMessage(request.request)
   272  	if err != nil {
   273  		return errors.Trace(err)
   274  	}
   275  
   276  	// Buf =>
   277  	// | total size | pb1 size | pb1 | pb2 size | pb2 | ...
   278  	buf := iohelper.NewPbBuffer()
   279  	buf.WriteDelimitedBuffers(bfrh, bfr)
   280  
   281  	c.mu.Lock()
   282  	c.ongoingCalls[id] = request
   283  	c.in <- buf
   284  	c.mu.Unlock()
   285  
   286  	return nil
   287  }
   288  
   289  func (c *connection) close() error {
   290  	return c.conn.Close()
   291  }