You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@dubbo.apache.org by GitBox <gi...@apache.org> on 2018/07/12 01:35:33 UTC

[GitHub] majinkai closed pull request #40: Add pinpoint doc to blog.

majinkai closed pull request #40: Add pinpoint doc to blog.
URL: https://github.com/apache/incubator-dubbo-website/pull/40
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/blog/zh-cn/pinpoint.md b/blog/zh-cn/pinpoint.md
new file mode 100644
index 0000000..b6155ea
--- /dev/null
+++ b/blog/zh-cn/pinpoint.md
@@ -0,0 +1,415 @@
+# 使用Pinpoint做分布式跟踪
+
+在使用Dubbo进行服务化或者整合应用后,假设某个服务后台日志显示有异常,这个服务又被多个应用调用的情况下,我们通常很难判断是哪个应用调用的,问题的起因是什么,因此我们需要一套分布式跟踪系统来快速定位问题,Pinpoint可以帮助我们快速定位问题(当然,解决方案也不止这一种)。
+
+## 什么是Pinpoint(摘自[Pinpoint学习笔记](https://skyao.gitbooks.io/learning-pinpoint/))
+
+[Pinpoint](https://github.com/naver/pinpoint)是一个开源的 APM (Application Performance Management/应用性能管理)工具,用于基于java的大规模分布式系统。
+仿照Google Dapper,Pinpoint通过跟踪分布式应用之间的调用来提供解决方案,以帮助分析系统的总体结构和内部模块之间如何相互联系。
+
+> 注:对于各个模块之间的通讯英文原文中用的是transaction一词,但是我觉得如果翻译为"事务"容易引起误解,所以替换为"交互"或者"调用"这种比较直白的字眼。
+
+在使用上力图简单高效:
+
+* 安装agent,不需要修改哪怕一行代码
+* 最小化性能损失
+
+### 服务器地图(ServerMap)
+
+通过可视化分布式系统的模块和他们之间的相互联系来理解系统拓扑。点击某个节点会展示这个模块的详情,比如它当前的状态和请求数量。
+
+### 实时活动线程图表(Realtime Active Thread Chart)
+
+实时监控应用内部的活动线程。
+
+### 请求/应答分布图表(Request/Response Scatter Chart)
+
+长期可视化请求数量和应答模式来定位潜在问题。通过在图表上拉拽可以选择请求查看更多的详细信息。
+
+### 调用栈(CallStack)
+
+在分布式环境中为每个调用生成代码级别的可视图,在单个视图中定位瓶颈和失败点。
+
+### 巡查(Inspector)
+
+查看应用上的其他详细信息,比如CPU使用率,内存/垃圾回收,TPS,和JVM参数。
+
+### 支持模块
+
+* JDK 6+
+* Tomcat 6/7/8, Jetty 8/9, JBoss EAP 6, Resin 4, Websphere 6/7/8, Vertx 3.3/3.4/3.5
+* Spring, Spring Boot (Embedded Tomcat, Jetty)
+* Apache HTTP Client 3.x/4.x, JDK HttpConnector, GoogleHttpClient, OkHttpClient, NingAsyncHttpClient
+* Thrift Client, Thrift Service, DUBBO PROVIDER, DUBBO CONSUMER
+* ActiveMQ, RabbitMQ
+* MySQL, Oracle, MSSQL, CUBRID,POSTGRESQL, MARIA
+* Arcus, Memcached, Redis, CASSANDRA
+* iBATIS, MyBatis
+* DBCP, DBCP2, HIKARICP
+* gson, Jackson, Json Lib
+* log4j, Logback
+* 自定义模块
+
+## Pinpoint与Dubbo的结合
+
+### 启动Pinpoint
+
+参考Pinpoint的[Quick start](http://naver.github.io/pinpoint/quickstart.html)搭建环境(不需要启动TestApp)
+
+### 准备Dubbo示例程序
+
+#### 创建API包
+
+pom.xml
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>com.example</groupId>
+    <artifactId>demo-api</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+</project>
+```
+
+新建API接口:
+```
+package com.example.demoapi;
+
+public interface HelloService {
+    String sayHello(String name);
+}
+```
+
+#### 实现 Dubbo 服务提供方
+
+pom.xml
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<groupId>com.example</groupId>
+	<artifactId>demo-provider</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<packaging>jar</packaging>
+
+	<name>demo-provider</name>
+
+	<parent>
+		<groupId>org.springframework.boot</groupId>
+		<artifactId>spring-boot-starter-parent</artifactId>
+		<version>2.0.3.RELEASE</version>
+		<relativePath/> <!-- lookup parent from repository -->
+	</parent>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+		<java.version>1.8</java.version>
+	</properties>
+
+	<repositories>
+		<repository>
+			<id>sonatype-nexus-snapshots</id>
+			<url>https://oss.sonatype.org/content/repositories/snapshots</url>
+			<releases>
+				<enabled>false</enabled>
+			</releases>
+			<snapshots>
+				<enabled>true</enabled>
+			</snapshots>
+		</repository>
+	</repositories>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba.boot</groupId>
+			<artifactId>dubbo-spring-boot-starter</artifactId>
+			<version>0.2.0</version>
+		</dependency>
+		<dependency>
+			<groupId>com.example</groupId>
+			<artifactId>demo-api</artifactId>
+			<version>0.0.1-SNAPSHOT</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>
+```
+
+1. 实现 `HelloService` 接口:
+```java
+package com.example.demoprovider.provider;
+
+import com.alibaba.dubbo.config.annotation.Service;
+import com.example.demoapi.HelloService;
+
+@Service(version = "${demo.service.version}",
+        application = "${dubbo.application.id}",
+        protocol = "${dubbo.protocol.id}",
+        registry = "${dubbo.registry.id}")
+public class HelloServiceImpl implements HelloService {
+    static int i = 0;
+    @Override
+    public String sayHello(String name) {
+        i++;
+        if (i % 3 == 0) {
+            throw new RuntimeException("ex");
+        }
+        return "Hello " + name + "!";
+    }
+}
+```
+
+2. 编写 Spring Boot 引导程序:
+```java
+package com.example.demoprovider;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class DemoProviderApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(DemoProviderApplication.class, args);
+	}
+}
+```
+
+3. 配置 `application.properties`:
+
+```properties
+# Spring boot application
+spring.application.name = dubbo-provider-demo
+server.port = 9090
+management.port = 9091
+
+# Service version
+demo.service.version = 1.0.0
+
+# Base packages to scan Dubbo Components (e.g @Service , @Reference)
+dubbo.scan.basePackages  = com.example.demoprovider
+
+# Dubbo Config properties
+## ApplicationConfig Bean
+dubbo.application.id = dubbo-provider-demo
+dubbo.application.name = dubbo-provider-demo
+
+## ProtocolConfig Bean
+dubbo.protocol.id = dubbo
+dubbo.protocol.name = dubbo
+dubbo.protocol.port = 12345
+
+## RegistryConfig Bean
+dubbo.registry.id = my-registry
+dubbo.registry.address = N/A
+```
+
+#### 实现 Dubbo 服务消费方
+
+pom.xml
+```xml
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+
+	<groupId>com.example</groupId>
+	<artifactId>demo-consumer</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<packaging>jar</packaging>
+
+	<name>demo-consumer</name>
+
+	<parent>
+		<groupId>org.springframework.boot</groupId>
+		<artifactId>spring-boot-starter-parent</artifactId>
+		<version>2.0.3.RELEASE</version>
+		<relativePath/> <!-- lookup parent from repository -->
+	</parent>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
+		<java.version>1.8</java.version>
+	</properties>
+
+	<repositories>
+		<repository>
+			<id>sonatype-nexus-snapshots</id>
+			<url>https://oss.sonatype.org/content/repositories/snapshots</url>
+			<releases>
+				<enabled>false</enabled>
+			</releases>
+			<snapshots>
+				<enabled>true</enabled>
+			</snapshots>
+		</repository>
+	</repositories>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-web</artifactId>
+		</dependency>
+		<dependency>
+			<groupId>com.alibaba.boot</groupId>
+			<artifactId>dubbo-spring-boot-starter</artifactId>
+			<version>0.2.0</version>
+		</dependency>
+		<dependency>
+			<groupId>com.example</groupId>
+			<artifactId>demo-api</artifactId>
+			<version>0.0.1-SNAPSHOT</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.springframework.boot</groupId>
+			<artifactId>spring-boot-starter-test</artifactId>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+
+	<build>
+		<plugins>
+			<plugin>
+				<groupId>org.springframework.boot</groupId>
+				<artifactId>spring-boot-maven-plugin</artifactId>
+				<configuration>
+					<classifier>exec</classifier>
+				</configuration>
+			</plugin>
+		</plugins>
+	</build>
+
+</project>
+```
+
+1. 通过 `@Reference` 注入 `HelloService`
+```java
+package com.example.democonsumer.controller;
+
+import com.alibaba.dubbo.config.annotation.Reference;
+import com.example.demoapi.HelloService;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+public class DemoConsumerController {
+    @Reference(version = "${demo.service.version}",
+            application = "${dubbo.application.id}",
+            url = "dubbo://<注意,这里填写具体IP>:12345")
+    private HelloService helloService;
+
+    @RequestMapping("/sayHello")
+    public String sayHello(@RequestParam String name) {
+        return helloService.sayHello(name);
+    }
+}
+```
+> 直连提供者调用需要填写具体IP地址,如果写localhost也可以,但是会被Pinpoint额外识别为一个未知服务
+
+2. 编写 Spring Boot 引导程序(Web 应用):
+```java
+package com.example.democonsumer;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class DemoConsumerApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(DemoConsumerApplication.class, args);
+	}
+}
+```
+
+3. 配置 `application.properties`:
+```properties
+# Spring boot application
+spring.application.name=dubbo-consumer-demo
+server.port=8080
+management.port=8081
+
+# Service Version
+demo.service.version=1.0.0
+
+# Dubbo Config properties
+## ApplicationConfig Bean
+dubbo.application.id=dubbo-consumer-demo
+dubbo.application.name=dubbo-consumer-demo
+
+## ProtocolConfig Bean
+dubbo.protocol.id=dubbo
+dubbo.protocol.name=dubbo
+dubbo.protocol.port=12345
+```
+
+### 使用Pinpoint-agent启动服务提供方和服务消费方
+
+#### 启动服务提供方
+
+1. 编译打包
+```
+mvn clean package
+```
+
+2. 附加参数启动服务提供方
+```
+java -jar -javaagent:$AGENT_PATH/pinpoint-bootstrap-$VERSION.jar -Dpinpoint.agentId=demo-provider -Dpinpoint.applicationName=DP target/demo-provider-0.0.1-SNAPSHOT.jar
+```
+
+3. 附加参数启动服务消费方
+```
+java -jar -javaagent:$AGENT_PATH/pinpoint-bootstrap-$VERSION.jar -Dpinpoint.agentId=demo-consumer -Dpinpoint.applicationName=DC target/demo-comsumer-0.0.1-SNAPSHOT-exec.jar
+```
+
+4. 访问消费方地址模拟用户请求
+
+`http://localhost:8080/sayHello?name=ABC`
+
+## 使用Pinpoint快速定位问题
+
+### 首页
+
+![/admin-guide/images/pinpoint-home.png](../../img/blog/pinpoint-home.png)
+
+> 这里的用户请求是请求DubboProvider数量的双倍,原因是记录了favicon.ico图标请求导致的
+
+### 调用树
+
+![/admin-guide/images/pinpoint-calltree.png](../../img/blog/pinpoint-calltree.png)
+
+### 深入跟踪
+
+![/admin-guide/images/pinpoint-mixedview.png](../../img/blog/pinpoint-mixedview.png)
+
+### 其他
+
+示例简单的模拟了Dubbo的提供和调用,并没有进行数据库等其他中间件的应用,详细使用请参照Pinpoint文档。
diff --git a/img/blog/pinpoint-calltree.png b/img/blog/pinpoint-calltree.png
new file mode 100644
index 0000000..c079cb8
Binary files /dev/null and b/img/blog/pinpoint-calltree.png differ
diff --git a/img/blog/pinpoint-home.png b/img/blog/pinpoint-home.png
new file mode 100644
index 0000000..fb61e04
Binary files /dev/null and b/img/blog/pinpoint-home.png differ
diff --git a/img/blog/pinpoint-mixedview.png b/img/blog/pinpoint-mixedview.png
new file mode 100644
index 0000000..cea9e5e
Binary files /dev/null and b/img/blog/pinpoint-mixedview.png differ
diff --git a/md_json/blog.json b/md_json/blog.json
index de1c9d5..8cd525e 100644
--- a/md_json/blog.json
+++ b/md_json/blog.json
@@ -28,6 +28,10 @@
       "filename": "optimization-branch-prediction.md",
       "__html": "<h1>优化技巧:提前if判断帮助CPU分支预测</h1>\n<hr>\n<h2>分支预测</h2>\n<p>在stackoverflow上有一个非常有名的问题:<a href=\"https://stackoverflow.com/questions/11227809/why-is-it-faster-to-process-a-sorted-array-than-an-unsorted-array\">为什么处理有序数组要比非有序数组快?</a>,可见分支预测对代码运行效率有非常大的影响。</p>\n<p>现代CPU都支持分支预测(branch prediction)和指令流水线(instruction pipeline),这两个结合可以极大提高CPU效率。对于像简单的if跳转,CPU是可以比较好地做分支预测的。但是对于switch跳转,CPU则没有太多的办法。switch本质上是据索引,从地址数组里取地址再跳转。</p>\n<p>要提高代码执行效率,一个重要的原则就是尽量避免CPU把流水线清空,那么提高分支预测的成功率就非常重要。</p>\n<p>那么对于代码里,如果某个switch分支概率很高,是否可以考虑代码层面帮CPU把判断提前,来提高代码执行效率呢?</p>\n<h2>Dubbo里ChannelEventRunnable的switch判断</h2>\n<p>在<code>ChannelEventRunnable</code>里有一个switch来判断channel state,然后做对应的逻辑:<a href=\"https://github.com/hengyunabc/incubator-dubbo/blob/dubbo-2.6.1/dubbo-remoting/dubbo-remoting-api/src/main/java/com/alibaba/dubbo/remoting/transport/dispatcher/ChannelEventRunnable.java#L54\">查看</a></p>\n<p>一个channel建立起来之后,超过99.9%情况它的state都是<code>ChannelState.RECEIVED</code>,那么可以考虑把这个判断提前。</p>\n<h2>benchmark验证</h2>\n<p>下面通过jmh来验证下:</p>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">TestBenchMarks</span> </span>{\n\t<span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">enum</span> ChannelState {\n\t\tCONNECTED, DISCONNECTED, SENT, RECEIVED, CAUGHT\n\t}\n\n\t<span class=\"hljs-meta\">@State</span>(Scope.Benchmark)\n\t<span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">static</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">ExecutionPlan</span> </span>{\n\t\t<span class=\"hljs-meta\">@Param</span>({ <span class=\"hljs-string\">\"1000000\"</span> })\n\t\t<span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">int</span> size;\n\t\t<span class=\"hljs-keyword\">public</span> ChannelState[] states = <span class=\"hljs-keyword\">null</span>;\n\n\t\t<span class=\"hljs-meta\">@Setup</span>\n\t\t<span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">setUp</span><span class=\"hljs-params\">()</span> </span>{\n\t\t\tChannelState[] values = ChannelState.values();\n\t\t\tstates = <span class=\"hljs-keyword\">new</span> ChannelState[size];\n\t\t\tRandom random = <span class=\"hljs-keyword\">new</span> Random(<span class=\"hljs-keyword\">new</span> Date().getTime());\n\t\t\t<span class=\"hljs-keyword\">for</span> (<span class=\"hljs-keyword\">int</span> i = <span class=\"hljs-number\">0</span>; i &lt; size; i++) {\n\t\t\t\t<span class=\"hljs-keyword\">int</span> nextInt = random.nextInt(<span class=\"hljs-number\">1000000</span>);\n\t\t\t\t<span class=\"hljs-keyword\">if</span> (nextInt &gt; <span class=\"hljs-number\">100</span>) {\n\t\t\t\t\tstates[i] = ChannelState.RECEIVED;\n\t\t\t\t} <span class=\"hljs-keyword\">else</span> {\n\t\t\t\t\tstates[i] = values[nextInt % values.length];\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t<span class=\"hljs-meta\">@Fork</span>(value = <span class=\"hljs-number\">5</span>)\n\t<span class=\"hljs-meta\">@Benchmark</span>\n\t<span class=\"hljs-meta\">@BenchmarkMode</span>(Mode.Throughput)\n\t<span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">benchSiwtch</span><span class=\"hljs-params\">(ExecutionPlan plan, Blackhole bh)</span> </span>{\n\t\t<span class=\"hljs-keyword\">int</span> result = <span class=\"hljs-number\">0</span>;\n\t\t<span class=\"hljs-keyword\">for</span> (<span class=\"hljs-keyword\">int</span> i = <span class=\"hljs-number\">0</span>; i &lt; plan.size; ++i) {\n\t\t\t<span class=\"hljs-keyword\">switch</span> (plan.states[i]) {\n\t\t\t<span class=\"hljs-keyword\">case</span> CONNECTED:\n\t\t\t\tresult += ChannelState.CONNECTED.ordinal();\n\t\t\t\t<span class=\"hljs-keyword\">break</span>;\n\t\t\t<span class=\"hljs-keyword\">case</span> DISCONNECTED:\n\t\t\t\tresult += ChannelState.DISCONNECTED.ordinal();\n\t\t\t\t<span class=\"hljs-keyword\">break</span>;\n\t\t\t<span class=\"hljs-keyword\">case</span> SENT:\n\t\t\t\tresult += ChannelState.SENT.ordinal();\n\t\t\t\t<span class=\"hljs-keyword\">break</span>;\n\t\t\t<span class=\"hljs-keyword\">case</span> RECEIVED:\n\t\t\t\tresult += ChannelState.RECEIVED.ordinal();\n\t\t\t\t<span class=\"hljs-keyword\">break</span>;\n\t\t\t<span class=\"hljs-keyword\">case</span> CAUGHT:\n\t\t\t\tresult += ChannelState.CAUGHT.ordinal();\n\t\t\t\t<span class=\"hljs-keyword\">break</span>;\n\t\t\t}\n\t\t}\n\t\tbh.consume(result);\n\t}\n\n\t<span class=\"hljs-meta\">@Fork</span>(value = <span class=\"hljs-number\">5</span>)\n\t<span class=\"hljs-meta\">@Benchmark</span>\n\t<span class=\"hljs-meta\">@BenchmarkMode</span>(Mode.Throughput)\n\t<span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">benchIfAndSwitch</span><span class=\"hljs-params\">(ExecutionPlan plan, Blackhole bh)</span> </span>{\n\t\t<span class=\"hljs-keyword\">int</span> result = <span class=\"hljs-number\">0</span>;\n\t\t<span class=\"hljs-keyword\">for</span> (<span class=\"hljs-keyword\">int</span> i = <span class=\"hljs-number\">0</span>; i &lt; plan.size; ++i) {\n\t\t\tChannelState state = plan.states[i];\n\t\t\t<span class=\"hljs-keyword\">if</span> (state == ChannelState.RECEIVED) {\n\t\t\t\tresult += ChannelState.RECEIVED.ordinal();\n\t\t\t} <span class=\"hljs-keyword\">else</span> {\n\t\t\t\t<span class=\"hljs-keyword\">switch</span> (state) {\n\t\t\t\t<span class=\"hljs-keyword\">case</span> CONNECTED:\n\t\t\t\t\tresult += ChannelState.CONNECTED.ordinal();\n\t\t\t\t\t<span class=\"hljs-keyword\">break</span>;\n\t\t\t\t<span class=\"hljs-keyword\">case</span> SENT:\n\t\t\t\t\tresult += ChannelState.SENT.ordinal();\n\t\t\t\t\t<span class=\"hljs-keyword\">break</span>;\n\t\t\t\t<span class=\"hljs-keyword\">case</span> DISCONNECTED:\n\t\t\t\t\tresult += ChannelState.DISCONNECTED.ordinal();\n\t\t\t\t\t<span class=\"hljs-keyword\">break</span>;\n\t\t\t\t<span class=\"hljs-keyword\">case</span> CAUGHT:\n\t\t\t\t\tresult += ChannelState.CAUGHT.ordinal();\n\t\t\t\t\t<span class=\"hljs-keyword\">break</span>;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tbh.consume(result);\n\t}\n}\n</code></pre>\n<ul>\n<li>benchSiwtch里是纯switch判断</li>\n<li>benchIfAndSwitch 里用一个if提前判断state是否<code>ChannelState.RECEIVED</code></li>\n</ul>\n<p>benchmark结果是:</p>\n<pre><code>Result &quot;io.github.hengyunabc.jmh.TestBenchMarks.benchSiwtch&quot;:\n  576.745 ±(99.9%) 6.806 ops/s [Average]\n  (min, avg, max) = (490.348, 576.745, 618.360), stdev = 20.066\n  CI (99.9%): [569.939, 583.550] (assumes normal distribution)\n\n\n# Run complete. Total time: 00:06:48\n\nBenchmark                         (size)   Mode  Cnt     Score    Error  Units\nTestBenchMarks.benchIfAndSwitch  1000000  thrpt  100  1535.867 ± 61.212  ops/s\nTestBenchMarks.benchSiwtch       1000000  thrpt  100   576.745 ±  6.806  ops/s\n</code></pre>\n<p>可以看到提前if判断的确提高了代码效率,这种技巧可以放在性能要求严格的地方。</p>\n<p>Benchmark代码:<a href=\"https://github.com/hengyunabc/jmh-demo\">https://github.com/hengyunabc/jmh-demo</a></p>\n<h2>总结</h2>\n<ul>\n<li>switch对于CPU来说难以做分支预测</li>\n<li>某些switch条件如果概率比较高,可以考虑单独提前if判断,充分利用CPU的分支预测机制</li>\n</ul>\n"
     },
+    {
+      "filename": "pinpoint.md",
+      "__html": "<h1>使用Pinpoint做分布式跟踪</h1>\n<p>在使用Dubbo进行服务化或者整合应用后,假设某个服务后台日志显示有异常,这个服务又被多个应用调用的情况下,我们通常很难判断是哪个应用调用的,问题的起因是什么,因此我们需要一套分布式跟踪系统来快速定位问题,Pinpoint可以帮助我们快速定位问题(当然,解决方案也不止这一种)。</p>\n<h2>什么是Pinpoint(摘自<a href=\"https://skyao.gitbooks.io/learning-pinpoint/\">Pinpoint学习笔记</a>)</h2>\n<p><a href=\"https://github.com/naver/pinpoint\">Pinpoint</a>是一个开源的 APM (Application Performance Management/应用性能管理)工具,用于基于java的大规模分布式系统。\n仿照Google Dapper,Pinpoint通过跟踪分布式应用之间的调用来提供解决方案,以帮助分析系统的总体结构和内部模块之间如何相互联系。</p>\n<blockquote>\n<p>注:对于各个模块之间的通讯英文原文中用的是transaction一词,但是我觉得如果翻译为&quot;事务&quot;容易引起误解,所以替换为&quot;交互&quot;或者&quot;调用&quot;这种比较直白的字眼。</p>\n</blockquote>\n<p>在使用上力图简单高效:</p>\n<ul>\n<li>安装agent,不需要修改哪怕一行代码</li>\n<li>最小化性能损失</li>\n</ul>\n<h3>服务器地图(ServerMap)</h3>\n<p>通过可视化分布式系统的模块和他们之间的相互联系来理解系统拓扑。点击某个节点会展示这个模块的详情,比如它当前的状态和请求数量。</p>\n<h3>实时活动线程图表(Realtime Active Thread Chart)</h3>\n<p>实时监控应用内部的活动线程。</p>\n<h3>请求/应答分布图表(Request/Response Scatter Chart)</h3>\n<p>长期可视化请求数量和应答模式来定位潜在问题。通过在图表上拉拽可以选择请求查看更多的详细信息。</p>\n<h3>调用栈(CallStack)</h3>\n<p>在分布式环境中为每个调用生成代码级别的可视图,在单个视图中定位瓶颈和失败点。</p>\n<h3>巡查(Inspector)</h3>\n<p>查看应用上的其他详细信息,比如CPU使用率,内存/垃圾回收,TPS,和JVM参数。</p>\n<h3>支持模块</h3>\n<ul>\n<li>JDK 6+</li>\n<li>Tomcat 6/7/8, Jetty 8/9, JBoss EAP 6, Resin 4, Websphere 6/7/8, Vertx 3.3/3.4/3.5</li>\n<li>Spring, Spring Boot (Embedded Tomcat, Jetty)</li>\n<li>Apache HTTP Client 3.x/4.x, JDK HttpConnector, GoogleHttpClient, OkHttpClient, NingAsyncHttpClient</li>\n<li>Thrift Client, Thrift Service, DUBBO PROVIDER, DUBBO CONSUMER</li>\n<li>ActiveMQ, RabbitMQ</li>\n<li>MySQL, Oracle, MSSQL, CUBRID,POSTGRESQL, MARIA</li>\n<li>Arcus, Memcached, Redis, CASSANDRA</li>\n<li>iBATIS, MyBatis</li>\n<li>DBCP, DBCP2, HIKARICP</li>\n<li>gson, Jackson, Json Lib</li>\n<li>log4j, Logback</li>\n<li>自定义模块</li>\n</ul>\n<h2>Pinpoint与Dubbo的结合</h2>\n<h3>启动Pinpoint</h3>\n<p>参考Pinpoint的<a href=\"http://naver.github.io/pinpoint/quickstart.html\">Quick start</a>搭建环境(不需要启动TestApp)</p>\n<h3>准备Dubbo示例程序</h3>\n<h4>创建API包</h4>\n<p>pom.xml</p>\n<pre><code class=\"language-xml\"><span class=\"php\"><span class=\"hljs-meta\">&lt;?</span>xml version=<span class=\"hljs-string\">\"1.0\"</span> encoding=<span class=\"hljs-string\">\"UTF-8\"</span><span class=\"hljs-meta\">?&gt;</span></span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">project</span> <span class=\"hljs-attr\">xmlns</span>=<span class=\"hljs-string\">\"http://maven.apache.org/POM/4.0.0\"</span>\n         <span class=\"hljs-attr\">xmlns:xsi</span>=<span class=\"hljs-string\">\"http://www.w3.org/2001/XMLSchema-instance\"</span>\n         <span class=\"hljs-attr\">xsi:schemaLocation</span>=<span class=\"hljs-string\">\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"</span>&gt;</span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">modelVersion</span>&gt;</span>4.0.0<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">modelVersion</span>&gt;</span>\n\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>com.example<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>demo-api<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version</span>&gt;</span>0.0.1-SNAPSHOT<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">version</span>&gt;</span>\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">project</span>&gt;</span>\n</code></pre>\n<p>新建API接口:</p>\n<pre><code>package com.example.demoapi;\n\npublic interface HelloService {\n    String sayHello(String name);\n}\n</code></pre>\n<h4>实现 Dubbo 服务提供方</h4>\n<p>pom.xml</p>\n<pre><code class=\"language-xml\"><span class=\"php\"><span class=\"hljs-meta\">&lt;?</span>xml version=<span class=\"hljs-string\">\"1.0\"</span> encoding=<span class=\"hljs-string\">\"UTF-8\"</span><span class=\"hljs-meta\">?&gt;</span></span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">project</span> <span class=\"hljs-attr\">xmlns</span>=<span class=\"hljs-string\">\"http://maven.apache.org/POM/4.0.0\"</span> <span class=\"hljs-attr\">xmlns:xsi</span>=<span class=\"hljs-string\">\"http://www.w3.org/2001/XMLSchema-instance\"</span>\n\t<span class=\"hljs-attr\">xsi:schemaLocation</span>=<span class=\"hljs-string\">\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">modelVersion</span>&gt;</span>4.0.0<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">modelVersion</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>com.example<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>demo-provider<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version</span>&gt;</span>0.0.1-SNAPSHOT<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">version</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">packaging</span>&gt;</span>jar<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">packaging</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">name</span>&gt;</span>demo-provider<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">name</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">parent</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>org.springframework.boot<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>spring-boot-starter-parent<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version</span>&gt;</span>2.0.3.RELEASE<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">version</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">relativePath</span>/&gt;</span> <span class=\"hljs-comment\">&lt;!-- lookup parent from repository --&gt;</span>\n\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">parent</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">properties</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">project.build.sourceEncoding</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">project.reporting.outputEncoding</span>&gt;</span>UTF-8<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">project.reporting.outputEncoding</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">java.version</span>&gt;</span>1.8<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">java.version</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">properties</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">repositories</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">repository</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">id</span>&gt;</span>sonatype-nexus-snapshots<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">id</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">url</span>&gt;</span>https://oss.sonatype.org/content/repositories/snapshots<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">url</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">releases</span>&gt;</span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">enabled</span>&gt;</span>false<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">enabled</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">releases</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">snapshots</span>&gt;</span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">enabled</span>&gt;</span>true<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">enabled</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">snapshots</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">repository</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">repositories</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependencies</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>org.springframework.boot<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>spring-boot-starter<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>com.alibaba.boot<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>dubbo-spring-boot-starter<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version</span>&gt;</span>0.2.0<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">version</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>com.example<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>demo-api<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version</span>&gt;</span>0.0.1-SNAPSHOT<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">version</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dependency</span>&gt;</span>\n\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>org.springframework.boot<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>spring-boot-starter-test<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope</span>&gt;</span>test<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">scope</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dependencies</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">build</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">plugins</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">plugin</span>&gt;</span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>org.springframework.boot<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">plugin</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">plugins</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">build</span>&gt;</span>\n\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">project</span>&gt;</span>\n</code></pre>\n<ol>\n<li>实现 <code>HelloService</code> 接口:</li>\n</ol>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">package</span> com.example.demoprovider.provider;\n\n<span class=\"hljs-keyword\">import</span> com.alibaba.dubbo.config.annotation.Service;\n<span class=\"hljs-keyword\">import</span> com.example.demoapi.HelloService;\n\n<span class=\"hljs-meta\">@Service</span>(version = <span class=\"hljs-string\">\"${demo.service.version}\"</span>,\n        application = <span class=\"hljs-string\">\"${dubbo.application.id}\"</span>,\n        protocol = <span class=\"hljs-string\">\"${dubbo.protocol.id}\"</span>,\n        registry = <span class=\"hljs-string\">\"${dubbo.registry.id}\"</span>)\n<span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">HelloServiceImpl</span> <span class=\"hljs-keyword\">implements</span> <span class=\"hljs-title\">HelloService</span> </span>{\n    <span class=\"hljs-keyword\">static</span> <span class=\"hljs-keyword\">int</span> i = <span class=\"hljs-number\">0</span>;\n    <span class=\"hljs-meta\">@Override</span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> String <span class=\"hljs-title\">sayHello</span><span class=\"hljs-params\">(String name)</span> </span>{\n        i++;\n        <span class=\"hljs-keyword\">if</span> (i % <span class=\"hljs-number\">3</span> == <span class=\"hljs-number\">0</span>) {\n            <span class=\"hljs-keyword\">throw</span> <span class=\"hljs-keyword\">new</span> RuntimeException(<span class=\"hljs-string\">\"ex\"</span>);\n        }\n        <span class=\"hljs-keyword\">return</span> <span class=\"hljs-string\">\"Hello \"</span> + name + <span class=\"hljs-string\">\"!\"</span>;\n    }\n}\n</code></pre>\n<ol start=\"2\">\n<li>编写 Spring Boot 引导程序:</li>\n</ol>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">package</span> com.example.demoprovider;\n\n<span class=\"hljs-keyword\">import</span> org.springframework.boot.SpringApplication;\n<span class=\"hljs-keyword\">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;\n\n<span class=\"hljs-meta\">@SpringBootApplication</span>\n<span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">DemoProviderApplication</span> </span>{\n\n\t<span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">static</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">main</span><span class=\"hljs-params\">(String[] args)</span> </span>{\n\t\tSpringApplication.run(DemoProviderApplication.class, args);\n\t}\n}\n</code></pre>\n<ol start=\"3\">\n<li>配置 <code>application.properties</code>:</li>\n</ol>\n<pre><code class=\"language-properties\"># Spring boot application\nspring.application.name = dubbo-provider-demo\nserver.port = 9090\nmanagement.port = 9091\n\n# Service version\ndemo.service.version = 1.0.0\n\n# Base packages to scan Dubbo Components (e.g @Service , @Reference)\ndubbo.scan.basePackages  = com.example.demoprovider\n\n# Dubbo Config properties\n## ApplicationConfig Bean\ndubbo.application.id = dubbo-provider-demo\ndubbo.application.name = dubbo-provider-demo\n\n## ProtocolConfig Bean\ndubbo.protocol.id = dubbo\ndubbo.protocol.name = dubbo\ndubbo.protocol.port = 12345\n\n## RegistryConfig Bean\ndubbo.registry.id = my-registry\ndubbo.registry.address = N/A\n</code></pre>\n<h4>实现 Dubbo 服务消费方</h4>\n<p>pom.xml</p>\n<pre><code class=\"language-xml\"><span class=\"php\"><span class=\"hljs-meta\">&lt;?</span>xml version=<span class=\"hljs-string\">\"1.0\"</span> encoding=<span class=\"hljs-string\">\"UTF-8\"</span><span class=\"hljs-meta\">?&gt;</span></span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">project</span> <span class=\"hljs-attr\">xmlns</span>=<span class=\"hljs-string\">\"http://maven.apache.org/POM/4.0.0\"</span> <span class=\"hljs-attr\">xmlns:xsi</span>=<span class=\"hljs-string\">\"http://www.w3.org/2001/XMLSchema-instance\"</span>\n\t<span class=\"hljs-attr\">xsi:schemaLocation</span>=<span class=\"hljs-string\">\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\"</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">modelVersion</span>&gt;</span>4.0.0<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">modelVersion</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>com.example<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>demo-consumer<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version</span>&gt;</span>0.0.1-SNAPSHOT<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">version</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">packaging</span>&gt;</span>jar<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">packaging</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">name</span>&gt;</span>demo-consumer<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">name</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">parent</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>org.springframework.boot<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>spring-boot-starter-parent<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version</span>&gt;</span>2.0.3.RELEASE<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">version</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">relativePath</span>/&gt;</span> <span class=\"hljs-comment\">&lt;!-- lookup parent from repository --&gt;</span>\n\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">parent</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">properties</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">project.build.sourceEncoding</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">project.reporting.outputEncoding</span>&gt;</span>UTF-8<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">project.reporting.outputEncoding</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">java.version</span>&gt;</span>1.8<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">java.version</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">properties</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">repositories</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">repository</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">id</span>&gt;</span>sonatype-nexus-snapshots<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">id</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">url</span>&gt;</span>https://oss.sonatype.org/content/repositories/snapshots<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">url</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">releases</span>&gt;</span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">enabled</span>&gt;</span>false<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">enabled</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">releases</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">snapshots</span>&gt;</span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">enabled</span>&gt;</span>true<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">enabled</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">snapshots</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">repository</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">repositories</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependencies</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>org.springframework.boot<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>spring-boot-starter-web<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>com.alibaba.boot<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>dubbo-spring-boot-starter<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version</span>&gt;</span>0.2.0<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">version</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>com.example<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>demo-api<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">version</span>&gt;</span>0.0.1-SNAPSHOT<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">version</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dependency</span>&gt;</span>\n\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>org.springframework.boot<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>spring-boot-starter-test<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">scope</span>&gt;</span>test<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">scope</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dependency</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">dependencies</span>&gt;</span>\n\n\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">build</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">plugins</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">plugin</span>&gt;</span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId</span>&gt;</span>org.springframework.boot<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">groupId</span>&gt;</span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId</span>&gt;</span>spring-boot-maven-plugin<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">artifactId</span>&gt;</span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">configuration</span>&gt;</span>\n\t\t\t\t\t<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">classifier</span>&gt;</span>exec<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">classifier</span>&gt;</span>\n\t\t\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">configuration</span>&gt;</span>\n\t\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">plugin</span>&gt;</span>\n\t\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">plugins</span>&gt;</span>\n\t<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">build</span>&gt;</span>\n\n<span class=\"hljs-tag\">&lt;/<span class=\"hljs-name\">project</span>&gt;</span>\n</code></pre>\n<ol>\n<li>通过 <code>@Reference</code> 注入 <code>HelloService</code></li>\n</ol>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">package</span> com.example.democonsumer.controller;\n\n<span class=\"hljs-keyword\">import</span> com.alibaba.dubbo.config.annotation.Reference;\n<span class=\"hljs-keyword\">import</span> com.example.demoapi.HelloService;\n<span class=\"hljs-keyword\">import</span> org.springframework.web.bind.annotation.RequestMapping;\n<span class=\"hljs-keyword\">import</span> org.springframework.web.bind.annotation.RequestParam;\n<span class=\"hljs-keyword\">import</span> org.springframework.web.bind.annotation.RestController;\n\n<span class=\"hljs-meta\">@RestController</span>\n<span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">DemoConsumerController</span> </span>{\n    <span class=\"hljs-meta\">@Reference</span>(version = <span class=\"hljs-string\">\"${demo.service.version}\"</span>,\n            application = <span class=\"hljs-string\">\"${dubbo.application.id}\"</span>,\n            url = <span class=\"hljs-string\">\"dubbo://&lt;注意,这里填写具体IP&gt;:12345\"</span>)\n    <span class=\"hljs-keyword\">private</span> HelloService helloService;\n\n    <span class=\"hljs-meta\">@RequestMapping</span>(<span class=\"hljs-string\">\"/sayHello\"</span>)\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> String <span class=\"hljs-title\">sayHello</span><span class=\"hljs-params\">(@RequestParam String name)</span> </span>{\n        <span class=\"hljs-keyword\">return</span> helloService.sayHello(name);\n    }\n}\n</code></pre>\n<blockquote>\n<p>直连提供者调用需要填写具体IP地址,如果写localhost也可以,但是会被Pinpoint额外识别为一个未知服务</p>\n</blockquote>\n<ol start=\"2\">\n<li>编写 Spring Boot 引导程序(Web 应用):</li>\n</ol>\n<pre><code class=\"language-java\"><span class=\"hljs-keyword\">package</span> com.example.democonsumer;\n\n<span class=\"hljs-keyword\">import</span> org.springframework.boot.SpringApplication;\n<span class=\"hljs-keyword\">import</span> org.springframework.boot.autoconfigure.SpringBootApplication;\n\n<span class=\"hljs-meta\">@SpringBootApplication</span>\n<span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">DemoConsumerApplication</span> </span>{\n\n\t<span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">static</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">main</span><span class=\"hljs-params\">(String[] args)</span> </span>{\n\t\tSpringApplication.run(DemoConsumerApplication.class, args);\n\t}\n}\n</code></pre>\n<ol start=\"3\">\n<li>配置 <code>application.properties</code>:</li>\n</ol>\n<pre><code class=\"language-properties\"># Spring boot application\nspring.application.name=dubbo-consumer-demo\nserver.port=8080\nmanagement.port=8081\n\n# Service Version\ndemo.service.version=1.0.0\n\n# Dubbo Config properties\n## ApplicationConfig Bean\ndubbo.application.id=dubbo-consumer-demo\ndubbo.application.name=dubbo-consumer-demo\n\n## ProtocolConfig Bean\ndubbo.protocol.id=dubbo\ndubbo.protocol.name=dubbo\ndubbo.protocol.port=12345\n</code></pre>\n<h3>使用Pinpoint-agent启动服务提供方和服务消费方</h3>\n<h4>启动服务提供方</h4>\n<ol>\n<li>编译打包</li>\n</ol>\n<pre><code>mvn clean package\n</code></pre>\n<ol start=\"2\">\n<li>附加参数启动服务提供方</li>\n</ol>\n<pre><code>java -jar -javaagent:$AGENT_PATH/pinpoint-bootstrap-$VERSION.jar -Dpinpoint.agentId=demo-provider -Dpinpoint.applicationName=DP target/demo-provider-0.0.1-SNAPSHOT.jar\n</code></pre>\n<ol start=\"3\">\n<li>附加参数启动服务消费方</li>\n</ol>\n<pre><code>java -jar -javaagent:$AGENT_PATH/pinpoint-bootstrap-$VERSION.jar -Dpinpoint.agentId=demo-consumer -Dpinpoint.applicationName=DC target/demo-comsumer-0.0.1-SNAPSHOT-exec.jar\n</code></pre>\n<ol start=\"4\">\n<li>访问消费方地址模拟用户请求</li>\n</ol>\n<p><code>http://localhost:8080/sayHello?name=ABC</code></p>\n<h2>使用Pinpoint快速定位问题</h2>\n<h3>首页</h3>\n<p><img src=\"../../img/blog/pinpoint-home.png\" alt=\"/admin-guide/images/pinpoint-home.png\"></p>\n<blockquote>\n<p>这里的用户请求是请求DubboProvider数量的双倍,原因是记录了favicon.ico图标请求导致的</p>\n</blockquote>\n<h3>调用树</h3>\n<p><img src=\"../../img/blog/pinpoint-calltree.png\" alt=\"/admin-guide/images/pinpoint-calltree.png\"></p>\n<h3>深入跟踪</h3>\n<p><img src=\"../../img/blog/pinpoint-mixedview.png\" alt=\"/admin-guide/images/pinpoint-mixedview.png\"></p>\n<h3>其他</h3>\n<p>示例简单的模拟了Dubbo的提供和调用,并没有进行数据库等其他中间件的应用,详细使用请参照Pinpoint文档。</p>\n"
+    },
     {
       "filename": "spring-boot-dubbo-start-stop-analysis.md",
       "__html": "<h1>Spring-boot+Dubbo应用启停源码分析</h1>\n<h3>背景介绍</h3>\n<p><a href=\"https://github.com/apache/incubator-dubbo-spring-boot-project\">Dubbo Spring Boot</a> 工程致力于简化 Dubbo RPC 框架在Spring Boot应用场景的开发。同时也整合了 Spring Boot 特性:</p>\n<ul>\n<li><a href=\"https://github.com/apache/incubator-dubbo-spring-boot-project/blob/master/dubbo-spring-boot-autoconfigure\">自动装配</a> (比如: 注解驱动, 自动装配等).</li>\n<li><a href=\"https://github.com/apache/incubator-dubbo-spring-boot-project/blob/master/dubbo-spring-boot-actuator\">Production-Ready</a> (比如: 安全, 健康检查, 外部化配置等).</li>\n</ul>\n<h3>DubboConsumer启动分析</h3>\n<p>你有没有想过一个问题?<code>incubator-dubbo-spring-boot-project</code>中的<code>DubboConsumerDemo</code>应用就一行代码,<code>main</code>方法执行完之后,为什么不会直接退出呢?</p>\n<pre><code class=\"language-java\"><span class=\"hljs-meta\">@SpringBootApplication</span>(scanBasePackages = <span class=\"hljs-string\">\"com.alibaba.boot.dubbo.demo.consumer.controller\"</span>)\n<span class=\"hljs-keyword\">public</span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class</span> <span class=\"hljs-title\">DubboConsumerDemo</span> </span>{\n\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">static</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">main</span><span class=\"hljs-params\">(String[] args)</span> </span>{\n        SpringApplication.run(DubboConsumerDemo.class,args);\n    }\n\n}\n</code></pre>\n<p>其实要回答这样一个问题,我们首先需要把这个问题进行一个抽象,即一个JVM进程,在什么情况下会退出?</p>\n<p>以Java 8为例,通过查阅JVM语言规范[1],在12.8章节中有清晰的描述:</p>\n<p>A program terminates all its activity and <em>exits</em> when one of two things happens:</p>\n<ul>\n<li>All the threads that are not daemon threads terminate.</li>\n<li>Some thread invokes the <code>exit</code> method of class <code>Runtime</code> or class <code>System</code>, and the <code>exit</code> operation is not forbidden by the security manager.</li>\n</ul>\n<p>也就是说,导致JVM的退出只有2种情况:</p>\n<ol>\n<li>所有的非daemon进程完全终止</li>\n<li>某个线程调用了<code>System.exit()</code>或<code>Runtime.exit()</code></li>\n</ol>\n<p>因此针对上面的情况,我们判断,一定是有某个非daemon线程没有退出导致。我们知道,通过jstack可以看到所有的线程信息,包括他们是否是daemon线程,可以通过jstack找出那些是非deamon的线程。</p>\n<pre><code class=\"language-sh\">➜  jstack 57785 | grep tid | grep -v <span class=\"hljs-string\">\"daemon\"</span>\n<span class=\"hljs-string\">\"container-0\"</span> <span class=\"hljs-comment\">#37 prio=5 os_prio=31 tid=0x00007fbe312f5800 nid=0x7103 waiting on condition  [0x0000700010144000]</span>\n<span class=\"hljs-string\">\"container-1\"</span> <span class=\"hljs-comment\">#49 prio=5 os_prio=31 tid=0x00007fbe3117f800 nid=0x7b03 waiting on condition  [0x0000700010859000]</span>\n<span class=\"hljs-string\">\"DestroyJavaVM\"</span> <span class=\"hljs-comment\">#83 prio=5 os_prio=31 tid=0x00007fbe30011000 nid=0x2703 waiting on condition  [0x0000000000000000]</span>\n<span class=\"hljs-string\">\"VM Thread\"</span> os_prio=31 tid=0x00007fbe3005e800 nid=0x3703 runnable\n<span class=\"hljs-string\">\"GC Thread#0\"</span> os_prio=31 tid=0x00007fbe30013800 nid=0x5403 runnable\n<span class=\"hljs-string\">\"GC Thread#1\"</span> os_prio=31 tid=0x00007fbe30021000 nid=0x5303 runnable\n<span class=\"hljs-string\">\"GC Thread#2\"</span> os_prio=31 tid=0x00007fbe30021800 nid=0x2d03 runnable\n<span class=\"hljs-string\">\"GC Thread#3\"</span> os_prio=31 tid=0x00007fbe30022000 nid=0x2f03 runnable\n<span class=\"hljs-string\">\"G1 Main Marker\"</span> os_prio=31 tid=0x00007fbe30040800 nid=0x5203 runnable\n<span class=\"hljs-string\">\"G1 Conc#0\"</span> os_prio=31 tid=0x00007fbe30041000 nid=0x4f03 runnable\n<span class=\"hljs-string\">\"G1 Refine#0\"</span> os_prio=31 tid=0x00007fbe31044800 nid=0x4e03 runnable\n<span class=\"hljs-string\">\"G1 Refine#1\"</span> os_prio=31 tid=0x00007fbe31045800 nid=0x4d03 runnable\n<span class=\"hljs-string\">\"G1 Refine#2\"</span> os_prio=31 tid=0x00007fbe31046000 nid=0x4c03 runnable\n<span class=\"hljs-string\">\"G1 Refine#3\"</span> os_prio=31 tid=0x00007fbe31047000 nid=0x4b03 runnable\n<span class=\"hljs-string\">\"G1 Young RemSet Sampling\"</span> os_prio=31 tid=0x00007fbe31047800 nid=0x3603 runnable\n<span class=\"hljs-string\">\"VM Periodic Task Thread\"</span> os_prio=31 tid=0x00007fbe31129000 nid=0x6703 waiting on condition\n\n</code></pre>\n<blockquote>\n<p>此处通过grep tid 找出所有的线程摘要,通过grep -v找出不包含daemon关键字的行</p>\n</blockquote>\n<p>通过上面的结果,我们发现了一些信息:</p>\n<ul>\n<li>有两个线程<code>container-0</code>, <code>container-1</code>非常可疑,他们是非daemon线程,处于wait状态</li>\n<li>有一些GC相关的线程,和VM打头的线程,也是非daemon线程,但他们很有可能是JVM自己的线程,在此暂时忽略。</li>\n</ul>\n<p>综上,我们可以推断,很可能是因为<code>container-0</code>和<code>container-1</code>导致JVM没有退出。现在我们通过源码,搜索一下到底是谁创建的这两个线程。</p>\n<p>通过对spring-boot的源码分析,我们在<code>org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer</code>的<code>startDaemonAwaitThread</code>找到了如下代码</p>\n<pre><code class=\"language-java\">\t<span class=\"hljs-function\"><span class=\"hljs-keyword\">private</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">startDaemonAwaitThread</span><span class=\"hljs-params\">()</span> </span>{\n\t\tThread awaitThread = <span class=\"hljs-keyword\">new</span> Thread(<span class=\"hljs-string\">\"container-\"</span> + (containerCounter.get())) {\n\n\t\t\t<span class=\"hljs-meta\">@Override</span>\n\t\t\t<span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">run</span><span class=\"hljs-params\">()</span> </span>{\n\t\t\t\tTomcatEmbeddedServletContainer.<span class=\"hljs-keyword\">this</span>.tomcat.getServer().await();\n\t\t\t}\n\n\t\t};\n\t\tawaitThread.setContextClassLoader(getClass().getClassLoader());\n\t\tawaitThread.setDaemon(<span class=\"hljs-keyword\">false</span>);\n\t\tawaitThread.start();\n\t}\n</code></pre>\n<p>在这个方法加个断点,看下调用堆栈:</p>\n<pre><code>initialize:115, TomcatEmbeddedServletContainer (org.springframework.boot.context.embedded.tomcat)\n&lt;init&gt;:84, TomcatEmbeddedServletContainer (org.springframework.boot.context.embedded.tomcat)\ngetTomcatEmbeddedServletContainer:554, TomcatEmbeddedServletContainerFactory (org.springframework.boot.context.embedded.tomcat)\ngetEmbeddedServletContainer:179, TomcatEmbeddedServletContainerFactory (org.springframework.boot.context.embedded.tomcat)\ncreateEmbeddedServletContainer:164, EmbeddedWebApplicationContext (org.springframework.boot.context.embedded)\nonRefresh:134, EmbeddedWebApplicationContext (org.springframework.boot.context.embedded)\nrefresh:537, AbstractApplicationContext (org.springframework.context.support)\nrefresh:122, EmbeddedWebApplicationContext (org.springframework.boot.context.embedded)\nrefresh:693, SpringApplication (org.springframework.boot)\nrefreshContext:360, SpringApplication (org.springframework.boot)\nrun:303, SpringApplication (org.springframework.boot)\nrun:1118, SpringApplication (org.springframework.boot)\nrun:1107, SpringApplication (org.springframework.boot)\nmain:35, DubboConsumerDemo (com.alibaba.boot.dubbo.demo.consumer.bootstrap)\n</code></pre>\n<p>可以看到,spring-boot应用在启动的过程中,由于默认启动了Tomcat暴露HTTP服务,所以执行到了上述方法,而Tomcat启动的所有的线程,默认都是daemon线程,例如监听请求的Acceptor,工作线程池等等,如果这里不加控制的话,启动完成之后JVM也会退出。因此需要显示的启动一个线程,在某个条件下进行持续等待,从而避免线程退出。</p>\n<p>下面我们在深挖一下,在Tomcat的<code>this.tomcat.getServer().await()</code>这个方法中,线程是如何实现不退出的。这里为了阅读方便,去掉了不相关的代码。</p>\n<pre><code class=\"language-java\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">await</span><span class=\"hljs-params\">()</span> </span>{\n    \t<span class=\"hljs-comment\">// ...</span>\n        <span class=\"hljs-keyword\">if</span>( port==-<span class=\"hljs-number\">1</span> ) {\n            <span class=\"hljs-keyword\">try</span> {\n                awaitThread = Thread.currentThread();\n                <span class=\"hljs-keyword\">while</span>(!stopAwait) {\n                    <span class=\"hljs-keyword\">try</span> {\n                        Thread.sleep( <span class=\"hljs-number\">10000</span> );\n                    } <span class=\"hljs-keyword\">catch</span>( InterruptedException ex ) {\n                        <span class=\"hljs-comment\">// continue and check the flag</span>\n                    }\n                }\n            } <span class=\"hljs-keyword\">finally</span> {\n                awaitThread = <span class=\"hljs-keyword\">null</span>;\n            }\n            <span class=\"hljs-keyword\">return</span>;\n        }\n\t\t<span class=\"hljs-comment\">// ...</span>\n    }\n</code></pre>\n<p>在await方法中,实际上当前线程在一个while循环中每10秒检查一次 <code>stopAwait</code>这个变量,它是一个<code>volatile</code>类型变量,用于确保被另一个线程修改后,当前线程能够立即看到这个变化。如果没有变化,就会一直处于while循环中。这就是该线程不退出的原因,也就是整个spring-boot应用不退出的原因。</p>\n<p>因为Springboot应用同时启动了8080和8081(management port)两个端口,实际是启动了两个Tomcat,因此会有两个线程<code>container-0</code>和<code>container-1</code>。</p>\n<p>接下来,我们再看看,这个Spring-boot应用又是如何退出的呢?</p>\n<h3>DubboConsumer退出分析</h3>\n<p>在前面的描述中提到,有一个线程持续的在检查<code>stopAwait</code>这个变量,那么我们自然想到,在Stop的时候,应该会有一个线程去修改<code>stopAwait</code>,打破这个while循环,那又是谁在修改这个变量呢?</p>\n<p>通过对源码分析,可以看到只有一个方法修改了<code>stopAwait</code>,即<code>org.apache.catalina.core.StandardServer#stopAwait</code>,我们在此处加个断点,看看是谁在调用。</p>\n<blockquote>\n<p>注意,当我们在Intellij IDEA的Debug模式,加上一个断点后,需要在命令行下使用<code>kill -s INT $PID</code>或者<code>kill -s TERM $PID</code>才能触发断点,点击IDE上的Stop按钮,不会触发断点。这是IDEA的bug</p>\n</blockquote>\n<p>可以看到有一个名为<code>Thread-3</code>的线程调用了该方法:</p>\n<pre><code class=\"language-java\">stopAwait:<span class=\"hljs-number\">390</span>, StandardServer (org.apache.catalina.core)\nstopInternal:<span class=\"hljs-number\">819</span>, StandardServer (org.apache.catalina.core)\nstop:<span class=\"hljs-number\">226</span>, LifecycleBase (org.apache.catalina.util)\nstop:<span class=\"hljs-number\">377</span>, Tomcat (org.apache.catalina.startup)\nstopTomcat:<span class=\"hljs-number\">241</span>, TomcatEmbeddedServletContainer (org.springframework.boot.context.embedded.tomcat)\nstop:<span class=\"hljs-number\">295</span>, TomcatEmbeddedServletContainer (org.springframework.boot.context.embedded.tomcat)\nstopAndReleaseEmbeddedServletContainer:<span class=\"hljs-number\">306</span>, EmbeddedWebApplicationContext (org.springframework.boot.context.embedded)\nonClose:<span class=\"hljs-number\">155</span>, EmbeddedWebApplicationContext (org.springframework.boot.context.embedded)\ndoClose:<span class=\"hljs-number\">1014</span>, AbstractApplicationContext (org.springframework.context.support)\nrun:<span class=\"hljs-number\">929</span>, AbstractApplicationContext$<span class=\"hljs-number\">2</span> (org.springframework.context.support)\n</code></pre>\n<p>通过源码分析,原来是通过Spring注册的<code>ShutdownHook</code>来执行的</p>\n<pre><code class=\"language-java\">\t<span class=\"hljs-meta\">@Override</span>\n\t<span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">registerShutdownHook</span><span class=\"hljs-params\">()</span> </span>{\n\t\t<span class=\"hljs-keyword\">if</span> (<span class=\"hljs-keyword\">this</span>.shutdownHook == <span class=\"hljs-keyword\">null</span>) {\n\t\t\t<span class=\"hljs-comment\">// No shutdown hook registered yet.</span>\n\t\t\t<span class=\"hljs-keyword\">this</span>.shutdownHook = <span class=\"hljs-keyword\">new</span> Thread() {\n\t\t\t\t<span class=\"hljs-meta\">@Override</span>\n\t\t\t\t<span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">run</span><span class=\"hljs-params\">()</span> </span>{\n\t\t\t\t\t<span class=\"hljs-keyword\">synchronized</span> (startupShutdownMonitor) {\n\t\t\t\t\t\tdoClose();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t\tRuntime.getRuntime().addShutdownHook(<span class=\"hljs-keyword\">this</span>.shutdownHook);\n\t\t}\n\t}\n</code></pre>\n<p>通过查阅Java的API文档[2], 我们可以知道ShutdownHook将在下面两种情况下执行</p>\n<blockquote>\n<p>The Java virtual machine <em>shuts down</em> in response to two kinds of events:</p>\n<ul>\n<li>The program <em>exits</em> normally, when the last non-daemon thread exits or when the <code>exit</code> (equivalently, <a href=\"https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#exit-int-\"><code>System.exit</code></a>) method is invoked, or</li>\n<li>The virtual machine is <em>terminated</em> in response to a user interrupt, such as typing <code>^C</code>, or a system-wide event, such as user logoff or system shutdown.</li>\n</ul>\n</blockquote>\n<ol>\n<li>调用了System.exit()方法</li>\n<li>响应外部的信号,例如Ctrl+C(其实发送的是SIGINT信号),或者是<code>SIGTERM</code>信号(默认<code>kill $PID</code>发送的是<code>SIGTERM</code>信号)</li>\n</ol>\n<p>因此,正常的应用在停止过程中(<code>kill -9 $PID</code>除外),都会执行上述ShutdownHook,它的作用不仅仅是关闭tomcat,还有进行其他的清理工作,在此不再赘述。</p>\n<h3>总结</h3>\n<ol>\n<li>在<code>DubboConsumer</code>启动的过程中,通过启动一个独立的非daemon线程循环检查变量的状态,确保进程不退出</li>\n<li>在<code>DubboConsumer</code>停止的过程中,通过执行spring容器的shutdownhook,修改了变量的状态,使得程序正常退出</li>\n</ol>\n<h3>问题</h3>\n<p>在DubboProvider的例子中,我们看到Provider并没有启动Tomcat提供HTTP服务,那又是如何实现不退出的呢?我们将在下一篇文章中回答这个问题。</p>\n<h4>彩蛋</h4>\n<p>在<code>Intellij IDEA</code>中运行了如下的单元测试,创建一个线程执行睡眠1000秒的操作,我们惊奇的发现,代码并没有线程执行完就退出了,这又是为什么呢?(被创建的线程是非daemon线程)</p>\n<pre><code class=\"language-java\">    <span class=\"hljs-meta\">@Test</span>\n    <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">test</span><span class=\"hljs-params\">()</span> </span>{\n        <span class=\"hljs-keyword\">new</span> Thread(<span class=\"hljs-keyword\">new</span> Runnable() {\n            <span class=\"hljs-meta\">@Override</span>\n            <span class=\"hljs-function\"><span class=\"hljs-keyword\">public</span> <span class=\"hljs-keyword\">void</span> <span class=\"hljs-title\">run</span><span class=\"hljs-params\">()</span> </span>{\n                <span class=\"hljs-keyword\">try</span> {\n                    Thread.sleep(<span class=\"hljs-number\">1000000</span>);\n                } <span class=\"hljs-keyword\">catch</span> (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n        }).start();\n    }\n</code></pre>\n<p>[1] <a href=\"https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.8\">https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.8</a></p>\n<p>[2] <a href=\"https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#addShutdownHook\">https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#addShutdownHook</a></p>\n"
diff --git a/site_config/blog.js b/site_config/blog.js
index 3e55712..cdd7fa9 100644
--- a/site_config/blog.js
+++ b/site_config/blog.js
@@ -37,6 +37,13 @@ export default {
     barText: '博客',
     postsTitle: '所有文章',
     list: [
+      {
+        title: '使用Pinpoint做分布式跟踪',
+        author: '@majinkai',
+        dateStr: 'July 12th, 2018',
+        desc: '利用Pinpoint对Dubbo分布式应用进行调用链跟踪与性能监控',
+        link: '/blog/pinpoint.md',
+      },
       {
         title: 'Dubbo 的同步与异步调用方式',
         author: '@Jerrick Zhu',
diff --git a/src/pages/home/index.scss b/src/pages/home/index.scss
index 2832d02..46ba1fb 100644
--- a/src/pages/home/index.scss
+++ b/src/pages/home/index.scss
@@ -81,7 +81,8 @@
       box-sizing: border-box;
       width: 100%;
       text-align: center;
-      padding: 0 20px;
+      padding: 40px 20px;
+      overflow: hidden;
       & > img {
         position: absolute;
         z-index: -1;


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@dubbo.apache.org
For additional commands, e-mail: notifications-help@dubbo.apache.org