Thanos源码专题【左扬精讲】——Thanos API 组件(release-0.26)源码阅读和分析(详解 pkg\api\query\v1.go)
Thanos API 组件(release-0.26)源码阅读和分析(详解 pkg\api\query\v1.go)
https://github.com/thanos-io/thanos/blob/v0.26.0/pkg/api/query/v1.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 | // Copyright (c) The Thanos Authors. // Licensed under the Apache License 2.0. // Copyright 2016 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // This package is a modified copy from // github.com/prometheus/prometheus/web/api/v1@2121b4628baa7d9d9406aa468712a6a332e77aff. package v1 import ( "context" // context 用于传递上下文信息,比如取消信号、截止时间等。 "math" // math 包提供了基本的数学运算函数,比如加、减、乘、除等。 "net/http" // net/http 包提供了 HTTP 客户端和服务器的实现。 "sort" // sort 包提供了对切片进行排序的函数。 "strconv" // strconv 包提供了字符串和基本数据类型之间的转换函数。 "strings" // strings 包提供了许多实用的字符串处理函数,比如查找、替换等。 "time" // time 包提供了时间的表示、计算和格式化功能。 "github.com/go-kit/log" // log 包提供了日志记录的功能。 "github.com/opentracing/opentracing-go" // opentracing-go 包提供了分布式追踪的功能。 "github.com/pkg/errors" // errors 包提供了错误处理的功能。 "github.com/prometheus/client_golang/prometheus" // prometheus 包提供了 Prometheus 监控指标的收集和暴露功能。 "github.com/prometheus/client_golang/prometheus/promauto" // promauto 包提供了 Prometheus 监控指标的自动注册功能。 "github.com/prometheus/common/model" // model 包提供了 Prometheus 数据模型的定义和操作功能。 "github.com/prometheus/common/route" // route 包提供了 HTTP 路由的功能。 "github.com/prometheus/prometheus/model/labels" // labels 包提供了 Prometheus 标签的定义和操作功能。 "github.com/prometheus/prometheus/model/timestamp" // timestamp 包提供了 Prometheus 时间戳的定义和操作功能。 "github.com/prometheus/prometheus/promql" // promql 包提供了 Prometheus 查询语言的解析和执行功能。 "github.com/prometheus/prometheus/promql/parser" // parser 包提供了 Prometheus 查询语言的解析功能。 "github.com/prometheus/prometheus/storage" // storage 包提供了 Prometheus 数据存储的功能。 "github.com/prometheus/prometheus/util/stats" // stats 包提供了 Prometheus 数据统计的功能。 "github.com/thanos-io/thanos/pkg/api" // api 包提供了 Thanos API 的定义和实现功能。 "github.com/thanos-io/thanos/pkg/exemplars" // exemplars 包提供了 Thanos 示例数据的功能。 "github.com/thanos-io/thanos/pkg/exemplars/exemplarspb" // exemplarspb 包提供了 Thanos 示例数据的序列化功能。 extpromhttp "github.com/thanos-io/thanos/pkg/extprom/http" // extpromhttp 包提供了 Thanos 扩展 Prometheus 监控指标的功能。 "github.com/thanos-io/thanos/pkg/gate" // gate 包提供了 Thanos 限流的功能。 "github.com/thanos-io/thanos/pkg/logging" // logging 包提供了 Thanos 日志记录的功能。 "github.com/thanos-io/thanos/pkg/metadata" // metadata 包提供了 Thanos 元数据的功能。 "github.com/thanos-io/thanos/pkg/metadata/metadatapb" // metadatapb 包提供了 Thanos 元数据的序列化功能。 "github.com/thanos-io/thanos/pkg/query" // query 包提供了 Thanos query 的功能。 "github.com/thanos-io/thanos/pkg/rules" // rules 包提供了 Thanos rules 的功能。 "github.com/thanos-io/thanos/pkg/rules/rulespb" // rulespb 包提供了 Thanos rules 的序列化功能。 "github.com/thanos-io/thanos/pkg/runutil" // runutil 包提供了 Thanos runutil 的功能。 "github.com/thanos-io/thanos/pkg/store/storepb" // storepb 包提供了 Thanos store 的序列化功能。 "github.com/thanos-io/thanos/pkg/targets" // targets 包提供了 Thanos target 的功能。 "github.com/thanos-io/thanos/pkg/targets/targetspb" // targetspb 包提供了 Thanos target 的序列化功能。 "github.com/thanos-io/thanos/pkg/tracing" // tracing 包提供了 Thanos tracing 的功能。 ) const ( DedupParam = "dedup" // DedupParam 是用于去重的参数。 PartialResponseParam = "partial_response" // PartialResponseParam 是用于部分响应的参数。 MaxSourceResolutionParam = "max_source_resolution" // MaxSourceResolutionParam 是用于最大源分辨率的参数。 ReplicaLabelsParam = "replicaLabels[]" // ReplicaLabelsParam 是用于副本标签的参数。 MatcherParam = "match[]" // MatcherParam 是用于匹配的参数。 StoreMatcherParam = "storeMatch[]" // StoreMatcherParam 是用于存储匹配的参数。 Step = "step" // Step 是用于步长的参数。 Stats = "stats" // Stats 是用于统计的参数。 ) // QueryAPI is an API used by Thanos Querier. // QueryAPI 类型是用于 Thanos Querier 的 API。 type QueryAPI struct { baseAPI *api.BaseAPI // baseAPI 是 QueryAPI 的基础 API。 logger log.Logger // logger 是用于记录日志的对象。 gate gate.Gate // gate 是用于控制访问的对象。 queryableCreate query.QueryableCreator // queryableCreate 是用于创建查询对象的函数。 // queryEngine returns appropriate promql.Engine for a query with a given step. // queryEngin 返回一个适用于给定步长的 promql.Engine。 queryEngine func (int64) *promql.Engine // queryEngine 是一个函数,用于根据给定的步长返回一个 promql.Engine。 ruleGroups rules.UnaryClient // ruleGroups 是用于规则组的对象。 targets targets.UnaryClient // targets 是用于目标的对象。 metadatas metadata.UnaryClient // metadatas 是用于元数据的对象。 exemplars exemplars.UnaryClient // exemplars 是用于示例的对象。 enableAutodownsampling bool // enableAutodownsampling 是用于启用自动降采样的参数。 enableQueryPartialResponse bool // enableQueryPartialResponse 是用于启用查询部分响应的参数。 enableRulePartialResponse bool // enableRulePartialResponse 是用于启用规则部分响应的参数。 enableTargetPartialResponse bool // enableTargetPartialResponse 是用于启用目标部分响应的参数。 enableMetricMetadataPartialResponse bool // enableMetricMetadataPartialResponse 是用于启用指标元数据部分响应的参数。 enableExemplarPartialResponse bool // enableExemplarPartialResponse 是用于启用示例部分响应的参数。 enableQueryPushdown bool // enableQueryPushdown 是用于启用查询下推的参数。 disableCORS bool // disableCORS 是用于禁用 CORS 的参数。 replicaLabels []string // replicaLabels 是用于副本标签的参数。 endpointStatus func () []query.EndpointStatus // endpointStatus 是用于获取端点状态的函数。 defaultRangeQueryStep time.Duration // defaultRangeQueryStep 是用于默认范围查询步长的参数。 defaultInstantQueryMaxSourceResolution time.Duration // defaultInstantQueryMaxSourceResolution 是用于默认即时查询最大源分辨率的参数。 defaultMetadataTimeRange time.Duration // defaultMetadataTimeRange 是用于默认元数据时间范围的参数。 queryRangeHist prometheus.Histogram // queryRangeHist 是用于查询范围直方图的参数。 } // NewQueryAPI returns an initialized QueryAPI type. // NewQueryAPI 创建并返回一个 QueryAPI 实例。 // // 参数: // - logger: 日志记录器 // - endpointStatus: 获取端点状态的函数 // - qe: 创建 PromQL 查询引擎的函数 // - c: 创建可查询实例的函数 // - ruleGroups: 规则组客户端 // - targets: 目标客户端 // - metadatas: 元数据客户端 // - exemplars: 示例客户端 // - enableAutodownsampling: 是否启用自动降采样 // - enableQueryPartialResponse: 是否启用查询部分响应 // - enableRulePartialResponse: 是否启用规则部分响应 // - enableTargetPartialResponse: 是否启用目标部分响应 // - enableMetricMetadataPartialResponse: 是否启用元数据部分响应 // - enableExemplarPartialResponse: 是否启用示例部分响应 // - enableQueryPushdown: 是否启用查询下推 // - replicaLabels: 副本标签列表 // - flagsMap: 标志映射 // - defaultRangeQueryStep: 默认范围查询步长 // - defaultInstantQueryMaxSourceResolution: 默认即时查询最大源分辨率 // - defaultMetadataTimeRange: 默认元数据时间范围 // - disableCORS: 是否禁用 CORS // - gate: 限流 // - reg: Prometheus 注册中心 // // 返回值: // - 返回一个 QueryAPI 实例 func NewQueryAPI( logger log.Logger, endpointStatus func () []query.EndpointStatus, qe func (int64) *promql.Engine, c query.QueryableCreator, ruleGroups rules.UnaryClient, targets targets.UnaryClient, metadatas metadata.UnaryClient, exemplars exemplars.UnaryClient, enableAutodownsampling bool, enableQueryPartialResponse bool, enableRulePartialResponse bool, enableTargetPartialResponse bool, enableMetricMetadataPartialResponse bool, enableExemplarPartialResponse bool, enableQueryPushdown bool, replicaLabels []string, flagsMap map [string]string, defaultRangeQueryStep time.Duration, defaultInstantQueryMaxSourceResolution time.Duration, defaultMetadataTimeRange time.Duration, disableCORS bool, gate gate.Gate, reg *prometheus.Registry, ) *QueryAPI { // 初始化 QueryAPI 结构体并返回 return &QueryAPI{ // 创建基础 API baseAPI: api.NewBaseAPI(logger, disableCORS, flagsMap), // 记录器 logger: logger, // 查询引擎 queryEngine: qe, // 创建可查询实例的函数 queryableCreate: c, // 限流门 gate: gate, // 规则组客户端 ruleGroups: ruleGroups, // 目标客户端 targets: targets, // 元数据客户端 metadatas: metadatas, // 示例客户端 exemplars: exemplars, // 自动降采样开关 enableAutodownsampling: enableAutodownsampling, // 查询部分响应开关 enableQueryPartialResponse: enableQueryPartialResponse, // 规则部分响应开关 enableRulePartialResponse: enableRulePartialResponse, // 目标部分响应开关 enableTargetPartialResponse: enableTargetPartialResponse, // 元数据部分响应开关 enableMetricMetadataPartialResponse: enableMetricMetadataPartialResponse, // 示例部分响应开关 enableExemplarPartialResponse: enableExemplarPartialResponse, // 查询下推开关 enableQueryPushdown: enableQueryPushdown, // 副本标签列表 replicaLabels: replicaLabels, // 端点状态获取函数 endpointStatus: endpointStatus, // 默认范围查询步长 defaultRangeQueryStep: defaultRangeQueryStep, // 默认即时查询最大源分辨率 defaultInstantQueryMaxSourceResolution: defaultInstantQueryMaxSourceResolution, // 默认元数据时间范围 defaultMetadataTimeRange: defaultMetadataTimeRange, // 禁用 CORS 开关 disableCORS: disableCORS, // 创建查询范围直方图 queryRangeHist: promauto.With(reg).NewHistogram(prometheus.HistogramOpts{ Name: "thanos_query_range_requested_timespan_duration_seconds" , Help: "A histogram of the query range window in seconds" , Buckets: prometheus.ExponentialBuckets(15*60, 2, 12), }), } } // Register the API's endpoints in the given router. // Register 方法用于将 QueryAPI 的各个处理函数注册到路由中。 // // 参数: // // r *route.Router: 路由实例,用于注册处理函数。 // tracer opentracing.Tracer: 用于追踪请求的 Tracer 实例。 // logger log.Logger: 用于记录日志的 Logger 实例。 // ins extpromhttp.InstrumentationMiddleware: 用于监控 HTTP 请求的中间件。 // logMiddleware *logging.HTTPServerMiddleware: 用于记录 HTTP 请求日志的中间件。 func (qapi *QueryAPI) Register(r *route.Router, tracer opentracing.Tracer, logger log.Logger, ins extpromhttp.InstrumentationMiddleware, logMiddleware *logging.HTTPServerMiddleware) { // 注册基础API qapi.baseAPI.Register(r, tracer, logger, ins, logMiddleware) // 获取API的Instrumentation instr := api.GetInstr(tracer, logger, ins, logMiddleware, qapi.disableCORS) // 注册GET和POST请求处理函数到/query路由 r.Get( "/query" , instr( "query" , qapi.query)) // GET /query r.Post( "/query" , instr( "query" , qapi.query)) // POST /query // 注册GET和POST请求处理函数到/query_range路由 r.Get( "/query_range" , instr( "query_range" , qapi.queryRange)) // GET /query_range r.Post( "/query_range" , instr( "query_range" , qapi.queryRange)) // POST /query_range // 注册GET请求处理函数到/label/:name/values路由 r.Get( "/label/:name/values" , instr( "label_values" , qapi.labelValues)) // GET /label/:name/values // 注册GET和POST请求处理函数到/series路由 r.Get( "/series" , instr( "series" , qapi.series)) // GET /series r.Post( "/series" , instr( "series" , qapi.series)) // POST /series // 注册GET和POST请求处理函数到/labels路由 r.Get( "/labels" , instr( "label_names" , qapi.labelNames)) // GET /labels r.Post( "/labels" , instr( "label_names" , qapi.labelNames)) // POST /labels // 注册GET请求处理函数到/stores路由 r.Get( "/stores" , instr( "stores" , qapi.stores)) // GET /stores // 注册GET请求处理函数到/rules路由 r.Get( "/rules" , instr( "rules" , NewRulesHandler(qapi.ruleGroups, qapi.enableRulePartialResponse))) // GET /rules // 注册GET请求处理函数到/targets路由 r.Get( "/targets" , instr( "targets" , NewTargetsHandler(qapi.targets, qapi.enableTargetPartialResponse))) // GET /targets // 注册GET请求处理函数到/metadata路由 r.Get( "/metadata" , instr( "metadata" , NewMetricMetadataHandler(qapi.metadatas, qapi.enableMetricMetadataPartialResponse))) // GET /metadata // 注册GET和POST请求处理函数到/query_exemplars路由 r.Get( "/query_exemplars" , instr( "exemplars" , NewExemplarsHandler(qapi.exemplars, qapi.enableExemplarPartialResponse))) // GET /query_exemplars r.Post( "/query_exemplars" , instr( "exemplars" , NewExemplarsHandler(qapi.exemplars, qapi.enableExemplarPartialResponse))) // POST /query_exemplars } type queryData struct { ResultType parser.ValueType `json: "resultType" ` // ResultType 是查询结果的类型,例如 vector、matrix 等。 Result parser.Value `json: "result" ` // Result 是查询结果的详细数据。它的类型取决于 ResultType。 Stats *stats.QueryStats `json: "stats,omitempty" ` // Stats 是查询的统计信息,例如查询耗时、查询的标签等。 // Additional Thanos Response field. Warnings []error `json: "warnings,omitempty" ` // Warnings 是查询过程中可能出现的警告信息。 } // parseEnableDedupParam 解析请求中的去重参数,并返回是否启用去重的布尔值。 // // 参数: // // r *http.Request: 包含请求参数的 HTTP 请求对象 // // 返回值: // // enableDeduplication bool: 是否启用去重的布尔值 // err *api.ApiError: 如果发生错误,则返回错误信息;否则为 nil func (qapi *QueryAPI) parseEnableDedupParam(r *http.Request) (enableDeduplication bool, _ *api.ApiError) { // 默认启用去重 enableDeduplication = true // 检查请求中是否包含去重参数 // r.FormValue 返回请求中指定参数的值,如果参数不存在则返回空字符串 if val := r.FormValue(DedupParam); val != "" { // 将字符串转换为布尔值 var err error // enabelDeduplication 是一个布尔值,表示是否启用去重,strconv.ParseBool 用于将字符串转换为布尔值。 enableDeduplication, err = strconv.ParseBool(val) // 如果转换失败,返回错误 if err != nil { // 返回错误,包括错误类型和错误信息,api.ApiError 是一个自定义的错误类型,用于返回 API 相关的错误信息。 return false, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter" , DedupParam)} } } return enableDeduplication, nil } // parseReplicaLabelsParam 解析请求表单,并返回replicaLabels参数值列表 // // 参数: // // r *http.Request: HTTP请求对象 // // 返回值: // // replicaLabels []string: replicaLabels参数值列表 // _ *api.ApiError: 如果出现错误,则返回ApiError对象;否则返回nil func (qapi *QueryAPI) parseReplicaLabelsParam(r *http.Request) (replicaLabels []string, _ *api.ApiError) { // 解析请求表单 if err := r.ParseForm(); err != nil { // 解析表单出错时返回ApiError return nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "parse form" )} } // 初始化replicaLabels为qapi的replicaLabels replicaLabels = qapi.replicaLabels // 当提供了查询参数时,覆盖cli标志 // Overwrite the cli flag when provided as a query parameter. if len(r.Form[ReplicaLabelsParam]) > 0 { // 使用请求表单中的replicaLabels覆盖qapi的replicaLabels replicaLabels = r.Form[ReplicaLabelsParam] } return replicaLabels, nil } // parseStoreDebugMatchersParam 解析请求中的storeDebugMatchers参数 // // 参数: // // r *http.Request: HTTP请求对象 // // 返回值: // // [][]*labels.Matcher: 解析后的storeMatchers列表 // *api.ApiError: 如果发生错误,则返回API错误对象,否则返回nil // // 说明: // // 该函数解析HTTP请求中的storeDebugMatchers参数,并将其转换为[][]*labels.Matcher类型的storeMatchers列表。 // 如果请求解析失败或参数解析出错,则返回相应的API错误对象。 func (qapi *QueryAPI) parseStoreDebugMatchersParam(r *http.Request) (storeMatchers [][]*labels.Matcher, _ *api.ApiError) { // 解析表单,r.ParseForm()会解析URL中的查询参数,并将其存储在r.Form中。 if err := r.ParseForm(); err != nil { // 解析表单出错,返回内部错误。api.ApiError 是一个自定义的错误类型,用于返回 API 相关的错误信息。 return nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "parse form" )} } // 遍历表单中的参数, r.Form[StoreMatcherParam] 获取所有名为 StoreMatcherParam 的参数值列表。 for _, s := range r.Form[StoreMatcherParam] { // 解析指标选择器。parser.ParseMetricSelector 用于解析指标选择器字符串,返回一个匹配器的切片。 matchers, err := parser.ParseMetricSelector(s) if err != nil { // 解析出错,返回数据错误 return nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 将解析出的匹配器添加到storeMatchers中, storeMatchers 是一个 [][]*labels.Matcher 类型的切片。 storeMatchers = append(storeMatchers, matchers) } return storeMatchers, nil } // parseDownsamplingParamMillis 函数从HTTP请求中提取最大源分辨率参数值,并将其转换为毫秒数。 // 如果请求中未提供参数值,或者启用了自动降采样,则使用默认值。 // 如果提供的参数值无法解析为有效的时间间隔,则返回错误。 // // 参数: // r *http.Request: 包含请求的HTTP请求对象。 // defaultVal time.Duration: 当启用自动降采样或请求中未提供参数值时要使用的默认值。 // // 返回值: // maxResolutionMillis int64: 解析后的最大源分辨率(以毫秒为单位)。 // *api.ApiError: 如果发生错误,则返回API错误对象;否则为nil。 func (qapi *QueryAPI) parseDownsamplingParamMillis(r *http.Request, defaultVal time.Duration) (maxResolutionMillis int64, _ *api.ApiError) { // 初始化最大源分辨率为0秒 maxSourceResolution := 0 * time.Second // 从请求中获取最大源分辨率参数的值 val := r.FormValue(MaxSourceResolutionParam) // 如果启用自动降采样或参数值为"auto",则使用默认值 if qapi.enableAutodownsampling || (val == "auto" ) { maxSourceResolution = defaultVal } // 如果参数值不为空且不为"auto" if val != "" && val != "auto" { var err error // 解析参数值为时间间隔 maxSourceResolution, err = parseDuration(val) if err != nil { // 如果解析失败,返回错误 return 0, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter" , MaxSourceResolutionParam)} } } // 如果最大源分辨率小于0,返回错误 if maxSourceResolution < 0 { return 0, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf( "negative '%s' is not accepted. Try a positive integer" , MaxSourceResolutionParam)} } // 将最大源分辨率转换为毫秒,并返回 return int64(maxSourceResolution / time.Millisecond), nil } // parsePartialResponseParam 解析请求中的部分响应参数,并返回是否启用部分响应和错误信息 // // 参数: // // r *http.Request:HTTP 请求对象 // defaultEnablePartialResponse bool:默认是否启用部分响应 // // 返回值: // // enablePartialResponse bool:是否启用部分响应 // *api.ApiError:错误信息,如果解析失败,则返回错误信息,否则返回 nil func (qapi *QueryAPI) parsePartialResponseParam(r *http.Request, defaultEnablePartialResponse bool) (enablePartialResponse bool, _ *api.ApiError) { // 覆盖作为查询参数提供的cli标志 // Overwrite the cli flag when provided as a query parameter. if val := r.FormValue(PartialResponseParam); val != "" { // 解析查询参数 var err error defaultEnablePartialResponse, err = strconv.ParseBool(val) if err != nil { // 如果解析错误,则返回错误 // Return an error if parsing fails return false, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter" , PartialResponseParam)} } } return defaultEnablePartialResponse, nil } // parseStep 函数用于解析查询参数中的步骤时间,并返回步骤时间和可能发生的错误。 // // 参数: // r *http.Request:HTTP 请求对象,用于获取查询参数。 // defaultRangeQueryStep time.Duration:默认的步骤时间。 // rangeSeconds int64:范围秒数,用于计算默认步骤时间。 // // 返回值: // time.Duration:解析后的步骤时间。 // *api.ApiError:可能发生的错误。 func (qapi *QueryAPI) parseStep(r *http.Request, defaultRangeQueryStep time.Duration, rangeSeconds int64) (time.Duration, *api.ApiError) { // 覆盖作为查询参数提供的cli标志 // Overwrite the cli flag when provided as a query parameter. if val := r.FormValue(Step); val != "" { var err error defaultRangeQueryStep, err = parseDuration(val) if err != nil { return 0, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Wrapf(err, "'%s' parameter" , Step)} } return defaultRangeQueryStep, nil } // 使用默认步骤的方式使其与UI一致 // Default step is used this way to make it consistent with UI. d := time.Duration(math.Max(float64(rangeSeconds/250), float64(defaultRangeQueryStep/time.Second))) * time.Second return d, nil } // query 函数是 QueryAPI 结构体中的一个方法,用于处理 HTTP 请求并返回查询结果。 // // 参数: // // r *http.Request: HTTP 请求对象。 // // 返回值: // // interface{}: 查询结果数据。 // []error: 查询过程中产生的警告信息。 // *api.ApiError: 查询过程中产生的 API 错误信息。 func (qapi *QueryAPI) query(r *http.Request) ( interface {}, []error, *api.ApiError) { // 解析时间参数。 // parseTimeParam 函数用于解析时间参数,并将其转换为 time.Time 类型。 // qapi.baseAPI.Now() 返回当前时间。 ts, err := parseTimeParam(r, "time" , qapi.baseAPI.Now()) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 获取请求上下文 ctx := r.Context() // 解析超时参数。r.FormValue("timeout") 用于获取请求中的 "timeout" 参数,该参数指定了查询的超时时间。 if to := r.FormValue( "timeout" ); to != "" { var cancel context.CancelFunc // 解析超时时间参数, parseDuration 函数用于解析超时时间参数,并将其转换为 time.Duration 类型。 timeout, err := parseDuration(to) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 设置超时上下文 ctx, cancel = context.WithTimeout(ctx, timeout) defer cancel() } // 解析去重参数, qapi.parseEnableDedupParam 函数用于解析请求中的去重参数,并返回是否启用去重的布尔值和可能发生的 API 错误。 enableDedup, apiErr := qapi.parseEnableDedupParam(r) if apiErr != nil { return nil, nil, apiErr } // 解析副本标签参数, qapi.parseReplicaLabelsParam 函数用于解析请求中的副本标签参数,并返回副本标签的字符串切片和可能发生的 API 错误。 replicaLabels, apiErr := qapi.parseReplicaLabelsParam(r) if apiErr != nil { return nil, nil, apiErr } // 解析调试匹配器参数, qapi.parseStoreDebugMatchersParam 函数用于解析请求中的调试匹配器参数,并返回是否启用调试匹配器的布尔值和可能发生的 API 错误。 storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r) if apiErr != nil { return nil, nil, apiErr } // 解析部分响应参数, qapi.parsePartialResponseParam 函数用于解析请求中的部分响应参数,并返回是否启用部分响应的布尔值和可能发生的 API 错误。 enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse) if apiErr != nil { return nil, nil, apiErr } // 解析降采样参数, qapi.parseDownsamplingParamMillis 函数用于解析请求中的降采样参数,并返回最大源分辨率的毫秒数和可能发生的 API 错误。 maxSourceResolution, apiErr := qapi.parseDownsamplingParamMillis(r, qapi.defaultInstantQueryMaxSourceResolution) if apiErr != nil { return nil, nil, apiErr } // 获取查询引擎, qapi.queryEngine 函数返回一个查询引擎实例,该实例用于执行即时查询。 qe := qapi.queryEngine(maxSourceResolution) // 启动PromQL追踪跨度, tracing.StartSpan 函数用于启动一个PromQL追踪跨度。tracing.StartSpan 函数用于启动一个追踪跨度,该跨度与 PromQL 的即时查询相关联。 // We are starting promQL tracing span here, because we have no control over promQL code. span, ctx := tracing.StartSpan(ctx, "promql_instant_query" ) defer span.Finish() // 创建即时查询, qe.NewInstantQuery 函数用于创建一个新的即时查询实例。该实例将执行 PromQL 表达式,并返回查询结果。 qry, err := qe.NewInstantQuery(qapi.queryableCreate(enableDedup, replicaLabels, storeDebugMatchers, maxSourceResolution, enablePartialResponse, qapi.enableQueryPushdown, false), r.FormValue( "query" ), ts) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // tracing.DoInSpan 函数用于在指定的上下文中执行一个操作,并创建一个追踪跨度。tracing.DoInSpan 函数用于在给定的上下文和追踪跨度中执行一个操作。 tracing.DoInSpan(ctx, "query_gate_ismyturn" , func (ctx context.Context) { err = qapi.gate.Start(ctx) }) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err} } // qapi.gate.Done 函数用于完成查询操作,释放资源。qapi.gate.Start 在执行查询之前获取锁,确保只有一个查询可以同时运行。 defer qapi.gate.Done() res := qry.Exec(ctx) // qry.Exec 函数执行查询,并返回一个包含结果和警告的 Result 对象。 // res.Err 检查查询结果中的错误。如果存在错误,则根据错误的类型返回相应的 API 错误信息。 if res.Err != nil { // res.Err.(type) 是 Go 语言中的类型断言,用于检查 res.Err 的具体类型。 switch res.Err.( type ) { case promql.ErrQueryCanceled: // 查询被取消 return nil, nil, &api.ApiError{Typ: api.ErrorCanceled, Err: res.Err} case promql.ErrQueryTimeout: // 查询超时 return nil, nil, &api.ApiError{Typ: api.ErrorTimeout, Err: res.Err} case promql.ErrStorage: // 存储错误 return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: res.Err} } // 其他错误类型,返回执行错误的 API 错误信息 return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: res.Err} } // 如果请求参数中包含"stats",则在响应中包含可选的统计信息字段 var qs *stats.QueryStats // r.FormValues(Stats) 返回一个包含请求参数中所有 "stats" 值的切片。如果切片不为空,则创建一个新的 QueryStats 对象。 // 【问】:为什么要在这种情况下创建一个新的 QueryStats 对象? // 【答】:stats.NewQueryStats(qry.Stats()) 创建一个新的 QueryStats 对象,并将查询的统计信息传递给它。QueryStats 对象用于存储查询的统计信息,例如查询的持续时间、查询的执行时间、查询的内存使用情况等。这些信息可以帮助用户了解查询的性能和资源使用情况。 if r.FormValue(Stats) != "" { qs = stats.NewQueryStats(qry.Stats()) } return &queryData{ ResultType: res.Value.Type(), // 查询结果的类型,例如向量、标量等。 Result: res.Value, // 查询结果,可以是向量、标量等。 Stats: qs, // 可选的统计信息字段,如果请求参数中包含 "stats",则包含查询的统计信息。 }, res.Warnings, nil // 返回查询结果、警告和错误信息。 } // queryRange 方法用于处理HTTP请求,并返回查询结果、错误列表和API错误。 // 参数 r 表示传入的HTTP请求。 // 返回值包括: // - interface{} 类型的查询结果。 // - []error 类型的错误列表。 // - *api.ApiError 类型的API错误。 func (qapi *QueryAPI) queryRange(r *http.Request) ( interface {}, []error, *api.ApiError) { // 解析开始时间 start, err := parseTime(r.FormValue( "start" )) if err != nil { // 如果解析开始时间失败,返回错误 return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 解析结束时间 end, err := parseTime(r.FormValue( "end" )) if err != nil { // 如果解析结束时间失败,返回错误 return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 如果结束时间在开始时间之前,返回错误 if end.Before(start) { err := errors.New( "end timestamp must not be before start time" ) return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 解析查询步长。qapi.parseStep 方法用于解析请求中的步长参数,并返回一个表示查询步长的整数和一个可能的 API 错误。 // qapi.defaultRangeQueryStep 是一个默认的查询步长,用于在解析请求时如果没有指定步长则使用。默认值是 30 秒。 // int64(end.Sub(start)/time.Second) 是一个表示查询时间范围内的秒数的整数,用于计算查询步长。 step, apiErr := qapi.parseStep(r, qapi.defaultRangeQueryStep, int64(end.Sub(start)/time.Second)) if apiErr != nil { // 如果解析步长失败,返回错误 return nil, nil, apiErr } // 如果步长小于等于0,返回错误 if step <= 0 { // 如果步长小于等于0,返回错误,错误的中文意思是:“不接受零或负查询分辨率步长。尝试一个正整数” err := errors.New( "zero or negative query resolution step widths are not accepted. Try a positive integer" ) return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // end.Sub(start)/step > 11000 是一个条件,用于检查查询时间范围内返回点的数量是否超过 11,000 个。如果超过,则返回错误。 if end.Sub(start)/step > 11000 { // 如果超过,返回错误,错误的中文意思是:“超出了11,000个点时每个时间序列的最大分辨率。尝试减少查询分辨率(?step=XX)”。 err := errors.New( "exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)" ) return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 获取请求上下文 ctx := r.Context() // 解析请求超时时间 if to := r.FormValue( "timeout" ); to != "" { var cancel context.CancelFunc // parseDuartion(to) 函数用于解析超时时间,如果解析失败,返回错误 timeout, err := parseDuration(to) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 设置超时上下文 ctx, cancel = context.WithTimeout(ctx, timeout) defer cancel() } // 解析去重参数, qapi.parseEnableDedupParam 方法用于解析请求中的去重参数,并返回一个表示是否启用去重的布尔值和一个可能的 API 错误。 enableDedup, apiErr := qapi.parseEnableDedupParam(r) if apiErr != nil { return nil, nil, apiErr } // 解析副本标签参数, qapi.parseReplicaLabelsParam 方法用于解析请求中的副本标签参数,并返回一个表示是否启用副本标签的布尔值和一个可能的 API 错误。 replicaLabels, apiErr := qapi.parseReplicaLabelsParam(r) if apiErr != nil { return nil, nil, apiErr } // 解析存储调试匹配器参数, qapi.parseStoreDebugMatchersParam 方法用于解析请求中的存储调试匹配器参数,并返回一个表示是否启用存储调试匹配器的布尔值和一个可能的 API 错误。 storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r) if apiErr != nil { return nil, nil, apiErr } // 解析下采样参数, qapi.parseDownsamplingParamMillis 方法用于解析请求中的下采样参数,并返回一个表示最大源分辨率的整数和一个可能的 API 错误。 maxSourceResolution, apiErr := qapi.parseDownsamplingParamMillis(r, step/5) if apiErr != nil { return nil, nil, apiErr } // 解析部分响应参数, qapi.parsePartialResponseParam 方法用于解析请求中的部分响应参数,并返回一个表示是否启用部分响应的布尔值和一个可能的 API 错误。 enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse) if apiErr != nil { return nil, nil, apiErr } // 获取查询引擎, qe 方法用于获取查询引擎实例。它接受一个表示最大源分辨率的整数作为参数,并返回一个查询引擎接口和一个可能的错误。 qe := qapi.queryEngine(maxSourceResolution) // 记录查询范围, qapi.queryRangeHist 是一个用于记录查询范围的直方图,它接受一个表示查询持续时间(以秒为单位)的浮点数作为参数。这里,它记录了查询范围的持续时间。 qapi.queryRangeHist.Observe(end.Sub(start).Seconds()) // 开始PromQL追踪跨度, tracing.StartSpan 方法用于开始一个新的追踪跨度(span),它接受一个上下文和一个字符串作为参数,并返回该跨度和更新后的上下文。这里,它创建了一个名为 "promql_range_query" 的追踪跨度,并记录了查询的开始时间。这里,它记录了查询的开始时间("start"),并将其作为追踪跨度的标签。 span, ctx := tracing.StartSpan(ctx, "promql_range_query" ) defer span.Finish() // 创建新的范围查询, qe.NewRangeQuery 方法用于创建一个新的范围查询,它接受一个表示是否启用去重、副本标签、存储调试匹配器、最大源分辨率、部分响应和查询推送的布尔值,以及查询表达式、开始时间、结束时间和步长作为参数,并返回一个范围查询和一个可能的错误。这里,它创建了一个新的范围查询,并记录了查询的开始时间("start")和结束时间("end")。这里,它记录了查询的开始时间("start")和结束时间("end")。 qry, err := qe.NewRangeQuery( // 启用去重、副本标签、存储调试匹配器、最大源分辨率、部分响应和查询推送的布尔值 qapi.queryableCreate( enableDedup, // 启用去重 replicaLabels, // 启用副本标签 storeDebugMatchers, // 启用存储调试匹配器 maxSourceResolution, // 最大源分辨率 enablePartialResponse, // 启用部分响应 qapi.enableQueryPushdown, false), // 启用查询推送down r.FormValue( "query" ), // query 表单字段中的查询表达式 start, // 开始时间 end, // 结束时间 step, // 步长 ) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 执行查询, query_gate_ismyturn 是一个追踪跨度,它用于跟踪查询 gate 是否处于打开状态。这里,它使用 tracing.DoInSpan 方法来执行查询,并在查询完成后关闭查询门。这里,它首先尝试获取查询 gate 的状态,如果成功则执行查询。 tracing.DoInSpan(ctx, "query_gate_ismyturn" , func (ctx context.Context) { err = qapi.gate.Start(ctx) }) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err} } defer qapi.gate.Done() // 关闭查询 gate // 执行查询 res := qry.Exec(ctx) if res.Err != nil { switch res.Err.( type ) { // 如果查询被取消,返回错误 case promql.ErrQueryCanceled: return nil, nil, &api.ApiError{Typ: api.ErrorCanceled, Err: res.Err} // 如果查询超时,返回错误 case promql.ErrQueryTimeout: return nil, nil, &api.ApiError{Typ: api.ErrorTimeout, Err: res.Err} } // 如果查询执行出错,返回错误 return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: res.Err} } // 如果请求中包含"stats"参数,则在响应中包含统计信息 var qs *stats.QueryStats if r.FormValue(Stats) != "" { qs = stats.NewQueryStats(qry.Stats()) } return &queryData{ ResultType: res.Value.Type(), Result: res.Value, Stats: qs, }, res.Warnings, nil } // labelValues 函数用于处理获取标签值的HTTP请求 // // 参数: // // r *http.Request: 传入的HTTP请求 // // 返回值: // // interface{}: 获取到的标签值切片 // []error: 错误信息切片(当前未使用) // *api.ApiError: API错误对象 func (qapi *QueryAPI) labelValues(r *http.Request) ( interface {}, []error, *api.ApiError) { // 获取请求上下文 ctx := r.Context() // 从请求上下文中获取参数name name := route.Param(ctx, "name" ) // 验证name是否符合标签名正则表达式 if !model.LabelNameRE.MatchString(name) { // 如果不符合,返回错误 return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf( "invalid label name: %q" , name)} } // 解析请求中的时间范围 start, end, err := parseMetadataTimeRange(r, qapi.defaultMetadataTimeRange) if err != nil { // 如果解析失败,返回错误 return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 解析请求中的partial_response参数 enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse) if apiErr != nil { // 如果解析失败,返回错误 return nil, nil, apiErr } // 解析请求中的store_debug_matchers参数 storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r) if apiErr != nil { // 如果解析失败,返回错误 return nil, nil, apiErr } // 解析请求中的Matcher参数 var matcherSets [][]*labels.Matcher for _, s := range r.Form[MatcherParam] { // 解析每个Matcher参数 matchers, err := parser.ParseMetricSelector(s) if err != nil { // 如果解析失败,返回错误 return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 将解析得到的matchers添加到matcherSets中 matcherSets = append(matcherSets, matchers) } // 创建Querier, qapi.queryableCreate 是一个函数,用于创建一个可查询的实例。这里它被用来创建Querier对象,该对象可以执行标签值查询等操作。 q, err := qapi.queryableCreate(true, nil, storeDebugMatchers, 0, enablePartialResponse, qapi.enableQueryPushdown, true). Querier(ctx, timestamp.FromTime(start), timestamp.FromTime(end)) if err != nil { // 如果创建Querier失败,返回错误 return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err} } // 关闭Querier时记录日志 defer runutil.CloseWithLogOnErr(qapi.logger, q, "queryable labelValues" ) // 存储标签值的切片和警告信息 var ( vals []string warnings storage.Warnings ) // 如果matcherSets不为空 if len(matcherSets) > 0 { // 存储每次调用LabelValues返回的警告信息 var callWarnings storage.Warnings // 存储所有标签值的集合 labelValuesSet := make( map [string] struct {}) // 遍历每个matcherSet for _, matchers := range matcherSets { // 调用LabelValues获取标签值 vals, callWarnings, err = q.LabelValues(name, matchers...) if err != nil { // 如果调用失败,返回错误 return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err} } // 将每次调用返回的警告信息添加到warnings中 warnings = append(warnings, callWarnings...) // 将每个标签值添加到labelValuesSet中 for _, val := range vals { labelValuesSet[val] = struct {}{} } } // 将labelValuesSet转换为切片并排序 vals = make([]string, 0, len(labelValuesSet)) for val := range labelValuesSet { vals = append(vals, val) } sort.Strings(vals) } else { // 如果matcherSets为空,直接调用LabelValues获取标签值 vals, warnings, err = q.LabelValues(name) if err != nil { // 如果调用失败,返回错误 return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err} } } // 如果vals为空,则初始化为空切片 if vals == nil { vals = make([]string, 0) } // 返回标签值切片、警告信息和nil错误 return vals, warnings, nil } // series 函数用于处理时间序列查询请求。 // // 参数: // - r: *http.Request, HTTP请求对象,包含查询参数和表单数据。 // // 返回值: // - interface{}, 查询结果,具体类型取决于查询结果。 // - []error, 错误列表,可能包含多个错误。 // - *api.ApiError, API错误,如果发生API级别的错误则返回此错误。 func (qapi *QueryAPI) series(r *http.Request) ( interface {}, []error, *api.ApiError) { // 解析请求表单 if err := r.ParseForm(); err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "parse form" )} } // 检查请求表单中是否提供了匹配参数 if len(r.Form[MatcherParam]) == 0 { return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.New( "no match[] parameter provided" )} } // 解析时间范围,parseMetadataTimeRange 用于解析请求中的时间范围参数,例如从和到的时间。如果解析失败,则返回错误信息。 start, end, err := parseMetadataTimeRange(r, qapi.defaultMetadataTimeRange) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 初始化匹配器集合 var matcherSets [][]*labels.Matcher for _, s := range r.Form[MatcherParam] { // 解析每个匹配器字符串 matchers, err := parser.ParseMetricSelector(s) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 将解析后的匹配器添加到匹配器集合中 matcherSets = append(matcherSets, matchers) } // 解析去重参数 enableDedup, apiErr := qapi.parseEnableDedupParam(r) if apiErr != nil { return nil, nil, apiErr } // 解析副本标签参数 replicaLabels, apiErr := qapi.parseReplicaLabelsParam(r) if apiErr != nil { return nil, nil, apiErr } // 解析存储调试匹配器参数 storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r) if apiErr != nil { return nil, nil, apiErr } // 解析部分响应参数 enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse) if apiErr != nil { return nil, nil, apiErr } // 创建查询器, qapi.queryableCrete 用于创建查询器实例,该函数接受一系列参数并返回一个Querier接口的实现。 q, err := qapi.queryableCreate(enableDedup, replicaLabels, storeDebugMatchers, math.MaxInt64, enablePartialResponse, qapi.enableQueryPushdown, true). Querier(r.Context(), timestamp.FromTime(start), timestamp.FromTime(end)) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err} } // 确保查询器在函数结束时关闭 defer runutil.CloseWithLogOnErr(qapi.logger, q, "queryable series" ) // 初始化存储指标集合和系列集合 var ( metrics = []labels.Labels{} sets []storage.SeriesSet ) // 对每个匹配器集合执行选择操作 for _, mset := range matcherSets { sets = append(sets, q.Select(false, nil, mset...)) } // 合并系列集合 set := storage.NewMergeSeriesSet(sets, storage.ChainedSeriesMerge) // 遍历合并后的系列集合 for set.Next() { // 将当前系列的标签添加到指标集合中 metrics = append(metrics, set.At().Labels()) } // 检查是否有错误发生 if set.Err() != nil { return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: set.Err()} } return metrics, set.Warnings(), nil } // labelNames 函数用于处理HTTP请求,并返回接口、错误列表和ApiError指针。 // 参数 r 表示HTTP请求对象。 // // 返回结果: // - 接口:返回标签名称的切片。 // - 错误列表:返回可能发生的错误列表。 // - ApiError指针:如果发生API错误,将返回ApiError指针。 func (qapi *QueryAPI) labelNames(r *http.Request) ( interface {}, []error, *api.ApiError) { // 解析请求中的时间范围参数,如果解析失败,则返回错误信息。 start, end, err := parseMetadataTimeRange(r, qapi.defaultMetadataTimeRange) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 解析部分响应参数 enablePartialResponse, apiErr := qapi.parsePartialResponseParam(r, qapi.enableQueryPartialResponse) if apiErr != nil { return nil, nil, apiErr } // 解析存储调试匹配器参数 storeDebugMatchers, apiErr := qapi.parseStoreDebugMatchersParam(r) if apiErr != nil { return nil, nil, apiErr } var matcherSets [][]*labels.Matcher for _, s := range r.Form[MatcherParam] { // 解析度量选择器参数 matchers, err := parser.ParseMetricSelector(s) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } matcherSets = append(matcherSets, matchers) } // 创建查询对象 q, err := qapi.queryableCreate(true, nil, storeDebugMatchers, 0, enablePartialResponse, qapi.enableQueryPushdown, true). Querier(r.Context(), timestamp.FromTime(start), timestamp.FromTime(end)) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err} } // 确保查询器在函数结束时关闭 defer runutil.CloseWithLogOnErr(qapi.logger, q, "queryable labelNames" ) var ( names []string warnings storage.Warnings ) // 如果匹配器集合不为空 if len(matcherSets) > 0 { var callWarnings storage.Warnings // 创建标签名集合 labelNamesSet := make( map [string] struct {}) for _, matchers := range matcherSets { // 获取标签名集合 names, callWarnings, err = q.LabelNames(matchers...) if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err} } // 将每次调用返回的警告信息添加到warnings中 warnings = append(warnings, callWarnings...) // 将每个标签名添加到labelNamesSet中 for _, val := range names { labelNamesSet[val] = struct {}{} } } // 将标签名集合转换为字符串切片并排序 names = make([]string, 0, len(labelNamesSet)) // 遍历标签名集合 for name := range labelNamesSet { names = append(names, name) } sort.Strings(names) // 对标签名进行排序 } else { // 如果匹配器集合为空,则获取所有标签名 names, warnings, err = q.LabelNames() } if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorExec, Err: err} } if names == nil { // 如果标签名集合为空,则初始化为空切片 names = make([]string, 0) } return names, warnings, nil } // stores 处理函数用于从QueryAPI结构体中获取所有组件类型的状态列表。 // // 参数: // // _ *http.Request: 请求对象,本函数不使用该参数。 // // 返回值: // // interface{}: 包含每种组件类型的状态列表的map,键为组件类型字符串,值为对应组件类型的状态列表。 // []error: 错误列表,本函数始终返回空列表。 // *api.ApiError: ApiError对象,本函数始终返回nil。 func (qapi *QueryAPI) stores(_ *http.Request) ( interface {}, []error, *api.ApiError) { // 创建一个map来存储每种组件类型的状态列表 statuses := make( map [string][]query.EndpointStatus) // 遍历所有端点状态 for _, status := range qapi.endpointStatus() { // 如果无法获取组件类型,则忽略该端点 // Don't consider an endpoint if we cannot retrieve component type. if status.ComponentType == nil { continue } // 将端点状态添加到对应组件类型的列表中 statuses[status.ComponentType.String()] = append(statuses[status.ComponentType.String()], status) } // 返回所有组件类型的状态列表、空错误列表和空ApiError return statuses, nil, nil } // NewTargetsHandler created handler compatible with HTTP /api/v1/targets https://prometheus.io/docs/prometheus/latest/querying/api/#targets // which uses gRPC Unary Targets API. // NewTargetsHandler 函数用于创建一个新的目标处理器。 // 该处理器根据请求中的参数,从客户端获取目标列表,并返回这些目标、警告信息和API错误(如果有的话)。 // // 参数: // // client targets.UnaryClient: 用于获取目标列表的客户端。 // enablePartialResponse bool: 是否启用部分响应。如果启用,则在获取目标时,如果某些目标无法获取,则返回这些目标的警告信息,而不是直接返回错误。 // // 返回值: // // func(*http.Request) (interface{}, []error, *api.ApiError): 一个函数,该函数接受一个HTTP请求,并返回一个接口(表示获取到的目标列表),一个错误切片(表示警告信息),以及一个API错误(如果有的话)。 func NewTargetsHandler(client targets.UnaryClient, enablePartialResponse bool) func (*http.Request) ( interface {}, []error, *api.ApiError) { // 定义部分响应策略,默认为ABORT ps := storepb.PartialResponseStrategy_ABORT // 如果启用部分响应,则将策略更改为WARN if enablePartialResponse { ps = storepb.PartialResponseStrategy_WARN } return func (r *http.Request) ( interface {}, []error, *api.ApiError) { // 从请求URL中获取state参数 stateParam := r.URL.Query().Get( "state" ) // 将state参数转换为大写,并尝试从targetspb.TargetsRequest_State_value中查找对应的枚举值 state, ok := targetspb.TargetsRequest_State_value[strings.ToUpper(stateParam)] // 如果未找到对应的枚举值 if !ok { // 如果state参数不为空且未找到对应的枚举值,则返回错误信息 if stateParam != "" { return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf( "invalid targets parameter state='%v'" , stateParam)} } // 如果state参数为空,则默认使用ANY状态 state = int32(targetspb.TargetsRequest_ANY) } // 构造TargetsRequest请求 req := &targetspb.TargetsRequest{ State: targetspb.TargetsRequest_State(state), PartialResponseStrategy: ps, } // 调用客户端的Targets方法获取目标列表 t, warnings, err := client.Targets(r.Context(), req) // 如果发生错误,则返回错误信息 if err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "retrieving targets" )} } // 返回获取到的目标列表和警告信息 return t, warnings, nil } } // NewRulesHandler created handler compatible with HTTP /api/v1/rules https://prometheus.io/docs/prometheus/latest/querying/api/#rules // which uses gRPC Unary Rules API. // NewRulesHandler 创建一个处理HTTP请求的函数,该函数用于处理与规则相关的请求。 // 参数: // // client:rules.UnaryClient接口的实现,用于处理与规则相关的请求。 // enablePartialResponse:布尔值,表示是否启用部分响应。 // // 返回值: // // 返回的函数接受一个*http.Request类型的参数,返回一个interface{}类型的响应,一个[]error类型的错误列表,以及一个*api.ApiError类型的API错误。 func NewRulesHandler(client rules.UnaryClient, enablePartialResponse bool) func (*http.Request) ( interface {}, []error, *api.ApiError) { // 设置部分响应策略 ps := storepb.PartialResponseStrategy_ABORT if enablePartialResponse { ps = storepb.PartialResponseStrategy_WARN } return func (r *http.Request) ( interface {}, []error, *api.ApiError) { // 开始追踪HTTP请求处理过程 span, ctx := tracing.StartSpan(r.Context(), "receive_http_request" ) defer span.Finish() // 声明变量 var ( groups *rulespb.RuleGroups warnings storage.Warnings err error ) // 获取请求参数 typeParam := r.URL.Query().Get( "type" ) // 将 typeParam 转换为大写,并尝试从 rulespb.RulesRequest_Type_value 中查找对应的枚举值 typ, ok := rulespb.RulesRequest_Type_value[strings.ToUpper(typeParam)] if !ok { // 如果参数类型无效 if typeParam != "" { return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf( "invalid rules parameter type='%v'" , typeParam)} } // 默认为ALL类型 typ = int32(rulespb.RulesRequest_ALL) } // 解析表单数据 if err := r.ParseForm(); err != nil { return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Errorf( "error parsing request form='%v'" , MatcherParam)} } // TODO(bwplotka): Allow exactly the same functionality as query API: passing replica, dedup and partial response as HTTP params as well. // 创建RulesRequest请求 req := &rulespb.RulesRequest{ Type: rulespb.RulesRequest_Type(typ), // 设置规则类型 PartialResponseStrategy: ps, // 设置部分响应策略 MatcherString: r.Form[MatcherParam], // 设置匹配器字符串 } // 在追踪上下文中执行获取规则操作 tracing.DoInSpan(ctx, "retrieve_rules" , func (ctx context.Context) { groups, warnings, err = client.Rules(ctx, req) }) if err != nil { // 如果获取规则失败 return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Errorf( "error retrieving rules: %v" , err)} } return groups, warnings, nil } } // NewExemplarsHandler creates handler compatible with HTTP /api/v1/query_exemplars https://prometheus.io/docs/prometheus/latest/querying/api/#querying-exemplars // which uses gRPC Unary Exemplars API. // NewExemplarsHandler 创建一个处理Exemplars请求的处理器。 // 该处理器返回一个函数,该函数接收一个http.Request对象,并返回一个接口、错误列表和ApiError指针。 // // 参数: // - client: exemplars.UnaryClient接口,用于与存储层进行通信。 // - enablePartialResponse: bool类型,表示是否启用部分响应。 // // 返回值: // - func(*http.Request) (interface{}, []error, *api.ApiError): 接收http.Request对象的函数, // 返回一个接口、错误列表和ApiError指针。 func NewExemplarsHandler(client exemplars.UnaryClient, enablePartialResponse bool) func (*http.Request) ( interface {}, []error, *api.ApiError) { // storepb.PartialResponseStrategy_ABORT 是部分响应策略的默认值,表示在部分响应策略中,如果遇到错误,则直接返回错误。 ps := storepb.PartialResponseStrategy_ABORT if enablePartialResponse { // 如果启用部分响应,则设置为警告策略 ps = storepb.PartialResponseStrategy_WARN } return func (r *http.Request) ( interface {}, []error, *api.ApiError) { // 创建跟踪跨度 span, ctx := tracing.StartSpan(r.Context(), "exemplar_query_request" ) defer span.Finish() // 声明存储返回数据的变量 var ( data []*exemplarspb.ExemplarData warnings storage.Warnings err error ) // 解析请求中的起始时间参数 start, err := parseTimeParam(r, "start" , infMinTime) if err != nil { // 如果解析起始时间参数出错,则返回错误 return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 解析请求中的结束时间参数 end, err := parseTimeParam(r, "end" , infMaxTime) if err != nil { // 如果解析结束时间参数出错,则返回错误 return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 创建请求 req := &exemplarspb.ExemplarsRequest{ Start: timestamp.FromTime(start), End: timestamp.FromTime(end), Query: r.FormValue( "query" ), PartialResponseStrategy: ps, } // 在跨度内执行获取示例的操作 tracing.DoInSpan(ctx, "retrieve_exemplars" , func (ctx context.Context) { data, warnings, err = client.Exemplars(ctx, req) }) if err != nil { // 如果获取示例出错,则返回内部错误 return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "retrieving exemplars" )} } return data, warnings, nil } } var ( infMinTime = time.Unix(math.MinInt64/1000+62135596801, 0) infMaxTime = time.Unix(math.MaxInt64/1000-62135596801, 999999999) ) // parseMetadataTimeRange 从HTTP请求中解析时间范围,并返回起始时间和结束时间 // // 参数: // // r: *http.Request, HTTP请求对象 // defaultMetadataTimeRange: time.Duration, 默认的时间范围 // // 返回值: // // time.Time, 起始时间 // time.Time, 结束时间 // error, 错误信息 // // 说明: // // 如果请求参数中未指定起始时间和结束时间,则默认从时间开始处获取时间范围。 // 如果defaultMetadataTimeRange为0,则默认时间范围为时间开始到时间结束。 func parseMetadataTimeRange(r *http.Request, defaultMetadataTimeRange time.Duration) (time.Time, time.Time, error) { // 如果未指定查询参数中的开始时间和结束时间,则默认获取从时间起点到当前时间的范围 // If start and end time not specified as query parameter, we get the range from the beginning of time by default. var defaultStartTime, defaultEndTime time.Time if defaultMetadataTimeRange == 0 { defaultStartTime = infMinTime defaultEndTime = infMaxTime } else { now := time.Now() defaultStartTime = now.Add(-defaultMetadataTimeRange) defaultEndTime = now } // 解析查询参数中的开始时间 // Parse the start time from the query parameter start, err := parseTimeParam(r, "start" , defaultStartTime) if err != nil { return time.Time{}, time.Time{}, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 解析查询参数中的结束时间 // Parse the end time from the query parameter end, err := parseTimeParam(r, "end" , defaultEndTime) if err != nil { return time.Time{}, time.Time{}, &api.ApiError{Typ: api.ErrorBadData, Err: err} } // 如果结束时间在开始时间之前,则抛出错误 // If the end time is before the start time, throw an error if end.Before(start) { return time.Time{}, time.Time{}, &api.ApiError{ Typ: api.ErrorBadData, Err: errors.New( "end timestamp must not be before start time" ), } } return start, end, nil } // parseTimeParam 从HTTP请求中解析时间参数,并返回时间 // // 参数: // // r: *http.Request, HTTP请求对象 // paramName: string, 参数名称 // defaultValue: time.Time, 默认时间 // // 返回值: // // time.Time, 时间 // error, 错误信息 // // parseTimeParam 从HTTP请求中解析时间参数,并返回时间 // // 参数: // // r: *http.Request, HTTP请求对象 // paramName: string, 参数名称 // defaultValue: time.Time, 默认时间 // // 返回值: // // time.Time, 时间 // error, 错误信息 func parseTimeParam(r *http.Request, paramName string, defaultValue time.Time) (time.Time, error) { // 从请求中获取参数值 val := r.FormValue(paramName) // 如果参数值为空,则返回默认值 if val == "" { return defaultValue, nil } // 解析时间值 result, err := parseTime(val) // 如果解析过程中出现错误,则返回错误信息 if err != nil { // 封装错误信息 return time.Time{}, errors.Wrapf(err, "Invalid time value for '%s'" , paramName) } // 返回解析后的时间值和错误信息 return result, nil } // parseTime 函数尝试将字符串 s 解析为 time.Time 类型的时间对象,并返回该对象以及错误信息。 // 如果解析成功,则返回 time.Time 对象和 nil 错误信息。 // 如果解析失败,则返回 time.Time{} 和错误信息。 // // 参数: // // s string:需要解析的时间字符串 // // 返回值: // // time.Time:解析得到的时间对象 // error:错误信息,如果解析成功则为 nil func parseTime(s string) (time.Time, error) { // 尝试将字符串转换为浮点数 if t, err := strconv.ParseFloat(s, 64); err == nil { // 分离秒和纳秒部分 s, ns := math.Modf(t) // 对纳秒部分进行四舍五入并转换为整数 ns = math.Round(ns*1000) / 1000 // 使用秒和纳秒部分构造时间对象 return time.Unix(int64(s), int64(ns*float64(time.Second))), nil } // 尝试按照RFC3339Nano格式解析时间字符串 if t, err := time.Parse(time.RFC3339Nano, s); err == nil { // 解析成功,返回时间对象 return t, nil } // 如果以上两种方式都失败,返回错误 return time.Time{}, errors.Errorf( "cannot parse %q to a valid timestamp" , s) } // parseDuration 函数将字符串解析为 time.Duration 类型。 // 如果字符串可以成功转换为浮点数,则将其转换为秒数,并返回对应的 time.Duration。 // 如果转换后的秒数超出了 int64 的范围,则返回错误。 // 如果字符串无法转换为浮点数,则尝试使用 model.ParseDuration 函数解析。 // 如果解析成功,则返回对应的 time.Duration;否则返回错误。 func parseDuration(s string) (time.Duration, error) { // 尝试将字符串转换为浮点数 if d, err := strconv.ParseFloat(s, 64); err == nil { // 将浮点数转换为秒数 ts := d * float64(time.Second) // 检查转换后的秒数是否溢出int64范围 if ts > float64(math.MaxInt64) || ts < float64(math.MinInt64) { // 如果溢出,则返回错误 return 0, errors.Errorf( "cannot parse %q to a valid duration. It overflows int64" , s) } // 返回转换后的time.Duration return time.Duration(ts), nil } // 使用自定义的模型解析函数尝试解析字符串 if d, err := model.ParseDuration(s); err == nil { // 返回解析后的time.Duration return time.Duration(d), nil } // 如果以上解析都失败,则返回错误 return 0, errors.Errorf( "cannot parse %q to a valid duration" , s) } // NewMetricMetadataHandler creates handler compatible with HTTP /api/v1/metadata https://prometheus.io/docs/prometheus/latest/querying/api/#querying-metric-metadata // which uses gRPC Unary Metadata API. // NewMetricMetadataHandler 创建一个处理指标元数据请求的函数。 // client: 元数据 UnaryClient 接口。 // enablePartialResponse: 是否启用部分响应。 // 返回值: 一个函数,该函数接受一个 *http.Request 参数,并返回一个 interface{} 类型的指标元数据、一个 []error 类型的错误列表,以及一个 *api.ApiError 类型的 API 错误。 func NewMetricMetadataHandler(client metadata.UnaryClient, enablePartialResponse bool) func (*http.Request) ( interface {}, []error, *api.ApiError) { // 设置部分响应策略 ps := storepb.PartialResponseStrategy_ABORT if enablePartialResponse { ps = storepb.PartialResponseStrategy_WARN } return func (r *http.Request) ( interface {}, []error, *api.ApiError) { // 开始追踪HTTP请求 span, ctx := tracing.StartSpan(r.Context(), "metadata_http_request" ) defer span.Finish() var ( // 存储指标元数据 t map [string][]metadatapb.Meta // 存储警告信息 warnings storage.Warnings // 存储错误信息 err error ) // 构建指标元数据请求 req := &metadatapb.MetricMetadataRequest{ // 默认情况下,我们使用-1,表示没有限制。 Limit: -1, Metric: r.URL.Query().Get( "metric" ), PartialResponseStrategy: ps, } // 获取URL中的limit参数 limitStr := r.URL.Query().Get( "limit" ) if limitStr != "" { // 将limit参数转换为整数 limit, err := strconv.ParseInt(limitStr, 10, 32) if err != nil { // 返回API错误 return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.Errorf( "invalid metric metadata limit='%v'" , limit)} } req.Limit = int32(limit) } // 在span中执行获取元数据操作 tracing.DoInSpan(ctx, "retrieve_metadata" , func (ctx context.Context) { // 从客户端获取指标元数据 t, warnings, err = client.MetricMetadata(ctx, req) }) if err != nil { // 返回API错误 return nil, nil, &api.ApiError{Typ: api.ErrorInternal, Err: errors.Wrap(err, "retrieving metadata" )} } // 返回指标元数据、警告信息和错误信息 return t, warnings, nil } } |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
2022-02-14 Go从入门到精通——映射(map)——建立事物关联的容器
2022-02-14 Go从入门到精通——切片(slice)(动态分配大小的连续空间)