当社は7年間のコード実行に時間のかかる統計関数を使用してきたので、とてもエレガントです!!

上の青いフォントをクリックして、「スター公式アカウント」を選択してください

高品質の記事、すぐに配達

舞台裏で公式アカウントをフォローして、支払いまたはモールに返信し、実際のプロジェクト情報とビデオ入手してください

著者:Jitwxs

ソース| https://jitwxs.cn/5aa91d10.html

I.はじめに

コードの時間のかかる統計は、特にコードパフォーマンスのボトルネックを見つける必要がある場合に、日常の開発で非常に一般的な要件です。

また、Javaの言語機能によっても制限される可能性があり、コードは記述できるほどエレガントではなく、時間のかかる大量の統計コードがビジネスロジックに干渉すると常に感じています。特に関数を開発するときは、開発直後はコードがすっきりしていてエレガントな感じがするので、補助コードをたくさん追加するとコード全体が肥大化し、自分で見るのはとても不快です。したがって、この部分をもっとエレガントに書くことができるかどうか常に疑問に思っています。今日、この記事では、「コードの時間のかかる統計」の部分について説明します。

本文を始める前に、「コードの時間のかかる統計」は特定の方法の時間ではなく、任意のコードセグメント間での時間のかかる前提についてお話しします。このコードセグメントは、メソッド内の数行のコードである場合もあれば、このメソッドの行から別の呼び出されたメソッドの行への場合もあるため、AOPではこの要件を達成できません。

第二に、従来の方法

2.1時差統計

この方法が最も簡単な方法です。開始時刻を記録し、終了時刻を記録して、時間差を計算します。

public class TimeDiffTest {
    public static void main(String[] args) throws InterruptedException {
        final long startMs = TimeUtils.nowMs();

        TimeUnit.SECONDS.sleep(5); // 模拟业务代码

        System.out.println("timeCost: " + TimeUtils.diffMs(startMs));
    }
}

/* output: 
timeCost: 5005
*/
public class TimeUtils {
    /**
     * @return 当前毫秒数
     */
    public static long nowMs() {
        return System.currentTimeMillis();
    }

    /**
     * 当前毫秒与起始毫秒差
     * @param startMillis 开始纳秒数
     * @return 时间差
     */
    public static long diffMs(long startMillis) {
       return diffMs(startMillis, nowMs());
    }
}

この方法の利点は、実装が簡単で理解しやすいことです。欠点は、コードの邪魔になり、見た目が愚かで、エレガントではないことです。

2.2ストップウォッチ

2番目の方法は、StopWatchを参照することです。StopWatchは通常、時間のかかる統計コードとして使用されます。各フレームワークとCommonパッケージには、独自の実装があります。

public class TraceWatchTest {
    public static void main(String[] args) throws InterruptedException {
        TraceWatch traceWatch = new TraceWatch();

        traceWatch.start("function1");
        TimeUnit.SECONDS.sleep(1); // 模拟业务代码
        traceWatch.stop();

        traceWatch.start("function2");
        TimeUnit.SECONDS.sleep(1); // 模拟业务代码
        traceWatch.stop();

        traceWatch.record("function1", 1); // 直接记录耗时

        System.out.println(JSON.toJSONString(traceWatch.getTaskMap()));
    }
}

/* output: 
{"function2":[{"data":1000,"taskName":"function2"}],"function1":[{"data":1000,"taskName":"function1"},{"data":1,"taskName":"function1"}]}
*/

public class TraceWatch {
    /** Start time of the current task. */
    private long startMs;

    /** Name of the current task. */
    @Nullable
    private String currentTaskName;

    @Getter
    private final Map<String, List<TaskInfo>> taskMap = new HashMap<>();

    /**
     * 开始时间差类型指标记录,如果需要终止,请调用 {@link #stop()}
     *
     * @param taskName 指标名
     */
    public void start(String taskName) throws IllegalStateException {
        if (this.currentTaskName != null) {
            throw new IllegalStateException("Can't start TraceWatch: it's already running");
        }
        this.currentTaskName = taskName;
        this.startMs = TimeUtils.nowMs();
    }

    /**
     * 终止时间差类型指标记录,调用前请确保已经调用
     */
    public void stop() throws IllegalStateException {
        if (this.currentTaskName == null) {
            throw new IllegalStateException("Can't stop TraceWatch: it's not running");
        }
        long lastTime = TimeUtils.nowMs() - this.startMs;

        TaskInfo info = new TaskInfo(this.currentTaskName, lastTime);

        this.taskMap.computeIfAbsent(this.currentTaskName, e -> new LinkedList<>()).add(info);

        this.currentTaskName = null;
    }

    /**
     * 直接记录指标数据,不局限于时间差类型
     *  @param taskName 指标名
     * @param data 指标数据
     */
    public void record(String taskName, Object data) {
        TaskInfo info = new TaskInfo(taskName, data);

        this.taskMap.computeIfAbsent(taskName, e -> new LinkedList<>()).add(info);
    }

    @Getter
    @AllArgsConstructor
    public static final class TaskInfo {
        private final String taskName;

        private final Object data;
    }
}

org.springframework.util.StopWatch 実装を模倣 してTraceWatchクラスを作成しました。このメソッドは、時間のかかる2つの統計メソッドを提供します。

Start(name) とStop() メソッドを呼び出して 時間のかかる統計を実行し ます。Record(name, timeCost)、メソッドを呼び出すことにより 、時間のかかる情報を直接記録します。この方法は、1つのレイヤーが抽出されることを除いて、基本的に「時差統計」と同じです。これは、もう少し洗練されています。

