Junco 运维手册
[TOC]
一、内置运维端点
启用 junco-web 后,框架内置以下端点。
| 端点 | 成功状态 | 成功响应 | 用途 |
|---|---|---|---|
GET /-/live |
200 | UP |
进程存活检查 |
GET /-/ready |
200 | READY |
流量接入检查 |
GET /-/metrics |
200 | Prometheus text | 指标抓取 |
/-/ready 在应用启动完成后返回 READY。当应用准备关闭时,框架会先把 readiness 标记为不可用,再等待 http.shutdown.readiness-drain-ms,用于给 Kubernetes Service 摘流时间。
二、Kubernetes 探针示例
livenessProbe:
httpGet:
path: /-/live
port: 9090
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /-/ready
port: 9090
initialDelaySeconds: 5
periodSeconds: 5
failureThreshold: 2
优雅停机建议配置:
http:
shutdown:
readiness-drain-ms: 10000
readiness-drain-ms 应大于 readiness 探针周期,常见取值为 10000 到 30000 毫秒。
Web 过载与连接保护建议配置:
http:
business:
queue-capacity: 1024
request-read-timeout-ms: 15000
idle-timeout-ms: 60000
http.business.queue-capacity:业务线程池等待队列容量,队列满后返回 503。http.request-read-timeout-ms:单个请求从请求头到完整 body 的最大读取时间,超时返回 408。http.idle-timeout-ms:Keep-Alive 连接的最大空闲时间,超时后关闭连接。
健康检查使用后台快照,避免探针请求线程直接访问数据库等外部依赖:
health:
interval-ms: 3000
timeout-ms: 1000
jdbc:
health:
enabled: true
redis:
health:
enabled: true
nats:
health:
enabled: true
health.interval-ms:后台健康快照刷新周期。health.timeout-ms:单轮健康检查的总超时时间;异常或超时统一按 DOWN 处理。jdbc.health.enabled:检查所有已配置数据源,默认关闭。redis.health.enabled:通过 Redis PING 检查连接,默认关闭。nats.health.enabled:检查 NATS 连接状态;首次检查会触发惰性建连,默认关闭。
依赖健康检查必须显式开启,避免只使用部分能力的应用因为未使用的外部依赖影响 readiness。应用关闭后,NATS 健康检查不会重新创建已经销毁的连接。
Gateway 转发客户端支持按应用容量调整线程、超时和连接上限:
gateway:
routes-location: classpath:router.yaml
response:
max-bytes: 10485760
http:
event-loop-threads: 2
connect-timeout-ms: 1000
response-timeout-ms: 3000
pool:
acquire-timeout-ms: 1000
max-total: 200
max-per-route: 50
websocket:
event-loop-threads: 2
connect-timeout-ms: 3000
handshake-timeout-ms: 5000
max-connections: 4096
max-frame-bytes: 65536
max-pending-frames: 64
gateway.routes-location:路由配置文件位置;本地文件可使用classpath:router.yaml,Kubernetes 挂载文件可使用file:路径。gateway.response.max-bytes:普通 HTTP 下游响应体上限;SSE 数据保持流式转发,不做全量聚合。gateway.http.event-loop-threads:HTTP 下游客户端的 Netty EventLoop 线程数。gateway.http.connect-timeout-ms:建立下游 HTTP 连接的超时时间。gateway.http.response-timeout-ms:等待下游响应头及普通 HTTP 响应体的超时时间;SSE 收到响应头后不再使用该读超时,由客户端断开或下游结束关闭流。gateway.http.pool.acquire-timeout-ms:从 HTTP 连接池取得连接许可的等待时间。gateway.http.pool.max-total:整个网关实例允许的 HTTP 下游并发连接上限。gateway.http.pool.max-per-route:单个下游地址允许的 HTTP 并发连接上限。gateway.websocket.event-loop-threads:WebSocket 下游客户端的 Netty EventLoop 线程数。gateway.websocket.connect-timeout-ms:建立下游 WebSocket TCP 连接的超时时间。gateway.websocket.handshake-timeout-ms:WebSocket 下游握手及关闭握手的等待时间。gateway.websocket.max-connections:整个网关实例允许的 WebSocket 并发连接上限。gateway.websocket.max-frame-bytes:单个 WebSocket 帧的最大字节数。gateway.websocket.max-pending-frames:下游握手已完成但上游握手尚未完成时,允许暂存的最大帧数;达到上限会关闭连接。
以上数值必须为正整数,配置非法时应用启动失败。连接上限是单个网关实例的限制,Kubernetes 多副本容量需要按副本数合并评估。
Gateway 命中路由后只在 Web 业务线程中完成路由匹配、过滤器和请求发起。连接许可、下游响应头和普通响应体均通过 Future 异步等待,完成后切回入站 Netty EventLoop 写出;客户端提前断开会取消尚未完成的下游请求。WebSocket 转发会根据对端可写状态暂停或恢复来源端读取,避免慢连接造成无界堆积。
普通 Controller 采用同步返回模型,参数解析、业务调用、拦截器、异常处理和响应写出保持在一条清晰的调用链中。Gateway 转发仍使用内部 Future 异步等待下游响应,但该实现细节不会暴露给 Controller。
流式端点建议同时配置连接数和单连接积压上限:
http:
sse:
max-connections: 4096
max-pending-bytes: 1048576
websocket:
max-connections: 4096
max-pending-messages: 256
SSE 待写字节或 WebSocket 待处理消息达到上限时,框架会主动关闭慢连接,避免单连接持续占用内存。
Schedule 的触发线程与业务执行线程相互隔离,分布式锁使用随机令牌并在任务运行期间续租:
schedule:
execution-threads: 4
default-lock-at-most-for-ms: 300000
default-timeout-ms: 0
drain-timeout-ms: 15000
任务超时会中断业务执行,但只有任务线程真正退出后才释放锁。服务停机时先停止触发与续租,再等待执行线程在 drain-timeout-ms 内完成,避免 Redis 连接池先关闭后仍执行解锁。
内存缓存可按 region 限制条目数,默认每个 region 最多 10000 条:
cache:
memory:
max-entries-per-region: 10000
达到上限时优先清理过期条目,否则淘汰最早写入的条目。Redis 延时队列通过 Lua 原子地将到期元素从 ZSET 搬到 ready list,支持多实例并发轮询。
JDBC 默认事务传播行为仍为 REQUIRED。需要独立提交或回滚时使用 @JuncoTransactional(propagation = TransactionPropagation.REQUIRES_NEW)。timeoutSeconds 从事务开始时计算总截止时间,每条 SQL 只获得剩余时间;截止后事务标记为 rollback-only,提交前也会再次检查。
三、外部配置和优先级
Junco 默认从 classpath 加载 application.properties、application.yml 和 application.yaml。
业务配置 Bean 可以继续在注解中保留默认文件名,便于通过文件名反查绑定关系:
@JuncoProperties(prefix = "path", path = "classpath:path.yaml")
public class PathProperties {
}
这里的 path.yaml 是 PathProperties 的默认配置源。application.yml、导入文件、JVM 参数或环境变量中相同 path 前缀的配置都会覆盖默认值,也可以补充默认文件中不存在的字段。
默认文件本身不是必需资源时,可以显式增加 optional: 前缀:
@JuncoProperties(prefix = "path", path = "optional:classpath:path.yaml")
public class PathProperties {
}
该文件不存在时,框架仅使用全局配置源绑定;全局配置源中也没有 path 配置时,容器注册一个无参构造的默认 Bean。文件存在但格式错误时仍会启动失败。
条件化 Bean 注册
@ConditionalOnBean 可以标记 @JuncoManaged 类或 @JuncoBean 方法,仅在容器中存在全部指定类型或名称的 Bean 时注册:
@ConditionalOnProperty 可以标记 @JuncoConfig 配置类、@JuncoManaged 类或 @JuncoBean 方法。未指定 havingValue 时,配置存在且值不为 false 即匹配:
@ConditionalOnMissingBean 用于 @JuncoManaged 类或 @JuncoBean 方法,可以按类型或 Bean 名提供缺省实现。类型和名称都未指定时,默认使用当前类或方法返回类型:
条件判断基于已注册的 BeanDefinition 和单例 Bean,不会提前实例化 Bean。多个条件同时出现时需要全部满足;只依赖配置的实现会先注册,再判断 @ConditionalOnMissingBean 默认实现是否需要退让。
应用主配置可以通过 junco.config.import 导入 ConfigMap 挂载文件:
junco:
config:
import:
- optional:file:/etc/junco/path.yaml
导入项按声明顺序加载,后面的文件覆盖前面的同名 key。支持 classpath: 和 file:;默认情况下文件不存在会导致应用启动失败,增加 optional: 前缀后仅在文件不存在时跳过,文件存在但格式错误时仍会启动失败。
junco.config.import 适合在应用配置中明确记录外部配置依赖。需要在不修改应用配置的情况下替换整份启动配置时,仍可使用环境变量:
JUNCO_CONFIG_LOCATION=file:/etc/junco/application.yml
配置较多时,使用复数配置项声明多个文件:
JUNCO_CONFIG_LOCATIONS=file:/etc/junco/base.yml,file:/etc/junco/jdbc.yml,file:/etc/junco/redis.yml
多个文件按从左到右的顺序加载,后面的文件覆盖前面的同名 key。JUNCO_CONFIG_LOCATION 保留为单文件兼容入口;环境变量未配置时,也可以使用 JVM 参数:
java -Djunco.config.locations=file:/etc/junco/base.yml,file:/etc/junco/redis.yml -jar app.jar
单复数配置项的选择顺序为:
JUNCO_CONFIG_LOCATIONS
> JUNCO_CONFIG_LOCATION
> -Djunco.config.locations
> -Djunco.config.location
配置值的覆盖优先级从低到高为:
@JuncoProperties.path 默认配置
< classpath application.properties
< classpath application.yml
< classpath application.yaml
< 外部配置文件(按声明顺序,后文件优先)
< junco.config.import(按声明顺序,后文件优先)
< 模块专用配置(如 gateway.routes-location)
< JVM -D 参数
< 环境变量
< 程序显式追加的 PropertySource
@JuncoProperties.path 只参与对应 Bean 的绑定,因此它的优先级低于所有全局配置源。上表按装载顺序列出全局配置源时,可以将它理解为该 Bean 的最前置默认值,而不是独立的全局 PropertySource。
除带 optional: 的配置路径外,显式配置的文件不存在、格式错误或不是 .properties、.yml、.yaml 时,应用启动失败,不会忽略错误继续运行。
Kubernetes ConfigMap 示例:
apiVersion: v1
kind: ConfigMap
metadata:
name: junco-app-config
data:
base.yml: |
http:
port: 9090
redis.yml: |
redis:
address: redis://redis:6379
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: junco-app
spec:
template:
spec:
containers:
- name: junco-app
image: example/junco-app:latest
env:
- name: JUNCO_CONFIG_LOCATIONS
value: file:/etc/junco/base.yml,file:/etc/junco/redis.yml
volumeMounts:
- name: config
mountPath: /etc/junco
readOnly: true
volumes:
- name: config
configMap:
name: junco-app-config
单个 @JuncoProperties 默认文件的覆盖示例:
apiVersion: v1
kind: ConfigMap
metadata:
name: junco-path-config
data:
path.yaml: |
path:
base-url: https://api.example.com
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: junco-app
spec:
template:
spec:
containers:
- name: junco-app
image: example/junco-app:latest
volumeMounts:
- name: path-config
mountPath: /etc/junco
readOnly: true
volumes:
- name: path-config
configMap:
name: junco-path-config
该 Deployment 与前面的 optional:file:/etc/junco/path.yaml 配合使用:本地未挂载文件时使用 classpath 默认值,Kubernetes 中挂载后自动覆盖 PathProperties。
Gateway 路由可以保存在独立文件中。应用主配置通过 gateway.routes-location 声明文件位置,框架会在启用 Gateway 时自动导入;本地开发配置示例:
gateway:
routes-location: classpath:router.yaml
Kubernetes 中将路由文件挂载到容器后,在主配置中直接指向挂载路径:
gateway:
routes-location: file:/etc/junco/router.yaml
gateway.routes-location 未配置时不会额外导入路由文件,此时仍可直接在主配置中声明 gateway.routes。路由文件不存在、格式错误或无法读取时,应用启动失败。
当前外部配置只在应用启动时加载,ConfigMap 内容变化后需要通过 RollingUpdate 重建 Pod 才会重新绑定配置。
应用启动时会输出一条配置源顺序日志,例如:
Junco config sources (low -> high): [application.yml, file:/etc/junco/base.yml, systemProperties, systemEnvironment]
业务代码需要定位单个配置项时,可以注入 Environment 并调用 Environment#explain:
PropertyResolution resolution = environment.explain("jdbc.url");
log.info("source={}, overriddenSources={}, placeholderResolved={}, decoded={}",
resolution.getSource(), resolution.getOverriddenSources(),
resolution.isPlaceholderResolved(), resolution.isDecoded());
诊断结果只包含标准化 key、最终来源、被覆盖来源以及占位符和解码状态,不包含原始值或最终值。框架不提供配置诊断 HTTP 端点,避免数据库地址、密码等配置被意外暴露。
四、Prometheus 接入
应用配置:
app:
name: junco-demo
metrics:
enabled: true
jvm:
enabled: true
tags:
application: ${app.name}
env: prod
Prometheus 抓取配置:
scrape_configs:
- job_name: junco-demo
metrics_path: /-/metrics
static_configs:
- targets:
- junco-demo:9090
指标清单详见 metrics.md。
五、Grafana 看板
Junco 的 JVM、系统、HTTP 指标使用 Micrometer 命名,优先复用 Spring Boot Actuator Prometheus 看板。
常用面板建议:
- JVM 内存:
jvm_memory_used_bytes。 - JVM GC:
jvm_gc_pause_seconds_*。 - Web 业务 HTTP QPS:
http_server_requests_seconds_count,不包含 RPC Provider 内部入口。 - Web 业务 HTTP 延迟:
http_server_requests_seconds_sum/http_server_requests_seconds_count。 - RPC Provider 调用结果:
junco_rpc_server_requests_total。 - RPC Provider 调用延迟:
junco_rpc_server_request_duration_seconds_*。 - 进程 CPU:
process_cpu_usage。 - 系统 CPU:
system_cpu_usage。 - 日志级别:
logback_events_total。 - Web 线程:
junco_web_threads_busy、junco_web_threads_current、junco_web_threads_config_max。
六、健康检查排查
/-/live 失败
/-/live 失败一般表示进程不可达或监听端口异常。
检查顺序:
- 确认容器或进程仍在运行。
- 确认
http.port与探针端口一致。 - 查看启动日志中 Web server 是否成功监听端口。
- 确认容器网络、Service、Ingress 或安全组未拦截。
/-/ready 返回 503
/-/ready 返回 503 表示当前不应接收流量。
常见原因:
- 应用正在关闭,框架已经进入 readiness drain。
- 注册的
HealthContributor返回非 UP。 - JDBC 健康检查失败,例如数据库不可达或 validation query 超时。
检查顺序:
- 查看应用日志中是否有 shutdown、health、jdbc 相关日志。
- 检查数据库、Redis、NATS 等依赖是否可达。
- 检查健康检查配置,例如
jdbc.health.enabled和jdbc.health.validation-query。
七、指标排查
/-/metrics 返回 404
确认配置:
metrics:
enabled: true
metrics.enabled=false 时,框架不会暴露 /-/metrics。
指标缺少 application 标签
确认已配置公共标签:
metrics:
tags:
application: ${app.name}
同时确认 ${app.name} 能解析为非空字符串。空 key 或空 value 会被忽略。
JVM 指标缺失
确认:
metrics:
jvm:
enabled: true
关闭 metrics.jvm.enabled 后,jvm_memory_used_bytes、process_cpu_usage、system_cpu_usage 等 JVM 和系统指标不会注册。
HTTP 指标路由不符合预期
HTTP 指标的 route 标签应使用路由模板,而不是原始 URL。
例如 /api/users/{id},不要记录为 /api/users/1001,避免高基数标签压垮 Prometheus。
八、故障排查手册
请求大量 400
优先检查:
Content-Type是否正确。- JSON body 是否符合接口入参结构。
@PathParam、@QueryParam、@HeaderParam是否能完成类型转换。- multipart 请求是否带了正确的 boundary。
请求大量 404
优先检查:
- Controller 是否被
@JuncoScan扫描到。 - 类和方法上的
@Path拼接后是否符合预期。 - HTTP 方法注解是否正确,例如
@GET、@POST。 - 是否误把
/-/live、/-/ready、/-/metrics放进业务网关重写规则。
请求大量 500
优先检查:
- 业务异常栈。
- 全局
@ExceptionHandler是否覆盖了预期异常。 - 返回对象是否能被 JSON 序列化。
- 依赖服务、数据库、缓存是否可用。
RPC 调用失败
优先检查:
service、method、parameterTypes是否和 provider 暴露的方法一致。- provider 是否启用了
@RpcService并被扫描。 - 请求体是否超过
rpc.request.max-bytes。 - byte[] 内联字段是否超过
rpc.binary.inline.max-bytes。 - 客户端重试和熔断配置是否符合预期。
- 熔断状态按 RPC
service隔离,一个下游服务熔断不会阻断其他服务。
九、生产使用建议
正式生产使用前建议至少完成以下检查。
- 所有服务配置
metrics.tags.application。 - Kubernetes 配置 liveness 和 readiness 探针。
- Prometheus 成功抓取
/-/metrics。 - Grafana 有 JVM、HTTP、线程、日志、RPC 或缓存核心面板。
- 业务异常统一通过
@ExceptionHandler输出稳定结构。 - 依赖数据库的服务启用 JDBC 健康检查。
- 压测确认
http_server_requests_seconds_*与 Web 业务负载一致,junco_rpc_server_*与 RPC Provider 负载一致,junco_web_threads_*与两者共同占用的执行资源一致。