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 }