目前很多业务使用微服务架构,服务模块划分有这2种方式:
不管哪种方式,一次接口调用都需要多个服务协同完成,其中一个服务出现问题,都会导致最终失败,虽然有logback + kafka + ELK 这样的神器架构,但是定位问题也很麻烦,如果在整个链路中,可以通过一个唯一ID(traceId)跟踪本次服务调用,就可以在ELK中查找当前traceId来定位问题。
2.1、pom.xml
4.0.0
pom
pratices-demo-consumer
pratices-demo-provider
pratices-demo-provider-core
pratices-demo-consumer-core
pratices-demo-web
pratices-demo-trace
org.springframework.boot
spring-boot-starter-parent
2.1.4.RELEASE
com.cn.dl
pratices-demo
0.0.1-SNAPSHOT
pratices-demo
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-autoconfigure
com.alibaba
dubbo
2.6.0
org.springframework
spring
ch.qos.logback
logback-core
ch.qos.logback
logback-access
ch.qos.logback
logback-classic
org.slf4j
slf4j-api
org.apache.zookeeper
zookeeper
3.4.10
com.101tec
zkclient
0.10
slf4j-log4j12
org.slf4j
org.springframework.boot
spring-boot-maven-plugin
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
spring-snapshots
Spring Snapshots
https://repo.spring.io/snapshot
true
spring-milestones
Spring Milestones
https://repo.spring.io/milestone
spring-snapshots
Spring Snapshots
https://repo.spring.io/snapshot
true
3.1、ProviderService
package com.cn.dl;
/**
* Created by yanshao on 2019-09-04.
*/
public interface ProviderService {
String sayHello(String name);
}
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.1、ConsumerService
package com.cn.dl;
/**
* Created by yanshao on 2019-09-04.
*/
public interface ConsumerService {
String toSayHello(String name);
int getRandomInt();
}
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.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 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 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 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.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 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);
}
}