1. 程式人生 > >springcloud+sleuth+zipkin+kafka+es

springcloud+sleuth+zipkin+kafka+es

前面已經完成了Springcloud+sleuth+zipkin的入門,以及kafka的安裝。至於ES這裡就不在說明了,網上安裝使用資料挺多的,這裡僅僅是將其作為持久化工具使用。

環境說明

  1. jdk1.8 server 64位
  2. intellij IDEA 2017
  3. springboot 1.5.2.RELEASE
  4. Springcloud Dalston.SR5
  5. kafka 2.11-1.0.0
  6. zookeeper 3.4.10
  7. elasticsearch 5.6.4

1、啟動環境

  1. 啟動zookeeper
  2. 啟動kafka
  3. 啟動elasticsearch

2、建立zipkinserver

在pom.xml中加入如下內容:

 <dependencies>
        <!--springboot依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--加入zipkin依賴-->
<dependency> <groupId>io.zipkin.java</groupId> <artifactId>zipkin</artifactId> <version>2.4.2</version> </dependency> <!--引入zipkin的ES儲存--> <dependency> <groupId
>
io.zipkin.java</groupId> <artifactId>zipkin-autoconfigure-storage-elasticsearch-http</artifactId> <version>2.4.2</version> <optional>true</optional> </dependency> <!--引入zipkin的流繫結--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-sleuth-zipkin-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-kafka</artifactId> </dependency> </dependencies>

同時在resources中建立application.yml

#配置kafka
spring:
  sleuth:
    enabled: false
    sampler:
      percentage: 1.0
  cloud:
    stream:
      kafka:
        binder:
          brokers: localhost:9092
          zkNodes: localhost:2181
  #ES配置
zipkin:
  storage:
    type: elasticsearch
    elasticsearch:
      host: localhost:9200
      cluster: elasticsearch
      index: zipkin
      index-shards: 1
      index-replicas: 1

啟動類增加如下註解:

@SpringBootApplication
@EnableZipkinStreamServer//配置可以作為zipkinserver
public class ZipkinServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ZipkinServerApplication.class,args);
    }
}

啟動後可以 在控制檯看到kafka的連線資訊

3、建立目標服務一

在pom.xml中引入:

<dependencies>
        <!--引入springboot-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--引入zipkin繫結-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream-binder-kafka</artifactId>
        </dependency>
        <!--引入logback日誌輸出配置,這時由於kafka繫結的是日誌事件-->
        <dependency>
            <groupId>net.logstash.logback</groupId>
            <artifactId>logstash-logback-encoder</artifactId>
            <version>4.6</version>
        </dependency>
        <!--引入feign依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
        </dependency>
    </dependencies>

注意對於日誌依賴的引入。
在application.yml中加入如下配置:

server:
  port: 8082
spring:
  application:
    name: serverone
  sleuth:
    sampler:
      percentage: 1.0
  cloud:
    stream:
      kafka:
        binder:
          brokers: localhost:9092
          zkNodes: localhost:2181

