当前位置: 首页 > news >正文

windows下以all-in-one模式快速启动jaeger

目录

    • 一、引言
    • 二、下载Jaeger Windows启动包
    • 三、all-in-one模式下的配置文件
      • 3.1 Memory存储
      • 3.2 Bagder存储
    • 四、以all-in-one模式启动Jaeger
    • 五、访问Jaeger UI验证安装结果

一、引言

Jaeger 是一款开源的分布式追踪系统,最初由 Uber 开发并贡献给 CNCF(云原生计算基金会)。它主要用于监控和分析微服务架构中的请求链路,帮助开发者定位性能瓶颈、分析服务依赖和故障原因。

  • Jaeger 支持多种数据采集方式,兼容 OpenTracing、OpenTelemetry标准,能够与主流语言和框架集成。
  • 通过可视化界面,用户可以直观地查看请求在各个服务间的流转过程,是现代云原生应用实现链路追踪和性能分析的重要工具。
  • 支持多种存储方式:Memory(内存)、Badger(本地存储)、OpenSearch、ElasticSearch、Cassandra等。

本文主要介绍如何在windows环境下快速安装Jaeger,并提供Jaeger UI查询界面,方便大家快速熟悉Jaeger相关生态。

二、下载Jaeger Windows启动包

进入Jaeger Github Releases页面,选择最新版本下assets下windows的相应安装包:
在这里插入图片描述
点击下载即可:
https://github.com/jaegertracing/jaeger/releases/download/1.72/jaeger-2.9.0-windows-amd64.zip

下载完成后解压缩后如下图:
在这里插入图片描述
上图中的jaeger.exe即为Jaeger的启动文件。

三、all-in-one模式下的配置文件

详细配置说明可参见:
https://www.jaegertracing.io/docs/2.9/deployment/configuration/
在这里插入图片描述

3.1 Memory存储

如下默认使用内存存储,重启后数据会丢失。具体的all-in-one模式配置文件示例如下:
https://github.com/jaegertracing/jaeger/blob/v2.9.0/cmd/jaeger/internal/all-in-one.yaml

jaeger-all-in-one.yaml:

service:extensions: [jaeger_storage, jaeger_query, remote_sampling, healthcheckv2, expvar, zpages]pipelines:traces:receivers: [otlp, jaeger, zipkin]processors: [batch]exporters: [jaeger_storage_exporter]telemetry:resource:service.name: jaegermetrics:level: detailedreaders:- pull:exporter:prometheus:host: "${env:JAEGER_LISTEN_HOST:-localhost}"port: 8888logs:level: info# TODO Initialize telemetry tracer once OTEL released new feature.# https://github.com/open-telemetry/opentelemetry-collector/issues/10663extensions:jaeger_query:storage:traces: some_storagejaeger_storage:backends:some_storage:memory:max_traces: 100000remote_sampling:# We can either use file or adaptive sampling strategy in remote_samplingfile:path:default_sampling_probability: 1reload_interval: 1s# adaptive:#   sampling_store: some_store#   initial_sampling_probability: 0.1http:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:5778"grpc:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:5779"healthcheckv2:use_v2: truehttp:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:13133"grpc:expvar:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:27777"zpages:# for some reason the official extension listens on ephemeral port 55679# so we override it with a normal portendpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:27778"receivers:otlp:protocols:grpc:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:4317"http:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:4318"jaeger:protocols:grpc:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:14250"thrift_http:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:14268"thrift_binary:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:6832"thrift_compact:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:6831"zipkin:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:9411"processors:batch:exporters:jaeger_storage_exporter:trace_storage: some_storage

可将上述的jaeger-all-in-one.yaml配置文件放在之前的jaeger解压目录下:
在这里插入图片描述

3.2 Bagder存储

若需要支持本地存储且重启后数据不丢失,可使用Badeger存储,调整后的all-in-one模式配置文件如下:

注: 需替换配置文件中的your_path为实际的存储路径。

