Posted in

Go语言微服务实战演练:用Go+gRPC+etcd+Prometheus搭建可观察的订单服务(含Docker Compose一键部署)

第一章:Go语言微服务架构全景概览

Go 语言凭借其轻量级协程(goroutine)、内置并发模型、静态编译与极低的运行时开销,天然契合微服务对高性能、高密度部署与快速启停的核心诉求。在云原生演进背景下,Go 已成为构建 API 网关、服务注册中心、配置中心及业务微服务的主流语言之一,广泛应用于 Kubernetes 生态工具链(如 etcd、Docker CLI)及头部互联网企业的服务网格实践。

核心组件生态

  • 服务发现:Consul、etcd 或基于 Kubernetes Service 的 DNS 发现机制,配合 Go 客户端(如 hashicorp/consul-api)实现自动服务注册与健康检查
  • RPC 框架:gRPC(默认使用 Protocol Buffers)提供强类型接口与跨语言互通能力;亦可选用轻量级 HTTP/JSON-RPC(如 go-micro v3 或 kit
  • 配置管理:支持从环境变量、Viper 驱动的 YAML/JSON 文件、或远程配置中心(如 Nacos、Apollo)动态加载配置
  • 可观测性:OpenTelemetry Go SDK 统一采集 traces、metrics 和 logs,导出至 Jaeger、Prometheus 与 Loki

典型服务启动结构

以下为一个符合十二要素应用原则的微服务入口示例:

package main

import (
    "log"
    "net/http"
    "os"

    "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
)

func main() {
    // 使用 OpenTelemetry 包装 HTTP 处理器,自动注入 trace 上下文
    handler := otelhttp.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "text/plain")
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("Hello from Go microservice"))
    }), "api-handler")

    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }

    log.Printf("Starting server on :%s", port)
    log.Fatal(http.ListenAndServe(":"+port, handler))
}

该代码通过 otelhttp.NewHandler 实现零侵入式链路追踪集成,并从环境变量读取端口,满足配置外置与进程隔离要求。

架构分层示意

层级 职责说明 典型 Go 技术选型
边界层 协议适配、认证鉴权、限流熔断 Gin/Echo + go-chi + sentinel-go
业务逻辑层 领域服务编排、事务协调 Clean Architecture + wire DI
数据访问层 多源数据整合(SQL/NoSQL/Cache) sqlx + gorm + redis-go + ent
基础设施层 日志、监控、配置、服务发现 Zap + Prometheus Client + Viper + consul-api

第二章:gRPC服务设计与订单核心逻辑实现

2.1 gRPC协议原理与ProtoBuf接口定义实践

gRPC 基于 HTTP/2 多路复用与二进制帧传输,天然支持流式通信与头部压缩。其核心依赖 Protocol Buffers(ProtoBuf)进行强类型接口契约定义。

ProtoBuf 接口定义示例

syntax = "proto3";
package example;

service UserService {
  rpc GetUser (UserRequest) returns (UserResponse);
}

message UserRequest {
  int32 id = 1;  // 用户唯一标识
}

message UserResponse {
  string name = 1;
  int32 age = 2;
}

该定义生成跨语言客户端/服务端桩代码,id = 1 中的字段序号决定二进制序列化顺序,不可随意变更;syntax = "proto3" 启用简洁语义(如默认字段可选、无 required 关键字)。

gRPC 通信流程(mermaid)

graph TD
  A[Client] -->|HTTP/2 Stream| B[Server]
  B -->|Serialized UserRequest| C[Deserialize]
  C --> D[Business Logic]
  D -->|UserResponse| E[Serialize]
  E -->|HTTP/2 Frame| A

关键优势对比

特性 REST/JSON gRPC/ProtoBuf
序列化体积 文本冗余高 二进制紧凑(≈1/3)
类型安全 运行时校验 编译期强约束
流式能力 需 SSE/WS 扩展 原生支持四种模式

2.2 Go语言构建高并发订单服务端与客户端

高并发服务端核心设计

采用 net/http + goroutine 池处理订单请求,避免无限制协程创建导致调度压力。关键使用 sync.Pool 复用 Order 结构体实例,降低 GC 频率。

