https://github.com/zq2599/blog_demos
内容:所有原创文章分类汇总及配套源码,涉及Java、Docker、Kubernetes、DevOPS等;
前文《分布式调用链跟踪工具Jaeger?两分钟极速体验》咱们体验了Jaeger的基本能力,今天就来编码实践,了解如何将让自己的应用集成Jaeger;
本文的目标:今天咱们要在一个分布式系统中部署和使用jaeger,使用方式包括两种:首先是SDK内置的span,例如web请求、mysql或redis的操作等,这些会自动上报,第二种就是自定义span;
总的来说,今天的实战步骤如下:
jaeger-service-consumer收到用户通过浏览器发来的http请求时,会调用jaeger-service-provider提供的web服务,而jaeger-service-provider又会操作一次redis,整个流程与典型的分布式系统类似
jaeger-service-consumer和jaeger-service-provider在响应服务的过程中,都会将本次服务相关的数据上报到jaeger,这样咱们在jaeger的web页面就能观察到客户的一次请求会经过那些应用,关键位置耗时多少,关键参数是哪些等等;
将所有应用制作成镜像,再编写docker-compose.yml文件集成它们
运行,验证
名称
链接
备注
项目主页
https://github.com/zq2599/blog_demos
该项目在GitHub上的主页
git仓库地址(https)
https://github.com/zq2599/blog_demos.git
该项目源码的仓库地址,https协议
git仓库地址(ssh)
git@github.com:zq2599/blog_demos.git
该项目源码的仓库地址,ssh协议
为了方便管理依赖库版本,jaeger-service-provider工程是作为spring-cloud-tutorials的子工程创建的,其pom.xml如下:
<artifactId>jaeger-service-provider</artifactId>
<dependencies><dependency>
<groupId>com.bolingcavalry</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.opentracing.contrib</groupId>
<artifactId>opentracing-spring-jaeger-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<layers>
<enabled>true</enabled>
</layers>
</configuration>
</plugin>
</plugins>
</build>
配置文件application.yml,注意由于后面会用到docker-compose,因此redis和jaeger的地址都无需填写具体的IP,只要填写它们的容器名即可:
spring:
application:
name: jaeger-service-provider
redis:
database: 0
# Redis服务器地址 写你的ip
host: redis
# Redis服务器连接端口
port: 6379
# Redis服务器连接密码(默认为空)
password:
# 连接池最大连接数(使用负值表示没有限制 类似于mysql的连接池
jedis:
pool:
max-active: 10
# 连接池最大阻塞等待时间(使用负值表示没有限制) 表示连接池的链接拿完了 现在去申请需要等待的时间
max-wait: -1
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
# 连接超时时间(毫秒) 去链接redis服务端
timeout: 6000
opentracing:
jaeger:
enabled: true
udp-sender:
host: jaeger
port: 6831
配置类:
package com.bolingcavalry.jaeger.provider.config;
import io.jaegertracing.internal.MDCScopeManager;
import io.opentracing.contrib.java.spring.jaeger.starter.TracerBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JaegerConfig {
@Bean
public TracerBuilderCustomizer mdcBuilderCustomizer() {
// 1.8新特性,函数式接口
return builder -> builder.withScopeManager(new MDCScopeManager.Builder().build());
}
}
另外,由于本篇的重点是jaeger,因此redis相关代码就不贴出来了,有需要的读者请在此查看:RedisConfig.java、RedisUtils.java
接下来看看如何使用Trace的实例来定制span,下面是定了span及其子span的web接口类,请注意trace的API的使用,代码中已有详细注释,就不多赘述了:
package com.bolingcavalry.jaeger.provider.controller;
import com.bolingcavalry.common.Constants;
import com.bolingcavalry.jaeger.provider.util.RedisUtils;
import io.opentracing.Span;
import io.opentracing.Tracer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.text.SimpleDateFormat;
import java.util.Date;
@RestController
@Slf4j
public class HelloController {
@Autowired
private Tracer tracer;
@Autowired
private RedisUtils redisUtils;
private String dateStr(){
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
}
/**
* 模拟业务执行,耗时100毫秒
* @param parentSpan
*/
private void mockBiz(Span parentSpan) {
// 基于指定span,创建其子span
Span span = tracer.buildSpan("mockBizChild").asChildOf(parentSpan).start();log.info("hello");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
span.finish();
}
/**
* 返回字符串类型
* @return
*/
@GetMapping("/hello")
public String hello() {
long startTime = System.currentTimeMillis();// 生成当前时间
String timeStr = dateStr();
// 创建一个span,在创建的时候就添加一个tag
Span span = tracer.buildSpan("mockBiz")
.withTag("time-str", timeStr)
.start();
// span日志
span.log("normal span log");
// 模拟一个耗时100毫秒的业务
mockBiz(span);
// 增加一个tag
span.setTag("tiem-used", System.currentTimeMillis()-startTime);
// span结束
span.finish();
// 写入redis
redisUtils.set("Hello", timeStr);
// 返回
return Constants.HELLO_PREFIX + ", " + timeStr;
}
}
编码已经结束,接下来要将此工程制作成docker镜像了,新建Dockerfile文件,和pom.xml在同一个目录下:
FROM openjdk:8-jdk-alpine as builder
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone
WORKDIR application
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} application.jar
RUN java -Djarmode=layertools -jar application.jar extract
FROM openjdk:8-jdk-alpine
WORKDIR application
COPY --from=builder application/dependencies/ ./
COPY --from=builder application/spring-boot-loader/ ./
COPY --from=builder application/snapshot-dependencies/ ./
COPY --from=builder application/application/ ./
ENTRYPOINT ["java", "org.springframework.boot.loader.JarLauncher"]
先在父工程spring-cloud-tutorials的pom.xml所在目录执行以下命令完成编译构建:
mvn clean package -U -DskipTests
再在Dockerfile所在目录执行以下命令制作docker镜像:
docker build -t bolingcavalry/jaeger-service-provider:0.0.1 .
至此,jaeger-service-provider相关开发已经完成
jaeger-service-consumer工程的创建过程和jaeger-service-provider如出一辙,甚至还要更简单一些(不操作redis),所以描述其开发过程的内容尽量简化,以节省篇幅
pom.xml相比jaeger-service-provider的,少了redis依赖,其他可以照抄
application.yml也少了redis:
spring:
application:
name: jaeger-service-consumer
opentracing:
jaeger:
enabled: true
udp-sender:
host: jaeger
port: 6831
配置类JaegerConfig.java可以照抄jaeger-service-provider的
由于要远程调用jaeger-service-provider的web接口,因此新增restTemplate的配置类:
package com.bolingcavalry.jaeger.consumer.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(ClientHttpRequestFactory factory) {
RestTemplate restTemplate = new RestTemplate(factory);
return restTemplate;
}
@Bean
public ClientHttpRequestFactory simpleClientHttpRequestFactory() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setReadTimeout(5000);
factory.setConnectTimeout(15000);
return factory;
}
}
关键代码是web接口的实现,会通过restTemplate调用jaeger-service-provider的接口:
package com.bolingcavalry.jaeger.consumer.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@Slf4j
public class HelloConsumerController {
@Autowired
RestTemplate restTemplate;
/**
* 返回字符串类型
* @return
*/
@GetMapping("/hello")
public String hello() {
String url = "http://jaeger-service-provider:8080/hello";
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
StringBuffer sb = new StringBuffer();
HttpStatus statusCode = responseEntity.getStatusCode();
String body = responseEntity.getBody();// 返回
return "response from jaeger-service-provider \nstatus : " + statusCode + "\nbody : " + body;
}
}
接下来是编译构建制作docker镜像,和前面的jaeger-service-provider一样;
完整的docker-compose.yml内容如下:
version: '3.0'
networks:
jaeger-tutorials-net:
driver: bridge
ipam:
config:
- subnet: 192.168.1.0/24
gateway: 192.168.1.1
services:
jaeger:
image: jaegertracing/all-in-one:1.26
container_name: jaeger
# 处理时钟漂移带来的计算出负数的问题
command: ["--query.max-clock-skew-adjustment=100ms"]
#选择网络
networks:
- jaeger-tutorials-net
#选择端口
ports:
- 16686:16686/tcp
restart: always
redis:
image: redis:6.2.5
container_name: redis
#选择网络
networks:
- jaeger-tutorials-net
restart: always
jaeger-service-provider:
image: bolingcavalry/jaeger-service-provider:0.0.1
container_name: jaeger-service-provider
#选择网络
networks:
- jaeger-tutorials-net
restart: always
jaeger-service-consumer:
image: bolingcavalry/jaeger-service-consumer:0.0.1
container_name: jaeger-consumer-provider
#选择端口
ports:
- 18080:8080/tcp
#选择网络
networks:
- jaeger-tutorials-net
restart: always
至此,开发工作已全部完成,开始验证
在docker-compose.yml所在目录执行命令docker-compose up -d,即可启动所有容器:
will$ docker-compose up -d
Creating network "jaeger-service-provider_jaeger-tutorials-net" with driver "bridge"
Creating jaeger-service-provider … done
Creating jaeger … done
Creating redis … done
Creating jaeger-consumer-provider … done
浏览器打开http://localhost:16686/search,熟悉的jaeger页面:
微信搜索「程序员欣宸」,我是欣宸,期待与您一同畅游Java世界…
手机扫一扫
移动阅读更方便
你可能感兴趣的文章