service:extensions: [jaeger_storage, jaeger_query, remote_sampling, healthcheckv2, expvar, zpages]pipelines:traces:receivers: [otlp, jaeger, zipkin]processors: [batch]exporters: [jaeger_storage_exporter]telemetry:resource:service.name: jaegermetrics:level: detailedreaders:- pull:exporter:prometheus:host: "${env:JAEGER_LISTEN_HOST:-localhost}"port: 8888logs:level: info# TODO Initialize telemetry tracer once OTEL released new feature.# https://github.com/open-telemetry/opentelemetry-collector/issues/10663extensions:jaeger_query:storage:# 使用Badger存储traces: badger_storagetraces_archive: badger_storage_archivejaeger_storage:backends:some_storage:memory:max_traces: 100000# Badger存储badger_storage:badger:directories:keys: "your_path/badger/jaeger/"values: "your_path/badger/jaeger/"ephemeral: falsettl:spans: 48h # 2 days# Badger存储 - 归档badger_storage_archive:badger:directories:keys: "your_path/badger/jaeger_archive/"values: "your_path/badger/jaeger_archive/"ephemeral: falsettl:spans: 720h # 30 daysremote_sampling:# We can either use file or adaptive sampling strategy in remote_samplingfile:path:default_sampling_probability: 1reload_interval: 1s# adaptive:#   sampling_store: some_store#   initial_sampling_probability: 0.1http:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:5778"grpc:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:5779"healthcheckv2:use_v2: truehttp:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:13133"grpc:expvar:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:27777"zpages:# for some reason the official extension listens on ephemeral port 55679# so we override it with a normal portendpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:27778"receivers:otlp:protocols:grpc:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:4317"http:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:4318"jaeger:protocols:grpc:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:14250"thrift_http:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:14268"thrift_binary:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:6832"thrift_compact:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:6831"zipkin:endpoint: "${env:JAEGER_LISTEN_HOST:-localhost}:9411"processors:batch:exporters:jaeger_storage_exporter:# 使用Badger存储trace_storage: badger_storage

四、以all-in-one模式启动Jaeger

通过cmd或PowerShell进入前文提到的jaeger解压缩目录,执行如下启动命令:

# 进入jaeger安装目录
cd your_jaeger_path# cmd下启动jaeger
jaeger.exe --config jaeger-all-in-one.yaml# PowderShell启动jaeger
./jaeger.exe --config jaeger-all-in-one.yaml

具体启动日志如下:

