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-08Java中BigInteger類的使用方法詳解(全網(wǎng)最新)
這篇文章主要介紹了Java中BigInteger類的使用方法詳解,常用最全系列,本章作為筆記使用,內(nèi)容比較全面,但常用的只有:構(gòu)造函數(shù),基本運(yùn)算以及compareTo(),intValue(),setBit(),testBit()方法,需要的朋友可以參考下2023-05-05springboot自動(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