This commit is contained in:
周伟
2022-05-11 19:04:14 +08:00
parent 9440ac7291
commit d9c5ffd627
826 changed files with 0 additions and 481675 deletions

View File

@@ -25,13 +25,7 @@
<meta name="generator" content="Hexo 4.2.0">
</head>
<body>
<div class="book-container">
<div class="book-sidebar">
@@ -55,289 +49,148 @@
<li><a href="/" class="current-tab">首页</a></li>
</ul>
<ul class="uncollapsible">
<li><a href="../">上一级</a></li>
</ul>
<ul class="uncollapsible">
<li>
<a href="/专栏/深入理解 Sentinel/01 开篇词:一次服务雪崩问题排查经历.md.html">01 开篇词:一次服务雪崩问题排查经历.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/02 为什么需要服务降级以及常见的几种降级方式.md.html">02 为什么需要服务降级以及常见的几种降级方式.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/03 为什么选择 SentinelSentinel 与 Hystrix 的对比.md.html">03 为什么选择 SentinelSentinel 与 Hystrix 的对比.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/04 Sentinel 基于滑动窗口的实时指标数据统计.md.html">04 Sentinel 基于滑动窗口的实时指标数据统计.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/05 Sentinel 的一些概念与核心类介绍.md.html">05 Sentinel 的一些概念与核心类介绍.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/06 Sentinel 中的责任链模式与 Sentinel 的整体工作流程.md.html">06 Sentinel 中的责任链模式与 Sentinel 的整体工作流程.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/07 Java SPI 及 SPI 在 Sentinel 中的应用.md.html">07 Java SPI 及 SPI 在 Sentinel 中的应用.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/08 资源指标数据统计的实现全解析(上).md.html">08 资源指标数据统计的实现全解析(上).md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/09 资源指标数据统计的实现全解析(下).md.html">09 资源指标数据统计的实现全解析(下).md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/10 限流降级与流量效果控制器(上).md.html">10 限流降级与流量效果控制器(上).md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/11 限流降级与流量效果控制器(中).md.html">11 限流降级与流量效果控制器(中).md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/12 限流降级与流量效果控制器(下).md.html">12 限流降级与流量效果控制器(下).md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/13 熔断降级与系统自适应限流.md.html">13 熔断降级与系统自适应限流.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/14 黑白名单限流与热点参数限流.md.html">14 黑白名单限流与热点参数限流.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/15 自定义 ProcessorSlot 实现开关降级.md.html">15 自定义 ProcessorSlot 实现开关降级.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/16 Sentinel 动态数据源:规则动态配置.md.html">16 Sentinel 动态数据源:规则动态配置.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/17 Sentinel 主流框架适配.md.html">17 Sentinel 主流框架适配.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/18 Sentinel 集群限流的实现(上).md.html">18 Sentinel 集群限流的实现(上).md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/19 Sentinel 集群限流的实现(下).md.html">19 Sentinel 集群限流的实现(下).md.html</a>
</li>
<li>
<a class="current-tab" href="/专栏/深入理解 Sentinel/20 结束语Sentinel 对应用的性能影响如何?.md.html">20 结束语Sentinel 对应用的性能影响如何?.md.html</a>
</li>
<li>
<a href="/专栏/深入理解 Sentinel/21 番外篇Sentinel 1.8.0 熔断降级新特性解读.md.html">21 番外篇Sentinel 1.8.0 熔断降级新特性解读.md.html</a>
</li>
</ul>
</div>
</div>
<div class="sidebar-toggle" onclick="sidebar_toggle()" onmouseover="add_inner()" onmouseleave="remove_inner()">
<div class="sidebar-toggle-inner"></div>
</div>
<script>
function add_inner() {
@@ -347,9 +200,6 @@
inner.classList.add('show')
}
function remove_inner() {
let inner = document.querySelector('.sidebar-toggle-inner')
@@ -357,9 +207,6 @@
inner.classList.remove('show')
}
function sidebar_toggle() {
let sidebar_toggle = document.querySelector('.sidebar-toggle')
@@ -389,9 +236,6 @@
}
function open_sidebar() {
let sidebar = document.querySelector('.book-sidebar')
@@ -415,13 +259,7 @@ function hide_canvas() {
overlay.classList.remove('show')
}
</script>
<div class="off-canvas-content">
<div class="columns">
@@ -511,9 +349,6 @@ function hide_canvas() {
}
return chain;
</code></pre>
<p>Sentinel 使用 Map 而非 ConcurrentMap 是为了尽量避免加锁大多数场景都是读多写少以上面代码为例ProcessorSlotChain 的新增只在资源第一次被访问时,例如接口第一次被调用,而后续都不会再写。假设有 10 个接口,这 10 个接口在应用启动起来就都被访问过了,那么这个 Map 后续就不会再出现写的情况,既然不会再有写操作,就没有必须加锁了,所以使用 Map 而不是使用 ConcurrentMap。</p>
@@ -557,9 +392,6 @@ function hide_canvas() {
&lt;/dependency&gt;
&lt;/dependencies&gt;
</code></pre>
<p>1.23 版本是 JMH 目前最新的版本。</p>
@@ -573,9 +405,6 @@ function hide_canvas() {
<p>@Benchmark 注解用于声明一个 public 方法为基准测试方法,如下代码所示。</p>
<pre><code class="language-java">public class MyTestBenchmark {
@Benchmark
@Test
@@ -585,13 +414,7 @@ function hide_canvas() {
//
}
}
</code></pre>
<p><strong>@BenchmarkMode</strong></p>
@@ -599,9 +422,6 @@ function hide_canvas() {
<p>通过 JMH 我们可以轻松的测试出某个接口的吞吐量、平均执行时间等指标的数据。假设我们想测试某个方法的平均耗时,那么可以使用 @BenchmarkMode 注解指定测试维度为 Mode.AverageTime代码如下。</p>
<pre><code class="language-java">public class MyTestBenchmark {
@BenchmarkMode(Mode.AverageTime)
@Benchmark
@@ -613,13 +433,7 @@ function hide_canvas() {
//
}
}
</code></pre>
<p><strong>@Measurement</strong></p>
@@ -627,9 +441,6 @@ function hide_canvas() {
<p>假设我们需要测量五次,那么可以使用 @Measurement 注解,代码如下。</p>
<pre><code class="language-java">public class MyTestBenchmark {
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@BenchmarkMode(Mode.AverageTime)
@@ -643,13 +454,7 @@ function hide_canvas() {
//
}
}
</code></pre>
<p>@Measurement 注解有三个配置项:</p>
@@ -667,9 +472,6 @@ function hide_canvas() {
<p>为了数据准确,我们可能需要让被测试的方法做下热身运动,一定的预热次数可提升测试结果的准备度。可使用 @Warmup 注解声明需要预热的次数、每次预热的持续时间,代码如下。</p>
<pre><code class="language-java">public class MyTestBenchmark {
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@@ -685,13 +487,7 @@ function hide_canvas() {
//
}
}
</code></pre>
<p>@Warmup 注解有三个配置项:</p>
@@ -713,9 +509,6 @@ function hide_canvas() {
<p>如果方法执行耗时为毫秒级别,为了便于观察结果,我们可以使用 @OutputTimeUnit 指定输出的耗时时间单位为毫秒,否则使用默认的秒做单位,会输出 10 的负几次方这样的数字,不太直观。</p>
<pre><code class="language-java">public class MyTestBenchmark {
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@@ -733,13 +526,7 @@ function hide_canvas() {
//
}
}
</code></pre>
<p><strong>@Fork</strong></p>
@@ -749,9 +536,6 @@ function hide_canvas() {
<p>假设我们不需要多个进程,那么可以使用 @Fork 指定进程数为 1如下代码所示。</p>
<pre><code class="language-java">public class MyTestBenchmark {
@Fork(1)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@@ -771,13 +555,7 @@ function hide_canvas() {
//
}
}
</code></pre>
<p><strong>@Threads</strong></p>
@@ -785,9 +563,6 @@ function hide_canvas() {
<p>@Threads 注解用于指定使用多少个线程来执行基准测试方法,如果使用 @Threads 指定线程数为 2那么每次测量都会创建两个线程来执行基准测试方法。</p>
<pre><code class="language-java">public class MyTestBenchmark {
@Threads(2)
@Fork(1)
@@ -807,13 +582,7 @@ function hide_canvas() {
//
}
}
</code></pre>
<p>如果 @Measurement 注解指定 time 为 1s基准测试方法的执行耗时为 1s那么如果只使用单个线程一次测量只会执行一次基准测试方法如果使用 10 个线程,一次测量就能执行 10 次基准测试方法。</p>
@@ -835,9 +604,6 @@ function hide_canvas() {
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
public class MyTestBenchmark {
@Benchmark
@Test
@@ -847,9 +613,6 @@ public class MyTestBenchmark {
//
}
@Benchmark
@Test
@@ -861,9 +624,6 @@ public class MyTestBenchmark {
}
}
</code></pre>
<p>下面我们以测试 Gson、Jackson 两个 JSON 解析框架的性能对比为例。</p>
@@ -883,15 +643,9 @@ public class MyTestBenchmark {
@State(Scope.Benchmark)
public class JsonBenchmark {
private GsonParser gsonParser = new GsonParser();
private JacksonParser jacksonParser = new JacksonParser();
@Benchmark
@Test
@@ -901,9 +655,6 @@ public class JsonBenchmark {
gsonParser.fromJson(&quot;{\&quot;startDate\&quot;:\&quot;2020-04-01 16:00:00\&quot;,\&quot;endDate\&quot;:\&quot;2020-05-20 13:00:00\&quot;,\&quot;flag\&quot;:true,\&quot;threads\&quot;:5,\&quot;shardingIndex\&quot;:0}&quot;, JsonTestModel.class);
}
@Benchmark
@Test
@@ -915,9 +666,6 @@ public class JsonBenchmark {
}
}
</code></pre>
<p>我们可以使用 @State 注解指定 gsonParser、jacksonParser 这两个字段的共享域。</p>
@@ -941,15 +689,9 @@ public class JsonBenchmark {
@State(Scope.Thread)
public class JsonBenchmark {
private GsonParser gsonParser = new GsonParser();
private JacksonParser jacksonParser = new JacksonParser();
@Benchmark
@Test
@@ -961,9 +703,6 @@ public class JsonBenchmark {
gsonParser.fromJson(&quot;{\&quot;startDate\&quot;:\&quot;2020-04-01 16:00:00\&quot;,\&quot;endDate\&quot;:\&quot;2020-05-20 13:00:00\&quot;,\&quot;flag\&quot;:true,\&quot;threads\&quot;:5,\&quot;shardingIndex\&quot;:0}&quot;, JsonTestModel.class);
}
@Benchmark
@Test
@@ -975,9 +714,6 @@ public class JsonBenchmark {
}
}
</code></pre>
<p>执行 testGson 方法输出的结果如下:</p>
@@ -999,9 +735,6 @@ current Thread:com.msyc.common.JsonBenchmark.testGson-jmh-worker-1==&gt;20636847
current Thread:com.msyc.common.JsonBenchmark.testGson-jmh-worker-2==&gt;1629232880
......
</code></pre>
<p><strong>@Param</strong></p>
@@ -1025,15 +758,9 @@ current Thread:com.msyc.common.JsonBenchmark.testGson-jmh-worker-2==&gt;16292328
@State(Scope.Thread)
public class JsonBenchmark {
private GsonParser gsonParser = new GsonParser();
private JacksonParser jacksonParser = new JacksonParser();
// 指定参数有三个值
@Param(value =
@@ -1045,9 +772,6 @@ public class JsonBenchmark {
&quot;{\&quot;flag\&quot;:true,\&quot;threads\&quot;:5,\&quot;shardingIndex\&quot;:0}&quot;})
private String jsonStr;
@Benchmark
@Test
@@ -1057,9 +781,6 @@ public class JsonBenchmark {
gsonParser.fromJson(jsonStr, JsonTestModel.class);
}
@Benchmark
@Test
@@ -1069,13 +790,7 @@ public class JsonBenchmark {
jacksonParser.fromJson(jsonStr, JsonTestModel.class);
}
}
</code></pre>
<p>测试结果如下:</p>
@@ -1083,29 +798,11 @@ public class JsonBenchmark {
<pre><code class="language-text">Benchmark (jsonStr) Mode Cnt Score Error Units
JsonBenchmark.testGson {&quot;startDate&quot;:&quot;2020-04-01 16:00:00&quot;,&quot;endDate&quot;:&quot;2020-05-20 13:00:00&quot;,&quot;flag&quot;:true,&quot;threads&quot;:5,&quot;shardingIndex&quot;:0} avgt 5 12180.763 ± 2481.973 ns/op
JsonBenchmark.testGson {&quot;startDate&quot;:&quot;2020-04-01 16:00:00&quot;,&quot;endDate&quot;:&quot;2020-05-20 14:00:00&quot;} avgt 5 8154.709 ± 3393.881 ns/op
JsonBenchmark.testGson {&quot;flag&quot;:true,&quot;threads&quot;:5,&quot;shardingIndex&quot;:0} avgt 5 9994.171 ± 5737.958 ns/op
JsonBenchmark.testJackson {&quot;startDate&quot;:&quot;2020-04-01 16:00:00&quot;,&quot;endDate&quot;:&quot;2020-05-20 13:00:00&quot;,&quot;flag&quot;:true,&quot;threads&quot;:5,&quot;shardingIndex&quot;:0} avgt 5 15663.060 ± 9042.672 ns/op
JsonBenchmark.testJackson {&quot;startDate&quot;:&quot;2020-04-01 16:00:00&quot;,&quot;endDate&quot;:&quot;2020-05-20 14:00:00&quot;} avgt 5 13776.828 ± 11006.412 ns/op
JsonBenchmark.testJackson {&quot;flag&quot;:true,&quot;threads&quot;:5,&quot;shardingIndex&quot;:0} avgt 5 9824.283 ± 311.555 ns/op
</code></pre>
<h4><strong>非注解方式使用</strong></h4>
@@ -1115,9 +812,6 @@ JsonBenchmark.testJackson
<p>使用非注解方式实现上面的例子,代码如下。</p>
<pre><code class="language-java">public class BenchmarkTest{
@Test
public void test() throws RunnerException {
@@ -1149,13 +843,7 @@ JsonBenchmark.testJackson
new Runner(options).run();
}
}
</code></pre>
<ul>
@@ -1175,9 +863,6 @@ JsonBenchmark.testJackson
<p>使用 Java 命令即可运行一个基准测试应用:</p>
<pre><code class="language-shell">java -jar my-benchmarks.jar
</code></pre>
<h4>在 IDEA 中执行</h4>
@@ -1185,9 +870,6 @@ JsonBenchmark.testJackson
<p>在 IDEA 中,我们可以编写一个单元测试方法,在单元测试方法中创建一个 org.openjdk.jmh.runner.Runner调用 Runner 的 run 方法执行基准测试。但 JMH 不会去扫描包,不会执行每个基准测试方法,这需要我们通过配置项来告知 JMH 需要执行哪些基准测试方法。</p>
<pre><code class="language-java">public class BenchmarkTest{
@Test
public void test() throws RunnerException {
@@ -1197,13 +879,7 @@ JsonBenchmark.testJackson
new Runner(options).run();
}
}
</code></pre>
<p>完整例子如下:</p>
@@ -1227,9 +903,6 @@ JsonBenchmark.testJackson
}
}
</code></pre>
<p>Options 在前面已经介绍过了,由于本例中 JsonBenchmark 这个类已经使用了注解,因此 Options 只需要配置需要执行基准测试的类。如果需要执行多个基准测试类include 方法可以多次调用。如果需要将测试结果输出到文件,可调用 output 方法配置文件路径,不配置则输出到控制台。</p>
@@ -1271,17 +944,11 @@ JsonBenchmark.testJackson
@State(Scope.Thread)
public class SentinelEntryBenchmark {
@Param({&quot;25&quot;, &quot;50&quot;, &quot;100&quot;, &quot;200&quot;, &quot;500&quot;, &quot;1000&quot;})
private int length;
private List&lt;Integer&gt; numbers;
@Setup
public void prepare() {
@@ -1295,9 +962,6 @@ public class SentinelEntryBenchmark {
}
}
@Benchmark
@Threads(8)
@@ -1309,9 +973,6 @@ public class SentinelEntryBenchmark {
Collections.sort(numbers);
}
@Benchmark
@Threads(8)
@@ -1339,13 +1000,7 @@ public class SentinelEntryBenchmark {
}
}
}
</code></pre>
<p>该基准测试类使用 @State 指定每个线程使用不同的 numbers 字段的实例,所以 @Setup 注解的方法也会执行 8 次,分别是在每个线程开始执行基准测试方法之前执行,用于完成初始化工作,与 Junit 中的 @Before 注解功能相似。</p>
@@ -1361,9 +1016,6 @@ public class SentinelEntryBenchmark {
(min, avg, max) = (295869.456, 300948.682, 316089.624), stdev = 8631.655
CI (99.9%): [267711.254, 334186.110] (assumes normal distribution)
</code></pre>
<p>最小 ops/s 为 295869.456,平均 ops/s 为 300948.682,最大 ops/s 为 316089.624。</p>
@@ -1377,9 +1029,6 @@ public class SentinelEntryBenchmark {
(min, avg, max) = (280835.799, 309934.827, 337712.803), stdev = 25686.753
CI (99.9%): [211024.287, 408845.366] (assumes normal distribution)
</code></pre>
<p>最小 ops/s 为 280835.799,平均 ops/s 为 309934.827,最大 ops/s 为 337712.803。</p>
@@ -1419,9 +1068,6 @@ public class SentinelEntryBenchmark {
</div>
</div>
</div>
</div>
@@ -1429,9 +1075,6 @@ public class SentinelEntryBenchmark {
</div>
</div>
<a class="off-canvas-overlay" onclick="hide_canvas()"></a>
</div>
@@ -1447,17 +1090,11 @@ public class SentinelEntryBenchmark {
<script>
window.dataLayer = window.dataLayer || [];
function gtag() {
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'G-NPSEEVD756');
@@ -1483,9 +1120,6 @@ public class SentinelEntryBenchmark {
setCookie("lastPath", path)
}
function setCookie(cname, cvalue) {
var d = new Date();
@@ -1497,9 +1131,6 @@ public class SentinelEntryBenchmark {
document.cookie = cname + "=" + cvalue + "; " + expires + ";path = /";
}
function getCookie(cname) {
var name = cname + "=";
@@ -1517,12 +1148,6 @@ public class SentinelEntryBenchmark {
return "";
}
</script>
</html>