【solon生态】- solon.cloud.micrometer插件使用指南及micrometer详解
solon.cloud.micrometer插件使用指南
- solon是什么
- solon的cloud生态图
- 快速入门
- micrometer指南
- micrometer是什么
- 监控系统 Supported Monitoring Systems
- 注册表 Registry
- 度量 Meters
- 度量名 Naming Meters
- 度量标签 Tag Naming
- 通用标签 Common Tags
- 指标过滤器 MeterFilter
- 聚合速率
- 指标类型
- 计数器 Counters
- 函数式计数器 Function-tracking Counters
- 仪表 Gauges
- 时间仪表 TimeGauge
- Multi-gauge
- 计时器 Timers
- @Timed 计时器注解
- 函数式计时器 Function-tracking Timers
- 暂停检测 Pause Detection
- 内存统计 Memory Footprint Estimation
- Distribution Summaries
- 长任务计时器 Long Task Timers
- Histograms and Percentiles
- solon-micrometer插件使用
- 观测地址
- 如何使用
- 标签定制
- 增加度量器和过滤器
- prometheus
- 安装使用
solon是什么
- Java 全新的生态型应用开发框架:更快、更小、更简单。
相比 spring 启动快 5 ~ 10 倍;qps 高 2~ 3 倍;运行时内存节省 1/3 ~ 1/2;打包可以缩到 1/2 ~ 1/10;同时支持 jdk8, jdk11, jdk17, jdk20, graalvm native image。 - 独特的 IOC/AOP 容器设计。不会因为插件变多而启动变很慢
- Http、WebSocket、Socket 三种信号统一的开发体验(俗称:三源合一)
- Not Servlet,可以适配任何基础通讯框架(最小 0.3m 运行rpc架构)
- 支持 Web、Data、Job、Remoting、Cloud 等任何开发场景
- 兼顾 Handler + Context 和 Listener + Message 两种架构模式
- 强调插件式扩展,可扩展可切换;适应不同的应用场景
- 支持 GraalVm Native Image 打包
- 允许业务插件“热插”、“热拔”、“热管理”
- 采用插件式应用开发的,内部集成了200+主流插件,能胜任企业日常开发,至今发展已有6年时间,已经有很完整的生态体系
- 官网传送门:官网包括了solon的全生态教程,能让不同阶段的道友快速上手
- gitee https://gitee.com/noear/solon
- github https://github.com/noear/solon
solon的cloud生态图
快速入门
- pom.xml
<parent><groupId>org.noear</groupId><artifactId>solon-parent</artifactId><version>${solon.version}</version>
</parent><dependencies><dependency><!-- 引入 Web 快速开发集成包 --><groupId>org.noear</groupId><artifactId>solon-web</artifactId></dependency>
</dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><!-- 引入打包插件 --><groupId>org.noear</groupId><artifactId>solon-maven-plugin</artifactId></plugin></plugins>
</build>
- 主启动类如下,@Controller加在启动类上,是为了更好的扫描mapping,正常的启动类是@SolonMain
@Controller
public class App {public static void main(String[] args) {Solon.start(App.class, args, app -> {//handler模式app.get("/hello1", ctx -> ctx.output("Hello world!"));});}@Get@Socket@Mapping("/hello2")public String hello2(String name) {return String.format("Hello %s!", name);}
}
- 配置文件
server.port: 8080
solon.app:group: "demo"name: "demoapp"
micrometer指南
io.micrometer是一种用于应用程序的度量库。它提供了一组简单且易于使用的API,用于收集和展示应用程序的各种度量指标,如计数器、计时器、分布式摘要和计量。io.micrometer能够与多种监控系统和工具集成,如Prometheus、Graphite、InfluxDB等,以便在应用程序运行时监控和可视化指标数据,并进行性能分析和故障排查。io.micrometer可以用于各种Java和JVM语言开发的应用程序,包括Spring Boot、Dropwizard、Quarkus等。
micrometer是什么
官网传送门
- Vendor-neutral application observability facade
- Micrometer provides a simple facade over the instrumentation clients for the most popular
observability systems, allowing you to instrument your JVM-based
application code without vendor lock-in. Think SLF4J, but for observability.
Micrometer 为最流行的可观察性系统提供了检测客户端的简单外观,允许您检测基于jvm的应用程序代码,而不受供应商的限制, 考虑SLF4J,但要考虑可观察性。
监控系统 Supported Monitoring Systems
Micrometer包含一个带有仪表SPl的核心模块、一组包含各种监控系统实现的模块(每个都称为注册表)和一个测试套件。你需要了解的三个重要特征监控系统:
- 维度(Dimensionality):描述系统是否支持多维度数据模型。
Dimensional | Hierarchical |
---|---|
AppOptics, Atlas, Azure Monitor, Cloudwatch, Datadog, Datadog StatsD, Dynatrace, Elastic, Humio, Influx, KairosDB, New Relic, Prometheus, SignalFx, Sysdig StatsD, Telegraf StatsD, Wavefront | Graphite, Ganglia, JMX, Etsy StatsD |
- 速率聚合(Rate Aggregation):指的是在规定的时间间隔内的一组样本聚合。一种是指标数据发送前在客户端做速率聚合,另一种是直接发送聚合值。
Client-side | Server-side |
---|---|
AppOptics, Atlas, Azure Monitor, Datadog, Dynatrace, Elastic, Graphite, Ganglia, Humio, Influx, JMX, Kairos, New Relic, all StatsD flavors, SignalFx | Prometheus, Wavefront |
- 发布(Publishing):描述的是指标数据的发布方式,一种是客户端定时将数据推送给监控系统,还有一种是监控系统在空闲时间自己调客户端接口拉数据。
Client pushes | Server polls |
---|---|
AppOptics, Atlas, Azure Monitor, Datadog, Dynatrace, Elastic, Graphite, Ganglia, Humio, Influx, JMX, Kairos, New Relic, SignalFx, Wavefront | Prometheus, all StatsD flavors |
从一个监视系统到另一个监视系统,还有其他更小的期望差异,例如它们的基本度量单位(特别是时间)的概念和度量标准的规范命名约定。Micrometer定制您的指标,以满足每个注册表的这些需求。
每一个监控系统模块都对应了不同的依赖,比如
<dependency><groupId>io.micrometer</groupId><artifactId>micrometer-registry-appoptics</artifactId><version>${micrometer.version}</version>
</dependency><dependency><groupId>io.micrometer</groupId><artifactId>micrometer-registry-prometheus</artifactId><version>${micrometer.version}</version>
</dependency>
注册表 Registry
Meter是收集关于应用程序的一组度量(我们单独称之为度量)的接口。Micrometer中的米是从MeterRegistry中创建并保存的。每个支持的监控系统都有一个MeterRegistry的实现。注册中心的创建方式因实现而异。
Micrometer包含一个SimpleMeterRegistry,它在内存中保存每个仪表的最新值,并且不会将数据导出到任何地方。如果你还没有一个首选的监控系统,你可以通过使用简单的注册表开始玩指标:
MeterRegistry registry = new SimpleMeterRegistry();
在Micrometer中还有其余几个注册表
- CompositeMeterRegistry 符合注册表,可以将多个注册表相关联
CompositeMeterRegistry composite = new CompositeMeterRegistry();Counter compositeCounter = composite.counter("counter");
compositeCounter.increment(); (1)SimpleMeterRegistry simple = new SimpleMeterRegistry();
composite.add(simple); (2)
- Global Registry 全局注册表
Micrometer提供了一个静态全局注册表Metrics.globalRegistry和一组基于此注册表生成仪表的静态构建器(请注意,globalRegistry是一个复合注册表):
Metrics.globalRegistry
在本插件中使用的就是全局注册表,如果开发者还想继续开发,可直接引入
度量 Meters
Micrometer支持一组Meter原语,包括Timer, Counter, Gauge, DistributionSummary, LongTaskTimer, FunctionCounter, FunctionTimer和TimeGauge。不同的度量类型会产生不同数量的时间序列度量。例如,虽然有一个表示Gauge的单一度量,但Timer测量计时事件的计数和所有计时事件的总时间。仪表由其名称和尺寸唯一标识。我们可以互换使用“尺寸”和“标签”这两个术语,而Micrometer接口就是“标签”,因为它更短。作为一般规则,应该可以使用名称作为枢轴。维度允许对特定的命名度量进行切片,以便向下钻取和推断数据。这意味着,如果只选择了名称,则可以通过使用其他维度向下钻取并推断所显示的值。
Metrics.gauge(meterName, getMeterTags(inv, anno.tags()), (Number) rst);
度量名 Naming Meters
Micrometer采用一种命名约定,用。(点)字符分隔小写单词。不同的监控系统对命名约定有不同的建议,有些命名约定在系统之间可能不兼容。监控系统的每个Micrometer实现都附带一个命名约定,该约定将小写点表示法名称转换为监控系统推荐的命名约定。此外,这个命名约定实现从度量名称和标记中删除了监视系统不允许的特殊字符。您可以通过实现NamingConvention并在注册表上设置它来覆盖注册表的默认命名约定。
registry.config().namingConvention(myCustomNamingConvention);
registry.counter("http.server.requests");
例如:
-
Prometheus - http_server_requests_duration_seconds
-
Atlas - httpServerRequests
-
Graphite - http.server.requests
-
InfluxDB - http_server_requests
度量标签 Tag Naming
对于 Tag 的命名,建议也采用跟 meter 一致的点号分隔小写单词的方式,这同样有助于将命名风格转换为各个监控系统推荐的命名模式。
registry.counter("counter.counter_name","uri", ctx.path(),"method", ctx.method(),"class", inv.target().getClass().getTypeName(),"executable", inv.method().getMethod().getName());
错误示例
registry.counter("calls","class", "database","db", "users");registry.counter("calls","class", "http","uri", "/api/users");
再来看一下上面这种命名方式,此时如果仅仅通过 name 属性calls来查看数据,得到的是包含了 db 访问和 http 调用的所有的指标数据。显然这种数据对于我们分析生产问题来说是毫无意义的,需要进一步选择class标签来细化数据维度。
注意:在tag标签中数量必须是偶数,用于做键对值匹配,如果存在相同的名字,那么只能通过标签来筛选结果
通用标签 Common Tags
common tags 属于 registry 级别的 tag,它会被应用到报告给监控系统的所有 metric 中,这类 tag 通常是系统维度的一些属性,比如 host、instance、region、堆栈信息等等。
附加一个公共标记列表,以应用于报告给监视系统的所有指标。必须是偶数个参数,表示标签的键/值对。
registry.config().commonTags("stack", "prod", "region", "us-east-1");
registry.config().commonTags(Arrays.asList(Tag.of("stack", "prod"), Tag.of("region", "us-east-1"))); // equivalently
common tags 必须在添加任何 meter 之前就被加入到 registry 中。
指标过滤器 MeterFilter
Meter Filter 用于控制meter注册时机、可以发布哪些类型的统计数据,我们可以给每一个 registry 配置过滤器。
过滤器提供以下三个基本功能:
拒绝/接受meter注册。
变更meter的 ID 信息(io.micrometer.core.instrument.Meter.Id)
针对某些类型的meter配置分布统计。
registry.config()// 多个filter配置按顺序生效.meterFilter(MeterFilter.ignoreTags("too.much.information")).meterFilter(MeterFilter.denyNameStartsWith("jvm"));// 拒绝/接受Meters
// 用于配置只接受指定形式的meters,或者屏蔽某些meters。
new MeterFilter() {@Overridepublic MeterFilterReply accept(Meter.Id id) {if(id.getName().contains("test")) {return MeterFilterReply.DENY;}return MeterFilterReply.NEUTRAL;}
}
MeterFilterReply有三种可能的状态:
public enum MeterFilterReply {// 拒绝meter注册请求,registry将会返回一个该meter的NOOP版本(如NoopCounter、NoopTimer)DENY,// 当没有任何过滤器返回DENY时,meter的注册流程继续向前推进NEUTRAL,// 表示meter注册成功,无需继续向下流转“询问”其他filter的accept(...)方法ACCEPT
}
针对Meter的 deny/accept 策略, MeterFilter为我们提供了一些常用的方法:
- accept():接受所有的meter注册,该方法之后的任何 filter 都是无效的。
- accept(Predicate<Meter.Id>):接收满足给定条件的meter注册。
- acceptNameStartsWith(String):接收 name 以指定字符打头的meter注册。
- deny():拒绝所有meter的注册请求,该方法之后的任何 filter 都是无效的。
- denyNameStartsWith(String):拒绝所有 name 以指定字符串打头的meter的注册请求。
- deny(Predicate<Meter.Id>):拒绝满足特定条件的meter的注册请求。
- maximumAllowableMetrics(int):当已注册的meters数量达到允许的注册上限时,拒绝之后的所有注册请求。
- maximumAllowableTags(String meterNamePrefix, String tagKey, int maximumTagValues, MeterFilter onMaxReached):设置一个tags上限,达到这个上限时拒绝之后的注册请求。
- denyUnless(Predicate<Meter.Id>):白名单机制,拒绝不满足给定条件的所有meter的注册请求。
下面是MeterFilter中修改指标的方法
- map(Meter.Id id) : 默认方法,返回当前指标ID
- commonTags(Iterable):为所有指标添加一组公共 tags。通常建议开发者为应用程序名称、host、region 等信息添加公共 tags。
- ignoreTags(String…):用于从所有meter中去除指定的 tag key。比如当我们发现某个 tag 具有过高的基数,并且已经对监控系统构成压力,此时可以在无法立即改变所有检测点的前提下优先采用这种方式来快速减轻系统压力。
- replaceTagValues(String tagKey, Function<String, String> replacement, String… exceptions):替换满足指定条件的所有 tag 值。通过这种方式可以某个 tag 的基数大小。
renameTag(String meterNamePrefix, String fromTagKey, String toTagKey):重命名所有以给定前缀命名的metric的 tag key。 - configure(Meter.Id id, DistributionStatisticConfig config):这只在过滤新的计时器和分布摘要(即那些使用DistributionStatisticConfig的仪表类型)时调用
Metrics.globalRegistry.config().meterFilter(new MeterFilter() {// 修改id@Overridepublic Meter.Id map(Meter.Id id) {if(id.getName().startsWith("test")) {return id.withName("extra." + id.getName()).withTag("extra.tag", "value");}return id;}@Overridepublic DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {if (id.getName().startsWith(prefix)) {return DistributionStatisticConfig.builder()// ID名称以指定前缀开头的请求提供指标统计直方图信息.publishPercentiles(0.9, 0.95).build().merge(config);}return config;}
});
MeterFilter 部分构建器如下:
- maxExpected(Duration/long): 控制从计时器或摘要发送的百分位数直方图桶的上限。
- minExpected(Duration/long): 控制从计时器或摘要发送的百分位数直方图桶的下界。
聚合速率
Micrometer知道特定的监视系统是希望在发布指标之前在客户端进行速率聚合,还是在服务器上作为查询的一部分进行临时聚合。它根据监视系统期望的样式累积度量并不是所有的测量都被报告或最好地被视为一个速率。例如,度量值和活动任务计数长任务计时器不是速率。
指标类型
计数器 Counters
计数器报告单个指标:计数。Counter接口允许您增加一个固定的数量,该数量必须为正。
Counter counter = Counter.builder("counter").baseUnit("beans") // optional.description("a description of what this counter does") // optional.tags("region", "test") // optional.register(registry);//计数 + 1counter.increment();counter.increment(1.0);
永远不要计算那些你可以用Timer计时或用DistributionSummary总结的事情!Timer和DistributionSummary总是在发布其他度量之外发布事件计数。
函数式计数器 Function-tracking Counters
Micrometer还提供了一种更不常用的计数器模式,可以跟踪单调增加的函数(保持不变或随时间增加但从不减少的函数)。一些监视系统(如Prometheus)将计数器的累积值推送到后端,但其他监视系统则发布计数器在推送间隔内的增量速率。通过采用此模式,您可以让监视系统的Micrometer实现选择是否对计数器进行评级规范化,并且您的计数器在不同类型的监视系统之间保持可移植性。
FunctionCounter counter = FunctionCounter.builder("counter", state, state -> state.count()).baseUnit("beans") // optional.description("a description of what this counter does") // optional.tags("region", "test") // optional.register(registry);
counter.count() // 自创建此计数器以来的累计计数。
仪表 Gauges
- 仪表是获取当前值的句柄。测量的典型示例是集合或映射的大小或处于运行状态的线程数。
- 千分尺采取的立场是,量规应该取样,而不是设置,所以没有关于什么可能发生在样品之间的信息。在度量值报告给度量后端时,在度量值上设置的任何中间值都将丢失,因此首先设置这些中间值几乎没有什么价值。把量规想象成“海森堡量规”:一个只有在被观察时才会改变的量规。
- 每一个其他的计量类型都会累积中间计数,直到数据被发送到计量后端。
List<String> list = registry.gauge("listGauge", Collections.emptyList(), new ArrayList<>(), List::size);
List<String> list2 = registry.gaugeCollectionSize("listSize2", Tags.empty(), new ArrayList<>());
Map<String, Integer> map = registry.gaugeMapSize("mapGauge", Tags.empty(), new HashMap<>());
// 创建一个gauge
Metrics.gauge(meterName, getMeterTags(inv, anno.tags()), (Number) rst);AtomicInteger myGauge = registry.gauge("numberGauge", new AtomicInteger(0));// 设置一个值
myGauge.set(27);
myGauge.set(11);
时间仪表 TimeGauge
TimeGauge是跟踪时间值的专用度量,该值将被缩放到每个注册中心实现所期望的基本时间单位。TimeGauge可以按照如下方式注册TimeUnit:
AtomicInteger msTimeGauge = new AtomicInteger(4000);
AtomicInteger usTimeGauge = new AtomicInteger(4000);
TimeGauge.builder("my.gauge", msTimeGauge, TimeUnit.MILLISECONDS, AtomicInteger::get).register(registry);
TimeGauge.builder("my.other.gauge", usTimeGauge, TimeUnit.MICROSECONDS, AtomicInteger::get).register(registry);
监控系统显示文本
# HELP my_gauge_seconds
# TYPE my_gauge_seconds gauge
my_gauge_seconds 4.0
# HELP my_other_gauge_seconds
# TYPE my_other_gauge_seconds gauge
my_other_gauge_seconds 0.004
Multi-gauge
千分尺支持最后一种特殊类型的量规,称为多量规,以帮助管理测量不断增长或缩小的标准列表。这个特性允许您从类中选择一组边界良好但稍有变化的标准SQL查询,并为每一行报告一些度量作为度量。下面的示例创建MultiGauge:
// SELECT count(*) from job group by status WHERE job = 'dirty'
MultiGauge statuses = MultiGauge.builder("statuses").tag("job", "dirty").description("The number of widgets in various statuses").baseUnit("widgets").register(registry);...// run this periodically whenever you re-run your query
statuses.register(resultSet.stream().map(result -> Row.of(Tags.of("status", result.getAsString("status")), result.getAsInt("count"))).collect(toList())
);
计时器 Timers
计时器用于测量短时间延迟和此类事件的频率。Timer的所有实现至少将总时间和事件计数作为单独的时间序列报告。虽然您可以在其他用例中使用计时器,但请注意不支持负值,并且记录更长的持续时间可能会导致Long的总时间溢出。MAX_VALUE纳秒(292.3年)。
Timer timer = Timer.builder(meterName).description(anno.description()).tags(getMeterTags(inv, anno.tags())).publishPercentiles(anno.percentiles()).register(Metrics.globalRegistry);//计时
long start = System.currentTimeMillis();
long span = System.currentTimeMillis() - start;
timer.record(span, TimeUnit.MICROSECONDS);timer.record(() -> span );
timer.recordCallable(() -> span );Runnable r = timer.wrap(() -> span );
Callable c = timer.wrap(() -> span );Timer.Sample sample = Timer.start(Metrics.globalRegistry);sample.stop(registry.timer("my.timer", "response", data));
基本定时器实现(如culativetimer和StepTimer)的最大统计值是时间窗口最大值(TimewlindowMax)。这意味着它的值是一个时间窗口内的最大值。如果时间窗口长度没有记录新的值,则在新时间窗口开始时将最大值重置为0。时间窗口大小是仪表注册表的步长,除非在DistributionStatisticConfig中显式地将过期设置为其他值。时间窗口最大值用于捕获在沉重的资源压力触发延迟并阻止发布指标后的后续间隔内的最大延迟。百分位数也是时间窗口百分位数(时间窗口百分位数直方图)。
@Timed 计时器注解
Micrometer的Spring Boot配置不能识别任意方法上的@Timed。
在solon中支持,替换成@MeterTimer 即可,默认注册到全局
@Get
@Mapping("/hello")
@MeterTimer
public String hello(){return "hello,world";
}
函数式计时器 Function-tracking Timers
Micrometer还提供了一个不太常用的计时器模式,它跟踪两个单调递增的函数(一个函数保持不变或随着时间的推移而增加,但从不减少):计数函数和总时间函数。一些监视系统,如Prometheus,将计数器的累积值(在本例中应用于计数和总时间函数)推送到后端,但其他监视系统则发布计数器在推送间隔内的增量速率。通过采用此模式,您可以让监视系统的Micrometer实现选择是否对计时器进行评级规范化,并且您的计时器在不同类型的监视系统之间保持可移植性。
/*** 跟踪count和totalTime单调递增函数的计时器。* @param name Name of the timer being registered.* @param tags Sequence of dimensions for breaking down the name.* @param obj State object used to compute a value.* @param countFunction Function that produces a monotonically increasing counter* value from the state object.* @param totalTimeFunction Function that produces a monotonically increasing* total time value from the state object.* @param totalTimeFunctionUnit The base unit of time produced by the total time* function.* @param <T> The type of the state object from which the function values are* extracted.* @return A new or existing function timer.*/public <T> FunctionTimer timer(String name, Iterable<Tag> tags, T obj, ToLongFunction<T> countFunction,ToDoubleFunction<T> totalTimeFunction, TimeUnit totalTimeFunctionUnit) {return FunctionTimer.builder(name, obj, countFunction, totalTimeFunction, totalTimeFunctionUnit).tags(tags).register(MeterRegistry.this);}
使用案例
FunctionTimer functionTimer = FunctionTimer.builder(meterName, Integer.parseInt("1"),r->r.intValue(),c->c.byteValue(),TimeUnit.NANOSECONDS).register(Metrics.globalRegistry);
暂停检测 Pause Detection
Micrometer使用LatencyUtils包来补偿协调遗漏——由系统和VM暂停引起的额外延迟,这会使延迟统计向下倾斜。
分布统计信息(如百分位数和SLO计数)受到暂停检测器实现的影响,该实现在这里或那里添加额外的延迟以补偿暂停。
Micrometer支持两种暂停检测器实现:基于时钟漂移的检测器和无op检测器。在Micrometer 1.0.10/1.1.4/1.2.0之前,时钟漂移检测器默认配置为报告尽可能精确的指标,而无需进一步配置。从1.0.10/1.1.4/1.2.0开始,默认配置no-op检测器,但可以配置时钟漂移检测器,如下例所示。
基于时钟漂移的检测器具有可配置的睡眠间隔和暂停阈值。CPU消耗与sleepInterval成反比,与暂停检测精度成反比。这两个值的默认值都是100ms,这是一个合理的默认值,可以很好地检测长暂停事件,同时消耗的CPU时间可以忽略不计。
registry.config().pauseDetector(new ClockDriftPauseDetector(sleepInterval, pauseThreshold));
registry.config().pauseDetector(new NoPauseDetector());
内存统计 Memory Footprint Estimation
计时器是消耗内存最多的仪表,它们的总占用可能会有很大的变化,这取决于您选择的选项。下表的内存消耗是基于各种功能的使用。这些数字假设没有标签,环形缓冲区长度为3。添加标记会在一定程度上增加总数,就像增加缓冲区长度一样。根据注册中心实现的不同,总存储空间也会有所不同。
- R =环缓冲区长度。在所有示例中,我们都假定默认值为3。R用Timer设置。Timer.Builder#distributionStatisticBufferLength.
- B =总直方图桶。可以是SLO边界或百分位直方图桶。默认情况下,计时器被限制为最小期望值1ms和最大期望值30秒,在适用的情况下,为百分位数直方图产生66个桶。
- I =暂停补偿的区间估计器。1.7 kb。
- M =时间衰减最大值。104字节。
- Fb=固定边界直方图。8bBR.Pp =百分位精度。缺省值是1。一般在[0,3]的范围内。Pp设置定时器。Timer.Builder#percentilePrecisio
- Hdr(Pp) =高动态范围直方图。
-
- 当Pp=0时:1.9kb*R+0.8kb
-
- 当Pp= 1:38 kb*R+1.1kb时
-
- 当Pp= 2时:18.2kb*R+4.7kb
暂停检测 | 客户端百分位数 | 直方图和/或slo | 公式 | 例子 |
---|---|---|---|---|
Yes | No | No | I + M | ~1.8kb |
Yes | No | Yes | I + M + Fb | For default percentile histogram, ~7.7kb |
Yes | Yes | Yes | I + M + Hdr(Pp) | For the addition of a 0.95 percentile with defaults otherwise, ~14.3kb |
No | No | No | M | ~0.1kb |
No | No | Yes | M + Fb | For default percentile histogram, ~6kb |
No | Yes | Yes | M + Hdr(Pp) | For the addition of a 0.95 percentile with defaults otherwise, ~12.6kb |
特别是对于Prometheus, R总是等于1,无论您如何尝试通过Timer配置它。构建器。这种特殊情况的存在是因为Prometheus期望累积的直方图数据永远不会滚动。
Distribution Summaries
分布摘要跟踪事件的分布。它在结构上类似于计时器,但记录的值不代表时间单位。例如,您可以使用分发摘要来度量到达服务器的请求的有效负载大小。
DistributionSummary summary = DistributionSummary.builder("response.size").description("a description of what this summary does").baseUnit("bytes") .tags("region", "test") .scale(100) // 比例因子,乘以.register(registry);
//以指定的金额更新汇总中保存的统计数据。
//参数:数量-正在测量的事件的数量。例如,如果来自服务器的响应的字节大小。如果数量小于0,则该值将被删除。
meter.record(1.0);
在插件中已经实现了@MeterSummary,在solon管理中使用即可,将函数返回值必须是Number的实现或者子类
对于基本的DistributionSummary实现,如CumulativeDistributionSummary和StepDistributionSummary,其最大值(命名为max)是一个时间窗口最大值(TimewindowMax)。这意味着它的值是一个时间窗口内的最大值。如果没有记录新的时间窗口长度,则在新的时间窗口开始时将最大值重置为0。时间窗口大小是仪表注册表的步长,除非在DistributionStatisticConfig中显式地将过期设置为另一个值。时间窗口最大值用于捕获在沉重的资源压力触发延迟并阻止发布指标后的后续间隔内的最大延迟。百分位数也是时间窗口百分位数(TimewindowPercentileHistogram)。
长任务计时器 Long Task Timers
长任务计时器是一种特殊类型的计时器,它允许您在正在测量的事件仍在运行时测量时间。普通定时器只记录任务完成后的持续时间。
长任务计时器至少发布以下统计信息:
- 活动任务数
- 活动任务的总持续时间
- 活动任务的最大持续时间
考虑一个从数据存储刷新元数据的后台进程。例如,Edda缓存AWS资源,如实例、卷、自动伸缩组等。正常情况下,几分钟内即可刷新所有数据。如果AWS服务出现问题,则可能需要更长的时间。可以使用长任务计时器来跟踪刷新元数据的活动时间。
LongTaskTimer longTaskTimer = LongTaskTimer.builder(getMeterName(inv, anno)).tags(getMeterTags(inv, anno.tags())).publishPercentiles(anno.percentiles()).description(anno.description()).register(Metrics.globalRegistry);
//计时return longTaskTimer.record(()->{try {return inv.invoke();} catch (Throwable e) {throw new RuntimeException(e);}});
例如,在Spring应用程序中,使用@Scheduled实现这种长时间运行的流程是很常见的。Micrometer提供了一个特殊的@Timed注释,用于使用长任务计时器来检测这些进程:
@Timed(value = "aws.scrape", longTask = true)
@Scheduled(fixedDelay = 360000)
void scrapeResources() {// find instances, volumes, auto-scaling groups, etc...
}
在solon中,我们直接使用即可
@Mapping("/longtime")
@MeterLongTimer("demo.longtime")
public String MeterLongTimer() throws InterruptedException {Thread.sleep(5000);return "MeterSummary";
}
Histograms and Percentiles
计时器和分布摘要支持收集数据以观察其百分位数分布。查看百分位数有两种主要方法:百分位直方图:Micrometer将值累积到底层直方图中,并将一组预先确定的桶发送到监控系统。监控系统的查询语言负责计算该直方图的百分位数。目前,只有Prometheus、Atlas和Wavefront分别通过histogram_quantile、:percentile和hs()支持基于直方图的百分位数近似值。如果您的目标是Prometheus、Atlas或Wavefront,则更喜欢这种方法,因为您可以跨维度聚合直方图(通过将一组维度上的桶的值相加),并从直方图中获得可聚合的百分位数。客户端百分位数:Micrometer计算每个仪表ID(一组名称和标签)的百分位数近近值,并将百分位数值发送到监控系统。这并不像百分位数直方图那样灵活,因为不可能在标签之间汇总百分位数近似值。然而,对于不支持基于直方图的服务器端百分位数计算的监控系统,它提供了对百分位数分布的某种程度的了解。
Timer.builder("my.timer").publishPercentiles(0.5, 0.95) // median and 95th percentile (1).publishPercentileHistogram() // (2).serviceLevelObjectives(Duration.ofMillis(100)) // (3).minimumExpectedValue(Duration.ofMillis(1)) // (4).maximumExpectedValue(Duration.ofSeconds(10))
- publishPercentiles:用于发布应用程序中计算的百分位数值。这些值在各个维度上是不可聚合的。
- publishPercentileffistogran:用于在Prometheus(通过使用histogram_quantile)、Atlas(通过使用:percentile)和Wavefront(通过使用hs())中发布适合计算可聚合(跨维度)百分位数近似值的直方图。对于Prometheus和Atlas,结果直方图中的桶是由Micrometer基于一个生成器预先设置的,该生成器由Netflix根据经验确定,可以在大多数真实世界的计时器和分布摘要上产生合理的误差界限。默认情况下,生成器产生276个桶,但是Micrometer只包括mininumExpectedValue和maximumExpectedValue设置范围内的桶。默认情况下,Micrometer夹住计时器的范围为1毫秒到1分钟,每个计时器维度产生73个直方图桶。publishPercentilehistogran对不支持聚合百分位数近似值的系统没有影响。这些系统没有提供直方图。
- serviceLevelObjectives:用于发布由slo定义的桶的累积直方图。当在支持可聚合百分位数的监视系统上与publishPercentileHistogram一起使用时,此设置将向发布的直方图添加额外的桶。当在不支持可聚合百分位数的系统上使用时,此设置将导致仅使用这些桶发布直方图。
- minimumExpectedValue / maximumExpectedValue:控制publishPercentileHistogram发送的桶的数量,并控制底层HdrHistogram结构的准确性和内存占用。
solon-micrometer插件使用
分布式扩展插件。在 solon.cloud 插件的基础上,添加基于 micrometer 度量的支持。此插件类似 slf4j,使用时需要添加具体的记录方案。该插件内置了prometheus,可以继续添加其他的检测器
v2.4.2 后支持
<dependency><groupId>org.noear</groupId><artifactId>solon.cloud.metrics</artifactId>
</dependency>
观测地址
GET /metrics/registrys 查看所有注册器
{"_registrys": ["xxx.xxx.Name1", "xxx.xxx.Name2"]
}
GET /metrics/meters 查看所有度量
{"_meters": ["name1", "name2"]
}
GET /metrics/meter/{meterName} 查看某个度量详情
{"name": "name1","description": "","baseUnit": "","measurements": {},"tags": {}
}
GET /metrics/prometheus 查看 prometheus 监控系统的输入数据
# HELP demo_longtime_seconds_max
# TYPE demo_longtime_seconds_max gauge
demo_longtime_seconds_max{solon_app_group="group",solon_app_name="name",} 0.0
# HELP demo_longtime_seconds
# TYPE demo_longtime_seconds summary
demo_longtime_seconds_active_count{solon_app_group="group",solon_app_name="name",} 0.0
demo_longtime_seconds_duration_sum{solon_app_group="group",solon_app_name="name",} 0.0
# HELP demo_MeterSummary
# TYPE demo_MeterSummary summary
demo_MeterSummary_count{solon_app_group="group",solon_app_name="name",} 2.0
demo_MeterSummary_sum{solon_app_group="group",solon_app_name="name",} 2.0
# HELP demo_MeterSummary_max
# TYPE demo_MeterSummary_max gauge
demo_MeterSummary_max{solon_app_group="group",solon_app_name="name",} 1.0
# HELP demo_hello_seconds
# TYPE demo_hello_seconds summary
demo_hello_seconds_count{solon_app_group="group",solon_app_name="name",} 2.0
demo_hello_seconds_sum{solon_app_group="group",solon_app_name="name",} 0.0
# HELP demo_hello_seconds_max
# TYPE demo_hello_seconds_max gauge
demo_hello_seconds_max{solon_app_group="group",solon_app_name="name",} 0.0
# HELP demo_test__total
# TYPE demo_test__total counter
demo_test__total{solon_app_group="group",solon_app_name="name",} 2.0
如何使用
@Controller
public class DemoController {@Mapping("/counter")@MeterCounter("demo.counter")public String counter() {return "counter";}@Mapping("/gauge")@MeterGauge("demo.gauge")public Long gauge() {return System.currentTimeMillis() % 100;}@Mapping("/summary")@MeterSummary(value = "demo.summary", maxValue = 88, minValue = 1, percentiles = {10, 20, 50})public Long summary() {return System.currentTimeMillis() % 100;}@Mapping("/timer")@MeterTimer("demo.timer")public String timer() {return "timer";}
}
标签定制
全局自动添加的 commandTags
solon.app.name //应用名
solon.app.group //应用组
solon.app.nameSpace //应用命名空间
使用注解时自动添加的 tags
uri //请求 uri
method //请求 method
class //执行类
executable //执行函数
增加度量器和过滤器
插件是直接使用全局注册器,我们在项目中直接引入即可
LongTaskTimer longTaskTimer = LongTaskTimer.builder(getMeterName(inv, anno)).tags(getMeterTags(inv, anno.tags())).publishPercentiles(anno.percentiles()).description(anno.description()).register(Metrics.globalRegistry);Metrics.globalRegistry.config().meterFilter(new MeterFilter() {@Overridepublic DistributionStatisticConfig configure(Meter.Id id, DistributionStatisticConfig config) {if(id.getName().startsWith("myservice")) {return DistributionStatisticConfig.builder().percentiles(0.95).build().merge(config);}return config;}});
prometheus
Prometheus是一个开源系统监控和警报工具包,最初由SoundCloud构建。自2012年成立以来,许多公司和组织都采用了Prometheus,并且该项目拥有非常活跃的开发人员和用户社区。它现在是一个独立的开源项目,独立于任何公司进行维护。为了强调这一点,并澄清项目的治理结构,Prometheus于2016年加入了云原生计算基金会,成为继Kubernetes之后的第二个托管项目。
Prometheus收集并存储其指标作为时间序列数据,即指标信息与记录时间戳一起存储,以及称为标签的可选键值对
官网地址 https://prometheus.io
下载地址 https://prometheus.io/download/
特性普罗米修斯的主要特点是:
- 一个多维数据模型,
- 其时间序列数据由度量名称和键/值对标识PromQL
- 一种灵活的查询语言
- 可以利用这个维度不依赖分布式存储
- 单个服务器节点是自治的时间序列收集通过HTTP上的拉模型进行
- 推送时间序列通过中间网关支持通过服务发现或静态配置发现目标多种模式的绘图和仪表板支持
安装使用
下文以windows为例
https://github.com/prometheus/prometheus/releases/download/v2.46.0/prometheus-2.46.0.windows-amd64.zip
下载并解压
tar xvfz prometheus-*.tar.gz
cd prometheus-*
查看帮助
.\prometheus.exe --help
usage: prometheus.exe [<flags>]The Prometheus monitoring serverFlags:-h, --[no-]help Show context-sensitive help (also try--help-long and --help-man).--[no-]version Show application version.--config.file="prometheus.yml"Prometheus configuration file path.--web.listen-address="0.0.0.0:9090"Address to listen on for UI, API, andtelemetry.--web.config.file="" [EXPERIMENTAL] Path to configuration file thatcan enable TLS or authentication.--web.read-timeout=5m Maximum duration before timing out read of therequest, and closing idle connections.--web.max-connections=512 Maximum number of simultaneous connections.--web.external-url=<URL> The URL under which Prometheus is externallyreachable (for example, if Prometheus is served......
配置prometheus 的配置是YAML。
Prometheus下载文件附带了一个名为Prometheus的文件中的样例配置。这是一个开始的好地方。
为了使示例文件更简洁,我们去掉了示例文件中的大部分注释(注释是带有#前缀的行)。
global:scrape_interval: 15sevaluation_interval: 15srule_files:# - "first.rules"# - "second.rules"scrape_configs:- job_name: 'micrometer-example'scrape_interval: 5smetrics_path: '/metrics/prometheus' # solon中的默认路径static_configs:- targets: ['127.0.0.1:8080'] # 项目地址,支持集群labels:instance: 'example1' #实例名字
下面的 prometheus 的数据集
# HELP demo_longtime_seconds_max
# TYPE demo_longtime_seconds_max gauge
demo_longtime_seconds_max{solon_app_group="group",solon_app_name="name",} 0.0
# HELP demo_longtime_seconds
# TYPE demo_longtime_seconds summary
demo_longtime_seconds_active_count{solon_app_group="group",solon_app_name="name",} 0.0
demo_longtime_seconds_duration_sum{solon_app_group="group",solon_app_name="name",} 0.0
# HELP demo_MeterSummary
# TYPE demo_MeterSummary summary
demo_MeterSummary_count{solon_app_group="group",solon_app_name="name",} 2.0
demo_MeterSummary_sum{solon_app_group="group",solon_app_name="name",} 2.0
# HELP demo_MeterSummary_max
# TYPE demo_MeterSummary_max gauge
demo_MeterSummary_max{solon_app_group="group",solon_app_name="name",} 1.0
# HELP demo_hello_seconds
# TYPE demo_hello_seconds summary
demo_hello_seconds_count{solon_app_group="group",solon_app_name="name",} 2.0
demo_hello_seconds_sum{solon_app_group="group",solon_app_name="name",} 0.0
# HELP demo_hello_seconds_max
# TYPE demo_hello_seconds_max gauge
demo_hello_seconds_max{solon_app_group="group",solon_app_name="name",} 0.0
# HELP demo_test__total
# TYPE demo_test__total counter
demo_test__total{solon_app_group="group",solon_app_name="name",} 2.0
运行 prometheus
.\prometheus.exe
查看注册实例
根据 度量器名字来查看不同的图像
本文参考:
solon官网
micrometer官网
相关文章:
【solon生态】- solon.cloud.micrometer插件使用指南及micrometer详解
solon.cloud.micrometer插件使用指南 solon是什么solon的cloud生态图快速入门 micrometer指南micrometer是什么监控系统 Supported Monitoring Systems注册表 Registry度量 Meters度量名 Naming Meters度量标签 Tag Naming通用标签 Common Tags 指标过滤器 MeterFilter聚合速率…...
【Spring Boot】Thymeleaf模板引擎 — Thymeleaf的高级用法
Thymeleaf的高级用法 主要介绍Thymeleaf的内联、内置对象、内置变量等高级用法。 1.内联 虽然通过Thymeleaf中的标签属性已经几乎满足了开发中的所有需求,但是有些情况下需要在CSS或JS中访问后台返回的数据。所以Thymeleaf提供了th:inline"text/javascript/…...
用html+javascript打造公文一键排版系统13:增加半角字符和全角字符的相互转换功能
一、实践发现了bug和不足 今天用了公文一键排版系统对几个PDF文件格式的材料进行文字识别后再重新排版,处理效果还是相当不错的,节约了不少的时间。 但是也发现了三个需要改进的地方: (一)发现了两个bug:…...
元宇宙3D数字虚拟客服打造年轻化、数字化营销新品牌
融合了元宇宙、AI和云计算等技术的虚拟数字人,成为元宇宙数字内容交互的载体,将现实世界中的人与虚拟数字世界的场景、模型及产品链接起来,特别是为电力企业打造的电力元宇宙平台,带来营销宣传多重好处的同时,树立了数…...
micromamba快速安装(windows版本)
快速安装 Micromamba Micromamba 是一个静态链接的 C++ 可执行文件,在 Windows 上就是一个 micromamba.exe 文件,下载下来就直接可以用,甚至都不需要专门安装。唯一需要做的就是设置 Shell 的 Profile 文件,使 micromamba 成为可以在命令行里调用的一个命令。 Micromamba…...
HTML <source> 标签
实例 拥有两份源文件的音频播放器。浏览器应该选择它所支持的文件(如果有的话): <audio controls><source src="horse.ogg" type="audio/ogg"><source src="horse.mp3" type="audio/mpeg">Your browser does n…...
香港第一金:加息预期仍令贵金属承压,黄金仍需关注破位情况
香港第一金基本面分析: 中国纸黄金交易通显示,全球最大黄金上市交易基金(ETF)截至06月27日持仓量为925.66吨,较上日减持1.44吨,本月止净减持13.90吨。 周二美国公布的上月新屋销售飙升12.2%,经季节调整后折合成年率为…...
C语言学习笔记 vscode使用外部console-11
前言 在默认情况下,我们运行C语言程序都是在vscode终端的,在小程序运行时这个是没有问题的,但是当程序变得复杂它就不好用了,这时我们可以将这个终端设置为外部console,这样方便处理更多、更复杂的程序。 步骤 1.点击…...
96 | Python 小项目—— 学生成绩管理系统
文章目录 项目概述功能点2. 登录界面3. 主页面4. 数据录入界面5. 数据删除界面6. 数据修改界面7. 数据查询界面8. 成绩排名界面9. 成绩分析界面10. 学生信息查询界面11. 运行和测试总结项目概述 学生成绩管理系统是一个简单的学生课程管理系统,旨在帮助学校或教育机构轻松管理…...
【uniapp使用web-view点击返回报错后返回不了】
问题及解决 问题解决 问题 使用web-view跳转到别人的网站之后点击返回报错,返回不了 解决 使用以下方法 <template><view></view> </template> <script> var wv;//计划创建的webview export default {onLoad() {// #ifdef APP-PL…...
Map Reduce教程_编程入门自学教程_菜鸟教程-免费教程分享
教程简介 MapReduce是一种编程模型,用于大规模数据集(大于1TB)的并行运算。概念"Map(映射)"和"Reduce(归约)",是它们的主要思想,都是从函数式编程语…...
吉利科技携手企企通,打造集团化数智供应链系统
近日,吉利科技集团有限公司(以下简称“吉利科技”)联合企企通成功召开SRM采购供应链管理项目启动会。企企通与吉利科技高层、项目负责人与团队成员出席此次启动会。 双方将携手在企业供应商全生命周期管理、采购全流程、电子招投标、采购分析…...
2023河南萌新联赛第(四)场:河南大学 F - 小富的idea
2023河南萌新联赛第(四)场:河南大学 F - 小富的idea 时间限制:C/C 1秒,其他语言2秒 空间限制:C/C 262144K,其他语言524288K 64bit IO Format: %lld 题目描述 要注意节约 卷王小富最近又在内卷&a…...
总结线程池
什么是线程池 线程池(Thread Pool)是一种用于管理和复用线程的技术,它可以在多线程编程中有效地管理线程的创建、执行和销毁。是一种有效管理线程的机制,可以提高多线程编程的效率、性能和资源利用率。它在许多并发编程的场景中被…...
基础的 lftp 使用方法
lftp 是一个功能强大的文件传输工具,支持FTP、HTTP、SFTP、FISH等多种协议。它提供了一套丰富的命令,使得文件传输和管理更加简便。以下是一些基础的 lftp 使用方法: 连接到FTP服务器: lftp ftp://username:passwordhostname如果不…...
python之prettytable库的使用
文章目录 一 什么是prettytable二 prettytable的简单使用1. 添加表头2. 添加行3. 添加列4. 设置对齐方式4. 设置输出表格样式5. 自定义边框样式6. 其它功能 三 prettytable在实际中的使用 一 什么是prettytable prettytable是Python的一个第三方工具库,用于创建漂亮…...
google PGS 下一代id
前言:为了进一步增强用户的隐私及其多平台游戏体验,Play 游戏服务(PGS) 正在推出下一代玩家 ID,用户第一次玩游戏时,他们将始终被分配一个唯一的下一代玩家 ID,无论用户在什么设备或平台上玩游戏,该 ID 都将…...
【elasticsearch】关于elasticsearch的max_result_window限制问题的解决方式思考
事情起因:我们使用es作为日志搜索引擎,客户收集到的业务日志非常之大,每次查询后,返回页数较多,由于我们web界面限制每页返回150条,当客户翻到66页之后就会报错。 文章目录 前言 二、实验 1.默认生成20条数…...
音频光耦合器
音频光耦合器是一种能够将电信号转换为光信号并进行传输的设备。它通常由发光二极管(LED)和光敏电阻(光电二极管或光敏电阻器)组成。 在音频光耦合器中,音频信号经过放大和调节后,被转换为电流信号…...
【C++精华铺】3.C++入门 引用(const)、内联函数
目录 1. 引用 1.1 引用特性 1.2 常引用 1.2.1 权限放大 1.2.2 权限缩小 1.3 使用场景 1.3.1 传参 1.3.2 做返回值 1.4 传值和传引用的效率比较 1.5 引用和指针的区别 2. 内联函数 2.1 inline 2.2 特性 1. 引用 在C中,引入了一个新的概念引用,与…...
生态系统服务(InVEST模型)供给与需求、价值核算技术及人类活动、重大工程项目、自然保护区、碳中和等领域中实际案例分析
对接工作实际项目及论文写作,解决参会者关注的重点及实际项目过程问题,采取逐步延伸的逻辑,不论您是小白亦或是已经能够成功运行InVEST模型生成结果,您可以自由选择课程内容,如果您是小白老师手把手教您,如…...
TiDB Serverless 正式商用,全托管的云服务带来数据管理和应用程序开发的全新体验
八 年 前 ,我们构建了 TiDB,一个开源分布式关系型数据库。 我们的目标是重新定义开发者和企业处理数据的方式,满足不断增长的可扩展性、灵活性和性能需求。 从那时起,PingCAP 便致力于为开发者和企业提供快速、灵活和规模化的数据…...
PXE-kickstart无人值守安装操作系统
PXE的概念: PXE(Pre-boot Execution Environment,预启动执行环境)是由Intel公司开发的最新技术,工作于C/S的网络模式,支持工作站通过网络从远端服务器下载映像,并由此支持通过网络启动操作系统…...
使用Flask.Request的方法和属性,获取get和post请求参数(二)
1、Flask中的request 在Python发送Post、Get等请求时,我们使用到requests库。Flask中有一个request库,有其特有的一些方法和属性,注意跟requests不是同一个。 2、Post请求:request.get_data() 用于服务端获取客户端请求数据。注…...
解决 idea maven依赖引入失效,无法正常导入依赖问题
解决 idea maven依赖引入失效,无法正常导入依赖问题_idea无法导入本地maven依赖_普通网友的博客-CSDN博客 解决 idea maven依赖引入失效,无法正常导入依赖问题 idea是真的好用,不过里面的maven依赖问题有时候还真挺让人头疼,不少小…...
Python之集合(set)基础知识点
文章目录 1. 创建集合2. 获取集合的元素个数3. 向集合中添加元素4. 从集合中移除元素5. 判断元素是否在集合中6. 遍历集合7. 常用的集合操作7.1 并集7.2 交集7.3 差集 在Python中,集合(Set)是一种无序且不重复的数据结构。它是由一组用花括号…...
flutter 没有open android module in Android studio 插件代码爆红
参考 1.结论 其实就是缺少这个文件 2.解决方案有两个 2.1 方案一 手动创建一个,命名规则是项目名字‘_android’‘.iml’ 内容如下: <?xml version"1.0" encoding"UTF-8"?> <module type"JAVA_MODULE" version"4">&l…...
计算机网络实验2:网络嗅探
文章目录 1. 主要教学内容2. Wireshark介绍3. Wireshark下载4. 使用Wireshark捕获包4.1 选择网卡4.2 停止抓包4.3 保存数据 5. Wireshark的过滤规则6. Wireshark实例 1. 主要教学内容 实验内容:安装、学习使用网络包分析工具Wireshark。所需学时:1。重难…...
智慧防灾:数字孪生技术的应用
最近的“杜苏芮”“卡努”有没有对大家产生影响呢? 频繁发生的台风和其他自然灾害引起了人们对于灾害预防和应对的高度关注。在这种背景下,数字孪生作为一项前沿技术,为灾害预防领域提供了全新的解决方案。本文就带大家了解一下数字孪生技术…...
Google 扫码器(仅限 Android)
Google 扫码器(仅限 Android) Google Code Scanner API 提供了全面的扫描解决方案,无需您的应用请求相机权限,同时保护用户隐私。这是通过将扫描代码委托给 Google Play 服务并仅将扫描结果返回给您的应用来完成的(视…...
平台网站开发/52种新颖的促销方式
重要国策《文化产业振兴规划》于9月26日正式对外公布。巧合的是,就在前一天(9月25日 ),盛大游戏(SDG)成功在NASDAQ上市,并且创下了美股IPO规模之最(10亿美元)。1…...
农用地转建设用地结果查询网站/百度云搜索引擎入口
2019独角兽企业重金招聘Python工程师标准>>> 随着宽带的普及和网速的提高,人们上网冲浪时对网站打开速度的容忍度在不断降低,网站的打开速度已经成为可用性的前提,甚至直接影响网站的收入。 Google最近第一次完整地书面提出网页访…...
如何设置wordpress的文章分类/黄页88网站推广方案
【Web技术】HTML详解head标签 一个网页由head和body标签组成 body用来呈现网页内容,而head标签中除了title标签还有什么作用与属性呢? <html><head><title>文档的标题</title></head><body>文档的内容... ...<…...
汕尾网站建设/seo站外优化平台
1. 向量组之间的线性表出的定义与性质 2. 向量组线性表出的矩阵形式 3. 矩阵乘积导出的线性表出...
智恒企业网站管理系统/网站是怎么优化推广的
闪黑屏的原因主要是我们启动Activity的时候,需要跑完onCreate和onResume才会显示界面闪黑屏的原因主要是我们启动Activity的时候,需要跑完onCreate和onResume才会显示界面。也就是说需要处理一些数据后,才会显示。按照这种思路,是…...
可以做网站的语言/提高销售的10种方法
CSS Sprites是一种网页图片应用处理方式。它允许你将一个页面涉及到的所有零星图片都包含到一张大图中去,这样一来,当访问该页面时,载入的图片就不会像以前那样一幅一幅地慢慢显示出来了。对于当前网络流行的速度而言,不高于200KB…...