建立logback-spring.xml並如下配置:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml"/>

    <springProperty scope="context" name="springAppName" source="spring.application.name"/>
    <!-- Example for logging into the build folder of your project -->
    <property name="LOG_FILE" value="${BUILD_FOLDER:-build}/${springAppName}"/>

    <property name="CONSOLE_LOG_PATTERN"
              value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr([${springAppName:-},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]){yellow} %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>

    <!-- Appender to log to console -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <!-- Minimum logging level to be presented in the console logs-->
            <level>INFO</level>
        </filter>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- Appender to log to file -->
    <appender name="flatfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_FILE}</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.gz</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>${CONSOLE_LOG_PATTERN}</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>

    <!-- Appender to log to file in a JSON format -->
    <appender name="logstash" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_FILE}.json</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${LOG_FILE}.json.%d{yyyy-MM-dd}.gz</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <timestamp>
                    <timeZone>UTC</timeZone>
                </timestamp>
                <pattern>
                    <pattern>
                        {
                        "severity": "%level",
                        "service": "${springAppName:-}",
                        "trace": "%X{X-B3-TraceId:-}",
                        "span": "%X{X-B3-SpanId:-}",
                        "exportable": "%X{X-Span-Export:-}",
                        "pid": "${PID:-}",
                        "thread": "%thread",
                        "class": "%logger{40}",
                        "rest": "%message"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>
    </appender>

    <root level="INFO">
        <!--<appender-ref ref="console"/>-->
        <appender-ref ref="logstash"/>
        <!--<appender-ref ref="flatfile"/>-->
    </root>
</configuration>

在啟動類加入如下配置:

@SpringBootApplication
@EnableFeignClients //引入feign支援
@EnableAutoConfiguration //引入自動配置,替代配置檔案
public class ServerOneApplication {

    public static void main(String[] args) {
        SpringApplication.run(ServerOneApplication.class,args);
    }
}

建立的restTemplate 配置類為:

@Configuration
public class RestConfiguration {

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

feign的客戶端為:

@FeignClient(name = "sleuthone",url = "http://localhost:8888")
public interface SleuthService {
    @RequestMapping("/sayHello/{name}")
    public String sayHello(@PathVariable(name = "name")String name);
}

呼叫的後臺controller為:

@RestController
public class SleuthController {
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private SleuthService sleuthService;

    @ResponseBody
    @RequestMapping("/restHello/{name}")
    public String restHello(@PathVariable String name) {
        return "rest " + restTemplate.getForObject("http://localhost:8888/sayHello/" + name,String.class);
    }

    @ResponseBody
    @RequestMapping("/feignHello/{name}")
    public String feignHello(@PathVariable String name) {
        return "feign " + sleuthService.sayHello(name);
    }
}

4、建立目標服務二

服務二與服務一基本相同,不同的地方在於服務二不需要引入feign了,其pom如下:

<dependencies>
        <!--依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--引入鏈路呼叫資訊-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-sleuth</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-stream-binder-kafka</artifactId>
        </dependency>
        <!--引入logback日誌輸出配置,這時由於kafka繫結的是日誌事件-->
        <dependency>
            <groupId>net.logstash.logback</groupId>
            <artifactId>logstash-logback-encoder</artifactId>
            <version>4.6</version>
        </dependency>
    </dependencies>

其他一致,服務二僅提供一個sayHello服務。

@RestController
public class SleuthController {

    @ResponseBody
    @RequestMapping("/sayHello/{name}")
    public String sayHello(@PathVariable String name) {
        return "hello " + name;
    }
}

驗證

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 37,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "zipkin:span-2018-01-08",
        "_type" : "span",
        "_id" : "AWDUsg4wSqtAFWoqJU1h",
        "_score" : 1.0,
        "_source" : {
          "traceId" : "45fc228abc4a4edf",
          "duration" : 4000,
          "shared" : true,
          "localEndpoint" : {
            "serviceName" : "servertwo",
            "ipv4" : "10.130.236.27",
            "port" : 8888
          },
          "timestamp_millis" : 1515396926392,
          "kind" : "SERVER",
          "name" : "http:/sayhello/lisi",
          "id" : "d852bf5a65f75acb",
          "parentId" : "45fc228abc4a4edf",
          "timestamp" : 1515396926392000,
          "tags" : {
            "mvc.controller.class" : "SleuthController",
            "mvc.controller.method" : "sayHello",
            "spring.instance_id" : "01C702601479820.corp.haier.com:servertwo:8888"
          }
        }
      },
      {
        "_index" : "zipkin:span-2018-01-08",
        "_type" : "span",
        "_id" : "AWDUsg8wSqtAFWoqJU1i",
        "_score" : 1.0,
        "_source" : {
          "traceId" : "45fc228abc4a4edf",
          "duration" : 36000,
          "localEndpoint" : {
            "serviceName" : "serverone",
            "ipv4" : "10.130.236.27",
            "port" : 8082
          },
          "timestamp_millis" : 1515396926361,
          "kind" : "CLIENT",
          "name" : "http:/sayhello/lisi",
          "id" : "d852bf5a65f75acb",
          "parentId" : "45fc228abc4a4edf",
          "timestamp" : 1515396926361000,
          "tags" : {
            "http.host" : "localhost",
            "http.method" : "GET",
            "http.path" : "/sayHello/lisi",
            "http.url" : "http://localhost:8888/sayHello/lisi",
            "spring.instance_id" : "01C702601479820.corp.haier.com:serverone:8082"
          }
        }
      },
      {
        "_index" : "zipkin:span-2018-01-08",
        "_type" : "span",
        "_id" : "AWDUsg8wSqtAFWoqJU1j",
        "_score" : 1.0,
        "_source" : {
          "traceId" : "45fc228abc4a4edf",
          "duration" : 56696,
          "localEndpoint" : {
            "serviceName" : "serverone",
            "ipv4" : "10.130.236.27",
            "port" : 8082
          },
          "timestamp_millis" : 1515396926355,
          "kind" : "SERVER",
          "name" : "http:/feignhello/lisi",
          "id" : "45fc228abc4a4edf",
          "timestamp" : 1515396926355000,
          "tags" : {
            "mvc.controller.class" : "SleuthController",
            "mvc.controller.method" : "feignHello",
            "spring.instance_id" : "01C702601479820.corp.haier.com:serverone:8082"
          }
        }
      }
    ]
  }
}

我們需要注意:duration這裡的單位是微妙,所以耗時56ms

問題彙總

1、資料沒有進入到kafka通道,沒有配置日誌輸出到日誌檔案,通過對原始碼分析,我們發現其kafka繫結的是日誌事件
2、啟動服務時,kafka停機或者不能消費參看上一篇關於kafka不能消費的問題。其實主要原因是jdk版本問題。

原始碼地址