2025/08/07 14:49:35 application version: git-commit=57f65148781af756f323ec27d8d080b20ff0bde1, git-version=v2.9.0, build-date=2025-08-06T18:10:13Z
2025-08-07T14:49:35.034+0800    info    service@v0.131.0/service.go:214 Setting up own telemetry...     {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}}
2025-08-07T14:49:35.034+0800    info    builders/builders.go:26 Development component. May change in the future.       {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger_storage_exporter", "otelcol.component.kind": "exporter", "otelcol.signal": "traces"}
2025-08-07T14:49:35.034+0800    info    builders/extension.go:50        Development component. May change in the future.{"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "healthcheckv2", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.034+0800    info    service@v0.131.0/service.go:276 Starting jaeger...      {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "Version": "v2.9.0", "NumCPU": 16}
2025-08-07T14:49:35.034+0800    info    extensions/extensions.go:41     Starting extensions...  {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}}
2025-08-07T14:49:35.035+0800    info    extensions/extensions.go:45     Extension is starting...        {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger_storage", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.035+0800    info    jaegerstorage/extension.go:159  Initializing storage 'some_storage'     {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger_storage", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.035+0800    info    extensions/extensions.go:62     Extension started.      {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger_storage", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.035+0800    info    extensions/extensions.go:45     Extension is starting...        {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger_query", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.035+0800    info    jaegerquery/server.go:144       Archive storage not configured  {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger_query", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.036+0800    info    jaegerquery/server.go:186       Metric storage not configured   {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger_query", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.037+0800    info    app/static_handler.go:92        Using UI configuration  {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger_query", "otelcol.component.kind": "extension", "path": ""}
2025-08-07T14:49:35.037+0800    info    app/server.go:256       Query server started    {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger_query", "otelcol.component.kind": "extension", "http_addr": "[::]:16686", "grpc_addr": "[::]:16685"}
2025-08-07T14:49:35.037+0800    info    extensions/extensions.go:62     Extension started.      {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger_query", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.037+0800    info    app/server.go:284       Starting HTTP server    {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger_query", "otelcol.component.kind": "extension", "port": 16686, "addr": ":16686"}
2025-08-07T14:49:35.037+0800    info    app/server.go:298       Starting GRPC server    {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger_query", "otelcol.component.kind": "extension", "port": 16685, "addr": ":16685"}
2025-08-07T14:49:35.037+0800    info    extensions/extensions.go:45     Extension is starting...        {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "zpages", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.037+0800    info    zpagesextension@v0.131.0/zpagesextension.go:56  Registered zPages span processor on tracer provider     {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "zpages", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.038+0800    info    zpagesextension@v0.131.0/zpagesextension.go:71  Registered Host's zPages       {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "zpages", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.038+0800    info    zpagesextension@v0.131.0/zpagesextension.go:83  Starting zPages extension      {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "zpages", "otelcol.component.kind": "extension", "config": {"Endpoint":"localhost:27778","TLS":{},"CORS":{},"Auth":{},"MaxRequestBodySize":0,"IncludeMetadata":false,"ResponseHeaders":null,"CompressionAlgorithms":null,"ReadTimeout":0,"ReadHeaderTimeout":0,"WriteTimeout":0,"IdleTimeout":0,"Middlewares":null,"Expvar":{"Enabled":false}}}
2025-08-07T14:49:35.038+0800    info    extensions/extensions.go:62     Extension started.      {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "zpages", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.038+0800    info    extensions/extensions.go:45     Extension is starting...        {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "expvar", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.039+0800    info    expvar/extension.go:52  Starting expvar server  {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "expvar", "otelcol.component.kind": "extension", "addr": "127.0.0.1:27777"}
2025-08-07T14:49:35.039+0800    info    extensions/extensions.go:62     Extension started.      {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "expvar", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.039+0800    info    extensions/extensions.go:45     Extension is starting...        {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "healthcheckv2", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.039+0800    info    extensions/extensions.go:62     Extension started.      {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "healthcheckv2", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.039+0800    info    extensions/extensions.go:45     Extension is starting...        {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "remote_sampling", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.039+0800    info    remotesampling/extension.go:104 Starting file-based sampling strategy provider {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "remote_sampling", "otelcol.component.kind": "extension", "path": ""}
2025-08-07T14:49:35.040+0800    info    file/provider.go:57     No sampling strategies source provided, using defaults {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "remote_sampling", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.040+0800    info    remotesampling/extension.go:245 Starting remote sampling HTTP server    {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "remote_sampling", "otelcol.component.kind": "extension", "endpoint": "localhost:5778"}
2025-08-07T14:49:35.040+0800    info    remotesampling/extension.go:279 Starting remote sampling GRPC server    {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "remote_sampling", "otelcol.component.kind": "extension", "endpoint": "localhost:5779"}
2025-08-07T14:49:35.041+0800    info    extensions/extensions.go:62     Extension started.      {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "remote_sampling", "otelcol.component.kind": "extension"}
2025-08-07T14:49:35.044+0800    info    otlpreceiver@v0.131.0/otlp.go:117       Starting GRPC server    {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "otlp", "otelcol.component.kind": "receiver", "endpoint": "localhost:4317"}
2025-08-07T14:49:35.044+0800    info    otlpreceiver@v0.131.0/otlp.go:175       Starting HTTP server    {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "otlp", "otelcol.component.kind": "receiver", "endpoint": "localhost:4318"}
2025-08-07T14:49:35.045+0800    info    jaegerreceiver@v0.131.0/trace_receiver.go:211   Starting UDP server for Binary Thrift   {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger", "otelcol.component.kind": "receiver", "otelcol.signal": "traces", "endpoint": "localhost:6832"}
2025-08-07T14:49:35.046+0800    info    jaegerreceiver@v0.131.0/trace_receiver.go:233   Starting UDP server for Compact Thrift  {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger", "otelcol.component.kind": "receiver", "otelcol.signal": "traces", "endpoint": "localhost:6831"}
2025-08-07T14:49:35.046+0800    info    jaegerreceiver@v0.131.0/trace_receiver.go:337   Starting HTTP server for Jaeger Thrift  {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger", "otelcol.component.kind": "receiver", "otelcol.signal": "traces", "endpoint": "localhost:14268"}
2025-08-07T14:49:35.046+0800    info    jaegerreceiver@v0.131.0/trace_receiver.go:362   Starting gRPC server for Jaeger Protobuf        {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}, "otelcol.component.id": "jaeger", "otelcol.component.kind": "receiver", "otelcol.signal": "traces", "endpoint": "localhost:14250"}
2025-08-07T14:49:35.047+0800    info    service@v0.131.0/service.go:299 Everything is ready. Begin running and processing data. {"resource": {"service.instance.id": "af24957d-f89c-4aee-baec-9db7c8a955c0", "service.name": "jaeger", "service.version": "v2.9.0"}}

除了直接通过命令行启动Jaeger,也可借助Maye这样的快速启动工具进行配置并一键启动:
在这里插入图片描述

在这里插入图片描述

五、访问Jaeger UI验证安装结果

访问Jaeger UI:
http://localhost:16686/

效果如下即说明已安装成功:
在这里插入图片描述

更多的端口、接口及使用说明可参见:
https://www.jaegertracing.io/docs/2.9/architecture/apis/
在这里插入图片描述

http://www.dtcms.com/a/329663.html

相关文章:

  • Linux学习-UI技术
  • ROS2实用工具
  • Spring AI 的特性 及其 最佳实践
  • CompletableFuture介绍及使用方式
  • 天猫商品评论API:获取商品热门评价与最新评价
  • Jmeter TPS与QPS
  • Ant Design 的 `Image` 组件,通过 `preview.src` 加载本地图片文件
  • Dockerhub 代理设置
  • 破解测试数据困境:5招兼顾安全与真实性
  • Nature Communications 西湖大学姜汉卿教授:弹电磁驱动新范式--赋能昆虫级软体机器人的肌肉仿生策略
  • HTML第三次作业
  • Redis ubuntu下载Redis的C++客户端
  • Ubuntu 20.04 虚拟机安装完整教程:从 VMware 到 VMware Tools
  • 如何在 Ubuntu 24.04 LTS Noble Linux 上安装 FileZilla Server
  • Python【算法中心 03】Docker部署Django搭建的Python应用流程实例(Docker离线安装配置+Django项目Docker部署)
  • java中list的api详细使用
  • MySQL宝典
  • 【Golang】 Context.WithCancel 全面解析与实战指南
  • 使用内联汇编获取在线OJ平台CPU的信息
  • 玩转Docker | 使用Docker部署WordPress网站服务
  • 基本计算器 II
  • 回归分析预测原神深渊血量
  • 【金仓数据库产品体验官】_从实践看金仓数据库与 MySQL 的兼容性
  • Windows系统设置内外网同时访问(小白友好版)
  • Docker部署 Neo4j 及集成 APOC 插件:安装与配置完整指南(docker-compose)
  • 【Android】RecyclerView多布局展示案例
  • Kubernetes(K8S)中,kubectl describe node与kubectl top pod命令显示POD资源的核心区别
  • C语言:队列的实现和剖析
  • Spring Boot 整合 Thymeleaf 模板引擎:从零开始的完整指南
  • 攒钱学概论:5、创业术