dubbo服務(wù)鏈路跟蹤方式
目前很多業(yè)務(wù)使用微服務(wù)架構(gòu),服務(wù)模塊劃分有這2種方式:
- 服務(wù)功能劃分
- 業(yè)務(wù)劃分
不管哪種方式,一次接口調(diào)用都需要多個(gè)服務(wù)協(xié)同完成,其中一個(gè)服務(wù)出現(xiàn)問(wèn)題,都會(huì)導(dǎo)致最終失敗,雖然有l(wèi)ogback + kafka + ELK 這樣的神器架構(gòu),但是定位問(wèn)題也很麻煩,如果在整個(gè)鏈路中,可以通過(guò)一個(gè)唯一ID(traceId)跟蹤本次服務(wù)調(diào)用,就可以在ELK中查找當(dāng)前traceId來(lái)定位問(wèn)題。
一、案例
1、案例結(jié)構(gòu)

pratices-demo-provider-core:定義服務(wù)接口pratices-demo-provider:具體實(shí)現(xiàn)pratices-demo-consumer-core:服務(wù)消費(fèi)者,同時(shí)也是服務(wù)提供者pratices-demo-consumer:具體實(shí)現(xiàn)pratices-demo-web:提供http服務(wù)pratices-demo-trace:本案例的核心模塊,在服務(wù)調(diào)用時(shí)攔截,設(shè)置traceId,跟蹤本次服務(wù)調(diào)用
2、pratices-demo
2.1、pom.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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<packaging>pom</packaging>
<modules>
<module>pratices-demo-consumer</module>
<module>pratices-demo-provider</module>
<module>pratices-demo-provider-core</module>
<module>pratices-demo-consumer-core</module>
<module>pratices-demo-web</module>
<module>pratices-demo-trace</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cn.dl</groupId>
<artifactId>pratices-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>pratices-demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.6.0</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
</exclusion>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</exclusion>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.10</version>
</dependency>
<!--Exception in thread "main" java.lang.NoClassDefFoundError: org/I0Itec/zkclient/IZkStateListener-->
<!--Caused by: java.lang.ClassNotFoundException: org.I0Itec.zkclient.IZkStateListener-->
<dependency>
<groupId>com.101tec</groupId>
<artifactId>zkclient</artifactId>
<version>0.10</version>
<exclusions>
<exclusion>
<artifactId>slf4j-log4j12</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</pluginRepository>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
3、pratices-demo-provider-core
3.1、ProviderService
package com.cn.dl;
/**
* Created by yanshao on 2019-09-04.
*/
public interface ProviderService {
String sayHello(String name);
}
4、pratices-demo-provider

