我试着使用分散跟踪系统Zipkin的经验

我是Java Advent Calendar第22天的miya10kei。
这次我会分享我在学习和尝试使用分布式跟踪系统Zipkin的经历。

首先

首先,由于我对分布式追踪完全是个菜鸟,所以我查看了一些幻灯片和博客来了解相关知识。

    • https://www.slideshare.net/rakutentech/spring-cloudzipkin

 

    http://www.baeldung.com/tracing-services-with-zipkin

分散追蹤是什麼?

首先,分散追踪是指在分布式系统中追踪处理的请求。在由多个服务组成的系统(分布式系统)中,由于一个请求会跨越多个服务来处理,因此很难掌握整个请求的处理流程(可追踪性)。此外,当可追踪性降低时,很难找出哪个服务存在问题,从而导致故障或请求延迟的恶化。为了解决这些问题,分布式追踪系统通过可视化服务的依赖关系和服务级别的延迟来帮助解决问题。

分散追踪术语

以下是关于分散追踪的两个术语。

Span: 1サービス内の処理を表す。

Trace: Requestのstart-endを含むSpanの集合をあらわす。

span_trace_diagram.png

Zipkin:中国话给中国人用

那么,接下来我们来讨论一下”Zipkin”这个主题。
Zipkin是由Twitter公司开发的开源分布式追踪系统,参考了Google的Dapper。
Zipkin提供了收集各个服务之间API调用数据的功能,并提供了用于可视化这些数据的UI界面。
你可以在Zipkin的官方网站上查看更多相关信息。

Zipkin的架构

zipkin-architecture.png

根据Zipkin的组织结构,它似乎被划分成以下5个主要组件。

记者

记者的角色是将日志从应用程序传输到Zipkin服务器。
为各种语言提供了一些经过仪表化的库。请参考支持的语言。

收藏家

Collector的作用是收集从每个应用程序传输过来的日志。
目前支持的收集方法有HTTP、Kafka、RabbitMQ和Scribe。
详细信息请参考https://zipkin.io/pages/existing_instrumentations.html。

存储

存储起到传输日志的持久化角色。
作为数据存储,支持内存数据库、MySQL、Cassandra和Elasticsearch。
https://github.com/openzipkin/zipkin/tree/master/zipkin-storage

API (应用程序接口)

API为从存储中提取数据提供多种方法的功能。

用户界面 (User Interface)

这是一个在浏览器上可视化存储在Storage中的持久化日志数据的用户界面。
Zipkin用户界面包含三个页面。

找出踪迹 chū jī)

zipkin_ui1.PNG

深入挖掘

zipkin_ui2.PNG

依赖关系

zipkin_ui3.PNG

尝试使用

zipkin_microservice_diagram.png

※ DB将在Vagrant + VirtualBox + Ubuntu 16.04 上安装MySQL并进行使用。
同时,决定将传输到Zipkin服务器的日志也持久化到相同的MySQL中。

本次使用的源代码是以下链接:
https://github.com/miya10kei/spring-cloud-sleuth_zipkin

建立Zipkin服务器

首先,我们将建立一个Zipkin服务器。
Zipkin服务器使用Spring Boot创建,可以像在Quickstart中介绍的那样使用”java -jar”命令进行启动。但是,这次我们想要尝试一种不同的方法来进行搭建。
我们将在pom.xml文件中添加以下依赖关系。

<dependency>
    <groupId>io.zipkin.java</groupId>
    <artifactId>zipkin-server</artifactId>
    <version>2.4.1</version>
</dependency>
<dependency>
    <groupId>io.zipkin.java</groupId>
    <artifactId>zipkin-autoconfigure-ui</artifactId>
    <version>2.4.1</version>
</dependency>

然后,在SpringApplication类中添加”@EnableZipkinServer”注解。

@SpringBootApplication
@EnableZipkinServer  // Zipkinサーバを有効化するアノテーションを追加
public class ZipkinServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZipkinServerApplication.class, args);
    }
}

這次我們將使用MySQL作為數據存儲,因此需要添加相應的設置。

server.port=9411

# Storage
zipkin.storage.type=mysql
zipkin.storage.mysql.host=192.168.33.10
zipkin.storage.mysql.port=3306
zipkin.storage.mysql.username=root
zipkin.storage.mysql.password=password
zipkin.storage.mysql.db=zipkin

以上就是Zipkin服务器构建的全部内容。

服务1

接下来我们将转移到Service1的构建。
在pom.xml中添加以下依赖关系。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>

为了通过HTTP向Service2和Service3发送请求,我们需要将RestTemplate作为Bean定义在SpringApplication类中。

@SpringBootApplication
public class Service1Application {

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

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

在Controller类中,编写逻辑以向Service2和Service3发送请求并返回响应。

@RestController
public class Service1Controller {
    private RestTemplate restTemplate;
    private ObjectMapper mapper;