客户端连接复用与限流

使用 http.Transport 配置连接池,设置 MaxIdleConnsPerHost: 100,配合 golang.org/x/time/rate.Limiter 实现每秒 50 单并发压测控制。

订单处理流程(mermaid)

graph TD
    A[HTTP Request] --> B{Valid?}
    B -->|Yes| C[Generate OrderID]
    B -->|No| D[400 Bad Request]
    C --> E[Save to Redis Cache]
    E --> F[Async Kafka Push]

示例:轻量订单结构体复用

var orderPool = sync.Pool{
    New: func() interface{} {
        return &Order{CreatedAt: time.Now()} // 预分配时间戳,避免每次 new
    },
}

// 使用时:order := orderPool.Get().(*Order)
// 处理完需归还:orderPool.Put(order)

sync.Pool 显著降低高频订单场景下内存分配开销;CreatedAt 预设避免 time.Now() 多次调用竞争。

2.3 基于Context与中间件的请求生命周期管理

HTTP 请求在 Go 中并非孤立事件,而是嵌入 context.Context 的有界、可取消、可携带元数据的生命周期单元。

中间件链式编排机制

中间件通过闭包包装 http.Handler,形成责任链:

func Logging(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // 注入请求ID到ctx
        ctx := context.WithValue(r.Context(), "req_id", uuid.New().String())
        r = r.WithContext(ctx) // 关键:传递增强后的上下文
        next.ServeHTTP(w, r)
    })
}

逻辑分析r.WithContext() 替换原请求的 Context,确保下游处理器(含后续中间件及最终 handler)均可通过 r.Context().Value("req_id") 安全获取;context.WithValue 仅适用于传递请求级元数据,不可用于传递可变状态或函数。

生命周期关键阶段对照表

阶段 触发时机 Context 可操作性
请求进入 中间件首层执行前 ✅ 可派生子ctx(超时/取消)
业务处理中 Handler 内部调用 ✅ 可监听 Done() 通道
响应写出后 ServeHTTP 返回后 ❌ ctx 已失效,禁止访问

请求流拓扑(简化版)

graph TD
    A[Client Request] --> B[Middleware 1]
    B --> C[Middleware 2]
    C --> D[Final Handler]
    D --> E[Response Write]

2.4 订单状态机建模与事务一致性保障(Saga模式初探)

订单生命周期需严格遵循“创建→支付→履约→完成/取消”状态跃迁,避免脏状态。我们采用基于事件驱动的 Saga 模式解耦分布式事务。

状态机核心定义

public enum OrderStatus {
    CREATED, PAID, SHIPPED, COMPLETED, CANCELLED, PAY_FAILED, SHIP_FAILED
}

该枚举明确边界状态,禁止非法跳转(如 CREATED → COMPLETED),所有变更必须经 StateTransitionValidator 校验。

Saga 协调流程

graph TD
    A[CreateOrder] --> B[ReserveInventory]
    B --> C[ProcessPayment]
    C --> D[ScheduleDelivery]
    D --> E[MarkCompleted]
    B -.-> F[CompensateInventory]
    C -.-> G[RefundPayment]
    D -.-> H[CancelDelivery]

补偿动作设计原则

  • 每个正向服务必须提供幂等、可逆的补偿接口
  • 补偿操作按反向顺序执行(LIFO)
  • 所有 Saga 步骤记录到 saga_log 表,含 saga_id, step, status, compensation_command
字段 类型 说明
saga_id UUID 全局唯一 Saga 跟踪标识
step VARCHAR 当前执行步骤名(如 “payment”)
compensation_command JSON 序列化后的补偿指令(含重试参数)

2.5 gRPC流式API实现订单实时通知与进度推送

核心设计动机

传统 REST 轮询导致延迟高、连接冗余;gRPC 双向流(stream)天然适配订单生命周期事件的持续广播场景。

定义流式服务契约

service OrderService {
  // 服务端流:客户端订阅单个订单的全生命周期事件
  rpc WatchOrder (OrderRequest) returns (stream OrderEvent);

  // 双向流:支持客户端动态启停多个订单监听,并接收系统级状态变更
  rpc StreamOrderUpdates (stream OrderAction) returns (stream OrderEvent);
}