4.1、ProviderServiceImpl
package com.cn.dl.provider.impl;
import com.alibaba.dubbo.config.annotation.Service;
import com.cn.dl.ProviderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
/**
* Created by yanshao on 2019-09-04.
*/
@Service
public class ProviderServiceImpl implements ProviderService {
private static final Logger log = LoggerFactory.getLogger(ProviderServiceImpl.class);
@Override
public String sayHello(String name) {
log.info("providerServiceImpl 服務(wù)提供 traceId:{},sayHello:{}", MDC.get("traceId"),name);
return "hello " + name ;
}
}
4.2、dubbo-provider.properties 配置文件
# dubbo-provider.properties dubbo.application.name=service2 dubbo.registry.address=zookeeper://127.0.0.1:2181 dubbo.protocol.name=dubbo dubbo.protocol.port=50010 dubbo.consumer.timeout=5000
4.3、ProviderMain服務(wù)啟動(dòng)類
注意:?jiǎn)?dòng)dubbo服務(wù)不需要暴露http服務(wù)
package com.cn.dl;
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.PropertySource;
import java.util.concurrent.locks.LockSupport;
/**
* Created by yanshao on 2019-09-04.
*/
@EnableDubbo(scanBasePackages = "com.cn.dl*")
@PropertySource("classpath:/dubbo-provider.properties")
@SpringBootApplication
public class ProviderMain{
private static final Logger log = LoggerFactory.getLogger(ProviderMain.class);
/**
* 啟動(dòng)dubbo服務(wù),不需要提供web服務(wù),但是默認(rèn)有8080端口,通過(guò)一下方式可以不暴露web服務(wù)
*
* 1、在application.properties加上一下配置
*
* spring:
* main:
* allow-bean-definition-overriding: true
* web-application-type: none
*
* 2、修改啟動(dòng)類
* new SpringApplicationBuilder(ProviderMain .class)
* .web(WebApplicationType.NONE)
* .run(args)
* */
public static void main(String[] args) {
new SpringApplicationBuilder(ProviderMain.class).web(WebApplicationType.NONE).run(args);
log.info("ProviderMain 啟動(dòng)了");
LockSupport.park();
}
}
@EnableDubbo(scanBasePackages = "com.cn.dl*")
掃描Dubbo的服務(wù)提供者以及Dubbo的服務(wù)消費(fèi)者,一定要注意@EnableDubbo和@SpringBootApplication的先后次序;
@PropertySource("classpath:/dubbo-provider.properties")
加載配置文件到上下文環(huán)境變量。
5、pratices-demo-consumer-core
5.1、ConsumerService
package com.cn.dl;
/**
* Created by yanshao on 2019-09-04.
*/
public interface ConsumerService {
String toSayHello(String name);
int getRandomInt();
}
6、pratices-demo-consumer