    public Service1Controller(final RestTemplate restTemplate, final ObjectMapper mapper) {
        this.restTemplate = restTemplate;
        this.mapper = mapper;
    }

    @GetMapping("/hello/{id}")
    public String hello(@PathVariable("id") final String id) {
        try {
            final ResponseEntity<String> res = restTemplate.getForEntity("http://localhost:8082/users/" + id, String.class);
            final String date = restTemplate.getForEntity("http://localhost:8083/date/", String.class).getBody();
            final String time = restTemplate.getForEntity("http://localhost:8083/time/", String.class).getBody();
            return "Hello "  + mapper.readTree(res.getBody()).get("name") + " @ " + date + " " + time;
        } catch (IOException | HttpClientErrorException e) {
            return "Hello World!";
        }
    }
}

请添加配置以将日志传送到Zipkin。

# port
server.port=8081
# サービスを一意に識別するための名称
spring.zipkin.service.name=service1
# ZipkinサーバのURL(デフォルト:http://localhost:9411)
spring.zipkin.base-url=http://localhost:9411/
# リクエストの何%のログをZipkinサーバに転送するか
spring.sleuth.sampler.percentage=1.0

以上就完成了Service1的构建。

服务2

接下来,我们将转到Service2。
由于Service2只是从MySQL中获取数据并响应,所以我们将使用Spring Data Rest进行构建。
首先,在pom.xml中添加以下依赖关系。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.18</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
</dependency>

我们将创建一个名为Entity的类和一个名为Repository的接口,以及一个名为User的类和一个名为UserRepository的接口。

@Getter
@Setter
@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;
}
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
}

最后,我们需要添加与Service1相同的配置选项,将日志传送到Zipkin并配置数据存储。

server.port=8082

# Zipkin settings
# サービスを一意に識別するための名称
spring.zipkin.service.name=service2
# ZipkinサーバのURL(デフォルト:http://localhost:9411)
spring.zipkin.base-url=http://localhost:9411
# リクエストの何%のログをZipkinサーバに転送するか
spring.sleuth.sampler.percentage=1.0

# spring data settings
spring.datasource.url=jdbc:mysql://192.168.33.10/zipkin?useSSL=false
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.jpa.database=MYSQL

Service2到此为止已经完成。

服务3

接下来我们转到Service3,但是由于Service1和它的设置几乎没有变化,所以我们省略。

数据库

请在Vagrant文件中添加以下内容,并使用“vagrant up”命令启动。Vagrant文件中的脚本将安装MySQL并创建Zipkin所需的表。DDL在此处可查阅,请确认一下。

Vagrant.configure("2") do |config|
  config.vm.box = "bento/ubuntu-16.04"
  config.vm.network "private_network", ip: "192.168.33.10"
  config.vm.provision "shell", inline: <<-SHELL
    apt-get update
    DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" upgrade
    localectl set-locale LANG=ja_JP.UTF-8 LANGUAGE="ja_JP:ja"
    source /etc/default/locale
    timedatectl set-timezone Asia/Tokyo
    debconf-set-selections <<< 'mysql-server mysql-server/root_password password password'
    debconf-set-selections <<< 'mysql-server mysql-server/root_password_again password password'
    apt-get install -y mysql-server
    mysql -uroot -ppassword -e 'grant all privileges on *.* to root@"%" identified by "password" with grant option;'
    mysql -uroot -ppassword -e 'create database zipkin;'
    mysql -uroot -ppassword zipkin < /vagrant/zipkin-mysql.sql
    mysql -uroot -ppassword zipkin < /vagrant/app-mysql.sql
    sed -ie 's/^bind-address/# bind-address/g' /etc/mysql/mysql.conf.d/mysqld.cnf
    systemctl restart mysql
  SHELL
end

如果您尚未安装Vagrant和VirtualBox,请尝试从以下位置进行安装。

    • https://www.vagrantup.com/

 

    https://www.virtualbox.org/

(注)此次我们将以简易方式使用MySQL,直接使用root用户。请在正式环境中进行适当配置。

尝试移动一下。

请在启动Zipkin服务器、Service1-3和数据库之后访问http://localhost:8081/hello/1/。
您将会看到类似下面的响应。

Hello "suzuki" @ 2017-12-20 00:58:55.457
zipkin_operation.PNG

很遗憾,目前似乎不支持在”spring-cloud-sleuth”中将JDBC日志转发。
不过,看起来brave支持JDBC,我打算试一试。

总结一下

我們在這次使用分散追蹤系統Zipkin來跨多個服務進行請求追蹤。
雖然這只是一個非常簡單的案例,但我認為我們能稍微介紹分散追蹤的便利性。
希望在未來引入分散追蹤時,能稍微對您有所參考之處。

祝你新年快乐