dubbo服务链路跟踪方式
作者:燕少༒江湖 发布时间:2023-08-24 09:54:21
目前很多业务使用微服务架构,服务模块划分有这2种方式:
服务功能划分
业务划分
不管哪种方式,一次接口调用都需要多个服务协同完成,其中一个服务出现问题,都会导致最终失败,虽然有logback + kafka + ELK 这样的神器架构,但是定位问题也很麻烦,如果在整个链路中,可以通过一个唯一ID(traceId)跟踪本次服务调用,就可以在ELK中查找当前traceId来定位问题。
一、案例
1、案例结构
pratices-demo-provider-core
:定义服务接口pratices-demo-provider
:具体实现pratices-demo-consumer-core
:服务消费者,同时也是服务提供者pratices-demo-consumer
:具体实现pratices-demo-web
:提供http服务pratices-demo-trace
:本案例的核心模块,在服务调用时拦截,设置traceId,跟踪本次服务调用
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 服务提供 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服务启动类
注意:启动dubbo服务不需要暴露http服务
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);
/**
* 启动dubbo服务,不需要提供web服务,但是默认有8080端口,通过一下方式可以不暴露web服务
*
* 1、在application.properties加上一下配置
*
* spring:
* main:
* allow-bean-definition-overriding: true
* web-application-type: none
*
* 2、修改启动类
* 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 启动了");
LockSupport.park();
}
}
@EnableDubbo(scanBasePackages = "com.cn.dl*")
扫描Dubbo的服务提供者以及Dubbo的服务消费者,一定要注意@EnableDubbo和@SpringBootApplication的先后次序;
@PropertySource("classpath:/dubbo-provider.properties")
加载配置文件到上下文环境变量。
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 * ,拦截所有请求,生成唯一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请求");
}
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下创建META-INF/dubbo/com.alibaba.dubbo.rpc.Filter,将扩展的 * 添加到dubbo调用链中
consumerTraceFilter=com.cn.dl.rpcTrace.RpcConsumerInterceptor
providerTraceFilter=com.cn.dl.rpcTrace.RpcProviderInterceptor
8、pratices-demo-web
8.1、TraceInterceptor注册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、分别启动providerMain、consumerMain、startWeb
来源:https://blog.csdn.net/qq_31289187/article/details/100576585
猜你喜欢
- 通过路径从磁盘直接读取图片这段时间在做Springboot和Vue的例子,读取图片给出路径直接可以读,太方便了,一直么有搞懂为什么。后面看到
- 提到Java发送HTTP请求,大家首先想到的是用apache的HttpClient,或者squareup的OkHttp。而在Java11之前
- Java Boolean 初始化方式1、Boolean(String boolString);以字符串的方式初始化,只有当字符串是“true
- String:字符串类型1、构造函数。String() :构造一个空字符串对象。String(byte[] bytes) :通过byte数组
- 本文实例为大家分享了C#+EmguCV使用摄像头读取、保存视频的具体代码,供大家参考,具体内容如下在Emgucv中调用摄像头需要用到Vide
- @pathvariable与@requestparam碰到的一些问题一、@pathvariable可以将 URL 中占位符参数绑定到控制器处
- 在传统的同步开发模式下,当我们调用一个函数时,通过这个函数的参数将数据传入,并通过这个函数的返回值来返回最终的计算结果。但在多线程的异步开发
- 一、项目简述环境配置:Jdk1.8 + Tomcat8.5 + mysql + Eclispe(IntelliJ IDEA,Eclispe,
- 前言使用Java8的新特性Stream流式处理,可以提高对于集合的一些操作效率,再配合lambda表达式,可以极致的简化代码,尤其还有并行流
- 一、项目背景在实际工作中,会遇到业务比较集中的情况,随着时间推延,这部分业务关联的mysql表就会越来越大,十分臃肿。尽管在项目架构上做了读
- 最近一个项目要导出word文档,折腾老半天,发现还是用freemarker的模板来搞比较方便省事,现总结一下关键步骤,供大家参考,这里是一个
- 前言最近新整了个博客网站,同事在gitee上找的,还不错,gitee上的地址在这里:拾壹博客管理系统。别人的业务,再好也有不满足自己的地方,
- 引言一个Java Gradle项目会涉及到资源的访问. 一般情况下会将当前项目所需的资源文件全部放置于resources文件夹下, 无论是m
- 目录对zygote的理解作用启动流程启动入口脚本讲解启动过程App_main::mainAndroidRuntime::start对zygo
- @RequestBody部分属性丢失问题描述JavaBean实现public class VerifyNewFriendApplyReq i
- association和collection用法1.单个关联查询association1.1实体之间的关联表示package com.wor
- 现在的项目基本上都是java web项目,所以导入jar包会出现问题,主要介绍一下java项目与javaweb项目的区别:java项目:在c
- DES一共就有4个参数参与运作:明文、密文、密钥、向量。为了初学者容易理解,可以把4个参数的关系写成:密文=明文+密钥+向量;明文=密文-密
- 也许很多朋友在学习NIO的时候都会感觉有点吃力,对里面的很多概念都感觉不是那么明朗。在进入Java NIO编程之前,我们今天先来讨论一些比较
- 静态库和动态库的区别1、静态库的扩展名一般为".a"或者".lib";动态库的扩展名一般为"