本人所在的产品线是公司统一的构建管理平台,该平台采用了微服务的构造,跟微服务自身的通病一样,服务数量多了之后,存在这样一些问题:
- 接口超时或者调用失败后,无法知道具体是那个下游接口超时
- 接口响应慢后,怎么去了解是那个服务的那个方法调用耗时较长
- 对前端暴露的接口,响应时间对用户的体验比较敏感,怎么监控到系统中存在的慢接口,进而优化
微服务发展到今天,业界对这类问题已经有很多通用的解决方案,最早的比较Google公司发表的Dapper应该算得上是始祖了吧。 我们也不想重复造轮子,比较了一些方案,综合对埋点语言的支持(Golang + Java),Zipkin进行最终的方案。
安装Zipkin Server
Zipkin Server主要包含三个Components:
-
Zipkin UI
-
Zipkin Server: 应用的埋点配置需要将Span, trace的数据发到这里
-
Span Storage: Zipkin Server默认后端使用内存存储, 数据大的话似乎只可以存储几个小时的数据,而且搜索的性能也会有问题,Zipkin支持较多其它的Storage, 比如Mysql, ElasticSearch。
为了简化这些组件的安装,直接采用了官方提供的Docker Image,参数通过环境变量来指定。
-
启动ElasticSearch6, 官方建议先调高系统的虚拟内存,不然会启动失败
docker run -d -p 9200:9200 zipkin-elasticsearch6:latest
-
启动Zipkin Server (含UI)
docker run -d -p 9411:9411 -e STORAGE_TYPE=elasticsearch -e ES_HOSTS=192.168.10.10:9200 zipkin:latest
一切就绪后,就可以通过http://< ip-address >:9411/zipkin/访问了。
应用埋点
埋点就是通过SDK将业务的接口数据生成trace信息,再由SDK通过http/消息将trace发送到后端的Zipkin服务。
前面提到选型时主要考虑了语言的兼容性,我们分别看一下Java与Go的埋点,以及其中一些比较tricky的配置。
JAVA 应用埋点
Zipkin官方对Java应用的埋点推荐提供的是Brave,它可以兼容Zipkin后端trace数据的格式,由于项目本身是Springboot的框架搭建的,因些我们采用的是对Brave封装了多一层的Spring Sleuth 。
在工程中添加下面的依赖:
compile('org.springframework.cloud:spring-cloud-starter-zipkin:1.3.3.RELEASE')
compile('org.springframework.boot:spring-boot-starter-aop:1.5.6.RELEASE')
埋点配置
埋点的配置主要包含下面常用的配置项 (注意你使用的版本,可能配置项名称会有些差异):
配置项 | 配置项解释 |
---|---|
application.name | 应用的名称,在Zipkin UI过滤时对应为 serviceName |
sleuth.enabled | 是否启动sleuth,只有启用后才会收集trace |
zipkin.baseUrl | Zipkin Server对应的链接,需要将trace的数据通过http接口发送给它 |
sleuth.sampler.percentage | 设置Span的采样率,默认似乎是0,初次使用会比较坑,在UI上看不到收集的Span数据 |
sleuth.web.skip-pattern | 排除掉一些不用监控的服务入口,比如 /health_check |
下面是一个具体的配置例子:
spring:
profiles: staging
application:
name: neith-gw
zipkin:
baseUrl: ${
ZIPKIN_SERVER:http://192.168.10.10:9411}
sleuth:
sampler:
percentage: ${
SLEUTH_SAMPLE_RATE:1.0}
web:
client:
enabled: true
skip-pattern: /prometheus|/_health_check|/ws/.*|/swagger.*|.*\\.ttf|/v2/api-docs|.*\\.png|.*\.css|.*\.js|.*\.html|/favicon.ico
enabled: ${
SLEUTH_ENABLE:true}
假如配置没问题,你应该可以看到下面的启动日志:
16:24:48.713 [main] INFO o.s.context.support.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'org.springframework.cloud.sleuth.annotation.SleuthAnnotationAutoConfiguration' of type [org.springframework.cloud.sleuth.annotation.SleuthAnnotationAutoConfiguration$$EnhancerBySpringCGLIB$$5766f243] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
Http客户端埋点
由于Sleuth的埋点是通过AOP代理的,而Spring的AOP默认又是基于Java Bean的proxy对象,即在调用target bean对应的方法前,先调用proxy对象的方法,并在proxy方法实现埋点的功能。
假如你的项目跟我一样没有使用Spring的RestTemplate类(并且必须是Bean,不能通过New生成实例),而是使用了第三方的http client包,为了埋点能工作以及减少埋点对代码的侵入,需要一些小技巧,在我的项目是这样做的:
-
首先需要创建一个新的Bean实现http的发送接口,例如
@Component public class HttpSender { public HttpResponse send(HttpRequest request) { return request.send(); } }
-
然后将原来的使用三方http client接口的调用替换为新的接口
... @Autowired private HttpSender httpSender; @Override public GitlabCommit findCommitByRef(String gitRepo, String refName) { HttpResponse response = httpSender.send(HttpRequest.get(GITLAB_COMMIT_URL) .query("branch_name", refName) .query("token", token