message OrderEvent {
  string order_id = 1;
  OrderStatus status = 2;  // enum: CREATED, PAYING, SHIPPED, DELIVERED
  int32 progress_percent = 3;  // 0–100,用于前端进度条
  string timestamp = 4;
}

逻辑分析WatchOrder 采用服务端流(Server Streaming),轻量高效;StreamOrderUpdates 支持动态增删监听目标(如通过 OrderAction{type: "SUBSCRIBE", order_id: "o-123"}),提升资源利用率。progress_percent 字段使前端无需轮询即可渲染实时进度。

事件类型对照表

事件类型 触发时机 前端响应动作
PAYING 支付网关回调成功 启动物流轨迹轮播
SHIPPED 仓库出库单生成 显示快递单号+ETA
DELIVERED 骑手上报签收 自动关闭通知通道

流控与可靠性保障

  • 使用 gRPC Keepalive 参数维持长连接(time=30s, timeout=10s
  • 每个 OrderEventevent_idsequence_number,支持断线重连时按序去重恢复
graph TD
  A[客户端发起 WatchOrder] --> B[服务端校验权限 & 订单归属]
  B --> C{订单存在且可读?}
  C -->|是| D[打开流,推送当前状态 + 后续变更]
  C -->|否| E[返回 FAILED_PRECONDITION]
  D --> F[监听领域事件总线]

第三章:服务注册发现与动态配置治理

3.1 etcd分布式键值存储原理与Go客户端深度集成

etcd 基于 Raft 共识算法实现强一致性的分布式键值存储,所有写操作经 Leader 节点日志复制与多数派确认后才提交,保障线性一致性。

数据同步机制

Raft 日志复制流程:

  • 客户端请求 → Leader 追加日志 → 并行发送 AppendEntries → Follower 持久化并响应 → Leader 提交日志 → 应用状态机
cli, _ := clientv3.New(clientv3.Config{
    Endpoints:   []string{"localhost:2379"},
    DialTimeout: 5 * time.Second,
})
// Endpoints: etcd 集群访问地址列表  
// DialTimeout: 建立 gRPC 连接超时阈值,避免初始化阻塞  

Go 客户端核心能力对比

功能 clientv3 支持 说明
Watch 事件监听 支持流式增量变更通知
事务(Txn) Compare-and-Swap 原子操作
租约(Lease) 自动续期、会话保活基础
graph TD
    A[Client Write] --> B[Leader Append Log]
    B --> C{Replicate to Followers?}
    C -->|Yes| D[Quorum Ack]
    C -->|No| E[Retry/Re-elect]
    D --> F[Commit & Apply]

3.2 基于etcd的自动服务注册/注销与健康探针实践

核心机制:TTL租约 + 监听驱动

etcd通过Lease实现服务生命周期绑定:服务启动时创建带TTL的租约,并将服务元数据(如/services/api-v1/10.0.1.5:8080)以该租约为父节点写入;心跳续期失败则自动删除键,触发下线。

健康探针实现

# 启动服务并注册(含30秒TTL)
ETCDCTL_API=3 etcdctl lease grant 30
# 输出:lease 326a7c49d7b7e5fc
ETCDCTL_API=3 etcdctl put /services/web/10.0.1.5:8080 '{"ip":"10.0.1.5","port":8080}' --lease=326a7c49d7b7e5fc

逻辑说明:lease grant 30 创建30秒生存期租约;--lease= 将键绑定至租约。后续需每15秒调用 etcdctl lease keep-alive <ID> 续期,否则键自动过期。

服务发现监听流

graph TD
    A[服务实例] -->|注册+心跳| B[etcd集群]
    B --> C[Watch /services/...]
    C --> D[客户端实时感知增删]

探针策略对比

策略 频率 开销 适用场景
TCP连接探测 5s 通用端口存活验证
HTTP GET /health 10s 应用层健康状态
自定义脚本执行 30s 复杂依赖检查

3.3 配置中心化管理:订单超时策略与限流阈值热更新

传统硬编码的超时与限流参数导致每次变更需重启服务,严重影响业务连续性。引入配置中心(如 Nacos / Apollo)实现动态治理。

数据同步机制

配置中心监听变更事件,通过长轮询或 WebSocket 推送至各订单服务实例。

动态刷新示例(Spring Cloud Alibaba)

@RefreshScope
@Component
public class OrderPolicyConfig {
    @Value("${order.timeout.seconds:300}") // 默认5分钟
    private int timeoutSeconds; // 订单支付超时(秒)

    @Value("${rate.limit.qps:100}") // 默认100 QPS
    private int qpsLimit;

    // getter 方法省略
}

逻辑分析:@RefreshScope 触发 Bean 重建;@Value 绑定配置项,支持运行时覆盖。timeoutSeconds 影响 OrderTimeoutTask 的调度周期,qpsLimit 被 Sentinel FlowRule 加载为并发控制阈值。

关键配置项对照表

配置项 含义 典型值 生效范围
order.timeout.seconds 支付超时时间 300, 600, 1800 订单状态机、定时扫描任务
rate.limit.qps 每秒订单创建上限 50, 100, 200 API 网关 + 应用层双控

热更新流程

graph TD
    A[配置中心修改] --> B[推送变更事件]
    B --> C[应用监听并刷新Bean]
    C --> D[Sentinel规则管理器重载FlowRule]
    D --> E[订单服务立即生效新限流策略]

第四章:可观测性体系构建与生产级运维支撑

4.1 Prometheus指标建模:订单QPS、延迟、错误率自定义埋点

核心指标定义与选型

订单系统需聚焦三大黄金信号:

  • QPSrate(order_created_total[1m])(每秒新建订单数)
  • 延迟histogram_quantile(0.95, rate(order_processing_duration_seconds_bucket[1m]))
  • 错误率rate(order_failed_total[1m]) / rate(order_created_total[1m])

自定义埋点代码示例

// 初始化Prometheus指标
var (
    orderCreated = promauto.NewCounterVec(
        prometheus.CounterOpts{
            Name: "order_created_total",
            Help: "Total number of orders created",
        },
        []string{"source"}, // 按渠道维度打标
    )
    orderDuration = promauto.NewHistogramVec(
        prometheus.HistogramOpts{
            Name:    "order_processing_duration_seconds",
            Help:    "Order processing latency in seconds",
            Buckets: prometheus.ExponentialBuckets(0.01, 2, 8), // 10ms~1.28s
        },
        []string{"status"}, // 成功/失败分桶
    )
)

逻辑说明:CounterVec支持多维标签聚合,便于按来源拆分QPS;HistogramVec自动构建_bucket_sum_count三类时序,支撑SLA延迟计算。ExponentialBuckets适配订单处理时间长尾分布。

指标关联关系

指标名 类型 关键标签 用途
order_created_total Counter source="app" QPS分母 & 错误率基准
order_processing_duration_seconds_bucket Histogram status="success" P95延迟计算源
graph TD
    A[订单创建请求] --> B[orderCreated.Inc]
    A --> C[Start timer]
    C --> D{处理完成?}
    D -->|Yes| E[orderDuration.WithLabelValues(status).Observe(latency)]
    D -->|No| F[记录失败并计数]

4.2 Grafana可视化看板搭建与SLO告警规则配置

创建SLO核心指标看板

在Grafana中新建Dashboard,添加Panel,选择Prometheus数据源,查询表达式:

# 计算HTTP请求成功率(95% SLO目标)
100 * (
  sum(rate(http_request_total{code=~"2.."}[1h])) 
  / 
  sum(rate(http_request_total[1h]))
)

该表达式以1小时滑动窗口统计成功请求占比;code=~"2.."精准匹配2xx响应,避免将302等重定向误计为成功。

配置SLO告警规则

在Prometheus alert.rules.yml中定义:

- alert: SLO_BurnRateHigh
  expr: (1 - avg_over_time(http_request_success_ratio[1h])) > 0.05
  for: 10m
  labels: { severity: "warning" }
  annotations: { summary: "SLO burn rate exceeds 5%" }

avg_over_time确保稳定性,for: 10m防止瞬时抖动误报。

告警通知路由表

Route Matcher Receiver
SLO-Alerts severity="warning" email+slack
SLO-Critical severity="critical" pagerduty

数据流闭环

graph TD
  A[Prometheus采集指标] --> B[Grafana渲染SLO看板]
  B --> C[Alertmanager触发SLO告警]
  C --> D[多通道通知+自动工单]

4.3 分布式链路追踪:OpenTelemetry + Jaeger全链路注入实践

在微服务架构中,请求横跨多个服务节点,传统日志难以定位性能瓶颈。OpenTelemetry 作为云原生可观测性标准,与 Jaeger(后端存储与UI)组合,构建零侵入、高兼容的追踪体系。

部署轻量 Jaeger 后端(All-in-One)

docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
  -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp \
  -p 5778:5778 -p 16686:16686 -p 4317:4317 -p 4318:4318 \
  -p 9411:9411 \
  jaegertracing/all-in-one:1.49

该命令启动 Jaeger 全组件容器:UDP 端口接收 OpenTelemetry 的 otlp 协议数据(4317/4318),同时暴露 Zipkin 兼容接口(9411)及 Web UI(16686)。COLLECTOR_ZIPKIN_HOST_PORT 环境变量启用 Zipkin 接收器,便于多协议共存。

OpenTelemetry SDK 注入示例(Go)

import (
    "go.opentelemetry.io/otel"
    "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
    "go.opentelemetry.io/otel/sdk/trace"
)

func initTracer() {
    exporter, _ := otlptracehttp.New(context.Background(),
        otlptracehttp.WithEndpoint("localhost:4318"), // Jaeger OTLP HTTP 端点
        otlptracehttp.WithInsecure(),                 // 开发环境禁用 TLS
    )
    tp := trace.NewTracerProvider(trace.WithBatcher(exporter))
    otel.SetTracerProvider(tp)
}

此代码初始化 OpenTelemetry TracerProvider,通过 otlptracehttp 导出器将 span 数据以 HTTP+JSON 格式推送至 Jaeger 的 /v1/traces 接口(端口 4318)。WithInsecure() 表明跳过证书校验,适用于本地调试;生产环境需替换为 WithTLSClientConfig(...)

关键配置对比表

组件 协议 默认端口 用途
Jaeger UI HTTP 16686 可视化查询追踪链路
OTLP HTTP HTTP/JSON 4318 OpenTelemetry 推送 span
OTLP gRPC gRPC 4317 更高效,推荐生产使用
Zipkin API HTTP/JSON 9411 兼容旧版 Zipkin 客户端

追踪数据流转流程

graph TD
    A[Service A] -->|OTLP HTTP| B(Jaeger Collector)
    B --> C[Jaeger Storage]
    C --> D[Jaeger Query]
    D --> E[Web UI 16686]

4.4 日志统一采集:结构化日志输出与ELK栈对接方案

为实现日志可检索、可分析、可告警,需从应用层输出结构化日志,并无缝接入ELK(Elasticsearch + Logstash + Kibana)栈。

结构化日志输出规范

采用 JSON 格式输出,关键字段包括 timestamplevelservice_nametrace_idspan_idmessage

{
  "timestamp": "2024-05-20T08:32:15.123Z",
  "level": "INFO",
  "service_name": "order-service",
  "trace_id": "a1b2c3d4e5f67890",
  "span_id": "z9y8x7w6v5",
  "message": "Order created successfully",
  "order_id": "ORD-2024-78901"
}

逻辑分析:该格式兼容 Logstash 的 json codec,trace_idspan_id 支持全链路追踪对齐;order_id 等业务字段作为 top-level 字段,可直接被 Elasticsearch 映射为 keyword 或 long 类型,避免嵌套解析开销。

ELK 接入拓扑

graph TD
  A[Java App] -->|HTTP/Fluentd TCP| B(Logstash)
  B --> C[Elasticsearch]
  C --> D[Kibana]

Logstash 配置要点

  • 输入插件:tcpbeats(推荐 Filebeat 轻量采集)
  • 过滤插件:json { source => "message" } 解析原始日志
  • 输出插件:elasticsearch { hosts => ["es:9200"] index => "%{service_name}-%{+YYYY.MM.dd}" }
字段 说明 示例值
index 模板 按服务名+日期分索引 order-service-2024.05.20
hosts ES 集群地址 ["http://es-node1:9200", "http://es-node2:9200"]
pipeline 启用 ILM 生命周期管理 true

第五章:Docker Compose一键部署与实战验证

快速启动多容器应用的标准化流程

Docker Compose 通过 docker-compose.yml 文件声明式定义服务依赖、网络拓扑与卷挂载策略,彻底替代手动执行十余条 docker run 命令的繁琐操作。以部署一个典型的微服务前端(React)、后端(Spring Boot)与数据库(PostgreSQL)三组件系统为例,仅需一个 YAML 文件即可完成全栈环境初始化。

核心配置文件结构解析

以下为生产就绪型 docker-compose.yml 片段,已启用健康检查、资源限制与自定义桥接网络:

version: '3.8'
services:
  web:
    build: ./frontend
    ports: ["3000:3000"]
    depends_on:
      api:
        condition: service_healthy
  api:
    build: ./backend
    environment:
      - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/appdb
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
      interval: 30s
      timeout: 10s
      retries: 3
  db:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: appdb
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: changeme
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser -d appdb"]
volumes:
  pgdata:

多环境差异化部署实践

通过 docker-compose.override.ymldocker-compose.prod.yml 实现开发与生产环境分离:开发环境启用热重载卷挂载与调试端口暴露,生产环境则关闭日志输出、启用 TLS 代理及 CPU 内存硬限制。执行 docker compose --profile prod up -d 即可按需激活生产配置集。

实战验证:从零构建并压测电商搜索服务

在阿里云 ECS(4C8G)上部署 Elasticsearch + Kibana + Logstash(ELK)栈用于商品搜索日志分析。使用 docker compose up -d 启动后,通过 docker compose exec kibana curl -X GET "http://localhost:9200/_cat/health?v" 验证集群状态;再运行 JMeter 脚本模拟 500 并发用户持续请求 /search?q=phone 接口,监控 docker stats 输出确认内存占用稳定在 2.1GB 以内,无 OOM kill 记录。

故障注入与恢复能力验证

人为停用 db 服务后,观察 api 容器因健康检查失败被自动重启(由 restart: on-failure 策略触发),并在 42 秒内恢复服务可用性;同时 web 层通过前端降级策略展示“搜索暂时不可用”提示,避免雪崩效应。

操作命令 说明 执行耗时(平均)
docker compose up -d 启动全部服务 18.3s
docker compose logs -f api 实时跟踪后端日志 即时响应
docker compose down --volumes 彻底清理含数据卷 5.7s

CI/CD 流水线集成关键点

在 GitLab CI 中配置 docker-compose 任务时,需预先加载 Docker socket 并设置 COMPOSE_DOCKER_CLI_BUILD=1 以支持 BuildKit 加速镜像构建;同时将 .env 文件设为受保护变量,防止数据库密码泄露至流水线日志。

网络连通性深度验证

使用 docker compose exec web ping -c 3 apidocker compose exec api telnet db 5432 组合命令,交叉验证服务间四层连通性;再通过 docker network inspect ecommerce_default 查看 IP 分配详情,确认所有容器均注册于同一自定义网络 ecommerce_default,子网为 172.20.0.0/16

监控指标采集配置

docker-compose.yml 中为每个服务添加 labels,供 Prometheus 自动发现:

labels:
  - "prometheus.io/scrape=true"
  - "prometheus.io/port=9100"

启动 prometheus 服务后,其 targets 页面实时显示 webapidb 三个 endpoint 的健康状态与抓取延迟。

性能瓶颈定位方法论

当搜索接口 P95 响应时间突增至 2.4s 时,执行 docker compose top api 发现 JVM 进程 CPU 占用率达 98%,结合 docker compose exec api jstat -gc $(pidof java) 输出确认频繁 Full GC;最终通过调整 -Xmx2g -XX:+UseG1GC JVM 参数并扩容 api 服务内存限制至 mem_limit: 3g 解决问题。

专治系统慢、卡、耗资源,让服务飞起来。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注