6.1、ConsumerServiceImpl
package com.cn.dl.consumer.impl;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.config.annotation.Service;
import com.cn.dl.ConsumerService;
import com.cn.dl.ProviderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.util.Random;
/**
* Created by yanshao on 2019-09-04.
*/
@Service
public class ConsumerServiceImpl implements ConsumerService {
private static final Logger log = LoggerFactory.getLogger(ConsumerServiceImpl.class);
@Reference
private ProviderService providerService;
@Override
public String toSayHello(String name) {
String sayHello = providerService.sayHello(name);
log.info("ConsumerServiceImpl >>>> traceId:{},sayHello:{}", MDC.get("traceId"),sayHello);
return sayHello;
}
@Override
public int getRandomInt() {
return new Random().nextInt(100);
}
}
6.2、dubbo-consumer.properties
dubbo.application.name=service1 dubbo.registry.address=zookeeper://127.0.0.1:2181 dubbo.protocol.name=dubbo dubbo.protocol.port=50020 dubbo.consumer.timeout=5000
6.3、ConsumerMain
package com.cn.dl;
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.annotation.PropertySource;
import java.util.concurrent.locks.LockSupport;
/**
* Created by yanshao on 2019-09-04.
*/
@EnableDubbo(scanBasePackages = "com.cn.dl*")
@PropertySource("classpath:/dubbo-consumer.properties")
@SpringBootApplication
public class ConsumerMain {
public static void main(String[] args) {
new SpringApplicationBuilder(ConsumerMain.class).web(WebApplicationType.NONE).run(args);
LockSupport.park();
}
}
7、pratices-demo-web
7.1、WebTraceFilter
定義web攔截器,攔截所有請(qǐng)求,生成唯一ID
package com.cn.dl.webTrace;
import com.cn.dl.utils.TraceUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import static com.cn.dl.config.TraceConfig.TRACE_ID;
/**
* Created by yanshao on 2019-09-04.
*/
public class WebTraceFilter implements Filter {
private static final Logger log = LoggerFactory.getLogger(WebTraceFilter.class);
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
if (! (servletRequest instanceof HttpServletRequest) || ! (servletResponse instanceof HttpServletResponse)) {
throw new ServletException("只支持http請(qǐng)求");
}
try {
String traceId = TraceUtil.getTraceId();
log.info("WebTraceFilter traceId:{}",traceId);
MDC.put(TRACE_ID,traceId);
filterChain.doFilter(servletRequest, servletResponse);
} finally {
MDC.remove(TRACE_ID);
}
}
}
7.2、TraceUtil
package com.cn.dl.utils;
import java.util.UUID;
/**
* Created by yanshao on 2019-09-04.
*/
public class TraceUtil {
public static String getTraceId(){
return UUID.randomUUID().toString().replace("-","");
}
public static void main(String[] args) {
System.out.println(getTraceId());
}
}
7.3、TraceConfig
package com.cn.dl.config;
/**
* Created by yanshao on 2019-09-04.
*/
public interface TraceConfig {
String TRACE_ID = "traceId";
}
7.4、RpcProviderInterceptor
package com.cn.dl.rpcTrace;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
import com.cn.dl.utils.TraceUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.util.StringUtils;
import java.util.Map;
import static com.cn.dl.config.TraceConfig.TRACE_ID;
/**
* Created by yanshao on 2019-09-04.
*/
@Activate(group = Constants.PROVIDER)
public class RpcProviderInterceptor implements Filter {
private static final Logger log = LoggerFactory.getLogger(RpcProviderInterceptor.class);
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Result result;
try {
Map<String, String> at = invocation.getAttachments();
MDC.put(TRACE_ID, ! StringUtils.isEmpty(at.get(TRACE_ID)) ? at.get(TRACE_ID): TraceUtil.getTraceId());
result = invoker.invoke(invocation);
} catch (Exception e) {
log.error("RpcProviderInterceptor 異常",e);
throw e;
} finally {
MDC.remove(TRACE_ID);
}
return result;
}
}
7.5、RpcConsumerInterceptor
package com.cn.dl.rpcTrace;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
import com.cn.dl.utils.TraceUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import java.util.Map;
import static com.cn.dl.config.TraceConfig.TRACE_ID;
/**
* Created by yanshao on 2019-09-04.
*/
@Activate(group = Constants.CONSUMER)
public class RpcConsumerInterceptor implements Filter {
private static final Logger log = LoggerFactory.getLogger(RpcProviderInterceptor.class);
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Result result;
try {
Map<String, String> at = invocation.getAttachments();
if (MDC.get(TRACE_ID) == null) {
MDC.put(TRACE_ID,TraceUtil.getTraceId());
}
at.put(TRACE_ID, MDC.get(TRACE_ID));
result = invoker.invoke(invocation);
}catch (Exception e){
log.error("RpcConsumerInterceptor 異常",e);
throw e;
}
return result;
}
}
7.6、RpcProviderInterceptor
package com.cn.dl.rpcTrace;
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.*;
import com.cn.dl.utils.TraceUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.util.StringUtils;
import java.util.Map;
import static com.cn.dl.config.TraceConfig.TRACE_ID;
/**
* Created by yanshao on 2019-09-04.
*/
@Activate(group = Constants.PROVIDER)
public class RpcProviderInterceptor implements Filter {
private static final Logger log = LoggerFactory.getLogger(RpcProviderInterceptor.class);
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
Result result;
try {
Map<String, String> at = invocation.getAttachments();
MDC.put(TRACE_ID, ! StringUtils.isEmpty(at.get(TRACE_ID)) ? at.get(TRACE_ID): TraceUtil.getTraceId());
result = invoker.invoke(invocation);
} catch (Exception e) {
log.error("RpcProviderInterceptor 異常",e);
throw e;
} finally {
MDC.remove(TRACE_ID);
}
return result;
}
}
然后在resources下創(chuàng)建META-INF/dubbo/com.alibaba.dubbo.rpc.Filter,將擴(kuò)展的攔截器添加到dubbo調(diào)用鏈中
consumerTraceFilter=com.cn.dl.rpcTrace.RpcConsumerInterceptor providerTraceFilter=com.cn.dl.rpcTrace.RpcProviderInterceptor
8、pratices-demo-web

