In three minutes, you will learn how to integrate the CAT call chain in the SpringBoot project

Posted by silverphpd on Wed, 09 Feb 2022 10:22:00 +0100

Buy settlement system


The annual double 11 shopping carnival is coming, and it's time for the hand choppers to start performing. When we put the goods planted with grass for a long time into the shopping cart and click the "settlement" button, we come to the essential settlement page of buying. Let's create a virtual buy and buy settlement system to provide settlement information such as goods, promotion and inventory for the settlement page, and discuss how to integrate the CAT call chain in the SpringBoot project. The purchase settlement system includes the following four items:

  1. Settlement UI: provides basic settlement data for front-end pages.
  2. Shopping cart API: get the goods in the user's shopping cart.
  3. Product API: get product details
  4. Commodity promotion API: get the promotion information of commodities
  5. Commodity inventory API: get the inventory information of commodities

The sequence diagram is as follows:

Add dependencies through Maven

<dependency>
    <groupId>com.dianping.cat</groupId>
    <artifactId>cat-client</artifactId>
    <version>3.0.0</version>
</dependency>

Configure client xml

Create the / data / appdata / cat / directory and create the client XML file:

<?xml version="1.0" encoding="utf-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema" xsi:noNamespaceSchemaLocation="config.xsd">
    <servers>
        <server ip="127.0.0.1" port="2280" http-port="8080" />
    </servers>
</config>

Note: replace 127.0.0.1 with the IP of CAT server.

Configuration item name

There are five items in the purchase settlement system, and each item needs to be configured with its own item name.
First, create the following files in each project:
src/main/resources/META-INF/app.properties
Then, add the following content to each item:

  1. Settlement UI:
    app.name=buy-buy-buy-checkout
  2. Shopping cart API:
    app.name=buy-buy-buy-cart
  3. Commodity API:
    app.name=buy-buy-buy-product
  4. Product promotion API:
    app.name=buy-buy-buy-promotion
  5. Commodity inventory API:
    app.name=buy-buy-buy-store

Note: the project name can only contain English letters (a-z, A-Z), numbers (0-9) and underscores () And dash (-)

Prepare burial point

Before burying a point, you need to write two public classes to facilitate calling when burying a point later.

  1. The first class implements cat The context interface is used to store the context information of the call chain:

    public class CatContext implements Cat.Context {
    
     private Map<String, String> properties = new HashMap<>();
    
     @Override
     public void addProperty(String key, String value) {
         properties.put(key, value);
     }
    
     @Override
     public String getProperty(String key) {
         return properties.get(key);
     }
    
     @Override
     public String toString() {
         return "CatContext{"
                 + "properties=" + properties + '}';
     }
    }
  2. The second class defines some constants as key s in the header when calling the API.

    public class CatHttpConstants {
     public static final String CAT_HTTP_HEADER_CHILD_MESSAGE_ID = "DD-CAT-CHILD-MESSAGE-ID";
     public static final String CAT_HTTP_HEADER_PARENT_MESSAGE_ID = "DD-CAT-PARENT-MESSAGE-ID";
     public static final String CAT_HTTP_HEADER_ROOT_MESSAGE_ID = "DD-CAT-ROOT-MESSAGE-ID";
    }

Start burying point

For distributed call chain monitoring using CAT, the code in the project needs to be modified for embedding:

  1. Bury the point when the request is just received.
  2. Bury points when preparing to call API.

So what code changes need to be made in the purchase settlement system? Just look at the changes of the sequence diagram:

The first buried point is implemented by Filter when the request is just received. The code is as follows:

public class CatServletFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) servletRequest;

        CatContext catContext = new CatContext();
        catContext.addProperty(Cat.Context.ROOT, request.getHeader(CatHttpConstants.CAT_HTTP_HEADER_ROOT_MESSAGE_ID));
        catContext.addProperty(Cat.Context.PARENT, request.getHeader(CatHttpConstants.CAT_HTTP_HEADER_PARENT_MESSAGE_ID));
        catContext.addProperty(Cat.Context.CHILD, request.getHeader(CatHttpConstants.CAT_HTTP_HEADER_CHILD_MESSAGE_ID));
        Cat.logRemoteCallServer(catContext);

        Transaction t = Cat.newTransaction(CatConstants.TYPE_URL, request.getRequestURI());

        try {
            
            Cat.logEvent("Service.method", request.getMethod(), Message.SUCCESS, request.getRequestURL().toString());
            Cat.logEvent("Service.client", request.getRemoteHost());

            filterChain.doFilter(servletRequest, servletResponse);

            t.setStatus(Transaction.SUCCESS);
        } catch (Exception ex) {
            t.setStatus(ex);
            Cat.logError(ex);
            throw ex;
        } finally {
            t.complete();
        }
    }

    @Override
    public void destroy() {

    }
}

The Filter has been written. Next, you need to register the Filter in SpringBoot:

@Configuration
public class CatConfiguration {
    @Bean
    public FilterRegistrationBean catServletFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        CatServletFilter filter = new CatServletFilter();
        registration.setFilter(filter);
        registration.addUrlPatterns("/*");
        registration.setName("cat-servlet-filter");
        registration.setOrder(1);
        return registration;
    }
}

For the second embedding point, add code uniformly in the HttpClient tool class calling API. Take the GET method as an example:

public static String doGet(String url) throws IOException {
    HttpGet httpGet = new HttpGet(url);
    CloseableHttpResponse response = null;
    CloseableHttpClient httpClient = HttpClientBuilder.create().build();
    String content = null;
    Transaction t = Cat.newTransaction(CatConstants.TYPE_URL, url);
    try {
        Context ctx = new CatContext();
        Cat.logRemoteCallClient(ctx);
        httpGet.setHeader(CatHttpConstants.CAT_HTTP_HEADER_ROOT_MESSAGE_ID, ctx.getProperty(Cat.Context.ROOT));
        httpGet.setHeader(CatHttpConstants.CAT_HTTP_HEADER_PARENT_MESSAGE_ID, ctx.getProperty(Cat.Context.PARENT));
        httpGet.setHeader(CatHttpConstants.CAT_HTTP_HEADER_CHILD_MESSAGE_ID, ctx.getProperty(Cat.Context.CHILD));

        response = httpClient.execute(httpGet);
        if (response.getStatusLine().getStatusCode() == 200) {
            content = EntityUtils.toString(response.getEntity(), "UTF-8");
            t.setStatus(Transaction.SUCCESS);
        }
    } catch (Exception e) {
        Cat.logError(e);
        t.setStatus(e);
        throw e;
    } finally {
        if (response != null) {
            response.close();
        }
        if (httpClient != null) {
            httpClient.close();
        }
        t.complete();
    }
    return content;
}

epilogue

The above is the whole example of SpringBoot integrating CAT call chain, which can be applied flexibly and integrated into actual projects more gracefully.
In addition, CAT has many rich functions. See Official website.

Topics: Java