github.com/sequix/cortex@v1.1.6/pkg/chunk/ts/client.go (about) 1 package ts 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "net/url" 11 "strings" 12 "time" 13 14 "github.com/baidubce/bce-sdk-go/auth" 15 "github.com/baidubce/bce-sdk-go/http" 16 "github.com/go-kit/kit/log/level" 17 18 "github.com/sequix/cortex/pkg/chunk" 19 "github.com/sequix/cortex/pkg/util" 20 ) 21 22 var ( 23 maxChunks = 5 24 signer = &auth.BceV1Signer{} 25 ) 26 27 type Config struct { 28 Endpoint string `yaml:"endpoint"` 29 AccessKey string `yaml:"ak"` 30 SecretKey string `yaml:"sk"` 31 Instance string `yaml:"instance"` 32 Table string `yaml:"table"` 33 } 34 35 type client struct { 36 credential *auth.BceCredentials 37 endpoint string 38 uri string 39 headersToSign map[string]struct{} 40 } 41 42 func New(c Config) (*client, error) { 43 cred, err := auth.NewBceCredentials(c.AccessKey, c.SecretKey) 44 if err != nil { 45 return nil, err 46 } 47 48 client := &client{ 49 credential: cred, 50 endpoint: c.Endpoint, 51 uri: "/v1/instance/" + c.Instance + "/table/" + c.Table + "/rows", 52 headersToSign: map[string]struct{}{ 53 "host": struct{}{}, 54 "x-bce-date": struct{}{}, 55 }, 56 } 57 return client, nil 58 } 59 60 func (c *client) Stop() { 61 // 为了实现 type chunk.ObjectClient interface 62 } 63 64 func splitReq(chunks []chunk.Chunk, max int, f func([]chunk.Chunk) error) error { 65 n := len(chunks) 66 for i := 0; i < n; i += maxChunks { 67 j := i + max 68 if j > n { 69 j = n 70 } 71 if err := f(chunks[i:j]); err != nil { 72 return err 73 } 74 } 75 return nil 76 } 77 78 func (c *client) PutChunks(ctx context.Context, chunks []chunk.Chunk) error { 79 return splitReq(chunks, maxChunks, c.put) 80 } 81 82 func (c *client) GetChunks(ctx context.Context, chunks []chunk.Chunk) ([]chunk.Chunk, error) { 83 if err := splitReq(chunks, maxChunks, c.get); err != nil { 84 return nil, err 85 } 86 return chunks, nil 87 } 88 89 func (c *client) newRequest(now time.Time) *http.Request { 90 req := &http.Request{} 91 req.SetProtocol("https") 92 req.SetEndpoint(c.endpoint) 93 req.SetHeader("host", c.endpoint) 94 req.SetHeader("x-bce-date", now.Format(time.RFC3339)) 95 req.SetHeader("Content-Type", "application/json") 96 //req.SetHeader("Accept", "application/json") 97 req.SetUri(c.uri) 98 return req 99 } 100 101 func (c *client) put(chunks []chunk.Chunk) error { 102 reqBody := &batchPutRowReq{ 103 Rows: make([]*row, 0, len(chunks)), 104 } 105 106 for i := range chunks { 107 r, err := chunkToRow(&chunks[i]) 108 if err != nil { 109 return err 110 } 111 reqBody.Rows = append(reqBody.Rows, r) 112 } 113 114 reqBodyBuf, err := json.Marshal(reqBody) 115 if err != nil { 116 return err 117 } 118 119 now := time.Now().UTC() 120 req := c.newRequest(now) 121 req.SetMethod(http.PUT) 122 req.SetLength(int64(len(reqBodyBuf))) 123 req.SetBody(ioutil.NopCloser(bytes.NewReader(reqBodyBuf))) 124 125 base := time.Now() 126 _, err = c.do(now, req) 127 level.Debug(util.Logger).Log("module", "TableStorageClient", "msg", "Put Rows", "size", len(reqBodyBuf), "duration", time.Since(base), "err", err) 128 if err != nil { 129 return err 130 } 131 return nil 132 } 133 134 func (c *client) get(chunks []chunk.Chunk) error { 135 reqBody := &batchGetRowReq{ 136 Rows: make([]*row, 0, len(chunks)), 137 } 138 139 for i := range chunks { 140 reqBody.Rows = append(reqBody.Rows, chunkToRowWithoutContent(&chunks[i])) 141 } 142 143 reqBodyBuf, err := json.Marshal(reqBody) 144 if err != nil { 145 return err 146 } 147 148 now := time.Now().UTC() 149 req := c.newRequest(now) 150 req.SetMethod(http.GET) 151 req.SetLength(int64(len(reqBodyBuf))) 152 req.SetBody(ioutil.NopCloser(bytes.NewReader(reqBodyBuf))) 153 154 base := time.Now() 155 rsp, err := c.do(now, req) 156 level.Debug(util.Logger).Log("module", "TableStorageClient", "msg", "Get Rows", "size", len(reqBodyBuf), "duration", time.Since(base), "err", err) 157 if err != nil { 158 return err 159 } 160 defer rsp.Body().Close() 161 162 rspBodyBytes, err := ioutil.ReadAll(rsp.Body()) 163 if err != nil { 164 return err 165 } 166 167 rspBody := &batchGetRowRsp{} 168 err = json.Unmarshal(rspBodyBytes, rspBody) 169 if err != nil { 170 return err 171 } 172 rows := rspBody.Rows 173 174 keyToRow := make(map[string]*row) 175 for i := range rows { 176 keyToRow[rawUrlDecode(rows[i].RowKey)] = rows[i] 177 } 178 179 for i := range chunks { 180 r, ok := keyToRow[chunks[i].ExternalKey()] 181 if !ok { 182 continue 183 } 184 err := rowToChunk(r, &chunks[i]) 185 if err != nil { 186 return err 187 } 188 } 189 return nil 190 } 191 192 func (c *client) do(now time.Time, req *http.Request) (*http.Response, error) { 193 signer.Sign(req, c.credential, &auth.SignOptions{ 194 HeadersToSign: c.headersToSign, 195 Timestamp: now.Unix(), 196 ExpireSeconds: 180, 197 }) 198 rsp, origErr := http.Execute(req) 199 if origErr != nil { 200 return rsp, nil 201 } 202 203 if rsp.StatusCode() > 399 { 204 content, err := ioutil.ReadAll(io.LimitReader(rsp.Body(), 256)) 205 if err != nil { 206 return nil, fmt.Errorf("ts client reading response body failed") 207 } 208 return nil, fmt.Errorf("ts client %s rows: %s", req.Method(), content) 209 } 210 return rsp, nil 211 } 212 213 func rawUrlEncode(s string) string { 214 es := url.QueryEscape(s) 215 return strings.ReplaceAll(es, "+", "%20") 216 } 217 218 func rawUrlDecode(s string) string { 219 rs := strings.ReplaceAll(s, "%20", "+") 220 ds, _ := url.QueryUnescape(rs) 221 return ds 222 } 223 224 func chunkToRow(chunk *chunk.Chunk) (*row, error) { 225 r := &row{ 226 RowKey: rawUrlEncode(chunk.ExternalKey()), 227 Cells: []*cell{ 228 { 229 Column: columnName, 230 }, 231 }, 232 } 233 buf, err := chunk.Encoded() 234 if err != nil { 235 return nil, err 236 } 237 r.Cells[0].Value = rawUrlEncode(string(buf)) 238 return r, nil 239 } 240 241 func chunkToRowWithoutContent(chunk *chunk.Chunk) *row { 242 return &row{ 243 RowKey: rawUrlEncode(chunk.ExternalKey()), 244 Cells: []*cell{ 245 { 246 Column: columnName, 247 }, 248 }, 249 } 250 } 251 252 func rowToChunk(r *row, c *chunk.Chunk) error { 253 decodeCtx := chunk.NewDecodeContext() 254 return c.Decode(decodeCtx, []byte(rawUrlDecode(r.Cells[0].Value))) 255 }