8.1、TraceInterceptor注冊(cè)web攔截器
package com.cn.dl.config;
import com.cn.dl.webTrace.WebTraceFilter;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import javax.annotation.Resource;
import javax.servlet.Filter;
/**
* Created by yanshao on 2019-09-04.
*/
@SpringBootConfiguration
public class TraceInterceptor {
@Bean(name = "webTraceFilter")
public WebTraceFilter getWebTraceFilter(){
return new WebTraceFilter();
}
@Bean
@Resource
public FilterRegistrationBean traceFilterRegistration(Filter webTraceFilter) {
FilterRegistrationBean<Filter> registration = new FilterRegistrationBean<>();
registration.setFilter(webTraceFilter);
registration.addUrlPatterns("/*");
registration.setName("webTraceFilter");
registration.setOrder(1);
return registration;
}
}
8.2、dubbo.properties
dubbo.application.name=consumer-service dubbo.registry.address=zookeeper://127.0.0.1:2181 dubbo.consumer.timeout=5000
8.3、DemoWebController
package com.cn.dl.controller;
import com.alibaba.dubbo.config.annotation.Reference;
import com.cn.dl.ConsumerService;
import org.springframework.web.bind.annotation.*;
/**
* Created by yanshao on 2019-09-04.
*/
@RestController
public class DemoWebController {
@Reference
private ConsumerService consumerService;
@PostMapping("sayHello")
public String sayHello(@RequestParam("name") String name){
return consumerService.toSayHello(name);
}
@GetMapping("getRandomInt")
public int getRandomInt(){
return consumerService.getRandomInt();
}
}
8.4、StartWeb
package com.cn.dl;
import com.alibaba.dubbo.config.spring.context.annotation.EnableDubbo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
/**
* Created by yanshao on 2019-09-04.
*/
@EnableDubbo(scanBasePackages = "com.cn.dl*")
@PropertySource("classpath:/dubbo.properties")
@SpringBootApplication
public class StartWeb {
public static void main(String[] args) {
SpringApplication.run(StartWeb.class,args);
}
}
9、分別啟動(dòng)providerMain、consumerMain、startWeb




以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java中的任務(wù)調(diào)度框架quartz詳細(xì)解析
這篇文章主要介紹了Java中的任務(wù)調(diào)度框架quartz詳細(xì)解析,Quartz 是一個(gè)完全由 Java 編寫的開(kāi)源作業(yè)調(diào)度框架,為在 Java 應(yīng)用程序中進(jìn)行作業(yè)調(diào)度提供了簡(jiǎn)單卻強(qiáng)大的機(jī)制,需要的朋友可以參考下2023-11-11
圖解Java中歸并排序算法的原理與實(shí)現(xiàn)
歸并排序是建立在歸并操作上的一種有效的排序算法。該算法是采用分治法(Divide and Conquer)的一個(gè)非常典型的應(yīng)用。本文將通過(guò)圖片詳解插入排序的原理及實(shí)現(xiàn),需要的可以參考一下2022-08-08
Java中BigInteger類的使用方法詳解(全網(wǎng)最新)
這篇文章主要介紹了Java中BigInteger類的使用方法詳解,常用最全系列,本章作為筆記使用,內(nèi)容比較全面,但常用的只有:構(gòu)造函數(shù),基本運(yùn)算以及compareTo(),intValue(),setBit(),testBit()方法,需要的朋友可以參考下2023-05-05
springboot自動(dòng)配置沒(méi)有生效的問(wèn)題定位(條件斷點(diǎn))
這篇文章主要介紹了springboot自動(dòng)配置未生效問(wèn)題定位,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,下面我們來(lái)學(xué)習(xí)一下吧2019-06-06
基于OpenID?Connect及Token?Relay實(shí)現(xiàn)Spring?Cloud?Gateway
這篇文章主要介紹了基于OpenID?Connect及Token?Relay實(shí)現(xiàn)Spring?Cloud?Gateway,Spring?Cloud?Gateway旨在提供一種簡(jiǎn)單而有效的方式來(lái)路由到API,并為API提供跨領(lǐng)域的關(guān)注點(diǎn),如:安全性、監(jiān)控/指標(biāo)和彈性2022-06-06

