At present, many businesses use micro-service architecture. There are two ways to divide service modules:
- Service Function Division
- Business Division
Either way, an interface call requires multiple services to be completed in collaboration. A service problem will lead to failure. Although there are artifact architectures such as logback + kafka + ELK, the positioning problem is also troublesome. If a unique ID (traceId) can be used to track the book throughout the link. With this service call, you can find the current traceId in the ELK to locate the problem.
I. Cases
1. Case structure
- pratices-demo-provider-core: Defining service interfaces
- pratices-demo-provider: implementation
- pratices-demo-consumer-core: service consumers and service providers
- pratices-demo-consumer: implementation
- pratices-demo-web: providing http services
- pratices-demo-trace: the core module of this case, intercepts service invocation, sets traceId, and tracks this service invocation
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 Service Provision traceId:{},sayHello:{}", MDC.get("traceId"),name); return "hello " + name ; } }
4.2. dubbo-provider.properties configuration file
# 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
ProviderMain service startup class
Note: Starting dubbo services does not require exposing http services
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); /** * Start dubbo service, do not need to provide web services, but the default port is 8080, through one way can not expose Web Services * * 1,Add a configuration to application.properties * * spring: * main: * allow-bean-definition-overriding: true * web-application-type: none * * 2,Modify Startup Class * 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 Start up"); LockSupport.park(); } }
@ EnableDubbo (scanBasePackages = com.cn.dl*): Scanning Dubbo's service providers and Dubbo's service consumers must pay attention to the order of @EnableDubbo and @SpringBook Application; @ PropertySource("classpath:/dubbo-provider.properties"): Load configuration files to context environment variables.
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
Define web interceptors, intercept all requests, and generate unique ID s
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("Support only http request"); } 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 abnormal",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 abnormal",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 abnormal",e); throw e; } finally { MDC.remove(TRACE_ID); } return result; } }
Then create META-INF/dubbo/com.alibaba.dubbo.rpc.Filter under resources and add the extended interceptor to the Dubbo call chain
consumerTraceFilter=com.cn.dl.rpcTrace.RpcConsumerInterceptor providerTraceFilter=com.cn.dl.rpcTrace.RpcProviderInterceptor
8,pratices-demo-web
8.1. TraceInterceptor Register web Interceptor
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. Start providerMain, consumerMain and startWeb, respectively