dubbo service link tracking

Keywords: Dubbo Spring Java Maven

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

 

 

 

 

 

 

 

 

Posted by rahulroy on Fri, 06 Sep 2019 01:33:47 -0700