注:TraceWatch 内部データ構造は、ビジネスニーズに応じて自分で変更できます。ここでは、簡単にするためにここにいます。 内部データ構造は単なる例です。

3つの高度な方法

2番目のセクションで説明した2つの方法は、一般的には「単純明快」です。コードを少し簡単に記述することもできます。

3.1機能

jdk 1.8では、java.util.function パッケージが導入されました 。このクラスによって提供されるインターフェースを介して、指定されたコードセグメントのコンテキストで追加のコードを実行する機能を実現できます。

public class TraceHolderTest {
    public static void main(String[] args) {
        TraceWatch traceWatch = new TraceWatch();

        TraceHolder.run(traceWatch, "function1", i -> {
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟业务代码
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        String result = TraceHolder.run(traceWatch, "function2", () -> {
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟业务代码
                return "YES";
            } catch (InterruptedException e) {
                e.printStackTrace();
                return "NO";
            }
        });

        TraceHolder.run(traceWatch, "function1", i -> {
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟业务代码
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        System.out.println(JSON.toJSONString(traceWatch.getTaskMap()));
    }
}

/* output: 
{"function2":[{"data":1004,"taskName":"function2"}],"function1":[{"data":1001,"taskName":"function1"},{"data":1002,"taskName":"function1"}]}
*/

public class TraceHolder {
    /**
     * 有返回值调用
     */
    public static <T> T run(TraceWatch traceWatch, String taskName, Supplier<T> supplier) {
        try {
            traceWatch.start(taskName);

            return supplier.get();
        } finally {
            traceWatch.stop();
        }
    }

    /**
     * 无返回值调用
     */
    public static void run(TraceWatch traceWatch, String taskName, IntConsumer function) {
        try {
            traceWatch.start(taskName);

            function.accept(0);
        } finally {
            traceWatch.stop();
        }
    }
}

ここでは、Supplier andIntConsumer インターフェイスを使用して 、 外部に戻り値を提供し、呼び出す価値のある戻り値を提供しません。TraceHolderクラスでは、コアコードブロックの前後でTraceWatch 、時間のかかる統計関数実現するために前のメソッドがそれぞれ呼び出されます。

3.2自動クローズ可能

Function 使用される機能に加えて、jdk1.7の機能も使用 できます AutoCloseable 。言われている AutoCloseable 一部の学生はそれを聞いていない可能性があることがありますが、次のコードを表示する場合、あなたはすぐにそれが何であるかを理解します。

// 未使用 AutoCloseable
public static String readFirstLingFromFile(String path) throws IOException {
    BufferedReader br = null;
    try {
        br = new BufferedReader(new FileReader(path));
        return br.readLine();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (br != null) {
            br.close();
        }
    }
    return null;
}

// 使用 AutoCloseable
public static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

では try 、バック、あなたはロードできるAutoCloseable というオブジェクトを実装し 、全体に作用するインタフェース、try 文ブロック、および呼び出しがバックAutoCloseable#close() メソッドを実行が完了した後 

TraceWatchクラスを変更してみましょう。

実装 AutoCloseable インターフェイスを実装する close() インタフェースを:

@Override
public void close() {
    this.stop();
}

start() チェーン呼び出しをサポートするようにメソッドを変更し ます。

public TraceWatch start(String taskName) throws IllegalStateException {
    if (this.currentTaskName != null) {
        throw new IllegalStateException("Can't start TraceWatch: it's already running");
    }
    this.currentTaskName = taskName;
    this.startMs = TimeUtils.nowMs();
    
    return this;
}

public class AutoCloseableTest {
    public static void main(String[] args) {
        TraceWatch traceWatch = new TraceWatch();

        try(TraceWatch ignored = traceWatch.start("function1")) {
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟业务代码
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try(TraceWatch ignored = traceWatch.start("function2")) {
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟业务代码
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try(TraceWatch ignored = traceWatch.start("function1")) {
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟业务代码
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(JSON.toJSONString(traceWatch.getTaskMap()));
    }
}

/* output: 
{"function2":[{"data":1001,"taskName":"function2"}],"function1":[{"data":1002,"taskName":"function1"},{"data":1002,"taskName":"function1"}]}
*/

4、まとめ

この記事では、時間のかかるコードをカウントする4つの方法を示します。

  • 時差統計

  • ストップウォッチ

  • 関数

  • 自動クローズ可能

記載されているスキームは、現在私が考えることができるものです。


ホットな推奨事項がありますか?

19枚の写真の概要SpringCloud

新しいCTOは、フロントエンドとバックエンドの分離を禁止し、多くの長所と短所も述べています。

SpringBootの単純な読み取り/書き込み分離を最初から実現することは難しくありません!

VS Codeを使用して、Spring Boot、ZhenniMaショーを開発してください。

ばかげて見分けがつかないCookie、セッション、トークン、JWT

乾物{1} SpringBoot統合ジグアンプッシュ完全実装コード(推奨コレクション)

Zhihu Gaozan:PinduoduoとState Grid Offerのどちらを選択しますか?

SELECTを使用しないでください*

JacksonとFastjsonの詳細な比較、結局私はまだ選択しました...

Dockerを使い始めるための究極のガイド、これは私が今まで見た中で最高のチュートリアルです!

クリックして元のテキストを読み、SpringCloudの実際の戦闘プロジェクトを学びましょ

おすすめ

転載: blog.csdn.net/qq_17231297/article/details/115302101