赞
踩
在 sentinel-extension 模块下,新增 sentinel-prometheus-metric-exporter 模块。依赖Prometheus 提供的 simpleclient 和 simpleclient_httpserver 来实现 exporter。
依赖 simpleclient 主要是为了实现自定义Collector:SentinelCollector, SentinelCollector 继承了抽象类 Collector,实现 collect 方法来获取指标样本数据。指标样本数据的获取是通过 MetricSearcher 对象读取 metrics.log 文件内容得到的。
metrics.log 文件内容,包含时间、资源名、通过 QPS、block QPS 等数据:
simpleclient_httpserver 主要用来实现一个简单的 HTTP 服务器,接收来自 Prometheus 获取指标数据的请求。
public void init() throws Exception { HTTPServer server = null; try { // 注册自定义 Collector new SentinelCollector().register(); // 开启http服务供prometheus调用 // 默认只提供一个接口 http://ip:port/metrics,返回所有指标 int promPort = PrometheusGlobalConfig.getPromFetchPort(); server = new HTTPServer(promPort); } catch (Throwable e) { RecordLog.warn("[PromExporterInit] failed to init prometheus exporter with exception:", e); } HTTPServer finalServer = server; Runtime.getRuntime().addShutdownHook(new Thread(() -> { if (finalServer != null) { finalServer.stop(); } })); }
init 方法启动,先注册 SentinelCollector ,然后启动 HTTPServer 用于接收 prometheus 等请求。
public List<MetricFamilySamples> collect() { // 初始化指标读取对象 if (searcher == null) { synchronized (lock) { if (searcher == null) { searcher = new MetricSearcher(MetricWriter.METRIC_BASE_DIR, MetricWriter.formMetricFileName(SentinelConfig.getAppName(), PidUtil.getPid())); } RecordLog.warn("[SentinelCollector] init sentinel metrics searcher with appName:{}", appName); lastFetchTime = System.currentTimeMillis() / ONE_SECOND * ONE_SECOND; } } List<MetricFamilySamples> list = new ArrayList<>(); long endTime = System.currentTimeMillis() / ONE_SECOND * ONE_SECOND - (long) delayTime * ONE_SECOND; try { // 读取指标数据 List<MetricNode> nodes = searcher.findByTimeAndResource(lastFetchTime, endTime, identify); if(nodes == null){ return list; } if(nodes.size() > fetchSize){ nodes = nodes.subList(0,fetchSize); } GaugeMetricFamily metricFamily = new GaugeMetricFamily(appName, MetricConstants.METRIC_HELP, Arrays.asList(MetricConstants.RESOURCE, MetricConstants.CLASSIFICATION, MetricConstants.METRIC_TYPE)); for (MetricNode node : nodes) { // 转化成 prometheus 要求的指标数据模型 long recordTime = node.getTimestamp(); for (String type : types) { double val = getTypeVal(node,type); metricFamily.addMetric(Arrays.asList(node.getResource(), String.valueOf(node.getClassification()),type), val,recordTime); } } list.add(metricFamily); } catch (Exception e) { RecordLog.warn("[SentinelCollector] failed to fetch sentinel metrics with exception:", e); }finally { lastFetchTime = endTime + ONE_SECOND; } return list; }
SentinelCollector collect 方法实现具体的读取 metrics.log 文件逻辑。
主要是通过增加 RuleManager 来统一管理普通规则和正则规则,为了减少正则解析/匹配所带来的性能损耗,增加了资源和匹配后的正则限流规则缓存,当获取资源规则时直接从Map 中读取缓存,避免每次都重新进行一次正则匹配。这是一种典型的空间换时间的做法。
另外在修改完规则后,更新时也会根据现有的缓存关系,重新构建新的缓存关系。
类图:
private Map<Pattern, List<R>> regexRules = new HashMap<>();
// 缓存资源和对应的正则规则
private Map<String, List<R>> regexCacheRules = new HashMap<>();
private Map<String, List<R>> simpleRules = new HashMap<>();
获取规则
public List<R> getRules(String resource) { List<R> result = new ArrayList<>(simpleRules.getOrDefault(resource, Collections.emptyList())); if (regexRules.isEmpty()) { return result; } if (regexCacheRules.containsKey(resource)) { result.addAll(regexCacheRules.get(resource)); return result; } synchronized (this) { if (regexCacheRules.containsKey(resource)) { result.addAll(regexCacheRules.get(resource)); return result; } List<R> compilers = matcherFromRegexRules(resource); regexCacheRules.put(resource, compilers); result.addAll(compilers); return result; } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。