這新專案我選擇使用了之前沒使用過的 Jersey Client ~ 由於希望分析打到其他服務的 P99 latency,所以我研究了一下怎麼去 log 下來所有的 duration (如果不知道什麼是 P99 latency 可以參考這篇文章 “What is P99 latency")。這邊分享一下,我最後使用的方法,由於不太熟 Jersey Client,所以最後也是花了一點時間才找到使用 ClientRequestFilter & ClientResponseFilter 來解決。

前置作業


這邊使用 Maven Project 作為範例

 1<?xml version="1.0" encoding="UTF-8"?>
 2<project xmlns="http://maven.apache.org/POM/4.0.0"
 3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5    <modelVersion>4.0.0</modelVersion>
 6
 7    <groupId>org.example</groupId>
 8    <artifactId>jerseyClient</artifactId>
 9    <version>1.0-SNAPSHOT</version>
10
11    <dependencies>
12        <dependency>
13            <groupId>org.projectlombok</groupId>
14            <artifactId>lombok</artifactId>
15            <version>1.18.20</version>
16            <scope>provided</scope>
17        </dependency>
18        <dependency>
19            <groupId>org.glassfish.jersey.core</groupId>
20            <artifactId>jersey-client</artifactId>
21            <version>2.33</version>
22        </dependency>
23        <dependency>
24            <groupId>org.glassfish.jersey.inject</groupId>
25            <artifactId>jersey-hk2</artifactId>
26            <version>2.28</version>
27        </dependency>
28    </dependencies>
29    <properties>
30        <maven.compiler.source>11</maven.compiler.source>
31        <maven.compiler.target>11</maven.compiler.target>
32    </properties>
33
34</project>
 1package com.demo;
 2
 3import javax.ws.rs.client.Client;
 4import javax.ws.rs.client.ClientBuilder;
 5import javax.ws.rs.client.WebTarget;
 6import javax.ws.rs.core.MediaType;
 7import java.util.concurrent.TimeUnit;
 8
 9public class MAIN {
10    public static void main(String[] args) {
11        Client client = ClientBuilder.newBuilder()
12                .connectTimeout(30, TimeUnit.SECONDS)
13                .build();
14        //Filter 在後面的範例中
15        WebTarget webTarget = client.target("https://google.com").register(new Filter());
16        webTarget
17                .request(MediaType.APPLICATION_JSON_TYPE)
18                .get(String.class);
19    }
20}

ClientResponseFilter


實作 Interface javax.ws.rs.client.ClientResponseFilter 可以處理所有的每個 Response 的結果 ~ 可以拿到 ClientRequestContext & ClientRequestContext,以下是範例。

 1package com.demo;
 2
 3import javax.ws.rs.client.ClientRequestContext;
 4import javax.ws.rs.client.ClientResponseContext;
 5import javax.ws.rs.client.ClientResponseFilter;
 6import java.io.IOException;
 7
 8public class Filter implements ClientResponseFilter {
 9    @Override
10    public void filter(ClientRequestContext requestContext,
11                       ClientResponseContext responseContext) throws IOException {
12        System.out.println(String.format("requestUri=\"%s\",  method=\"%s\", responseStatus=\"%s\", requestTime=\"%s\", responseTime=\"%s\"",
13                requestContext.getUri(),
14                requestContext.getMethod(),
15                responseContext.getStatus(),
16                requestContext.getDate(),
17                responseContext.getDate()));
18    }
19}

這邊可以看到,ClientRequestContext & ClientResponseContext 都有 getDate() 的函式返回一個 java.util.Date!天真的我一開始以為可以靠這兩個 getDate() 算出這個 response 的 duration,但實際執行結果卻出乎我的意料,requestTime 竟然是 null。

1requestUri="https://google.com",  method="GET", responseStatus="200", requestTime="null", responseTime="Wed Jun 30 22:41:53 CST 2021"

ClientRequestFilter


當時有點犯蠢,想說 requestTime 是 Null 就差點放棄,不知道怎麼完全沒想到竟然有 ClientResponseFilter ,那怎麼會沒有 ClientRequestFilter 呢!所以這邊最後靠著 ClientRequestFilter ,在每個 request 送出去前,把系統當下時間塞進去 requestContext ,這樣就可以在 response 回來時拉到當初 request 送出時間。

 1package com.demo;
 2
 3import javax.ws.rs.client.ClientRequestContext;
 4import javax.ws.rs.client.ClientRequestFilter;
 5import javax.ws.rs.client.ClientResponseContext;
 6import javax.ws.rs.client.ClientResponseFilter;
 7import java.io.IOException;
 8
 9public class Filter implements ClientResponseFilter, ClientRequestFilter {
10    private static final String REQUEST_TIME = "REQUEST_TIME";
11
12    @Override
13    public void filter(ClientRequestContext requestContext) throws IOException {
14        long currentTimeMillis = System.currentTimeMillis();
15        requestContext.setProperty(REQUEST_TIME, currentTimeMillis);
16    }
17    
18    @Override
19    public void filter(ClientRequestContext requestContext,
20                       ClientResponseContext responseContext) throws IOException {
21        long requestTime = (Long) requestContext.getProperty(REQUEST_TIME);
22        long currentTimeMillis = System.currentTimeMillis();
23        long duration = currentTimeMillis - requestTime;
24
25        System.out.println(String.format("requestUri=\"%s\",  method=\"%s\", responseStatus=\"%s\", duration=\"%d\"",
26                requestContext.getUri(),
27                requestContext.getMethod(),
28                responseContext.getStatus(),
29                duration));
30    }
31}

透過 ClientResponseFilter & ClientRequestFilter 就可以簡單算出每個 request duration ~ 希望有幫助到看到這邊文章的人